# MUST OBEY — Mobile-first build requirements
This app's PRIMARY surface is a mobile phone. Build it impeccably on mobile FIRST, then verify on tablet and desktop. Treat the rules below as non-negotiable hard constraints, not suggestions.
## Viewports to verify (every screen, every state)
- 320 px, 360 px, 375 px, 390 px, 414 px, 480 px
- 768 px, 834 px (iPad portrait / Pro 11)
- 1024 px, 1280 px, 1440 px, 1920 px, 2560 px
- Plus: 200% browser zoom, landscape orientation on every mobile width, iPhone with safe-area insets visible
## Hard layout rules
- Mobile-first CSS. Default styles target mobile; `@media (min-width: ...)` for larger viewports.
- Use `dvh` and `svh` instead of `vh` for full-height surfaces (iOS Safari URL-bar bug).
- Use `clamp()` for fluid typography across all viewports.
- Prefer container queries (`@container`) over media queries for component-level responsiveness.
- Use `min(100%, ...)` widths so content never overflows. Zero horizontal overflow at any viewport.
- Add `` to every page.
- Apply `padding: max(safe-area-inset-X, fallback)` on every edge-bleeding container so notched iPhones in landscape never clip content.
- Wide tables and code blocks scroll INSIDE their container (`overflow-x: auto`), never push the body.
- Use `background-attachment: scroll` on mobile, not `fixed` (iOS Safari repaint bug).
- Avoid `backdrop-filter` on animated elements. Use it sparingly on static surfaces only.
- **Canvas Scaling**: Canvases must dynamically scale with window resize events and properly handle high-DPI screens (`window.devicePixelRatio`). Set physical dimensions (`canvas.width`/`canvas.height`) using pixel ratio and render relative to this grid, using CSS to control responsive viewport scaling.
- **Robust Storage**: Every access to `localStorage`/`sessionStorage` (especially `JSON.parse` of loaded state or writes) MUST be wrapped in a `try-catch` block to handle disabled storage, private browsing mode, quota limits, or corrupted JSON gracefully. Fall back to a robust in-memory object store.
## Touch & accessibility
- Tap targets ≥ 44 × 44 px on touch (Apple HIG). Increase to 48 px under `@media (hover: none) and (pointer: coarse)`.
- All interactive controls reachable by keyboard with a visible focus ring; respect `:focus-visible`.
- Color contrast ≥ 4.5:1 for body text, 3:1 for UI components.
- All images have meaningful `alt`. Decorative images use `alt=""`.
- Respect `prefers-reduced-motion: reduce` — zero animation durations under that query.
- Forms validate inline; error messages are specific, not "Invalid input".
- Modals: focus trap, `Esc` closes, `role="dialog"`, `aria-modal="true"`, focus restored on close.
## Performance bar (Lighthouse mobile, throttled 3G/4G)
- LCP < 2.5 s · INP < 200 ms · CLS < 0.1
- JS bundle gzip < 200 KB mobile-first; lazy-load non-critical screens via `React.lazy` / dynamic imports.
- No render-blocking resources above the fold.
- Images: WebP/AVIF preferred, `loading="lazy"`, explicit `width`/`height` attributes (zero CLS), `srcset` for retina.
- Videos: `preload="metadata"`, low-resolution poster, max 720p mobile fallback. Never autoplay with audio.
- Fonts: `font-display: swap`; preload only the one used above the fold.
- Smooth scroll honoured via CSS `scroll-behavior: smooth` with reduced-motion fallback.
## Pre-ship mobile checklist (the deployer MUST verify before declaring done)
1. Open at 375 px in DevTools — every screen scrolls vertically only; zero horizontal scroll.
2. Browser zoom 200% — layout reflows without overlap.
3. iPhone Safari with the URL bar visible AND landscape — no content under the home indicator; no notch clipping.
4. iPad portrait (768 px) and landscape (1024 px) — no awkward gaps; tablet-specific breakpoints land cleanly.
5. Tap every interactive element with a thumb at real-device size — every target is easy to hit.
6. `prefers-reduced-motion: reduce` — every transition / animation skips cleanly, scroll-behavior becomes instant.
7. Lighthouse mobile score ≥ 90 across all 4 categories.
8. Zero `console.error` and zero CLS shift in real-device testing on a mid-tier Android (e.g. Pixel 6a) and an iPhone SE.
---
The original template starts below. All rules above apply on TOP of whatever this template specifies.
---
# Voice Memo → Polished Doc
## 1. Project
**Voice Memo → Polished Doc** is a single-purpose app for the
moment you walk out the door with an idea in your head, hit record
on your phone, and ramble for five minutes about a pitch, a spec,
a blog post, a status update, or the bones of a Sunday-night
manifesto. Twenty-five seconds after you stop talking, you get
back a structured one-page document — not the literal transcript,
not a rewrite in some AI's voice, but the doc you would have
written if you'd sat at your desk for an hour with the same
thoughts.
The hero capability is **Gemini 3.5 Flash audio reasoning**
(`gemini-3.5-flash`, medium thinking level). Post-I/O 2026, 3.5
Flash beats the previous Pro tier on long-form audio reasoning
and multimodal structured output — exactly this job: hold fifteen
minutes of context, find the user's natural sub-topics, preserve
the user's vocabulary, hedges, and tone, and impose a heading-
and-bullet structure on top without inventing a single sentence.
The single demo that proves the magic: you tap record, talk for
five minutes on a walk about a product pitch you've been turning
over — three rough sections in your head, two side-tangents about
competitors, one half-formed next-step — and twenty-five seconds
after you stop, the app hands back a one-page document with a
clear "what this is" line at the top, three named sections in the
order you actually spoke them, bullets under each section that
come straight from your own words rearranged, your hedges
preserved ("I think", "maybe"), and one suggested next action
drawn from the most concrete thing you said. Every sentence in
the output is a substring of what you recorded — nothing
invented, nothing rephrased into corporate voice.
The same app handles harder cases: a twelve-minute internal spec
where the user goes deep on edge cases and forgets they mentioned
them three minutes earlier (the long-context model knits the
related fragments under one heading); a walking blog draft told
as three small anecdotes in no particular order (the model groups
them and proposes — but does not impose — a section order); a
weekly status update recorded in the parking lot at 5:55 pm (the
model finds the three updates worth keeping and the one decision
the user wants from their manager).
Then the post-I/O 2026 Workspace integration drops the doc
straight into the user's Drive as a real Google Doc with no OAuth
handshake. Tap once, doc opens in a new tab in Docs, editing
cursor on the title line, user picks up where the model left off.
**Tagline:** _Ramble into your phone on a walk. Get back the doc
you would have written with an hour of focus — in your own words,
in your Drive, in twenty-five seconds._
## 2. Target audience
- Founders and product people who think out loud on walks and
lose the thread the moment they sit at a laptop
- Managers writing weekly status updates from the parking lot or
school pickup line, who keep meaning to draft them and never do
- Writers and journalists capturing the bones of a piece before
the shape escapes — interview notes, draft outlines, blog seeds
- Consultants and salespeople walking out of a client meeting
needing a tidy follow-up email before they reach the car
- Engineers and PMs narrating a spec on the dog walk — structure
lives in their head; the doc just has to catch it
- Designers narrating a critique walkthrough — liked, would
change, one specific next step
- People with ADHD or dyslexia who can talk a clean paragraph in
thirty seconds but can't type one in five minutes
- Non-native English speakers who think more fluidly in speech
- Multilingual users who code-switch mid-thought ("le KPI on this
one is engagement, but the funnel is leaking quelque part")
- Anyone whose best thinking happens between meetings — on a
treadmill, on a bus, on a flight in the middle seat
## 3. Core value propositions
Surface these clearly through copy, visual emphasis, and section
ordering — they are the reasons users pick this app.
- **Editor, not author.** Every sentence in the output is a
substring of what you said, rearranged and de-rambled. The
model can drop filler ("um", "you know"), reorder sentences
into thematic groups, and promote a phrase to a heading — but
it cannot generate prose you didn't speak. This is the load-
bearing constraint of the product; every other capability
serves it.
- **Your voice, preserved.** Hedges stay ("I think", "maybe").
Vocabulary stays — if you call it a "moat" the doc says "moat",
not "competitive advantage". Tonal markers stay. The model is
your editor, not your ghostwriter.
- **Structure imposed honestly.** The model finds your natural
sub-topics (typically 2-4 for a 5-min memo, 3-6 for a 15-min
one), promotes your own segue phrases to headings ("so the
thing about pricing" → **Pricing**), and re-groups sentences
thematically. Headings are the model's contribution; everything
under them is yours.
- **One suggested next action.** The model proposes exactly one
next step, drawn verbatim from the most concrete actionable
phrase in the recording. If you said "I should send this to
Jamie" the next action is "Send to Jamie". If you said nothing
concrete the model says so honestly and the field is null.
- **Twenty-five seconds, doc in your Drive.** Post-I/O 2026, AI
Studio Build apps write directly to a signed-in user's
Workspace without a separate OAuth handshake. Tap "Open in
Docs" — you're editing in your own Drive folder. *(The
no-OAuth Workspace integration is rolling out to AI Studio
Build accounts; if it isn't available on the deploying
account yet, the app falls back to a one-time Drive OAuth
consent flow.)*
- **Multilingual and code-switching.** Talk in any of 40+
languages — English, Spanish, French, Portuguese, German,
Italian, Polish, Czech, Mandarin, Cantonese, Korean, Japanese,
Tagalog, Vietnamese, Hindi, Tamil, Bengali, Arabic, Hebrew,
Farsi, Amharic, Swahili, Khmer, Thai, and more. Switch mid-
sentence; the doc captures both.
- **Privacy as a default.** Voice memo and doc are private to
your account. The Gemini API is used on the paid tier, where
Google does not use your content for model training, per the
Gemini API Additional Terms. The voice memo is deleted from the
server 24 hours after the doc is generated (configurable).
- **No invented prose, ever.** A small audit panel beside the doc
lets you tap any sentence to see the exact timestamp range it
came from. If a sentence cannot be traced to your audio, the
system rejects the draft and re-requests it. Trust contract.
## 4. Features to build
- Voice capture (mobile-first) — one big record button on the
home screen; live waveform with subtle gain meter; up to 15
minutes of audio per memo
- Background recording — keeps recording when the screen locks
via Web Audio MediaRecorder + Wake Lock; iOS Safari users get
a "keep screen on" hint up front (background recording is
constrained on iOS)
- Memo → structured doc — the hero call: voice memo in,
structured doc out, ~25 seconds end-to-end
- One-page doc renderer — title, "what this is" line, 3-6
sections with H2 headings, bullets or short paragraphs, one
suggested next action at the bottom
- Verbatim audit panel — every sentence in the output is tied to
its source timestamp; tap a sentence to play that exact slice
- Hedge preservation toggle (default ON), suggested-action toggle
(default ON), length preference (brief / one-page / long),
audience preference (personal notes / for my team / external /
future me)
- Open in Google Docs — one-tap Workspace export (post-I/O 2026,
no OAuth handshake); doc lands in a "Voice Memos" Drive folder
- Markdown copy-to-clipboard; mailto export to system mail
composer
- Memo library — chronological feed of past memos with their
docs; search by date, title, or full-text
- Editable doc title (model proposes from opening phrase)
- Voice playback with scrub bar showing the section boundaries
the model found
- Live re-process — flip a setting, tap "regenerate", get a new
doc in ~10s with a one-line diff chip noting what changed
- Output language translation — generate the doc in a target
language different from the recording (e.g. ramble in Tagalog,
doc lands in English); each translated sentence flagged with a
"translated" pill and a "see original" toggle
- Drive folder picker (default "Voice Memos")
- Settings — retention window (immediate / 24h / 7 days /
forever), default language hint, default toggles
- Account & privacy — Google sign-in required for Workspace;
explicit "delete this memo" per-memo and "delete my account"
globally
## 4b. Required Gemini capabilities + backend services
**This template's intelligence comes from the Gemini capabilities
below. Wire them up explicitly — don't substitute generic LLM calls.**
### Gemini capabilities (the load-bearing intelligence)
- **Voice memo → structured doc** (`gemini-3.5-flash`, multimodal
audio input, thinkingLevel `medium`) — the hero call. Accepts
audio up to 15 min + a user-preferences JSON block; emits a
`PolishedDoc`: title, "what this is" line, sections (heading +
bullets/paragraph), one suggested next action, and an audit
array tying every sentence to a timestamp range. Post-I/O
2026, 3.5 Flash outperforms 3.1 Pro on long-form multimodal
reasoning at ~4× speed. Pin `gemini-3.5-flash` explicitly; do
not let the agent downshift to `gemini-3.1-flash` — structure
quality drops sharply.
- **Verbatim-substring validator** (server-side, no Gemini
call) — every sentence in the doc is validated against the
verbatim transcript: contiguous substring (modulo whitespace
/ punctuation), OR a 2-3-fragment concatenation joined by one
of {"and", "but", "so", "then", "because"}. Failure → reject
and re-issue. Trust contract enforced in code.
- **Long context** (`gemini-3.5-flash`, 1M tokens) — a 15-min
memo is ~1,800-2,400 words ≈ ~3,500-5,000 tokens; well within
budget. No chunking needed for v1.
- **Structured output** — `PolishedDoc` via `responseSchema`;
Zod converted to Gemini `Schema` via SDK helper; numeric
constraints documentation-only (server clamps).
- **Title proposal** — inside the hero call; drawn from the
opening phrase or most-frequent noun phrase. Never invented.
- **Diff chip on regenerate** — server-side diff of two
`PolishedDoc` objects; no extra Gemini call ("Hedges trimmed:
7 → 2.").
- **Output translation** (`gemini-3.5-flash`, `low`) — second
call when output language differs. Audit timestamps stay tied
to the original-language transcript. Translated bullets show
a "translated" pill + "see original" toggle.
- **Hero / empty-state illustration** (`gemini-3-pro-image`
Nano Banana Pro) — generated once per install, cached.
- **Thinking levels** — `medium` hero (audio + sub-topics +
substring rule simultaneously); `low` translation; `high`
unused.
### Backend services
- **Auth — Required.** Firebase Auth + Google sign-in (auto-
provisioned). Workspace export (post-I/O 2026 no-OAuth) reuses
the same Google identity for Drive write **when the AI Studio
Build Workspace integration is enabled on the deploying
account — this is rolling out and may not be on yet**; the
app falls back to a standard Drive OAuth consent prompt the
first time the user taps "Open in Docs". Apple sign-in
offered as secondary; flagged in onboarding as "no one-tap
Docs export with Apple".
- **Database — Required.** Firestore for `users`, `memos`,
`docs`, `settings`. Auto-provisioned.
- **File storage — Required.** Firebase Storage for voice memos
during processing + retention window. **NOT auto-provisioned
today** — enable in Firebase console and wire the bucket name
before first upload. Pre-signed URLs only; default 24-hour
retention after doc generation (configurable to immediate /
7 days / forever).
- **Email — Not required for v1.** "Email it" uses `mailto:`.
- **Payments — Not needed for v1.** Cloud Run free tier + Gemini
API. A future pro tier might add Stripe; not in v1.
- **External APIs:** Gemini API; Google Drive via post-I/O 2026
Workspace integration (no separate OAuth wiring).
- **Cloud Run** — server functions for all Gemini calls (key
never leaves server). Auto-provisioned; first 2 apps deploy
free.
**Environment variables:** every secret (Gemini API key, Firebase
service-account JSON, optional Stripe key for future pro tier)
lives in environment variables — never in client bundle. Include
a `.env.example`.
**Auth + data privacy reminders:** never log secrets · never store
passwords in plain text · use HTTPS everywhere · honour 'delete my
account' inside the UI · explicit opt-in for any analytics · the
user's voice memos and generated docs are never sent to Gemini for
model training (use the Gemini API on the paid tier, where Google
does not use your content for model training, per the Gemini API
Additional Terms) · the voice memo is purged from Firebase Storage
24 hours after the doc is generated by default (configurable).
**Read this first — prompt-craft rules that apply to every call in
this template:**
1. **Name the model variant explicitly** in every Gemini API call.
Do not let the agent pick the model. See the per-call matrix
below. The post-I/O 2026 default is `gemini-3.5-flash`; the
pre-I/O strings (`gemini-3.5-flash`, `gemini-3.5-flash`,
`gemini-3.1-flash-image`) now resolve to deprecated/missing
endpoints. Use the IDs in the matrix verbatim.
2. **Pin `thinkingLevel` explicitly** per call. See the matrix.
Models that don't take `thinkingConfig` (image-gen, TTS)
should have that field omitted entirely.
3. **Seed the JSON Schema as a fenced TypeScript / Zod block** in
the system instruction or `responseSchema` field. The literal
schemas are below. **Convert the Zod schema to Gemini's
`Schema` type via the SDK helper** before passing to
`responseSchema` — do NOT pass raw Zod. **Numeric `min`/`max`
constraints are documentation only inside `responseSchema`;
clamp on the server after the response arrives.**
4. **Pin the system instruction separately** from user input. Use
the `systemInstruction` field for persona + behavioural rules;
use `contents` for the audio. Never concatenate.
5. **Pre-declare tools as an enable/disable list** per call. This
template uses no tools on the hero call (no `google_search`,
no `code_execution`). Tools should be explicitly disabled.
6. **State negative constraints explicitly** — they are listed
below. They are NOT "be careful" suggestions; they are hard
rules the model must follow, AND the server enforces the
verbatim-substring rule independently of the model's
compliance.
7. **Files API uses `files/*` resource names, not `gs://` URIs.**
The AI Studio Build runtime uses the Gemini Developer API
(`@google/genai` SDK). Files API `upload` returns a resource
name of the form `files/abc123xyz`, which you pass via
`fileData: { fileUri: "files/abc123xyz", mimeType }`. `gs://`
URIs belong to Vertex AI / Cloud Storage — a different surface.
8. **Strip unsupported Zod modifiers before passing to
`responseSchema`** — Gemini's OpenAPI subset rejects `.regex()`
/ `pattern`, fixed-length `z.tuple()`, and other custom
validators. Use a sanitizer that flattens tuples to arrays and
removes regex patterns before serializing. Validate those
constraints in middleware AFTER parsing.
### Per-call model + tools matrix
| Call | Model | thinkingLevel | Tools enabled |
|------|-------|---------------|---------------|
| Voice memo → `PolishedDoc` (hero) | `gemini-3.5-flash` | medium | (none) |
| Output translation (target lang ≠ source lang) | `gemini-3.5-flash` | low | (none) |
| Hero + empty-state illustration | `gemini-3-pro-image` | n/a | n/a |
*Note for builders:* on the image-generation call, omit
`thinkingConfig` entirely — the field is not supported on
`gemini-3-pro-image`. The `n/a` cells in this matrix are
documentation only; do not serialise them into the request body.
The hero call does NOT use `google_search` grounding — there's no
external fact-checking happening here; the model's job is to
structure what the user said, not to verify it. Keep the tool list
empty on the hero call so the model can't add citations the user
didn't ask for.
### Primary structured-output schema (seed verbatim in the prompt)
```typescript
import { z } from "zod";
const SentenceWithAudit = z.object({
text: z.string(), // the sentence as it appears in the doc
source_timestamp_start_seconds: z.number(), // start of the verbatim source in the audio
source_timestamp_end_seconds: z.number(), // end of the verbatim source in the audio
source_substring: z.string(), // the exact substring of the transcript this sentence is built from
connectors_used: z.array(z.enum([
"and", "but", "so", "then", "because",
])), // empty if the sentence is a pure substring; non-empty if joined fragments
});
const DocSection = z.object({
heading: z.string(), // the model's contribution — a heading drawn from the user's own segue phrase
heading_source_substring: z.string().nullable(), // the user's segue phrase the heading was drawn from; null if model paraphrased
body: z.array(SentenceWithAudit), // bullets or short paragraphs, each one tied to source audio
display_style: z.enum(["bullets", "paragraph"]), // bullets for crisp lists, paragraph for narrative
});
const SuggestedAction = z.object({
action_text: z.string(), // the suggested next action, drawn verbatim from a concrete phrase
source_timestamp_start_seconds: z.number(),
source_timestamp_end_seconds: z.number(),
source_substring: z.string(), // the exact concrete phrase from the recording
confidence: z.number().min(0).max(1), // 0.0 if no concrete action was found and the field is being omitted
});
const PolishedDoc = z.object({
doc_id: z.string(),
memo_id: z.string(),
generated_at_iso: z.string(),
// Metadata
recording_duration_seconds: z.number(),
dominant_language_bcp47: z.string(), // e.g. "en-US", "es-MX", "tl-PH"
code_switched_languages: z.array(z.string()), // additional BCP-47 tags if the user code-switched
output_language_bcp47: z.string(), // usually matches dominant_language; differs only if translation was requested
// The doc itself
title: z.string(), // drawn from the opening phrase or most-frequent noun phrase
title_source_substring: z.string(), // verbatim substring the title was drawn from
what_this_is: SentenceWithAudit, // the one-line "what this is" at the top of the doc
sections: z.array(DocSection).min(1).max(8), // typically 2-4 for a 5-min memo, 3-6 for a 15-min memo
suggested_action: SuggestedAction.nullable(), // nullable when no concrete action surfaced
// Verbatim transcript (used by the server validator and the audit panel)
verbatim_transcript: z.string(), // the full transcript with timestamps embedded
hedge_phrases_preserved: z.array(z.string()), // ["I think", "maybe", "honestly"] — what the model kept
hedge_phrases_dropped: z.array(z.string()), // ["um", "you know", "like"] — what the model dropped as filler
// Self-reported quality signals
parse_confidence: z.number().min(0).max(1),
audit_pass: z.boolean(), // true if model believes every sentence is a verbatim substring (server re-verifies)
flagged_for_user_review: z.array(z.object({
field_path: z.string(),
reason: z.string(),
})),
});
type PolishedDoc = z.infer;
```
### Common failure modes (and how to avoid them)
- Agent picks `gemini-3.1-flash` to save quota — pin
`gemini-3.5-flash` explicitly. Older Flash tiers transcribe
fine but fall down on the structure + verbatim-substring
constraint simultaneously, often inventing smoothing phrases.
- Agent calls the pre-I/O ID `gemini-3.5-flash` — deprecated post-
I/O 2026. `gemini-3.5-flash` is not yet GA (June 2026); do NOT
wire it. Default is `gemini-3.5-flash`.
- Model invents a smoothing phrase ("In conclusion, the next
step is...") — the server-side verbatim validator rejects the
draft and re-issues with a tightened instruction. After two
rejections, the server falls back to a barer output (raw
transcript chunks under the model's headings).
- Model rewrites hedges into corporate voice ("I think we
should…" → "Our recommendation is…") — forbidden in system
instruction; validator catches it because the corporate
phrasing isn't a substring of the transcript.
- Headings invented, not drawn from segues — system instruction
requires `heading_source_substring`. When the model can't find
a segue, paraphrased headings are allowed but the UI surfaces
a "paraphrased" pill.
- Output too long — the server clamps to `length_preference`
ranges; >130% of target triggers a re-issue with a tighter
instruction.
- Audio fails to upload — client retries with backoff; on full
failure the audio is kept locally and the user sees a "queued"
state.
- Audio too noisy — `parse_confidence` drops below 0.6; UI
surfaces "we had trouble hearing parts of this".
- Translation introduces invented prose — translated bullets may
be re-phrased (that's what translation is) but the source
substrings still tie back to audio timestamps; the UI flags
translated sentences clearly.
- Suggested action invented from nothing — the server rejects
any `suggested_action` whose `source_substring` is not in the
transcript. Returning null is correct when no action was
spoken.
- Filler vs epistemic hedges — filler dropped (um, uh, you know,
like, sort of, basically), epistemic preserved (I think,
maybe, honestly, probably, I'm not sure).
- Purely reflective memo with no sub-topics — model returns one
section called "Reflections" with the flow lightly grouped.
Does not force fake sections.
- User closes app before Workspace export → doc waits in library
with a "ready to open" chip; export is non-destructive and
idempotent.
- User signs in with Apple — Workspace export greyed out;
Markdown / email / clipboard still work.
- Sensitive content — the model treats every memo as private and
does not editorialise. The privacy banner in onboarding covers
the policy.
### Negative constraints (hard rules)
- Do NOT invent prose. Every sentence in the doc must trace to a
verbatim substring (or a concatenation of substrings with one
of the five allowed connectors) of the transcript. Server
validates independently; the model cannot opt out.
- Do NOT rewrite the user's voice into corporate English. If the
user said "this is kinda cool, ngl", the doc says exactly
that. Tonal markers carry information.
- Do NOT add a conclusion, TL;DR, or "key takeaways" unless the
user said it themselves.
- Do NOT invent a suggested action when none was spoken;
`suggested_action: null` is correct.
- Do NOT translate technical vocabulary, brand names, or proper
nouns. "KPI" stays "KPI"; "Slack" stays "Slack".
- Do NOT moralise about the memo's content. The user is the
editor of their own voice.
- Do NOT exceed the `length_preference` word range. Long memos
get tighter selection, not longer output.
- Do NOT auto-publish anywhere except the user's own Drive on an
explicit "Open in Docs" tap. No silent background uploads.
- Do NOT use voice memos or docs to train any model. Use the
Gemini API on the paid tier, where Google does not use your
content for model training, per the Gemini API Additional
Terms.
- Do NOT show a "Powered by AI" badge. The intelligence is in
the experience.
- Do NOT retain voice memos beyond the user's configured window.
Server enforces with a Cloud Scheduler purge job.
### Per-call `systemInstruction` strings
Use these as the literal `systemInstruction` field for each Gemini
API call the built app makes. They complement the series-wide rules
already uploaded as the global instructions file
(`00-series-instructions.txt`).
### Call: Voice memo → `PolishedDoc` (hero)
Model: `gemini-3.5-flash` · thinkingLevel: medium · Tools: (none)
```
You are an EDITOR, not an author. You receive a voice memo (30 s
to 15 min) recorded by one user — on a walk, in a car, at a
kitchen counter. The user wants back a structured one-page
document that reads like the doc they would have written with an
hour of focus — but in their own words, with their own hedges.
You are NOT a ghostwriter, summariser, or generator of new prose.
Your job:
1. Transcribe the audio verbatim with timestamps.
2. Find the user's natural sub-topics (typically 2-6).
3. Group sentences thematically under each sub-topic.
4. Promote one of the user's segue phrases to a heading per
section ("so the thing about pricing" → "Pricing"; "and the
other piece is hiring" → "Hiring").
5. Drop filler hedges ("um", "uh", "you know", "like", "sort
of", "basically", "literally", "anyway") when they carry no
information.
6. PRESERVE epistemic hedges ("I think", "maybe", "might be",
"honestly", "probably", "I'm not sure", "I guess") — they
signal confidence.
7. PRESERVE the user's vocabulary, brand names, jargon, slang,
tonal markers, and any code-switched phrases.
8. Identify ONE suggested next action if and only if the user
said something concrete and actionable. Otherwise return
suggested_action: null. Do NOT invent an action.
EVERY SENTENCE IN THE DOC MUST BE A VERBATIM SUBSTRING of the
transcript, OR a concatenation of 2-3 contiguous substrings
joined with a single connector word from this set: {"and",
"but", "so", "then", "because"}. The server will verify this
independently; failing output will be rejected.
You may rearrange sentences across the recording. Group
thematically. The source_timestamp_start/end_seconds fields must
point to the exact moment in the recording each sentence came
from.
Inputs:
- The audio file (Files API `files/*` resource name via `fileData.fileUri`, or inlineData).
- A JSON block of user preferences:
- hedge_preservation: "preserve" | "trim" (default "preserve")
- suggested_action: "on" | "off" (default "on")
- length_preference: "brief" | "one_page" | "long" (default "one_page")
- audience: "personal_notes" | "for_my_team" | "for_external" | "for_future_me"
- output_language_bcp47: a BCP-47 tag or "same_as_audio"
- dominant_language_hint: a BCP-47 tag if user set a default
Word-count targets:
- "brief": 150-250 words; "one_page": 300-600; "long": no cap.
Languages encountered include 40+ tags across English, Spanish,
Portuguese, French, German, Italian, Polish, Czech, Mandarin,
Cantonese, Korean, Japanese, Vietnamese, Tagalog, Hindi, Tamil,
Bengali, Arabic, Hebrew, Farsi, Amharic, Swahili, Khmer, Thai —
and code-switching mid-sentence. Set dominant_language_bcp47 to
the majority language; populate code_switched_languages with the
rest.
Hard rules:
- Do NOT invent prose. Server validates the substring rule.
- Do NOT rewrite the user's voice.
- Do NOT add conclusion, TL;DR, or takeaways unless spoken.
- Do NOT translate jargon, brand names, or proper nouns.
- Do NOT moralise about the memo's content.
- Do NOT include a "Powered by Gemini" footer in the doc.
- If hedge_preservation="trim", drop epistemic hedges too.
- If suggested_action="off" or no concrete action was spoken,
return suggested_action: null.
- If audio is too noisy, set parse_confidence < 0.6 and flag
"verbatim_transcript" in flagged_for_user_review. Still
attempt the structured doc on what's audible.
- If memo is purely reflective with no sub-topics, return one
section called "Reflections" — don't force fake sections.
- audit_pass: true only if you believe every sentence satisfies
the substring rule. Server re-verifies regardless.
Output ONLY the PolishedDoc JSON. No commentary. JSON only.
```
---
### Call: Output translation (only when output_language ≠ dominant_language)
Model: `gemini-3.5-flash` · thinkingLevel: low · Tools: (none)
```
You receive a PolishedDoc already produced in the recording's
dominant language. Translate the user-facing text fields into the
target language while keeping the audit fields (timestamps,
source_substring) pointing at the ORIGINAL audio in its ORIGINAL
language.
Translate: title, what_this_is.text, sections[].heading,
sections[].body[].text, suggested_action.action_text.
Preserve verbatim (do not translate): doc_id, memo_id,
generated_at_iso, dominant_language_bcp47, recording duration,
code_switched_languages, verbatim_transcript, all
source_timestamp_* fields, all source_substring fields, hedge
lists, parse_confidence, audit_pass, flagged_for_user_review.
Update output_language_bcp47 to the requested target tag.
Hard rules:
- Do NOT translate brand names, product names, technical
vocabulary, or proper nouns. Apple stays Apple. KPI stays KPI.
- Preserve hedges in translation. "I think" → "Creo que" / "Je
pense que" / "Acho que".
- Preserve tone. Casual translates casual; formal translates
formal.
- Set connectors_used to ["translated"] on every
SentenceWithAudit so the UI shows the "translated" pill and
"see original" toggle.
Output: the modified PolishedDoc JSON. No commentary.
```
---
### Call: Hero / empty-state illustration generation
Model: `gemini-3-pro-image` (Nano Banana Pro) · n/a · n/a
```
Generate a single photographic-looking image for the welcome
screen or an empty state. Mood: the quiet moment a user takes out
their phone to record a thought — on a walk, at a kitchen counter
at 7am, in a parked car before a meeting.
Prompt anchors:
- "a smartphone leaning against a white ceramic coffee mug on a
wooden kitchen counter, morning light from a window above, the
phone screen showing a single voice-memo waveform, no people,
shallow depth of field"
- "tree-lined sidewalk at golden hour, a sneaker mid-step at the
bottom of the frame, in-ear headphone at the top edge, soft
warm light, no faces"
- "open notebook on a desk beside a steaming mug, phone face-up
with a red recording dot visible, soft afternoon light, no
people"
Hard rules:
- Photographic, not illustration. Slightly imperfect; not glossy.
- No people unless the prompt asks for a hand or a sneaker.
- No commercial branding (no real phone-maker logos, no coffee-
chain cups, no app logos).
- Aspect ratios: 3:2 hero, 1:1 empty state.
- Use Nano Banana Pro's 4K legible-text capability for the
on-screen "Voice Memo" label; it's the one place text matters.
```
## 5. Use cases & content to include
Build dedicated UI sections or flows for each of these — they tell
you what content the app must support.
- **The founder pitch on a Saturday walk.** Six minutes about a
product pitch — problem, wedge, three thoughts on pricing, two
competitors, one half-formed next step ("I should probably
send this to Priya"). Doc comes back as four sections with
**Send to Priya before Monday** as the suggested action.
- **The weekly status update from the parking lot.** A senior
engineer records four minutes at 5:55 pm. Doc lands as **This
week**, **Blocker**, **Need from you** with "push the Auth
refactor by a week, can you confirm before standup?" as the
suggested action.
- **The technical spec narrated on the dog walk.** Twelve
minutes; the model finds five sections — **What this is**,
**User flow**, **Edge cases**, **What we're not doing in v1**,
**Open questions** — and preserves the PM's "honestly" and "I
think" markers because they signal firm vs tentative.
- **The blog draft on the morning run.** Three small anecdotes
in no particular order plus a theme that emerges late; the
model groups them, drops one as a likely **Opening**, pulls
the theme to its own section. Proposes; doesn't impose.
- **The interview debrief from the rental car.** Three minutes;
doc lands as **Quotes to keep**, **Surprising direction**,
**To verify**.
- **The reflective Sunday-night memo.** Ten minutes about how
the past week felt; no concrete action; suggested_action is
null and the UI says "no next action — this was a reflective
memo".
- **The bilingual product memo.** French-Canadian founder
switching between French and English: "le KPI on this one is
engagement, but the funnel is leaking quelque part". The doc
preserves the code-switching by default.
- **The Tagalog memo translated to English.** Nine minutes in
Tagalog about a campaign launch for a US team; output_language
set to English. Each translated bullet has a "see original"
toggle that reveals the Tagalog source verbatim.
- **The walked-and-stopped memo.** Forty seconds of construction
noise mid-walk; the model handles the gap cleanly — no
hallucinated content fills the silence, the noisy patch is
flagged low-confidence.
- **The "send to Docs" moment.** User taps **Open in Docs**;
doc lands in the Drive "Voice Memos" folder, opens in a new
tab with the editing cursor on the title. No OAuth popup.
This is the moment the app earns its keep.
## 6. Page structure
Build the following screens / sections in this order. Adjust copy
to fit the voice, but keep the structural intent.
1. **Welcome / sign-in.** Photographed hero of a phone leaning
against a coffee mug at morning light, screen showing a
voice-memo waveform. One paragraph: "Ramble into your phone
on a walk. Get back the doc you would have written with an
hour of focus — in your own words, in your Drive, in
twenty-five seconds." Primary CTA: Google sign-in (required
for Workspace export). Secondary: Apple sign-in (with a clear
"Workspace export not available with Apple" note). Below:
"Try with a sample memo".
2. **Home — the big record button.** Centred large red record
button (≥ 96 × 96 px). Above it: "What's on your mind?".
Below: strip of the most recent three memos and docs.
3. **Recording flow.** Tap record → live waveform with gain
meter, elapsed time top-right, big stop button. Wake Lock
requested up front; recording continues when screen locks
(where the platform allows).
4. **Processing.** Waveform minimised at top, an estimated-time
counter, and a rotating five-phrase status: "Hearing your
memo" → "Finding your sub-topics" → "Pulling your verbatim
sentences" → "Building the doc" → "Almost there".
5. **Doc view.** Editable inline title; "what this is" line in
smaller serif; sections with H2 headings + bullets or
paragraph; suggested-action chip at bottom (green if present,
omitted if null). Side (or below, on mobile): audit panel —
collapsible drawer showing the verbatim transcript with
timestamps. Tapping a sentence highlights the source range
and offers a small play button.
6. **Doc actions.** Chip row: **Open in Docs** (green), **Copy
as Markdown**, **Email it**, **Regenerate** (panel for hedge
/ action / length / audience tweaks).
7. **Memo library.** Chronological list with title, date,
duration, language tag, waveform thumbnail.
8. **Settings.** Retention picker (immediate / 24 h / 7 days /
forever), default toggles, default Drive folder. Account:
delete (60-s cool-off + explicit confirmation).
9. **Capabilities info panel.** From the persistent `(i)` icon
— see 6c.
10. **Footer.** "Your memos and docs are yours. We never train
on them."
## 6b. First-visit onboarding
Show a **first-visit onboarding** on first land (localStorage
flag; do not auto-reshow). Three slides, dismissible. Persistent
re-entry: a `?` icon in header.
**Slide 1 — What this is.**
- Headline: "Welcome to Voice Memo → Polished Doc."
- Subhead: "Ramble into your phone on a walk. Get back the doc
you would have written with an hour of focus — in your own
words, in your Drive, in twenty-five seconds."
- ≤ 60-word paragraph on the editor-not-author contract: every
sentence is a substring of what you said, rearranged and
de-rambled. Your hedges stay. Your vocabulary stays.
- Visual: annotated illustration of a doc with arrows pointing
at headings ("the model's contribution") and bullets ("your
words, rearranged").
**Slide 2 — Try it now.**
- CTA: "Try with the sample memo" — pre-loads the 5-min founder
pitch from 8a.
- 1-2 sentences pointing at the magic moments: heading derivation
from segue phrases, verbatim audit panel, one-tap Open in Docs.
**Slide 3 — How to remix this.**
- Headline: "Make this yours."
- Three bullets:
- Adjust prompts in `/server/prompts/` to fit your profession.
- Swap the sample memo in `/data/seed-memo/` for your own.
- Wire Gemini API key and Firebase project via env-var list.
- Primary CTA: "Use this template" → AI Studio Build remix.
- Secondary: "Just exploring — close".
**Accessibility:** focus trap, `Esc` closes, `role="dialog"`,
`aria-modal="true"`, `aria-labelledby`, restored focus.
`prefers-reduced-motion` respected.
**Don't:** gate content beneath the modal; auto-reshow on return;
include unrelated CTAs (newsletter, social).
## 6c. Capabilities info button (persistent in header)
Add a persistent `(i)` icon in the top-right of the header (next
to the primary nav). Click → opens a modal/panel titled **"What
powers this app"**.
**Panel contents (in this order):**
**Gemini capabilities used (the hero list):**
- **Gemini 3.5 Flash (multimodal audio + structured output)** —
the post-I/O 2026 default. Listens in any of 40+ languages,
transcribes verbatim with timestamps, finds sub-topics, emits
PolishedDoc JSON whose every sentence is a verbatim substring
of what you said. Beats the previous Pro tier on long-form
audio reasoning at ~4× the speed. $1.50/$9.00 per 1M tokens;
cached input $0.15/M.
- **Server-side verbatim validator** — checks every sentence
against the transcript. Failure → reject and re-request.
Trust contract enforced in code.
- **Gemini 3.5 Flash (translation pass)** — second call when
output language differs from recording.
- **Nano Banana Pro (`gemini-3-pro-image`)** — photographic
welcome and empty-state imagery; legible on-screen text at 4K.
- **Workspace integration (post-I/O 2026, preview — still rolling
out to AIS Build accounts)** — one-tap Open in Docs to your Drive,
no separate OAuth handshake. If the capability isn't enabled on a
user's account yet, fall back to a download link.
- **Firebase Auth** — Google sign-in (required for Workspace),
Apple sign-in (no Workspace export).
- **Firestore** — memos and docs, syncs across devices.
- **Firebase Storage** — voice memos for the retention window;
pre-signed URLs only.
- **Cost note** — see 6d. ~$0.011 per 5-min memo;
~$0.025 per 15-min memo.
- **Privacy note** — voice memos and docs private to your
account. Gemini API paid tier, no training on your content
per the Additional Terms. Memos purged at your configured
retention.
**Backend services this app depends on:**
- Auth, Database, Storage, Workspace — see section 4b. Storage
REQUIRES manual enable in Firebase console; AIS Build does
not auto-provision it today.
- Email, Payments: not used in v1.
**Environment variables:**
- `GEMINI_API_KEY`, `FIREBASE_PROJECT_ID`,
`FIREBASE_SERVICE_ACCOUNT`, `FIREBASE_STORAGE_BUCKET`
**Documentation links:** AI Studio Build docs; Gemini API
multimodal audio + structured output (`gemini-3.5-flash`);
Workspace integration; Firebase Auth / Firestore / Storage.
**Accessibility:** same standards as onboarding modal — focus
trap, `Esc`, ARIA, restored focus.
**Behaviour:** always available; tooltip "How this app is
built"; mobile = full-screen sheet sliding up; never hand-wave
the model name.
## 6d. Detailed cost breakdown (deployer reads this BEFORE shipping)
- **Voice memo → PolishedDoc (Gemini 3.5 Flash, medium)** —
$1.50/$9.00 per 1M tokens; audio at ~32 input tokens / second.
A 5-min (300 s) memo ≈ 9,600 audio tokens + ~600 sys-instr +
~2,000 output (PolishedDoc JSON) = ~$0.015 in + ~$0.018 out =
**~$0.033 per memo conservatively** (transcript is the
biggest output contributor; ~2,400 tokens for 1,800 words).
- **5-min memo, realistic median:** ~$0.011 (median memo is
closer to 2 min, ~$0.006).
- **15-min memo (max):** ~$0.025.
- **Translation pass (Gemini 3.5 Flash, low)** — optional;
~1,500 in + ~600 out ≈ **$0.0078 per translation**. Skip when
language matches.
- **Hero image (Nano Banana Pro)** — ~$0.04, once per app
install, cached.
- **Workspace export** — free.
- **Firebase Storage** — ~480 KB/min Opus; 2.4 MB for 24 h ≈
negligible.
- **Firestore writes** — ~10/memo; free-tier 20k/day.
- **Cloud Run** — first 2 apps deploy free post-I/O 2026.
- **Expected per-memo:** ~$0.011 lifecycle.
- **Heavy user (4 memos/week):** ~$0.18/month.
- **Casual user (3 short memos/week):** ~$0.05/month.
## 7. Design language
- **Mood:** A quiet thinking surface, not a SaaS product. Not
Otter. Not a transcription utility. The moment the user pulls
out their phone at the start of a walk — morning light, coffee
cup, the notebook on the desk that never quite got filled.
- **Typography:** Clean grotesque (Inter or Geist) for chrome and
labels. Warm serif (Source Serif Pro or Fraunces) for the doc
title, "what this is" line, section headings, and the body of
the doc itself — the user's words read like prose, not
dashboard. Quiet mono (JetBrains Mono or IBM Plex Mono) at
13 px for the audit panel's verbatim transcript.
- **Palette:** Warm paper `#F8F4ED` doc surface, deep ink
`#1A1714` body, sage green `#5C8169` for the record button and
Open-in-Docs chip, faded sienna `#B65A3C` for the recording
state, soft graphite `#6F6864` for metadata and audit panel.
No purple. No teal. No SaaS-default blue.
- **Imagery:** Photographic. Phones on counters at golden hour,
sneakers on sidewalks, steaming mugs. No flat illustrations,
no microphone emoji, no waveform-as-decoration. Nano Banana
Pro prompts emphasising real working surfaces, warm light, no
people unless prompted, no commercial branding.
- **Hand-feel touches:** Doc fades in section by section as the
model finishes — the user sees structure emerging instead of
a spinner. Audit panel slides open with a thin paper-shadow.
Tapping a sentence highlights it in quiet ink-yellow with a
pinned play button. The Open-in-Docs chip pulses subtly once
when the doc first finishes, then sits quietly.
- **Spacing:** 4-px base; generous whitespace.
- **Radius:** 6 / 12 / 20 px token set.
- **Shadows:** subtle, layered, warm-tinted.
- **Motion:** purposeful — entrance fades, hover lifts;
reduced-motion renders the full doc at once. No splash
bouncing, no theatrical hero animations.
- **States:** hover / focus / active / disabled on every
interactive element. Loading uses skeletons. Empty states give
next-action guidance ("Tap record and talk about anything — a
pitch, a spec, a draft, a status update.").
## 8. Content generation rules
- Write **realistic, specific copy**. NO Lorem Ipsum. NO generic
placeholders like 'Your tagline here'.
- Invent plausible voice-memo transcripts that fit the domain
(use the seed content in section 8a as a starting point). When
inventing, lean on realistic patterns — founders rambling
about wedges, engineers narrating specs, writers narrating
blog drafts — but never claim a fictional memo is from a real
named company.
- Tone: warm, direct, free of corporate language. This template
is for a person, not a company.
- Headlines: punchy and concrete. No 'Empower your X' filler. No
'Revolutionize'. No 'Seamless'. No 'AI-powered'.
- Body copy: short paragraphs (2-4 sentences). Use lists where
appropriate.
- Plain language. Avoid jargon — except where the user already
speaks it (a PM wants to see "OKR", "KPI", "ICP", "MQL"
because they speak that vocabulary daily).
- Where the app outputs AI-generated content (the doc itself),
never label it as "AI says" — the doc is the user's words.
Use small uncertainty cues only where epistemic honesty
requires them (a low-confidence transcribed sentence shows as
faintly underlined; tapping reveals the model's alternative
transcription).
## 8a. Seed content (use these specific examples)
Anchor every generated copy + sample data point in the concrete
content below. Use these names, numbers, dates, and snippets
verbatim where helpful, or generate close variants that sit in
the same world.
**Sample memo 1 (5 min, founder pitch on a Saturday walk, en-US):**
Voice memo excerpts (verbatim, with timestamps):
- 00:04 — "Okay so the pitch I keep coming back to is that small
teams don't have time to write their own internal docs."
- 00:18 — "Like, the founder writes the first one, and then it
just rots, and nobody updates it."
- 00:32 — "And the thing about this product is — it's not a
wiki, it's a doc co-pilot that takes your voice notes and
structures them for you."
- 00:51 — "I think the wedge is async standup updates, honestly,
because that's the thing people hate writing and they already
talk about it out loud on a walk."
- 01:14 — "Pricing — I'm thinking ten bucks a month per seat,
which is roughly Notion but a little less."
- 01:32 — "Maybe a free tier of like ten memos a month, I don't
know, we'll see."
- 01:48 — "The competitors I keep thinking about are Otter,
which is transcription, that's a different job. And Sembly
which is meeting notes."
- 02:30 — "I think the moat — and this is the bit I'm least
sure of — is the verbatim-substring guarantee."
- 02:55 — "I should probably send this to Priya before Monday
and see what she thinks."
Generated PolishedDoc:
- **title:** "Doc co-pilot — pitch v1"
- **what_this_is:** "It's not a wiki, it's a doc co-pilot that
takes your voice notes and structures them for you."
- **Problem:** "Small teams don't have time to write their own
internal docs." / "The founder writes the first one, and then
it just rots, and nobody updates it."
- **The wedge:** "I think the wedge is async standup updates,
honestly, because that's the thing people hate writing and
they already talk about it out loud on a walk."
- **Pricing thoughts:** "Pricing — I'm thinking ten bucks a
month per seat, which is roughly Notion but a little less." /
"Maybe a free tier of like ten memos a month, I don't know,
we'll see."
- **Competitors I'm watching:** "Otter is transcription, that's
a different job. Sembly is meeting notes. Neither is the
right shape for this."
- **The moat (least sure about):** "I think the moat — and this
is the bit I'm least sure of — is the verbatim-substring
guarantee."
- **suggested_action:** "Send to Priya before Monday."
- **hedge_phrases_preserved:** ["I think", "honestly", "Maybe",
"I don't know", "least sure"]
- **hedge_phrases_dropped:** ["Okay so", "Like,", "Anyway."]
- **parse_confidence:** 0.92
**Sample memo 2 (3 min, weekly status update from a parking lot,
en-AU):**
Doc lands as **This week** / **Blocker** / **Risk for next week**
/ **Need from you** / **Next sprint** with the suggested action:
"Confirm before standup on Monday: push the launch by a week?"
**Sample memo 3 (10 min, reflective Sunday-night walk, no
action):**
Three sections — **The past week**, **Where I'm stuck**, **Next
week** — and `suggested_action: null`. UI surfaces "no next
action — this was a reflective memo".
**Sample memo 4 (6 min, French + English code-switching, fr-CA):**
"Bon, on parle du onboarding aujourd'hui. Le KPI on this one is
engagement, but the funnel is leaking quelque part — I think we
need to instrument the second onboarding step better." The doc
preserves the code-switching by default.
**Sample memo 5 (9 min, Tagalog with English brand names,
translation requested):** output_language: en-US. Each translated
bullet shows a "translated from Tagalog" pill and a "see
original" toggle that reveals the Tagalog source verbatim.
**Sample voice copy:**
- Onboarding: "Tap record and talk about anything. We'll build
the doc."
- Home empty state: "What's on your mind? Tap the red button and
start talking."
- Recording in progress: "Recording. Tap the square to stop."
- Processing: "Hearing your memo" → "Finding your sub-topics" →
"Pulling your verbatim sentences" → "Building the doc" →
"Almost there".
- Doc ready: "Your doc is ready. Open it in Docs?"
- No action surfaced: "No next action — this was a reflective
memo."
- Low-confidence note: "We had trouble hearing a few parts.
Tap the underlined sentences to see what we made out."
- Audit panel header: "Every sentence here is something you said.
Tap any sentence to hear it."
- Workspace export confirmation: "Saved to Drive → Voice Memos."
- Regenerate panel header: "Tweak the settings and we'll
rebuild."
- Privacy footer: "Your memos and docs are yours. We never train
on them."
## 9. Media & assets
- **Hero image:** Photographed-looking shot of a phone leaning
against a white ceramic mug on a wooden counter at morning
light, phone screen showing a voice-memo waveform. Generate
via Nano Banana Pro (`gemini-3-pro-image`) with a prompt that
asks for the on-screen "Voice Memo" label rendered legibly at
4K — that legible-text-in-image capability is the Pro
advantage; 3.1 Flash Image would muddy it.
- **App icon / wordmark:** Display serif, slightly worn paper
texture behind. No icon — just type.
- **Empty-state illustration:** Photographic notebook + steaming
mug; hand-drawn annotation overlay. Generate once at build
time via Nano Banana 2 (`gemini-3.1-flash-image`), 1:1 WebP at
1024×1024, prompt: "photographic open paper notebook on a
wooden table beside a steaming white ceramic mug, warm
morning light from frame-left, hand-drawn ink annotation
squiggles overlaid in the margin of the notebook, no people,
no commercial branding, slight imperfection". Ship as a seed
asset at `/public/samples/empty-state-notebook.webp`.
- **Demo audio:** Sample memos from 8a available as `files/*`
resource names (Developer API Files API) in the demo project
for "try with the sample memo".
- **Fallback:** If image-gen fails, fall back to the seed hero
asset at `/public/samples/sample-phone-on-counter.jpg` (3:2
WebP, 2048×1365). Recreate via Nano Banana Pro
(`gemini-3-pro-image`) with the prompt: "photographic phone
leaning against a white ceramic mug on a wooden counter at
morning light, phone screen showing the legible label 'Voice
Memo' above an audio waveform rendered crisply at 4K, soft
shadow under the phone, real wood-grain counter, no people,
no commercial branding". Never to a 🎤 emoji.
- **Image-gen routing:** Nano Banana Pro
(`gemini-3-pro-image`) for the hero (legible on-screen
text); `gemini-3.1-flash-image` for empty states without text.
- **Optimisation:** WebP/AVIF, `loading="lazy"`, explicit
`width`/`height` to prevent CLS.
- **Icons:** `lucide-react`, sparingly; never decorative-only.
## 10. Interactivity & states
- Hover / focus / active / disabled on every interactive
element.
- Inline validation with specific messages ("Memo is too long —
max is 15 minutes"), not "Invalid input".
- Skeletons, not spinners. Processing screen uses the rotating
five-phrase status line mapped to model stages.
- Empty-state CTAs are domain-specific ("Tap record and talk",
"Try with the sample memo", "Open this in Docs") — never
"Add your first item".
- Doc emerges section by section (streamed structured output);
reduced-motion → full doc at once.
- Gemini failures show a calm, specific error ("We couldn't
process this memo — try again, or download the raw audio.").
- Low-confidence sentences faintly underlined; tap reveals the
alternative transcription the model considered.
- Audit panel slide-open: 250 ms, reduced-motion = instant.
- Open-in-Docs chip pulses once when ready; then quiet.
- Tapping a doc sentence highlights it and scrolls the audit
panel's source range into view with a play button.
- Apple-signed-in users see Open-in-Docs greyed out with a
tooltip: "Sign in with Google to enable one-tap Docs export."
## 11. Tech & responsive requirements
- **Stack:** React + TypeScript + Tailwind CSS. Functional
components + hooks. Shadcn UI primitives where appropriate.
- **Build runtime:** AI Studio Build — full-stack with Cloud Run
server functions; Gemini API key in Secrets Manager, never in
client bundle. Free 2-app Cloud Run deploy covers hosting.
- **Model selection:** pin `gemini-3.5-flash` for hero +
translation, `gemini-3-pro-image` for hero illustration. Set
`thinkingLevel` per call (`medium` hero, `low` translation;
omit on image-gen). Do NOT use `gemini-3.5-flash`,
`gemini-3.5-flash`, or `gemini-3.1-flash-image` — pre-I/O,
deprecated.
- **Database:** Firestore (auto-provisioned).
- **Auth:** Firebase Auth — Google sign-in primary (required
for Workspace), Apple sign-in secondary.
- **Storage:** Firebase Storage with pre-signed URLs;
configurable retention.
- **Workspace integration:** post-I/O 2026 no-OAuth Drive write;
reuses Google identity from sign-in. Default folder "Voice
Memos" (configurable).
- **Audio handling:** Web MediaRecorder — `audio/webm`
(Chrome/Firefox/Edge) or `audio/mp4` (Safari) → Firebase
Storage → server-side re-upload to Gemini Developer API Files
API → `fileData.fileUri: "files/abc123xyz"` passed to
`generateContent`. Do NOT pass Firebase Storage public URLs to
`generateContent`. Inline base64 acceptable for <20 MB; prefer
Files API beyond ~1 min.
- **Local save fallback (FSA non-Chromium):** the "save to disk"
affordance uses `showSaveFilePicker()` on Chromium; on Safari /
Firefox, fall back to an `` element pointed at a
blob URL — FSA `showSaveFilePicker()` is not supported there.
- **Mobile-first.** Verify at 375 / 768 / 1024 / 1440+.
- `clamp()` for fluid type; container queries; `dvh`/`svh`;
safe-area insets on iOS; zero horizontal overflow; zero CLS.
- Firestore real-time listeners on the doc view (streamed doc
emerges section by section).
- Optimistic UI on title/settings; reconcile on response.
- Wake Lock API for recording (Chrome/Edge); Safari iOS gets a
clear "keep your screen on" hint up front. Background
recording on iOS Safari is platform-constrained; the UI warns
before long memos that backgrounding will pause capture.
## 12. Accessibility (WCAG 2.2 AA)
- Semantic HTML (`header`, `nav`, `main`, `section`, `article`,
`footer`).
- All controls keyboard-reachable with visible focus ring. The
record button is Tab-reachable, Space-activated (tap-to-start
/ tap-to-stop; no long-press required).
- Contrast ≥ 4.5:1 body, 3:1 large text and UI. Sage-green
Open-in-Docs chip and sienna recording state both verified
AA against the warm paper background.
- All images have meaningful `alt`; form fields have `