SPEC.md
16,021 bytes
| 1 | # SPEC: VoiceTask |
|---|---|
| 2 | |
| 3 | ## Goal |
| 4 | |
| 5 | A local tool for anyone who thinks out loud about something they want built, technical or not. The user talks about their idea. An AI interviewer conducts a spoken Socratic interview in plain language: one targeted question at a time, driven by a coverage model of what a good spec needs, with no jargon assumed. When coverage is sufficient (or the user says done), the tool writes a spec pack (SPEC.md, PLAN.md, TASKS.md, VERIFICATION.md, HANDOFF.md) into a target project directory, ready to hand to a sandboxed Claude Code session, and lets the user download a zip of that pack (or the raw transcript) to send to whoever is going to build it. Every requirement in the generated spec carries provenance markers pointing to the exact transcript segments it came from, so the user can always tell "I said this" apart from "the AI invented this". |
| 6 | |
| 7 | The first-run experience explains the outcome before asking for setup details. The interview uses familiar controls, shows progress in words as well as color, and ends with an obvious handoff action for a developer, agency, or coding assistant. |
| 8 | |
| 9 | Differentiators over existing tools (verified 2026-07): ChatPRD interviews but has no voice; WhisperCode takes voice but does not interview; Spec Kit / Kiro clarify in text only; no tool provides requirement-to-utterance provenance or closes the loop from a coding agent's blockers back into a voice interview. |
| 10 | |
| 11 | ## Users |
| 12 | |
| 13 | Single user on their own machine. No auth, no multi-tenancy, no accounts. The primary user is not assumed to be a developer. They may be a non-technical founder, product person, or stakeholder describing an idea that someone else (a developer, an agency, a sandboxed coding agent) will build. Anything shown to the user, including coverage category names, interviewer tone, and empty states, is written in plain language, not developer jargon. A technical user who wants direct control (typing an exact path, editing files by hand afterward) is never blocked from doing so. |
| 14 | |
| 15 | ## User stories |
| 16 | |
| 17 | P1 (must have): |
| 18 | - US-1: As a user, I hold a button, talk about my idea, and see my words transcribed into the session. |
| 19 | - US-2: As a user, I am asked one concrete question at a time, in plain language, chosen to fill the biggest gap in the spec, and I can answer by voice or by typing. |
| 20 | - US-3: As a user, I can see which spec areas are covered and which are still open, described in terms I understand without a technical background. |
| 21 | - US-4: As a user, I can generate a spec pack into a project folder and every requirement shows where in my own words it came from. |
| 22 | - US-9: As a user, I can pick or create the project folder by browsing, without knowing what a file path is. |
| 23 | - US-10: As a user, once the spec pack is ready, I can download it as a single zip file, or copy/download the full transcript, so I can send it to whoever is going to build it. |
| 24 | - US-11: As a first-time user, I can understand what VoiceTask does, how the interview works, what I receive, and that my work stays on my computer before I start. |
| 25 | - US-12: As a user, I can click once to start recording and click again to send, while still having the Space key as a hold-to-talk shortcut. |
| 26 | - US-13: As a user, progress is explained in plain words and does not depend on color alone. |
| 27 | - US-14: As a user, when my build brief is ready, I can download it, copy a ready-to-send handoff message, and understand who to send it to. |
| 28 | - US-17: As a first-time user, I can choose between my own API keys and the offline demo inside the app, without editing a file, running a setup script, or restarting the server. |
| 29 | |
| 30 | P2 (should have): |
| 31 | - US-5: As a user, I hear the interviewer's question spoken aloud so I can keep my eyes off the screen. |
| 32 | - US-6: As a user, when I contradict something I said earlier, the interviewer points at both statements and asks which one holds. |
| 33 | - US-7: As a user, I can close the tool and resume the same session later. |
| 34 | - US-15: As a user who found VoiceTask useful, I can copy a short recommendation with the public project link after my own work is complete. |
| 35 | - US-16: As a keyboard or screen-reader user, I can hear new questions and move through dialogs without losing focus. |
| 36 | |
| 37 | P3 (nice to have): |
| 38 | - US-8: As a user, after a sandboxed Claude Code run leaves questions in BLOCKED.md, I can import them and answer them in a new voice round, and the spec pack is regenerated with the answers. |
| 39 | |
| 40 | ## Functional requirements |
| 41 | |
| 42 | Interview session: |
| 43 | - FR-001: The user can create a session with a project name and a target directory path. The session gets a unique id. The target directory can be chosen by browsing the local filesystem (list subfolders, navigate up/down, create a new folder in place) instead of typing a path; a technical user may still type or paste a path directly into the same field. |
| 44 | - FR-002: The client records audio push-to-talk style (press and hold, or click to start/stop) using the browser microphone and sends the recording to the server when released. |
| 45 | - FR-003: The server transcribes audio through the configured STT provider and appends a transcript segment `{id, timestamp, speaker: "user", text}` to the session. Segment ids are sequential (S1, S2, ...). |
| 46 | - FR-004: A text input is always available as a fallback; a typed answer goes through the same pipeline as a transcribed one and also becomes a transcript segment. |
| 47 | - FR-005: After each user answer, the interview engine produces exactly one next question. The question is stored as a transcript segment with `speaker: "interviewer"`. |
| 48 | - FR-006: The engine maintains a coverage state over the nine categories listed in PLAN.md, each rated `missing`, `partial`, or `clear`, updated after every answer. The next question targets the weakest category. Category names shown in the UI are plain-language (e.g. "What's NOT included" rather than "Non-goals"); the underlying category ids in PLAN.md are unchanged. |
| 49 | - FR-007: If a new answer conflicts with an earlier statement, the next question must surface the contradiction, quoting or referencing both segments, instead of a coverage question. |
| 50 | - FR-008: The user can end the interview at any time by clicking Done or by saying/typing "done". If categories are still `missing`, generation proceeds but the UI first shows which categories are missing and asks for confirmation. |
| 51 | - FR-009: Each interviewer question is displayed as text and, when the TTS toggle is on, spoken via the browser `speechSynthesis` API. |
| 52 | - FR-010: The coverage state is visible in the UI at all times and updates after every turn. |
| 53 | |
| 54 | Spec pack generation: |
| 55 | - FR-011: On generate, the server writes `spec/SPEC.md`, `spec/PLAN.md`, `spec/TASKS.md`, `spec/VERIFICATION.md`, `spec/HANDOFF.md`, and `spec/sources.json` into the target directory. If any of these files already exist, generation fails with a clear error unless the request sets `overwrite: true`. |
| 56 | - FR-012: Every functional requirement in the generated SPEC.md ends with one or more provenance markers `[S<n>]` referencing transcript segments. `sources.json` maps each segment id to its text and timestamp. |
| 57 | - FR-013: After generation, the server validates all `[S<n>]` markers against the session transcript. Markers pointing at nonexistent segments are removed and the requirement is suffixed with `[unverified]`. |
| 58 | - FR-014: Every task in the generated TASKS.md includes at least one verification command, and the generated HANDOFF.md contains a ready-to-copy command for launching a Claude Code session against the pack. |
| 59 | |
| 60 | Sharing and export: |
| 61 | - FR-019: Once a spec pack has been generated, the user can download it as a single zip file (SPEC.md, PLAN.md, TASKS.md, VERIFICATION.md, HANDOFF.md, sources.json) without needing filesystem access to the target directory. |
| 62 | - FR-020: At any point in a session, the user can copy the full transcript to the clipboard or download it as a text file, formatted as a readable back-and-forth (not raw JSON). |
| 63 | - FR-021: The home screen explains the product in plain language before the create form. It shows the three-part flow (talk, answer focused questions, send the build brief), a representative requirement with its source marker, what the downloaded pack is for, and that the session stays on the user's computer. |
| 64 | - FR-022: The create form explains why a project name and folder are needed. Folder browsing is the primary path, while direct path entry remains available. |
| 65 | - FR-023: The microphone button supports click to start and click to send. Holding Space outside a form control remains push-to-talk. Visible text and accessible labels always state whether the microphone is idle, recording, sending audio, or waiting for a typed answer. |
| 66 | - FR-024: Coverage progress states how many topics are ready, in progress, and still to discuss. Every category exposes its status in visible text or an accessible label, not color alone. |
| 67 | - FR-025: The completion screen leads with "Your build brief is ready", explains that the zip can be sent to a developer, agency, or coding assistant, and makes the zip download the primary action. The generated file list is secondary detail. |
| 68 | - FR-026: A completed session checks whether a spec pack already exists when opened, so the download action remains available after a refresh. It also offers a copyable handoff message that names the project and points the recipient to HANDOFF.md. |
| 69 | - FR-027: After a successful result, the UI offers a separate, optional action that copies a short VoiceTask recommendation with `https://github.com/rasmusjy/voicetask`. This recommendation is never inserted into the user's transcript or spec pack. |
| 70 | - FR-028: `npm run demo` starts the client and server with deterministic mock providers even when `.env` selects real providers. It never requires or calls a provider API. Automated verification must work on Windows, macOS, and Linux without relying on an OS-specific `unzip` executable. |
| 71 | - FR-029: Spoken questions prefer a browser voice matching `navigator.language`, then the browser's default voice. English is not hardcoded. If the voice list is not ready, browser speech uses `navigator.language` with the default voice. |
| 72 | - FR-030: New interviewer questions are announced through a polite live region. The read-aloud switch has a visible keyboard focus state. The folder dialog moves focus inside when opened, keeps Tab focus inside, closes with Escape, and returns focus to the control that opened it. |
| 73 | |
| 74 | Persistence and resume: |
| 75 | - FR-015: Sessions are persisted to disk after every turn. Opening the app lists existing sessions and lets the user resume one, with full transcript and coverage state restored. |
| 76 | |
| 77 | Blocker loop: |
| 78 | - FR-016: The user can import a `BLOCKED.md` file from the target directory. Each blocker entry becomes an open question; the interview resumes asking only those questions, and a subsequent generate regenerates the pack with the new answers (overwrite implied, previous pack backed up to `spec/backup-<timestamp>/`). |
| 79 | |
| 80 | Modes and safety: |
| 81 | - FR-017: With `MOCK_PROVIDERS=1`, both STT and the LLM are replaced by deterministic mocks and the full flow (record or type, interview, generate) works offline with no API keys. |
| 82 | - FR-018: Provider API keys are read from environment variables only, whether they were set by hand, by `npm run setup`, or by the in-app setup screen, and never appear in logs, session files, or generated output. |
| 83 | |
| 84 | Setup and provider keys: |
| 85 | - FR-031: The server starts even when no provider key is set. It never exits because keys are missing; it reports the unconfigured state and serves the app so setup can finish in the browser. |
| 86 | - FR-032: `GET /api/config` reports the provider mode (`demo`, `real`, or `unconfigured`), whether each key is set, and at most the last four characters of a saved key. Key values are never sent to the client. |
| 87 | - FR-033: `POST /api/config` accepts either the offline demo or one or both provider keys, writes them to the local `.env` while preserving the rest of that file, and applies them to the running server without a restart. It is accepted only from the local app origin. When the server was started with `npm run demo`, keys are saved for the next start, the running process stays on mocks, and the response reports that a restart is needed. |
| 88 | - FR-034: The home screen shows the setup step in place of the create form until a mode is chosen, and shows the current mode in the header with a control to change it at any time. |
| 89 | - FR-035: A request that needs a provider key that is not set fails with a plain-language message pointing at the setup screen, not a generic server error. |
| 90 | |
| 91 | ## Edge cases |
| 92 | |
| 93 | - Empty or unusable audio (silence, too short): the server returns a distinct error and the UI prompts the user to repeat; no empty segment is stored. |
| 94 | - STT provider failure or timeout: error surfaced in the UI, text input remains usable, session state unchanged. |
| 95 | - LLM returns output that does not match the expected schema: retry once; on second failure, surface the error and keep the session at the previous turn. |
| 96 | - Target directory does not exist or is not writable: generation fails with the OS error shown to the user; the session is unaffected. |
| 97 | - "done" as an ordinary word inside a longer answer must not end the interview; only an answer that is exactly "done" (case-insensitive, trimmed) or the Done button ends it. |
| 98 | - Very long sessions: the engine prompt includes at most the last 40 transcript segments verbatim plus a running summary of earlier ones (summary maintained by the engine). |
| 99 | - BLOCKED.md missing or empty on import: informative message, nothing changes. |
| 100 | - Folder browser: a directory the user can't read (permissions) shows an inline error and keeps the browser at the last folder that worked; it never crashes the picker. |
| 101 | - Zip download requested before the spec pack has been generated: a clear "generate the spec pack first" error, no partial download. |
| 102 | - Microphone click while already recording: stop the recording and send exactly one audio blob. A repeated click while the audio is being submitted does nothing. |
| 103 | - Clipboard access denied: keep the download actions available and show a clear copy failure message. |
| 104 | - A completed session opened after a refresh: detect an existing spec pack and show the ready state without regenerating it. |
| 105 | - Browser speech voice list is empty on the first question: speak with the browser default and the browser language instead of failing. |
| 106 | - Only one of the two keys is submitted from the setup screen: the missing one is named in the error and nothing is written unless the other one is already saved. |
| 107 | - The local `.env` cannot be written (permissions, read-only checkout): the setup screen shows the write error and the running mode is unchanged. |
| 108 | |
| 109 | ## Out of scope (v1) |
| 110 | |
| 111 | - Multi-user support, auth, or any cloud deployment. Local only. Sharing means the user downloads a file and sends it themselves (email, Slack, upload), not a hosted link to a session or spec pack. |
| 112 | - Real-time streaming conversation (barge-in, VAD). Push-to-talk only. |
| 113 | - Paid TTS voices. Browser `speechSynthesis` only. |
| 114 | - Running or supervising Claude Code from inside the app. The app only writes files and prints the handoff command. |
| 115 | - Editing the generated spec inside the app. |
| 116 | - Mobile support. |
| 117 | - Any languages other than the one the user speaks in; generated spec pack files are always written in English regardless of interview language. |
| 118 | |
| 119 | ## Assumptions (pre-answered, do not ask) |
| 120 | |
| 121 | - Platform: developed and run on Windows 11, but nothing platform-specific outside standard Node APIs; must also work on macOS/Linux. |
| 122 | - Browser target: current Chrome/Edge (MediaRecorder and speechSynthesis available). No polyfills for other browsers. |
| 123 | - One interview session maps to one target project directory. Multiple sessions may exist side by side. |
| 124 | - The interviewer speaks the language the user uses; spec pack output is English. |
| 125 | - Audio format: whatever MediaRecorder produces by default (webm/opus); the STT provider must accept it. |
| 126 | - No streaming transcription needed; per-utterance transcription latency of 1-3 s is acceptable. |
| 127 | |