Every new app runs the same four stages. Each one has a fixed set of deliverables, and the next stage doesn't start until they exist.
| # | Stage | Deliverables |
|---|---|---|
| 01 | Aim |
WHY.mdone person, one pain, the outcome, success in numbers, what this is not
DEFINITIONS.mdevery term defined once: the words the UI, the code, and conversation all use
FLOWS.mdmain flows: trigger, steps, outcome, written from the user's side
SCOPE.mdIn / Later / Never, with every In line naming the flow it protects
|
| 02 | Sketch |
STORYBOARDSone row of wireframes per flow, screens numbered by flow and step (3b = flow 3, step 2)
SCREEN MAPone navigation graph; a node no flow reaches is scope creep
|
| 03 | Architecture |
ENTITY MAPthe nouns, their labeled relationships, and what each one records
SYSTEM MAPthe parts, bought vs built, where state lives, every arrow labeled with what moves
FLOW TRACESa numbered index of every flow, plus a tree and numbered hops for each one worth tracing
DECISIONS.mdone line per decision, the stack included: date, choice, why, the rejected alternative
|
| 04 | Ship |
PLAN.mdvertical milestones, skeleton first, each ending in a demo; tickets carry flow numbers and one "done when" line
PROOF GRIDflow × pass/fail/blocked, walked end to end by someone who didn't build it
|
A page is the target, not a hard limit. Write clearly and concisely rather than exhaustively.
If one stops fitting on a screen, split it by concern or zoom out a level.
Number the flows once, then every screen, ticket, and test carries the number of the flow it serves. If a piece of work can't name its flow, cut it.
When the build contradicts a doc, fix it that day and add a line to DECISIONS.md.
Clipper gets a solo podcaster from published episode to postable clip in five minutes.
Dana ships a weekly interview podcast, alone. No editor, no VA.
The painCutting a promo clip takes about an hour in a desktop editor, so most weeks she skips it and the episode goes unpromoted.
The outcomeA postable clip within five minutes of publishing, every week.
Why nowShort clips are the one discovery channel working for shows her size, and she isn't in it.
Success, in numbersNot an editor. Not a scheduler. Not analytics.
A podcast feed connected by RSS. One Show per feed.
EpisodeOne published audio file from a Show, with its transcript.
ClipA segment of an Episode, rendered as a captioned mp4 for sharing.
MomentThe transcript point Dana taps to start a Clip. Never stored; it becomes the Clip's start.
Words we don't useSegment, snippet, cut. It is a Clip everywhere.
Empty episodes screen, paste RSS url, episodes appear. Outcome: latest episode ready to open.
Flow 2 · find the momentOpen episode, scroll transcript, tap a line to hear it. Outcome: the moment found without scrubbing audio.
Flow 3 · first export (expanded)Share sheet with a captioned mp4. Outcome: the clip is live where her audience is.
Screens inherit flow numbers: 3b is flow 3, step 2.
Nodes carry the screen numbers they serve. A node with no numbers is a screen nobody asked for.
The names come from DEFINITIONS.md. Fields are logical identifiers and state, not a database schema.
Show ├─ identity → showId ├─ feed address → feedUrl ├─ display name → title └─ last successful fetch → fetchedAt Episode ├─ identity → episodeId ├─ Show → showId ├─ audio file → audioUrl ├─ transcript → transcriptKey ├─ published date → publishedAt └─ length → durationMs Clip ├─ identity → clipId ├─ Episode → episodeId ├─ Moment in the Episode → startMs ├─ end of the Clip → endMs ├─ draft, rendering, ready, or failed → status └─ rendered file, once ready → fileKey
A Clip carries a fileKey only once its status is ready.
Re-fetching a feed adds Episodes and never edits existing ones, so a Clip's source cannot change underneath it.
What talks to what, and what moves along each arrow.
Every arrow names what moves, state gets a badge, and dashed outlines are parts we do not build.
Use these flow numbers on screens, tickets, tests, and release notes. A hop number marks one system boundary.
| Flow | Outcome |
|---|---|
| F1 | Connect a Show and list its Episodes. |
| F2 | Find the Moment in an Episode without scrubbing audio. |
| F3 | Render a Clip from a Moment and save it. |
| F4 | Share a rendered Clip. |
Dana pastes a feed url
└─ the app sends it to the api
└─ the api fetches the feed from outside
├─ the feed is unreachable → nothing is saved, the app says so
└─ the feed parses → the api saves the Show and its Episodes
└─ the app lists the Episodes, newest first
An unreachable feed writes nothing. There is no half-connected Show.
F3 starts from an Episode saved at F1.4.
Dana marks a Moment in the transcript
└─ the app asks the api to cut a Clip
└─ the api saves the Clip as draft and queues a render
└─ the transcoder renders the mp4
├─ the render fails → the Clip becomes failed
└─ the render succeeds → the file lands in the store
└─ the Clip becomes ready and the app shows it
F3.2 writes the Clip before F3.3 queues the render, so a Clip always exists to carry the failed state.
A failed render leaves the Clip in failed and does not retry. Dana marks a new Moment.
F3 is the row that must be green on the proof grid before Ship.
F2 and F4 carry numbers but no trace. F2 reads what F1 already saved, and F4 hands a finished file to the operating system, so neither crosses a boundary the system map does not already show.
One line per decision, the stack included. Written as the graphs take shape, and again whenever the build later overrules one. A decision that needs its argument written out also gets a file in docs/adr/, and its line points to it.
All flows, thin: hardcoded show, one transcript line, render, share sheet. Demo: the full path on a real device.
M2 · flow 3 fullReal moment marking, adjustable start and end, Clip statuses.
M3 · flows 1–2 fullRSS connect, transcript browsing and preview.
M4 · flow 4 full + proofBaked-in captions on the share sheet, empty and failure states, then the proof pass.
A ticket, whole#41 · flow 3 · screens 3b–3d · export to mp4 · done when: a real device puts the mp4 in the share sheet
Walked end to end by someone who didn't build it, on a real device.
Two bug piles only: blocks a flow, or goes to Later. Ship when the column is green.