Build the Right Thing First
Most bugs start in the requirements phase, not the code. Use Gemini CLI to analyze TaskFlow, write a Product Requirements Document for a new Recurring Tasks feature, and produce a machine-readable spec before a single line of code is written.
- Generate a complete PRD by feeding TaskFlow's GEMINI.md to Gemini CLI
- Decompose the PRD into INVEST-scored user stories
- Run a gap analysis against TaskFlow's existing models.py and routers/tasks.py
- Critique an existing requirements document for edge cases and ambiguities
- Output a JSON spec that M06 (design) and M07 (implementation) will consume
The Requirements Problem
Before we write our first Gemini CLI command, let's be honest about why requirements go wrong — because that's exactly what Gemini CLI is about to fix.
BEFORE: Imagine hiring a contractor to build you a kitchen. You say "I want more storage." They start work immediately — tearing out cabinets and framing new ones.
PAIN: Two weeks in, you realize you meant a pantry in the corner, not floor-to-ceiling cabinets on every wall. The work is half done. Tearing it out costs more than doing it right the first time.
MAPPING: Software requirements are the blueprint. Without one, every developer builds their mental model of "what was meant." Gemini CLI lets you cross-examine that blueprint before concrete gets poured.
TaskFlow right now has basic task CRUD: create a task, read it, update it, delete it. The product manager says "we need Recurring Tasks — tasks that auto-recreate on a schedule." Sounds simple. But consider what's actually unspecified:
- Does the schedule run server-side or does the client call an endpoint?
- What schedule format — cron expressions, human-readable intervals, or fixed days?
- Does a completed recurring task auto-reset, or spawn a new task?
- What happens if the server is down during a scheduled spawn?
- Does the recurrence belong to the task, or is it a separate entity?
A developer who jumps straight to models.py will answer every one of these questions silently — with assumptions. Gemini CLI makes those assumptions visible and questionable before they become database schemas.
IBM's Systems Sciences Institute found defects cost 100x more to fix in production than in requirements. A 30-minute Gemini CLI requirements session can prevent a 3-day implementation reversal. On a 4-person team shipping weekly, that's 12% of a sprint saved per feature.
PRD Generation
A PRDProduct Requirements Document — the canonical written description of a feature: what it does, who it serves, what success looks like, and what is explicitly out of scope. answers the question "what are we building and why?" before the team debates "how." Let's generate one for Recurring Tasks by feeding Gemini CLI TaskFlow's existing context document.
The Prompt
TaskFlow ships with GEMINI.md — a project context file that tells Gemini what every file does. Use @GEMINI.md to load it automatically:
gemini "@GEMINI.md Write a Product Requirements Document for a Recurring Tasks feature.
The feature: tasks that auto-recreate on a schedule after they are completed.
Include:
1. Problem Statement
2. Goals and Non-Goals
3. User Personas (who needs this?)
4. Functional Requirements (numbered, testable)
5. Acceptance Criteria for each requirement
6. Out of Scope items
7. Open Questions
Be specific to TaskFlow's existing data model. Reference actual fields
from models.py where relevant."
@GEMINI.md prefix tells Gemini CLI to read the file and inject its contents into the prompt. Gemini sees the entire project context before generating anything.WHY: Without context, Gemini would generate a generic PRD. With GEMINI.md it knows Task has
owner_id, priority, and due_date — and can write requirements that reference real fields.GOTCHA: Run this command from the
sample-project/taskflow/ directory so relative file paths resolve correctly.Realistic Output
Gemini will produce a structured markdown document. Here's what a representative output looks like — annotated section by section:
# Product Requirements Document: Recurring Tasks
## Problem Statement
TaskFlow users currently manage repetitive obligations (weekly team reports,
monthly expense submissions, daily standup notes) by manually recreating
identical tasks after completion. This creates cognitive overhead and risks
tasks being forgotten.
## Goals
- Users can mark any Task as recurring with a defined schedule
- When a recurring task is completed, a new instance auto-spawns
- Users can pause, modify, or delete a recurrence without losing task history
## Non-Goals (Out of Scope v1)
- Email/push notifications when a recurrence triggers
- Sharing recurrences across users
- Calendar integration
## Functional Requirements
FR-1: A user SHALL be able to set a recurrence on any existing task via
POST /tasks/{id}/recurrence
FR-2: The recurrence SHALL support three schedule types:
daily, weekly (specific days), monthly (specific date)
FR-3: When a task with active recurrence is marked completed, the system
SHALL automatically create a new task with identical fields within
60 seconds
FR-4: A user SHALL be able to retrieve the recurrence settings for a task
via GET /tasks/{id}/recurrence
FR-5: A user SHALL be able to delete a recurrence, which stops future
auto-spawn but does not affect existing tasks
## Acceptance Criteria
FR-1 AC: POST /tasks/42/recurrence with {"schedule_type": "daily"} returns
201 with the recurrence object; GET /tasks/42 shows has_recurrence: true
FR-3 AC: Mark task 42 completed at T=0; by T=60s a new task exists with
the same title, description, priority, and owner_id
## Open Questions
- OQ-1: Who triggers the spawn — a background scheduler or an endpoint call?
- OQ-2: What timezone does "daily" mean? User's timezone or UTC?
- OQ-3: Should the spawned task inherit the original task's tags?
WHY THIS MATTERS: FR-3 is specific: "within 60 seconds." That SLA becomes a test assertion. Without it, "auto-spawn" means different things to every developer.
NOTICE: The Open Questions section surfaces the three decisions that would have been made silently in code. Answer OQ-1 before design begins — it determines whether you need a background scheduler or a webhook.
Pipe Gemini's output directly to a file: gemini "@GEMINI.md Write a PRD..." > spec/recurring-tasks-prd.md. Commit it to git. When a developer asks "why did we build it this way?" the answer is in version control.
User Story Decomposition
A PRD describes the feature. User storiesSmall, user-centered requirements written as: "As a [persona], I want [goal] so that [benefit]." They are the unit of work in agile development — each story should be shippable independently. break it into shippable units of work. The INVEST criteriaA quality checklist for user stories: Independent (can be built without other stories), Negotiable (scope can be discussed), Valuable (delivers user benefit), Estimable (team can size it), Small (fits in one sprint), Testable (can be verified). ensure each story is actually ready to develop — not a vague paragraph that balloons in scope.
Decompose the PRD
gemini "@GEMINI.md @spec/recurring-tasks-prd.md
Convert the Recurring Tasks PRD into 5-8 user stories.
For each story:
- Write it in 'As a [user] I want [goal] so that [benefit]' format
- Score it on the INVEST criteria (I/N/V/E/S/T — each: Pass/Partial/Fail)
- List 3 acceptance criteria
- Estimate story points (1/2/3/5/8)
- Flag any dependencies on other stories"
WHY: Without GEMINI.md, Gemini wouldn't know that "create a recurrence" requires auth (all /tasks/ routes use
get_current_user) — and might omit the auth story entirely.Sample Output
## Story 1 — Set a Recurrence
As a TaskFlow user, I want to attach a recurring schedule to any of my tasks
so that I don't have to manually recreate repetitive tasks.
INVEST: I=Pass, N=Pass, V=Pass, E=Pass, S=Pass, T=Pass
Acceptance Criteria:
AC1: POST /tasks/{id}/recurrence with valid schedule returns 201
AC2: GET /tasks/{id} returns has_recurrence: true
AC3: POST to a non-existent task_id returns 404
Story Points: 3
Dependencies: None (builds on existing Task CRUD)
---
## Story 2 — View Recurrence Settings
As a user, I want to retrieve the recurrence settings for a task
so that I can verify what schedule is active.
INVEST: I=Pass, N=Pass, V=Pass, E=Pass, S=Pass, T=Pass
Acceptance Criteria:
AC1: GET /tasks/{id}/recurrence returns recurrence object for recurring task
AC2: GET /tasks/{id}/recurrence returns 404 for non-recurring task
AC3: Only the task owner can view recurrence settings (401 otherwise)
Story Points: 1
Dependencies: Story 1
---
## Story 3 — Auto-Spawn on Completion
As a user, I want a new task to be automatically created when I complete
a recurring task so that the next instance appears without manual work.
INVEST: I=Partial (depends on scheduler), N=Pass, V=Pass, E=Partial, S=Fail, T=Pass
Story Points: 8
Dependencies: Stories 1, 2, and background scheduler (OQ-1 must be resolved)
NOTE: S=Fail — this story is too large. Split into 3a (spawn logic) and
3b (background scheduler integration)
NOTICE: Story 3 depends on resolving Open Question OQ-1 (who triggers the spawn). The PRD open question became a story dependency. Fix the dependency before sprint planning, not during implementation.
Gap Analysis
The PRD says what we need. The gap analysis compares that to what we have. Load TaskFlow's actual source files to get a specific, actionable list of what needs to change.
gemini "@models.py @schemas.py @routers/tasks.py
Identify gaps between the current TaskFlow implementation and the
Recurring Tasks feature described here:
Feature: Tasks that auto-recreate on a schedule when completed.
- New endpoint: POST /tasks/{id}/recurrence
- New endpoint: GET /tasks/{id}/recurrence
- New endpoint: DELETE /tasks/{id}/recurrence
- Background spawn: when Task.completed = True, create new Task
For each gap:
1. Which file needs to change
2. What specifically needs to be added or modified
3. Risk level (Low/Medium/High) and reason"
@models.py, @schemas.py, and @routers/tasks.py. Gemini reads all three before answering.WHY: A gap analysis without the actual code produces generic advice. With the actual code, Gemini spots that
Task.completed is a plain Boolean field — there's no hook or event system to detect completion changes, so a background scheduler is mandatory.Gap Analysis Output
## Gap Analysis: TaskFlow → Recurring Tasks Feature
### models.py
GAP-1: No RecurringTask model exists
- Add: class RecurringTask(Base) with fields:
task_id (FK → tasks.id), schedule_type (str), interval_value (int),
next_run_at (DateTime), is_active (bool), created_at (DateTime)
- Also add: Task.recurrence = relationship("RecurringTask", ..., uselist=False)
Risk: Medium — requires Alembic migration; existing rows unaffected
GAP-2: Task model has no completion event/hook
- Task.completed is a plain Boolean; there is no SQLAlchemy event listener
- A scheduler polling Task.completed changes is required
Risk: High — architectural decision (polling vs. event sourcing)
### schemas.py
GAP-3: No RecurringTaskCreate, RecurringTask response schemas
- Add: RecurringTaskCreate(schedule_type, interval_value)
- Add: RecurringTask(id, task_id, schedule_type, next_run_at, is_active)
- Pattern to follow: existing Tag / TagCreate pair (lines 7-16)
Risk: Low
### routers/tasks.py
GAP-4: No recurrence endpoints in tasks router
- Add: POST /tasks/{id}/recurrence
- Add: GET /tasks/{id}/recurrence
- Add: DELETE /tasks/{id}/recurrence
- Pattern: follow existing get_task() / update_task() auth pattern
Risk: Low
### NEW FILE REQUIRED
GAP-5: No background scheduler exists
- Need: APScheduler or similar; polls for tasks where
completed=True AND recurrence.is_active=True AND next_run_at <= now()
- Or: use FastAPI startup event to launch scheduler thread
Risk: High — requires new dependency; impacts app startup
models.py and noticed that Task.completed is just a Boolean column — there's no trigger or event. This is a real architectural decision you need to make before writing a single line of implementation code.GAP-2 is the big one: it tells you that auto-spawn requires either a poller or an event hook. That decision belongs in the design phase (M06), not discovered mid-implementation.
You ran three Gemini CLI commands and produced: a complete PRD, 6 user stories with INVEST scores, and 5 specific gaps with risk ratings. A junior developer starting fresh would spend 2-3 hours researching the codebase to produce equivalent output. This took under 5 minutes — and the output is grounded in the actual source files, not assumptions.
Feeding Existing Docs
Not every project starts from scratch. More often, you inherit an existing requirements doc that was written without AI assistance — and it has gaps. Here's how to use Gemini CLI to critique documents you already have.
Critique an Existing Spec
# Assume you have a spec file at docs/recurring-tasks-spec.md
gemini "@GEMINI.md @docs/recurring-tasks-spec.md
Review this requirements document as a senior engineer.
Identify:
1. Ambiguous requirements (could be interpreted multiple ways)
2. Missing edge cases (what happens when X fails?)
3. Untestable requirements (no pass/fail criteria)
4. Contradictions with the existing codebase
5. Security concerns (auth, data isolation, rate limits)
For each issue, quote the relevant line and suggest a fix."
@GEMINI.md context gives Gemini knowledge of TaskFlow's auth model. It can then flag "the spec doesn't mention auth on the recurrence endpoint" because it knows all /tasks/ routes require get_current_user.GOTCHA: Gemini will sometimes flag things that are intentionally simple for v1. Read each issue and decide — don't treat every flag as a required change.
Find Edge Cases
gemini "@GEMINI.md @spec/recurring-tasks-prd.md
List every edge case that is NOT addressed in this PRD.
Focus on:
- What happens when the task is deleted while a recurrence is active?
- What happens when next_run_at is in the past (server was down)?
- What if a user sets daily recurrence on a task with a past due_date?
- What if the spawned task should inherit tags — but a tag was deleted?
For each edge case: state the scenario, the expected behavior, and
whether it needs a new requirement or just a code comment."
task_tags join table in models.py). Gemini knows about it from GEMINI.md. A generic AI without context would never surface "what if the inherited tag was deleted?"The quality of Gemini's analysis depends entirely on the quality of context you provide. If your GEMINI.md is out of date or missing key files, Gemini will make assumptions. Keep GEMINI.md current — it's the single source of truth for AI-assisted development on this project.
Machine-Readable Specs
Markdown PRDs are readable by humans. But the real power of Gemini CLI comes when you output a structured JSON specA machine-readable version of your requirements. JSON can be parsed by scripts, fed back into future Gemini prompts, or used as input to code generation commands in M07. that downstream modules (design in M06, implementation in M07) can consume directly.
Generate the JSON Spec
gemini "@GEMINI.md @spec/recurring-tasks-prd.md
Output the Recurring Tasks feature spec as valid JSON only.
No markdown, no explanation — just the JSON object.
Use this structure:
{
'feature': string,
'version': '1.0',
'user_stories': [{ 'id': string, 'story': string, 'points': number, 'acceptance_criteria': string[] }],
'new_models': [{ 'name': string, 'fields': object, 'relationships': string[] }],
'new_endpoints': [{ 'method': string, 'path': string, 'auth': boolean, 'request_body': object, 'response': object }],
'acceptance_criteria': string[],
'out_of_scope': string[],
'open_questions': string[]
}" > spec/recurring-tasks.json
> spec/recurring-tasks.json redirect saves Gemini's JSON output directly to a file. This file is your contract between requirements, design, and implementation.WHY JSON: In M07, you'll run
gemini "@spec/recurring-tasks.json @models.py Implement the RecurringTask model..." — Gemini reads the spec you generated here and produces code that matches it exactly.GOTCHA: Gemini sometimes adds markdown code fences around JSON output. Add "No markdown, no explanation — just the JSON object" to suppress them.
Sample JSON Output
{
"feature": "Recurring Tasks",
"version": "1.0",
"user_stories": [
{
"id": "US-1",
"story": "As a user I want to set a recurring schedule on a task so that it auto-recreates on completion",
"points": 3,
"acceptance_criteria": [
"POST /tasks/{id}/recurrence returns 201 with recurrence object",
"GET /tasks/{id} shows has_recurrence: true",
"POST to non-existent task returns 404"
]
}
],
"new_models": [
{
"name": "RecurringTask",
"fields": {
"id": "Integer PK",
"task_id": "Integer FK tasks.id UNIQUE",
"schedule_type": "String (daily|weekly|monthly)",
"interval_value": "Integer nullable",
"next_run_at": "DateTime timezone=True",
"is_active": "Boolean default=True",
"created_at": "DateTime server_default=func.now()"
},
"relationships": ["Task.recurrence backref via task_id"]
}
],
"new_endpoints": [
{
"method": "POST",
"path": "/tasks/{task_id}/recurrence",
"auth": true,
"request_body": { "schedule_type": "string", "interval_value": "int?" },
"response": { "status": 201, "body": "RecurringTask schema" }
},
{
"method": "GET",
"path": "/tasks/{task_id}/recurrence",
"auth": true,
"request_body": null,
"response": { "status": 200, "body": "RecurringTask schema" }
},
{
"method": "DELETE",
"path": "/tasks/{task_id}/recurrence",
"auth": true,
"request_body": null,
"response": { "status": 204, "body": null }
}
],
"out_of_scope": ["Notifications", "Cross-user sharing", "Calendar integration"],
"open_questions": [
"OQ-1: Background scheduler vs endpoint-triggered spawn?",
"OQ-2: User timezone vs UTC for schedule evaluation?",
"OQ-3: Should spawned task inherit parent's tags?"
]
}
new_models array becomes the SQLAlchemy model. The new_endpoints array becomes the OpenAPI spec. Everything flows from this one file.Lab: Plan the Recurring Tasks Feature
By the end of this lab, you will have a complete feature spec in spec/recurring-tasks.json ready for the design phase in M06.
cd sample-project\taskflow
# Verify GEMINI.md exists
Get-Item GEMINI.md
# Create the spec directory
New-Item -ItemType Directory -Force -Path spec
gemini "@GEMINI.md Write a full PRD for a Recurring Tasks feature.
Tasks should auto-recreate when completed based on a schedule.
Include: Problem Statement, Goals, Non-Goals, 5+ Functional Requirements
with Acceptance Criteria, and Open Questions.
Reference actual field names from models.py." > spec/recurring-tasks-prd.md
# Verify it was created
Get-Content spec\recurring-tasks-prd.md | Select-Object -First 20
gemini "@GEMINI.md @spec/recurring-tasks-prd.md
Convert this PRD into 5-8 user stories in 'As a user I want...' format.
Score each on INVEST. Flag any stories with S=Fail and suggest splits.
Include story points (1/2/3/5/8) and dependency chains." \
>> spec/recurring-tasks-prd.md
gemini "@models.py @schemas.py @routers/tasks.py @spec/recurring-tasks-prd.md
Identify all gaps between the current TaskFlow implementation
and the Recurring Tasks feature. For each gap: file name, what
to add/change, and risk level (Low/Medium/High)." \
>> spec/recurring-tasks-prd.md
gemini "@GEMINI.md @spec/recurring-tasks-prd.md
Output the Recurring Tasks spec as valid JSON only. No markdown.
Include: feature, user_stories, new_models, new_endpoints,
out_of_scope, open_questions." > spec/recurring-tasks.json
# Validate it's valid JSON
python -c "import json; json.load(open('spec/recurring-tasks.json')); print('Valid JSON!')"
Expected output: Valid JSON! — your spec is ready for M06.
Quiz
gemini "@models.py Write a PRD..." from a different directory. What is the most likely problem?@models.py won't resolve — Gemini looks in the current directory