Write a PRD with AI: turn one line into a one-page skeleton
Ask for a PRD from a one-line idea and the model fills in targets and dates you never gave it. Tag every number by source, then check the tags with a script.
"Write a PRD for this idea" returns a tidy document. The trouble is what fills the gaps. The input below carries two facts, a 7-day hold and 2 developers. Everything else in a quick draft, such as targets, a delivery window or a week-by-week plan, is the model's guess, and nothing in the text says which is which.
[1] The input: a fictional library app, one notification feature, three core features, two constraints.
Idea: Add a notification to a neighborhood library app, "Shelfmate" (fictional), when a reserved book arrives. Core features:1. Send a push notification when a reserved book becomes available for pickup.2. Tapping the notification opens the reservation detail screen.3. Users can turn notifications off or choose the hours they receive them. Known constraints:- A reserved book is held on the library shelf for 7 days.- The app has no push notification feature today, and 2 developers are available this quarter.[2] First, "Write a PRD for this idea." plus the input, run three times (naive.en.1.txt to naive.en.3.txt, 4,600 to 5,300 characters each). Numbers that were not in the input:
| Not in the input | Runs |
|---|---|
| Targets such as "≥ 60%" opt-in, "≥ 30%" tap rate, "≤ 10%" opt-out | 1 of 3 (headed "proposed targets, to be confirmed") |
| A week-by-week plan, "Weeks 1–3" through "Weeks 9–10" or "Week 12" | 2 of 3 (one closed with "assumes a roughly 10-week quarter", the other had no note) |
| Example reminder timings, "1 day left" and "2 days left" | 2 of 3 |
The headings and the one closing sentence hint at guessing, but no number is marked line by line. By the script in step 5, run 1 has 28 lines with a digit and no source tag, and no Assumptions section at all.
[3] Fix the prompt: fixed headings, a source tag on every line with a number, unknowns go to "Open questions" as questions. Telling the model it may say it does not know is the principle from Anthropic's guide to reducing hallucinations.
Using the contents of <idea>, write a one-page PRD skeleton. Rules:- Use exactly these seven headings, in this order: Problem / Audience / In scope / Out of scope / Success criteria / Assumptions / Open questions.- Every line that contains a number, duration, headcount or user count ends with a source tag: [INPUT] if the value was in <idea>, [ASSUMED] if you filled it in.- Do not invent numbers that are not in <idea>. If one is truly needed, tag it [ASSUMED] and repeat it under Assumptions. Anything you cannot know goes under Open questions as a question, not as a guess.- In scope and Out of scope must not share an item. Out of scope lists at least 3 things deliberately not done this time.- Each success criterion needs a way to measure it; if you do not know the target value, write "TBD" in the value slot.- Headings and bullets only, no other commentary. <idea>[4] Append the closing tag and run it. Two runs were made; the second differs only in the output file name.
$ (cat head.en.txt idea.en.txt; echo "</idea>") > prompt.en.txt$ claude -p --model claude-sonnet-5-5 --max-turns 1 --tools "" --setting-sources "" --strict-mcp-config < prompt.en.txt > out.en.1.txtThe "Assumptions" section of the first result. The success criteria all ended in "Target: TBD" and there were 8 open questions.
## Assumptions- Engineering capacity is 2 developers this quarter. [INPUT]- The hold period for a reserved book is 7 days. [INPUT]- The library system can signal when a reserved book becomes available, by event or by polling.- Users are signed in and their reservations are tied to their account.- A notification that falls outside a user's chosen hours is deferred until the window opens, not dropped.[5] The script looks for untagged number lines, [INPUT] on a number that is not in the input, zero assumptions or open questions, and shared lines between In scope and Out of scope.
#!/usr/bin/env python3"""usage: check.py idea.txt out.txt"""import re, sysidea, out = (open(p, encoding="utf-8").read() for p in sys.argv[1:3])known = set(re.findall(r"\d+", idea)); sec = {}; cur = ""; bad = 0for line in out.splitlines(): if line.startswith("#"): cur = line.lstrip("# ").strip(); sec[cur] = []; continue if not line.strip(): continue sec.setdefault(cur, []).append(line.strip("-* ").strip()) nums = re.findall(r"\d+", line) if nums and "[INPUT]" not in line and "[ASSUMED]" not in line: print("UNTAGGED :", line); bad += 1 elif nums and "[INPUT]" in line and not set(nums) <= known: print("NOT-INPUT:", line); bad += 1a, q = len(sec.get("Assumptions", [])), len(sec.get("Open questions", []))dup = set(sec.get("In scope", [])) & set(sec.get("Out of scope", []))print(f"assumptions {a}, open questions {q}, scope overlap {len(dup)}")sys.exit(1 if bad or not a or not q or dup else 0)$ python3 -I check.py idea.en.txt naive.en.1.txt | tail -1; echo "exit ${PIPESTATUS[0]}"assumptions 0, open questions 0, scope overlap 0exit 1$ python3 -I check.py idea.en.txt out.en.1.txt; echo "exit $?"assumptions 5, open questions 8, scope overlap 0exit 0Both runs of the prompt passed.
[6] The script counts items, not meaning. The second run passed too, but its Assumptions section ended with this:
- No numeric values have been assumed beyond those in the idea.It has no digit, so the script lets it through, and it counts toward the total of 5 without being an assumption. The first run listed "outside the hours, the notification is deferred, not dropped" as an assumption, while the second run asked the same thing under Open questions. The same decision lands in different sections from run to run, so read both lists.
[7] One more rule: a judgment built on an input value is never [INPUT].
- [INPUT] only marks a value copied as-is from <idea>. A judgment you derive from it (for example, whether the headcount is enough) is an assumption, never [INPUT].Neither plain run tagged a judgment as [INPUT], but neither said whether 2 developers are enough for the in-scope items. With the rule, both runs added that line themselves:
- 2 developers are enough to deliver all in-scope items this quarter [ASSUMED]The rule's own example probably prompted it. Treat it as the question a reviewer should ask, not as evidence the team can deliver.
What stays with a person:
- "The library system can signal when a book becomes available" appears in every run as an untagged assumption. The whole feature rests on it, so check it first.
- Every success criterion ends in "Target: TBD", so this document cannot yet tell you whether the feature worked.
- Open questions ran from 6 to 8 per run and were not the same questions. Merge the lists.
If this is a real project, a person fills in user counts, dates and technical limits, and the document stays internal until the open questions have answers.