Project Automated Post Drafter from Solo SaaS Ops Logs
Turn daily ops logs into voice-true AI productivity post drafts in seconds
Primary user
Solo bootstrapped AI productivity SaaS founders with intermediate Next.js/Cloudflare experience who run operations alone
Naming note: The title is provisional and needs domain, trademark, handle, and competitor-conflict checks before launch
Product Spec Brief
This is the plain-English opening brief for the Blueprint: what it is, who it helps, what pain it removes, and what must be true for the MVP to count as working.
Working Title
Project Automated Post Drafter from Solo SaaS Ops Logs
Naming Note
provisional name; run separate availability check before launch
What We Are Building
A deployable full-stack Next.js application on Cloudflare (using Workers/Hono, D1, and AI Gateway) that accepts simple daily ops logs from a solo AI productivity SaaS founder via a web UI, stores them with light tagging in D1, extracts review-ready post drafts using a voice-tuned prompt via AI Gateway, and surfaces the draft in an in-app dashboard (with optional email/Slack delivery) for final human approval before any external use.
Who It Is For
Solo bootstrapped AI productivity SaaS founders who already run daily operations and customer support alone, possess intermediate full-stack experience with Next.js and Cloudflare, and need a repeatable system that turns existing work logs into consistent niche content without adding new manual capture steps.
Problem It Solves
Founders currently lose timely niche observations because every hour is consumed by product work and support, leaving only fragmented attention that produces blank-page guilt instead of stored angles; the app captures the observation at the moment it occurs and converts it into a ready draft on a weekly or on-demand schedule.
Why This Product Exists
Existing tools either require the founder to supply topics manually or generate generic outputs that demand extensive rewriting; this blueprint supplies the missing end-to-end harvesting layer that keeps all logic and data inside the founder's own Cloudflare account.
MVP Proof
When a sample ops log containing one daily support interaction and one feature decision is submitted through the deployed app's UI, the system returns a single complete, editable draft (viewable in the dashboard) that includes traceable source notes and a voice-match indicator, ready for human review within the same app.
Not Building in V1
Multi-platform auto-posting, analytics dashboards, custom model fine-tuning, or any customer-facing output without explicit human approval.
Blueprint Contents
The remaining imported markdown is preserved here so type-specific build details, tables, scripts, setup steps, tests, and citations stay intact.
How to Use This Blueprint
Use this Blueprint to turn each automation into trigger, action, condition, retry, error, logging, and approval blocks. Ask AI to verify connector names, auth scopes, permissions, quotas, and trigger behavior against current docs before activating anything.
Paste the Blueprint into a strong current AI model with reasoning enabled. Enable web search when you need current platform docs, package choices, pricing, UI patterns, APIs, product examples, or licensing checked. Ask the AI to keep the MVP boundary intact, challenge risky assumptions, cite current docs for unstable claims, and produce an implementation plan before building.
First prompt to use:
Using this Blueprint as the exact spec, convert the workflows into trigger/action/error/retry blocks for my environment. Validate current connector names, permissions, quotas, and trigger conditions against official docs. Identify failure modes and ask the smallest set of setup questions before implementation.
Do not skip:
- Validate assumptions, licensing, permissions, current docs, and any platform limits before production build.
- Confirm test users, roles, and access boundaries before building screens or workflows.
- Seed realistic test data before judging formulas, filters, or performance.
- Run the acceptance tests with real users in each role before launch.
- Keep human review gates for security, approvals, compliance, and production sharing.
- Avoid production data until the MVP passes end-to-end tests.
Core User Flows
Flow 1: Submit Log → AI Extract → Review Draft (Primary)
The core loop that converts raw operational notes into review-ready content. This is the only flow that must work flawlessly for MVP.
- User navigates to
/logs/new— The submit page loads immediately with a free-form textarea. No structured fields required; messy input is expected and handled. - User pastes or types raw ops notes — Any format works: bullet lists, timestamps, shorthand, stream-of-consciousness. A lightweight tagging interface lets the user add 1-3 tags (e.g., "support", "feature-decision") before submission.
- User clicks "Submit Log" — The form submits via a Next.js Server Action. The log is validated (minimum 10 characters), stored in the
ops_logstable in D1 with the current user's ID and timestamp, and associated tags are written to thelog_tagsjunction table. - AI extraction pipeline triggers — The system calls AI Gateway with the voice-tuned system prompt (see Prompt Templates section), passing the log content and the user's calibrated voice signature. The call uses GPT-4o at temperature 0.4 with strict JSON output mode.
- Draft is stored — The AI response is parsed and validated against the output JSON schema. On success, a record is inserted into the
draftstable with statuspending, the full draft content, source citations, voice-match score, and token usage metadata. The AI call is logged toai_extractsfor audit. - User is redirected to
/drafts— After a 1.5-second delay with a success toast ("Log submitted. AI is extracting drafts..."), the browser navigates to the Review Queue. - Draft appears in the review queue — The new draft card shows: title, body preview, voice-match score badge, source notes count, and action buttons (Approve, Edit & Approve, Reject).
- Human review gate — The user expands the draft, reads the full content, checks the Source Notes panel for traceability, and evaluates the Voice Match indicator. They can edit inline before making a decision.
- Decision is recorded — On Approve: status becomes
accepted, a confirmation toast appears, and export/share options unlock. On Reject: a reason is required (dropdown: Inaccurate, Wrong voice, Missing context, Other), status becomesrejected, and the draft is soft-archived after 30 days. On Edit & Approve: the edited content overwrites the draft, status becomesaccepted, and the edit is logged. - Audit trail written — Every decision is appended immutably to the
ai_extractstable with timestamp, user ID, action, and reason.
Flow 2: Dashboard Check → Quick Actions
The morning ritual page that tells the founder what needs attention.
- User logs in and lands on
/dashboard— The dashboard is the default authenticated landing page. - Stats row loads — Four cards render: Total Logs (lifetime count), Pending Review (count of drafts with status
pending— badge highlighted if >0), Approved This Week (count ofaccepteddrafts in last 7 days), and AI Extractions (totalai_extractscount). - Recent activity list — The last 5 logs or drafts appear with status badges, log date, and a content preview. Clicking any item navigates to its detail view.
- Quick actions — The primary CTA "Submit New Log" is always visible. If pending drafts exist, a secondary CTA "Review N Pending Drafts" appears with an amber badge, linking directly to
/drafts. - Empty state — For first-time users with zero data, the dashboard shows a welcome message, a "Submit Your First Log" button, and an optional "Load Sample Data" button that creates 1-2 example logs with pre-generated drafts so the user can explore the review workflow.
Flow 3: Browse History → Revisit Past Logs
Full audit trail for accountability and content reuse.
- User navigates to
/history— A server-side rendered data table loads with the 25 most recent logs, ordered bylog_datedescending. - Table columns — Date | Log Preview (first 80 chars) | Tags | Draft Status (badge: Pending/Accepted/Rejected/None) | Actions ("View Details" link).
- Search and filter — A text input searches across
contentvia a D1LIKEquery. A status filter dropdown filters by draft status. A tag filter shows only logs with selected tags. All filters are server-side. - Row expansion or detail view — Clicking a row opens a detail panel or navigates to
/logs/[id], showing the full log content, all associated tags, the generated draft (if any) with its status, and the full source citations list. - Re-extraction — From the detail view, a "Regenerate Draft" button re-runs the AI extraction pipeline on the existing log (useful if voice profile was updated since the first extraction). This creates a new draft record and supersedes the old one.
- Pagination — Server-side pagination at 25 rows per page. D1 offset/limit queries with indexed
user_id+log_datefor performance.
Screen and View Inventory
| Page | Route | Primary Job | Primary CTA | Empty State | Error State | Permission |
|---|---|---|---|---|---|---|
| Dashboard | /dashboard | Morning overview: stats, recent activity, quick actions | "Submit New Log" (→ /logs/new) | Welcome message + "Submit Your First Log" + optional "Load Sample Data" | Segment error.tsx: error card with "Something went wrong loading your dashboard" + Retry button | Auth-only; middleware redirects unauthenticated to /login |
| Submit Log | /logs/new | Friction-free input for raw operational notes | "Submit Log" (Server Action) | N/A — form page always ready | Form-level: Zod inline errors. Server-level: Sonner toast "Failed to process log. Please try again." | Auth-only |
| Review Queue | /drafts | Human review gate — approve, reject, or edit AI drafts before any external use | Per-item: "Approve" / "Edit & Approve" / "Reject". Bulk: "Approve All" / "Reject All" | "All caught up! No drafts pending review." + "Submit a New Log" outline button | Segment error.tsx: "Unable to load drafts" + retry. Per-item: toast feedback | Auth-only |
| History | /history | Searchable, filterable audit trail of all logs and drafts | "View Details" (per row) | "No history yet. Your submitted logs and approved drafts will appear here." + "Submit Your First Log" | Segment error.tsx: "Unable to load history" + retry. Pagination errors inline | Auth-only |
| Settings | /settings | Profile, voice calibration, preferences, connected accounts | "Save Changes" (per section) | N/A — settings always have form fields | Per-section error display. Profile save: inline error. Toast for async confirmation | Auth-only |
Navigation Structure
A collapsible sidebar (shadcn/ui) on desktop, hamburger-triggered Sheet drawer on mobile.
Sidebar items (top to bottom):
| Icon | Label | Route | Badge |
|---|---|---|---|
| LayoutDashboard | Dashboard | /dashboard | — |
| FileText | Submit Log | /logs/new | — |
| ClipboardList | Review Queue | /drafts | Pending draft count |
| RotateCcw | History | /history | — |
| Settings | Settings | /settings | — |
Keyboard shortcut: Ctrl/Cmd+B toggles sidebar collapse to icon-only mode.
Mobile (<768px): Sidebar collapses to hamburger menu. Sheet slides from left at 18rem width. Content padding reduces from p-6 to p-4. Toast notifications move to top-center.
First-Run Experience
The first-run flow prioritizes getting the user to their first successful draft as fast as possible. No forced tutorial; the UI is self-explanatory for intermediate developers.
Step 1: Voice Calibration (One-Time Setup)
On first login with zero data, the dashboard empty state includes a banner: "Before you submit your first log, set up your writing voice so AI drafts sound like you." Three substeps:
- Voice Extraction — The user pastes 3-5 existing blog posts or writing samples into a textarea. The system runs a voice extraction prompt via AI Gateway that returns a structured Voice Pattern block (Identity, Audience, Voice Rules, Structural Patterns, Output Calibration). This is stored in the user's profile.
- Gap Test — The user reviews a 100-word sample paragraph generated in their extracted voice, identifies discrepancies, and edits the Voice Rules directly in a text editor.
- Save Profile — The validated Voice Pattern block is saved to the
userstable (or KV) asvoice_profile. It can be re-calibrated anytime from Settings.
Estimated time: 3-5 minutes. Skippable — user can submit logs with a default voice profile and calibrate later.
Step 2: First Log Submission
After voice setup (or skip), the dashboard empty state shows the primary "Submit Your First Log" CTA. Clicking it navigates to /logs/new. The textarea has a placeholder with a real example:
Paste your operational notes here... e.g.,
SUPPORT: User emailed saying they loved the transcription feature but
wished it worked offline. They'd pay double for offline mode.
PRODUCT: Decided to kill the PDF export feature. Only 0.3% of users
ever used it. Deleted 2,400 lines of code today. Felt surprisingly good.
After submission, the user is redirected to /drafts where their first AI-generated draft appears.
Step 3: First Draft Review
The first draft card in the review queue includes subtle tooltip hints on the Source Notes panel ("These links trace each claim back to your original log") and Voice Match indicator ("Score above 75% means the draft sounds like you"). Tooltips are one-time, tracked via localStorage flag hasSeenTooltips.
An optional "Load Sample Data" button on the dashboard creates 1-2 pre-generated example drafts in pending state, letting the user explore the review workflow with realistic content before committing their own data.
Data Model
Cloudflare D1 (SQLite) with 7 tables. Drizzle ORM for type-safe queries. Attachments table can be deferred post-MVP if file storage is not needed immediately.
SQL CREATE TABLE Statements
-- ============================================================ -- OpsLog Schema for Cloudflare D1 (SQLite) -- ============================================================ PRAGMA foreign_keys = ON;
Drizzle ORM Schema
// src/db/schema.ts
import {
sqliteTable, integer, text, real, primaryKey, index, uniqueIndex,
} from "drizzle-orm/sqlite-core";
import { relations, sql } from "drizzle-orm";
import { createId } from "@paralleldrive/cuid2";import { defineConfig } from "drizzle-kit";
export default defineConfig({
out: "./drizzle",
schema: "./src/db/schema.ts",
dialect: "sqlite",Database Client Setup
// src/db/client.ts
import { drizzle } from "drizzle-orm/d1";
import * as schema from "./schema";
export function getDb(d1Binding: D1Database) {
return drizzle(d1Binding, { schema });
}
Sample Records
users:
| id | name | role | created_at | |
|---|---|---|---|---|
usr_abc123def456 | alice@example.com | Alice Chen | user | 1753987200 |
ops_logs:
| id | user_id | log_date | content | mood | energy_level | word_count | created_at |
|---|---|---|---|---|---|---|---|
1 | usr_abc123def456 | 2025-07-28 | Morning standup ran long due to deployment issues. Fixed the DNS misconfiguration for staging by noon. Paired with Bob on the auth migration. | good | 7 | 42 | 1753728000 |
tags:
| id | name | color | description |
|---|---|---|---|
1 | standup | #4CAF50 | Daily standup notes |
2 | blocker | #F44336 | Blocked items |
3 | shipped | #2196F3 | Completed/deployed work |
log_tags:
| log_id | tag_id |
|---|---|
1 | 1 |
1 | 3 |
drafts:
| id | log_id | user_id | status | content | model_used | tokens_input | tokens_output | confidence | created_at |
|---|---|---|---|---|---|---|---|---|---|
1 | 1 | usr_abc123def456 | pending | ## Daily Summary - Jul 28, 2025\n\n- Infra: Resolved DNS misconfiguration... | gpt-4o | 486 | 312 | 0.92 | 1753728600 |
Migration Commands
# Generate migration from schema
npx drizzle-kit generate --name=init
# Apply to local database
npx wrangler d1 migrations apply <db-name> --local
# Apply to production
npx wrangler d1 migrations apply <db-name> --remote
# Verify
npx wrangler d1 migrations list <db-name>
# Optimize indexes after creation
npx wrangler d1 execute <db-name> --command="PRAGMA optimize"
Automation Workflow Inventory
AI Extraction Pipeline
The pipeline that converts a submitted ops log into a review-ready draft.
[Log Submitted]
|
v
[Store log in D1] ----(on success)----> [Queue AI extraction job]
| |
v vPipeline Steps
- Log Storage — The submitted log is inserted into
ops_logsvia a Server Action. Tag associations are written tolog_tagsin the same transaction using D1 batch operations. - AI Extraction Trigger — Two options based on load:
- In-request processing (default for MVP, 30 logs/month): The AI call happens synchronously within the Server Action, returning the draft to the client after 5-25 seconds.
- Queue-based processing (scalability option): The log ID is sent to a Cloudflare Queue for background processing. The queue consumer calls AI Gateway and stores the result.
- AI Gateway Call — The system sends a
messagesarray to AI Gateway with the voice-tuned system prompt and the log content as user message. Model: GPT-4o, temperature 0.4, strict JSON output mode. - Response Handling — The JSON response is validated against the output schema. On success, a
draftsrecord is created with statuspending. On failure, the error is logged toai_extractsand a retry is scheduled (max 3 attempts with exponential backoff: 2s, 5s, 10s). - Draft Surfacing — The new draft appears in the
/draftsreview queue. If the user is already on that page, a toast notification appears: "New draft ready for review." - Human Review Gate Enforcement — This is an architectural constraint, not a prompt instruction. The
statusfield on thedraftstable controls what actions are possible:pending: Only "Approve", "Reject", "Edit & Approve" actions available. No external sharing enabled.accepted: Share/Export/Email/Slack options unlock.rejected: Draft is hidden from queue after 30 days.superseded: Replaced by a newer draft; read-only.
Retry Logic
| Attempt | Delay | Action on Failure |
|---|---|---|
| 1 | Immediate | If AI Gateway returns 5xx or invalid JSON, wait 2s and retry |
| 2 | 2 seconds | If still failing, wait 5s and retry |
| 3 | 5 seconds | If still failing, mark draft status as error, log full error to ai_extracts, notify user via toast with "Retry" button |
Error Handling
- AI Gateway timeout (>45s): Returns error to client, draft record created with placeholder "Draft generation timed out. Click to retry."
- Invalid JSON response: Logged to
ai_extracts, retry initiated. If all retries exhausted, user sees "Draft generation failed. The AI returned an unexpected format. Retry?" - Rate limiting (429): Automatic backoff with jitter. Max wait 30s.
- Model unavailability: Fallback to Claude Sonnet via AI Gateway fallback routing (configured in dashboard).
Human Review Gate Rules
- No external output without approval — Share, Export, Email, Slack buttons are disabled/greyed out until
status = 'accepted'. - Edit resets approval — Any edit to an
accepteddraft changes status back topending, requiring re-approval. - Rejection requires reason — Rejection must include a reason from dropdown (Inaccurate, Wrong voice, Missing context, Other) + optional text note.
- Timeout safety — Drafts in
pendingstate auto-archive after 30 days of inactivity. - Audit trail — Every review decision (who, what, when, reason) is logged immutably to
ai_extracts. - Single reviewer — For MVP, only the submitting user can approve their own drafts (no multi-user approval chain).
Prompt Templates
Voice Calibration Prompt
Run once during first-run setup. Paste 3-5 writing samples where indicated.
You are a voice encoding specialist. Analyze the writing samples below and extract the author's Voice Patterns into a structured block. IDENTITY (2 sentences): Who the author is, what they do, credibility markers. Specific, not generic.
Main Extraction System Prompt
<SYSTEM_PROMPT> ## ROLE & CONTEXT You are a ghostwriter for a solo bootstrapped AI productivity SaaS founder. Your output must sound exactly like the founder wrote it themselves -- raw, honest, with specific operational detail and zero corporate polish. You
User Message Template
Generate a blog post draft from the following operational logs.
Voice profile: [validated voice pattern block or reference to stored profile]
Post type: [standard | deep_dive]
Target audience: [indie_hackers | ai_builders | founders]
Date range: [e.g., Jan 10-16, 2025]
Focus angle (optional): [technical_decision | customer_lesson | growth_moment]
Log entries:
[paste log content here]
Respond ONLY with valid JSON matching the output schema. No markdown fences.
Output JSON Schema
{
"$schema": "https://json-schema.org/draft-07/schema",
"title": "FounderBlogDraft",
"type": "object",
"required": ["metadata", "draft", "source_citations", "voice_match_note"],
"properties": {Model Parameters
| Parameter | Value | Reason |
|---|---|---|
| Primary Model | gpt-4o-2024-08-06 or later | Best balance of instruction following, voice adherence, structured JSON output via response_format: { type: "json_schema" } |
| Fallback Model | claude-sonnet-4-20250514 | Superior for nuanced voice work when GPT-4o drifts |
| Temperature | 0.4 | Lower than default for voice consistency; higher than 0.2 to preserve natural cadence |
| Top-P | 0.85 | Balanced filtering -- prevents unlikely tokens while allowing vocabulary variety |
| Top-K | 50 | Moderate constraint that works well with Top-P |
| Max Tokens | 2,048 | Sufficient for 1,200-word posts plus JSON wrapper. Increase to 4,096 for deep-dives |
| Frequency Penalty | 0.3 | Prevents repetitive signature phrases |
| Presence Penalty | 0.2 | Light encouragement for topic variety |
| JSON Mode | strict: true | Guarantees schema adherence |
| System Prompt Caching | Enable via cache_control | Reduces latency and cost 40-60% on repeated calls |
Temperature by Task Stage
| Stage | Temperature | When to Use |
|---|---|---|
| Voice extraction/calibration | 0.7 | Creative exploration during voice discovery |
| Draft generation (production) | 0.4 | Voice consistency |
| Self-check/evaluation | 0.2 | Deterministic compliance checking |
| Title generation | 0.6 | Slightly more creative for hook optimization |
Build Order
Week 1-2: Project Setup + D1 Schema + Auth
| Day | Task | Deliverable |
|---|---|---|
| 1 | Create Cloudflare account, install wrangler, run npm create cloudflare@latest | Project scaffolded |
| 2 | Install @opennextjs/cloudflare, Drizzle ORM, Better Auth dependencies | Dependencies installed |
| 3 | Configure wrangler.jsonc, open-next.config.ts, next.config.ts | Config files ready |
| 4 | Create D1 database, apply initial schema migration | Database live with all 7 tables |
| 5 | Set up Drizzle client, create database connection layer | src/db/client.ts working |
| 6-7 | Configure Better Auth with D1 + KV, create auth routes | /api/auth/[...all]/route.ts handling login |
| 8-9 | Create login/signup pages, protect dashboard routes | Auth flow complete, middleware redirects working |
| 10 | Test auth end-to-end locally, verify D1 writes | Local auth + DB pipeline verified |
| 11-12 | Create sidebar navigation, dashboard layout shell | Dashboard layout with nav rendering |
| 13-14 | Create dashboard page with stats cards (static data) | /dashboard loads with stat placeholders |
Key commands:
npm create cloudflare@latest my-app -- --framework=next --experimental
pnpm add @opennextjs/cloudflare drizzle-orm better-auth better-auth-cloudflare
pnpm add -D drizzle-kit wrangler
npx wrangler d1 create my-app-db
npx drizzle-kit generate --name=init
npx wrangler d1 migrations apply my-app-db --local
Week 3-4: Log Submission + Storage
| Day | Task | Deliverable |
|---|---|---|
| 15-16 | Create ops_logs table queries, insert with tags | Server Action for log creation |
| 17-18 | Build /logs/new page with auto-expanding textarea | Submit page with form validation |
| 19-20 | Implement tag input (comma-separated, inline pills) | Tagging UI functional |
| 21-22 | Create loading.tsx and error.tsx for submit flow | Skeleton + error states |
| 23-24 | Wire form submission to D1, test with sample data | Logs stored with tags in D1 |
| 25-26 | Add success toast, redirect to /drafts | Post-submit UX complete |
| 27-28 | End-to-end test: submit log → verify in D1 → check history | Log submission pipeline solid |
Week 5-6: AI Integration + Prompt Tuning
| Day | Task | Deliverable |
|---|---|---|
| 29-30 | Create AI Gateway connection, test basic completion | src/services/ai.service.ts calling AI Gateway |
| 31-32 | Implement voice calibration flow in Settings | Voice extraction prompt working |
| 33-34 | Build full system prompt with voice signature injection | System prompt assembled dynamically |
| 35-36 | Implement JSON schema validation for AI responses | Response parsing + validation |
| 37-38 | Wire AI call into log submission pipeline, store draft | Draft generated and stored on log submit |
| 39-40 | Test with 5+ sample logs, tune prompt for quality | Voice match score >= 70% on test inputs |
| 41-42 | Add retry logic (3 attempts), error handling | Resilient AI pipeline |
Week 7-8: Draft Review UI + Human Gate
| Day | Task | Deliverable |
|---|---|---|
| 43-44 | Create /drafts page with draft cards | Draft list rendering with status badges |
| 45-46 | Build Source Notes panel (collapsible sidebar) | Traceable links from draft to log |
| 47-48 | Build Voice Match indicator (score + breakdown) | Visual score with color coding |
| 49-50 | Implement Approve action with confirmation toast | Approve flow + status change |
| 51-52 | Implement Reject action with reason dropdown | Reject flow + audit logging |
| 53-54 | Implement Edit & Approve with inline editor | Editable drafts + re-approval |
| 55-56 | Enforce "no external sharing until approved" gate | Share buttons disabled until accepted |
| 57-58 | Add bulk actions (Approve All / Reject All) | Bulk review workflow |
| 59-60 | End-to-end test: submit → review → approve → verify unlock | Human gate fully functional |
Week 9-10: History + Search + Polish
| Day | Task | Deliverable |
|---|---|---|
| 61-62 | Create /history page with data table | Sortable table of all logs |
| 63-64 | Implement server-side text search across log content | Search via D1 LIKE queries |
| 65-66 | Add status filter and tag filter | Multi-filter working |
| 67-68 | Implement pagination (25 rows/page) | Server-side pagination |
| 69-70 | Add log detail view with associated draft | Drill-down from history to full content |
| 71-72 | Implement "Regenerate Draft" from history | Re-extraction from existing logs |
| 73-74 | Polish empty states on all pages | Every page has meaningful empty state |
| 75-76 | Add loading skeletons to all async pages | Skeleton screens matching content dimensions |
Week 11-12: Testing + Deployment + Voice Fine-Tuning
| Day | Task | Deliverable |
|---|---|---|
| 77-78 | Write Vitest unit tests for utility functions | Unit tests passing |
| 79-80 | Write Playwright E2E test for critical path | E2E: submit → review → approve |
| 81-82 | Set up GitHub Actions CI/CD pipeline | .github/workflows/ci.yml + deploy.yml |
| 83-84 | Fine-tune voice prompt with 3-5 real founder samples | Voice match >= 75% on calibrated inputs |
| 85-86 | Deploy to production Cloudflare Workers | Live at https://your-app.workers.dev |
| 87-88 | Run smoke tests against production | Production pipeline verified |
| 89-90 | Final polish, bug fixes, README documentation | Production-ready v1 |
Acceptance Tests
MVP Test Scenario
Test ID: MVP-001 Objective: Verify the complete end-to-end pipeline produces a review-ready draft with traceable source notes and voice-match indicator within 30 seconds.
Preconditions:
- App deployed to Cloudflare Workers with D1 database
- AI Gateway configured and responding
- Voice profile trained with at least 3 sample writings
- User authenticated on the dashboard page
- Test data does not exist in the database
Minimal Input Log:
2025-01-15 — Daily Ops SUPPORT: - Had a call with Sarah from Acme Corp. She's frustrated that the CSV export doesn't include custom fields. She's been asking for this for 3 weeks. Her exact words: "I'm spending 2 hours every Friday manually
Expected Output:
- Draft appears within 30 seconds of submission
- Draft contains complete, coherent write-up based on the input log
- Source Notes panel shows at least 2 traceable entries linking to original log
- Voice Match indicator displays a score >= 70%
- Draft content is fully editable before approval
- No external sharing options appear until after human approval
Acceptance Criteria Checklist
| ID | Criteria | Priority | Test Method |
|---|---|---|---|
| AC-01 | Draft appears in dashboard within 30 seconds of log submission | MUST | Playwright — measure wall-clock time from click to render |
| AC-02 | Draft contains all key information from input log (no dropped items) | MUST | Manual — compare input sections against draft content |
| AC-03 | Source Notes panel shows at least one traceable entry per major log section | MUST | Manual + Playwright — verify DOM contains linked entries |
| AC-04 | Each Source Note links back to specific original log text | MUST | Playwright — click source note, verify highlight in original |
| AC-05 | Voice Match indicator displays composite score 0-100% | MUST | Playwright — verify score element renders with numeric value |
| AC-06 | Voice Match score is >= 70% for well-trained voice profile | SHOULD | Manual — verify on calibrated voice profile |
| AC-07 | Draft content is fully editable in dashboard editor before approval | MUST | Playwright — type in editor, verify content change |
| AC-08 | Share/Export/External send buttons disabled until draft approved | MUST | Playwright — verify button state transitions |
| AC-09 | Approving draft unlocks external sharing options | MUST | Playwright — click approve, verify buttons enable |
| AC-10 | Rejecting draft requires selecting reason from dropdown | MUST | Manual — attempt rejection without reason, verify blocked |
| AC-11 | All review decisions (approve, reject, edit) logged with timestamp | MUST | Manual — check ai_extracts table for decision entries |
| AC-12 | Input log preserved immutably and associated with draft | MUST | Playwright — verify API response includes original_log field |
| AC-13 | Draft survives page refresh without data loss | MUST | Playwright — refresh page, verify draft state persists |
| AC-14 | AI Gateway request/response logged with latency and token count | SHOULD | Manual — verify AI Gateway dashboard shows request |
| AC-15 | Drafts can be deleted by owner from drafts list | COULD | Manual — verify delete button and confirmation flow |
Human Review Gate Test
1. Submit the minimal input log via /logs/new
2. Navigate to /drafts, verify draft appears with status "pending"
3. Expand draft card, verify Source Notes panel shows 2+ entries
4. Verify Voice Match score is visible (any numeric value 0-100)
5. Verify Share/Export buttons are disabled/greyed out
6. Click "Approve" → verify confirmation toast → verify status "accepted"
7. Verify Share/Export buttons are now enabled
8. Click "Edit & Approve" → modify draft text → save
9. Verify status returns to "pending" (edit resets approval)
10. Click "Reject" → attempt without reason → verify blocked
11. Select "Wrong voice" as reason → confirm rejection → verify status "rejected"
12. Refresh page → verify all decisions persisted
13. Check ai_extracts table → verify all 4 decisions logged with timestamps
Risks and Validation
| Risk | Impact | Likelihood | Mitigation | Validation Test |
|---|---|---|---|---|
| LLM output quality inconsistency — Drafts vary in quality between submissions even with same voice profile; generic or off-voice outputs damage trust | High | Medium | (1) Lower temperature to 0.4 for consistency; (2) Include self-check step in prompt; (3) Implement voice-match score threshold (< 70% triggers regeneration warning); (4) Fallback to Claude Sonnet if GPT-4o drifts | Run 10 identical logs through pipeline, measure voice-match score variance. Target: std dev < 8%. Score >= 70% on 8/10 runs |
| Voice drift over time — As the founder's writing evolves, the calibrated voice profile becomes stale, producing increasingly mismatched drafts | Medium | High | (1) Monthly recalibration prompt (re-extract from 3 most recent published posts); (2) Track confidence_score trend in ai_extracts; (3) Alert user when 2+ consecutive drafts score < 75%; (4) One-click recalibration from Settings | Compare voice-match scores of first 5 drafts vs. drafts generated after 30 days. Flag if average drops > 10 points |
| Founder stops capturing logs — The core behavioral change (daily logging) may not stick, leading to app abandonment | High | Medium | (1) Keep submit page friction-free — one textarea, no required fields; (2) Optional daily reminder email (toggle in Settings); (3) "Load Sample Data" shows value before commitment; (4) Low stakes: logs are private by default | Track daily active submitters in first 30 days. Target: 60% of registered users submit at least 3 logs in first week |
Cloudflare adapter breaking changes — @opennextjs/cloudflare is v1.0-beta; breaking changes could require migration work | Medium | Low | (1) Pin exact version in package.json; (2) Monitor adapter releases weekly; (3) Keep migration notes in README; (4) Abstract Cloudflare-specific code behind service layers | Verify build passes with pinned version weekly. Subscribe to adapter release notes |
| Cost scaling beyond $5/mo — Usage grows beyond 30 logs/month or AI token consumption exceeds free tier | Low | Low | (1) All usage well within Workers Paid included quotas up to 3,000 logs/month; (2) AI Gateway caching reduces redundant calls; (3) Monitor monthly spend via Cloudflare dashboard; (4) Voice prompt caching reduces per-call cost 40-60% | Simulate 300 logs/month locally, verify all usage within included quotas. Track actual spend for first month |
| Auth system complexity — Better Auth with D1 + KV may have edge cases that complicate the solo build | Medium | Medium | (1) Use better-auth-cloudflare package with documented patterns; (2) Start with email/password only, add OAuth later; (3) Abstract auth behind src/auth/ module; (4) Test auth flow on every deploy | Run full auth flow (login, session, logout, protected route redirect) in CI on every push |
| D1 query performance at scale — Unindexed queries on large datasets cause slow page loads and high row-read costs | Medium | Low | (1) All queries use composite index on (user_id, log_date); (2) Run PRAGMA optimize after migrations; (3) Monitor D1 row-read metrics in Cloudflare dashboard; (4) Add query-level caching with Cache API for dashboard stats | Load test with 10,000 log rows, verify dashboard loads in < 1.5s. Monitor D1 row-read count |
Non-Functional Requirements
Performance
| Metric | Threshold | Measurement Method |
|---|---|---|
| Draft generation latency (p95) | < 30 seconds | Playwright wall-clock + AI Gateway logs |
| Dashboard page load (p95) | < 1.5 seconds | Lighthouse / Playwright timing |
| API response time (p95) | < 500 ms | Cloudflare Workers analytics |
| Time to first byte (TTFB) | < 200 ms | WebPageTest / Lighthouse |
| AI Gateway timeout | 45 seconds | Configurable via AI Gateway settings |
| History table filter/sort | < 500 ms | Server-side with indexed queries |
Security
| Requirement | Implementation |
|---|---|
| Transport encryption | TLS 1.3 (handled by Cloudflare) |
| Authentication | Better Auth with email/password + optional OAuth; sessions stored in KV |
| API key storage | Encrypted via Cloudflare Secrets (wrangler secret put) |
| Input sanitization | All user inputs validated with Zod before DB write |
| SQL injection prevention | D1 prepared statements via Drizzle ORM |
| Draft access control | Row-level: users can only access their own logs and drafts (WHERE user_id = ?) |
| Audit logging | Immutable ai_extracts table records every AI call and review decision |
| Session expiry | 7 days with 1-day refresh window |
Reliability
| Metric | Threshold |
|---|---|
| Monthly uptime | 99.0% (~7 hours downtime acceptable) |
| AI Gateway fallback | Claude Sonnet if GPT-4o fails |
| Data retention | Drafts retained 1 year; rejected drafts soft-deleted for 30 days |
| Backup strategy | D1 Time Travel for point-in-time recovery |
| Error handling | Graceful degradation: if AI fails, show error toast with retry |
| Error boundaries | Segment-level error.tsx on every route + root global-error.tsx |
Cost (30 logs/month)
| Service | Cost | Notes |
|---|---|---|
| Workers Paid Plan | $5.00/mo | Base subscription; all other usage within included quotas |
| D1 | $0 | <1GB storage, <3M rows written |
| AI Gateway | $0 | Core features (cache, rate limit, analytics) free |
| Workers AI tokens | $0 | ~30K tokens/month within 10K/day free tier |
| R2 storage | $0 | <100MB within 10GB free tier |
| Queues | $0 | <100 operations within 1M/month |
| Total | $5.00/mo | All usage well within included quotas |
Scalability Headroom
| Scale | Logs/Month | Est. Cost | Notes |
|---|---|---|---|
| Current | 30 | $5.00 | Within all free tiers |
| Growth (10x) | 300 | $5.00 | Still within all included quotas |
| Growth (100x) | 3,000 | $5.00 | AI tokens ~$0.05/mo overage |
| Growth (1000x) | 30,000 | $5.50 | AI tokens ~$0.45/mo; still negligible |
Not in MVP
The following features are intentionally excluded from V1 to keep scope tight. They are listed here to prevent scope creep and provide a roadmap for future versions.
| Feature | Why Not V1 | Future Version |
|---|---|---|
| Auto-posting to social platforms (Twitter/X, LinkedIn, etc.) | Requires human approval gate to be solid first; external posting is irreversible | V2 — after review gate is battle-tested |
| Analytics/engagement dashboard (views, likes, click-through) | Not the problem this app solves; founder can use native platform analytics | V2 — if content distribution becomes a module |
| Team/multi-user support | Single-founder MVP; RBAC adds unnecessary complexity | V2 — when team features justified by demand |
| Custom model fine-tuning | Voice calibration via prompt engineering is sufficient; fine-tuning requires training data pipeline | V3 — if prompt-based voice match cannot reach 80%+ |
| Mobile native app | Web UI is sufficient for a daily log submission tool | V3 — if mobile usage exceeds 40% of sessions |
| Real-time collaboration on drafts | Single user only; no collaboration needed | V2 — with team support |
| Advanced formatting / rich text editor | Markdown output is sufficient; editing is for polish, not composition | V2 — if non-technical users request WYSIWYG |
| API rate limiting beyond basic | 30 logs/month generates minimal load; Cloudflare's built-in rate limiting sufficient | V2 — if external API consumers added |
| Webhook integrations (Zapier, Make, etc.) | Email/Slack delivery is manual copy-paste from approved drafts | V2 — if automation demand is high |
| Scheduled/recurring draft generation | On-demand extraction is simpler and more predictable | V2 — weekly digest feature |
| AI-powered improvement on rejected drafts | User edits manually; AI feedback loop adds complexity | V2 — if rejection rate > 30% |
| Billing or payment processing | Self-hosted on user's Cloudflare account; no SaaS billing needed | V3 — if offering managed hosting |
Open Questions
- Which LLM model provides the best voice match for this use case? GPT-4o is the recommended primary with Claude Sonnet as fallback, but the optimal model may vary by founder's writing style. Run A/B tests: generate the same draft with both models and score voice match. Document the winner in the user's profile for future consistency.
- How many writing samples are needed for effective voice calibration? The Voice Patterns method recommends 3-5 samples, but the minimum viable number is unknown. Test with 1, 2, 3, and 5 samples and measure the resulting voice-match score. Evidence from research suggests diminishing returns after 3 high-quality samples.
- Should rejected drafts be auto-deleted after a period, or retained indefinitely? The current spec soft-archives rejected drafts for 30 days, then hard-deletes. Retaining them indefinitely provides a training corpus for future fine-tuning but consumes D1 storage. Decide based on user preference in Settings (default: retain 30 days).
- What is the optimal log length for best AI extraction quality? Too short (under 100 words) may not yield enough substance for a meaningful draft. Too long (over 2,000 words) may dilute the narrative thread or exceed token limits. The current assumption is 200-800 words is the sweet spot, but this should be validated against the first 50 real submissions.
- How should voice drift be detected automatically? The current approach tracks
confidence_scoretrend inai_extractsand alerts when 2+ consecutive drafts score below 75%. A more sophisticated approach would compare n-gram patterns or embedding similarity between recent approved drafts and the original voice profile. Start simple; add NLP comparison only if the threshold-based approach produces false positives. - Should there be a "regenerate" option for drafts that are close but not quite right? Yes — a "Regenerate with different angle" button on the draft card would re-run extraction with a different
focus_angleparameter (e.g., shift from "technical_decision" to "customer_lesson"). This is a V1.5 feature, not MVP. - What happens when a log contains sensitive information (customer names, revenue numbers, proprietary details)? The current system stores logs and drafts in the user's own D1 database (data never leaves their Cloudflare account). However, log content is sent to AI Gateway for processing. Document clearly that: (a) logs are processed through Cloudflare AI Gateway, (b) the user should sanitize customer names and sensitive numbers before submission if concerned, (c) a future "redaction" feature could auto-detect and mask sensitive patterns pre-submission.
FAQ
Q: Do I need to know Cloudflare already? Intermediate familiarity is assumed — you should know how to create a Worker, set up a D1 database, and deploy with Wrangler. If you're new to Cloudflare, budget an extra 2-3 days to work through the Cloudflare Workers getting started guide before Week 1.
Q: What if the AI draft doesn't sound like me? The voice calibration step (first-run setup) is designed to prevent this. Paste 3-5 pieces of your actual writing, and the system extracts your voice patterns into a structured profile. If a draft still feels off, the Voice Match indicator shows a score — below 70%, click "Regenerate" or edit the draft directly. You can also re-run voice calibration anytime from Settings with newer writing samples.
Q: How much does it cost to run? At 30 logs per month: $5/month for the Cloudflare Workers Paid plan. All other usage (D1 storage, AI Gateway, Workers AI tokens, R2, Queues) stays within the included free tiers. Even at 3,000 logs/month, the cost stays around $5.00 — only AI token overages would apply, estimated at ~$0.05/month.
Q: Can I use this for Twitter/LinkedIn/Newsletter? The draft output is Markdown-formatted blog posts. For Twitter, you can manually excerpt a paragraph. For LinkedIn, the full post works with minor formatting tweaks. For newsletters, the draft body can be copied directly. Auto-posting to any platform is intentionally not in V1 — every draft requires your approval before it can be shared externally.
Q: What happens to my data? Everything stays in your own Cloudflare account. Logs and drafts live in your D1 database, files in your R2 bucket. No third party (including the app developer) has access to your content. The only external call is to Cloudflare AI Gateway for draft generation, which processes your log content through the LLM. You can export all your data anytime from Settings.
Q: Can I export my logs and drafts? Yes. Settings includes an "Export All Data" button that downloads a JSON file containing all logs, drafts, tags, and review history. This is your data — you own it completely.
Q: Do I need to write logs in a specific format? No. The whole point is to accept messy, unstructured input. Bullet lists, timestamps, stream-of-consciousness, shorthand — the AI extraction pipeline handles all of it. There are no required fields. The only constraint is minimum 10 characters (to prevent accidental empty submissions).
Q: How long does the AI take to generate a draft? Typically 5-15 seconds for a standard post (600-1,200 words) from a single log entry. Complex logs or deep-dive posts may take up to 25 seconds. The system times out at 45 seconds and shows a retry button. Voice prompt caching (enabled via AI Gateway) reduces repeat-call latency by 40-60%.
Q: What if I miss a day of logging? The system has no daily requirement. Submit logs whenever you have something worth capturing. You can also submit multiple days at once — just paste logs from multiple days into the textarea and the AI will synthesize them into a single coherent draft or multiple drafts depending on narrative threads detected.
Q: Can I connect this to my existing note-taking app?
Not in V1. The app is designed as a standalone capture tool to minimize integration complexity. If you use Obsidian, Notion, or Apple Notes, the workflow is: capture in your existing tool throughout the day, then paste into /logs/new when you're ready to process. A future integration could pull directly from these apps via API, but manual copy-paste is the V1 pattern.
Citations Used
- Monolit — Best AI Writing Tool for Social Media in 2026
- Monolit — How AI Marketing Software Saves Founders 10 Hours Per Week
- Reddit r/indiehackers — How do you balance product development vs. content/SEO
- GetContentPlan — AI Post Generator for Freelancers: Workflow That Saves 4h Weekly
- The AI Hat — The AI Content Machine, The Solopreneur's Guide to the Best AI Tools for Content Creation
- Reddit r/Solopreneur — I built a tool to solve my own marketing problem
- Matt Giaro (Substack) — How I Use Obsidian To Write Daily Content
- Tubarks Blog — Leveraging Voice Recording and AI for Content Creation
- Kieran Glover (Medium) — Building a content creation system with Notion
- Ekofi (Substack) — Solo Founder's Guide to Using Notion
- Reddit r/Solopreneur — I spent 3 months building alone, and posting publicly felt harder than building
- Reddit r/startups — Do you also feel that being a founder is 99% content creation?
- Reddit r/indiehackers — Built a LinkedIn automation tool - $3K MRR in 3 months
- Reddit r/Freelancers — content creation taking too much time, anyone else
- Vect AI — Newsletter Empire: Scale To 50k Subs With AI Ops Model
- Reddit r/B2BSaaS — I personally lost 90 days of inbound pipeline when I stopped posting
- Reddit r/SaaS — For those who have successfully acquired their first 10-20 customers
- Monolit — AI Tools for Startup Founders That Save Hours Per Week
- Monolit — The Founder's Daily Content Creation Routine and Workflow
- Getting started with Cloudflare D1
- Use Drizzle ORM With a D1 Database in a Cloudflare Worker
- Full-stack Next.js + Cloudflare template
- D1 Best Practices: Use Indexes
- D1 SQL Statements Reference
- D1 Foreign Keys Documentation
- How to Use Cloudflare D1 Database
- Drizzle ORM SQLite Column Types
- Drizzle ORM Relations v2
- Drizzle ORM Get Started with D1
- Cloudflare Workers vs Pages recommendation
- @opennextjs/cloudflare adapter
- better-auth-cloudflare
- Cloudflare AI Gateway
- Cloudflare Queues
- Hono
- On Persona Prompting: Language as LLM Control Surface
- Atom Writer — Creating a Brand Voice Prompt for AI: Complete Template
- QuillBot — Few Shot Prompting: Use Cases & Examples
- Amir Teymoori — LLM Parameters: Temperature, Top-P, Top-K Guide
- Pressmaster.ai — AI Writing Prompts for a Consistent Brand Voice and Tone
- Catch Me If You Can? Not Yet: LLMs Still Struggle to Imitate Implicit Writing Styles (arXiv:2509.14543v1)
- On Verbalized Confidence Scores for LLMs (arXiv:2412.14737)
- Using Prompts to Guide LLMs in Imitating a Real Person's Language Style (arXiv:2410.03848v1)
- CyberSEO — Prompt Pipelines and the Logic of Long-Form AI Article Generation
- Prompts Daily — Voice Pattern Prompts: How I Get AI to Write in My Voice
- IndieHackers — The Complete Guide to Writing Agent System Prompts
- Evidently AI — LLM-as-a-judge: A Complete Guide
- OpenAI — Structured model outputs
- Forbes — A Guide To Aligning ChatGPT With Your Unique Writing Voice
- SaaS Empty State Design: 9 Patterns That Drive Activation
- shadcn/ui Sidebar
- Sonner — shadcn/ui Toast
- Next.js 14 App Router Project Structure: The Patterns That Actually Scale
- Next.js Error Boundaries: error.tsx, global-error.tsx, and Sentry Integration
- Next.js Middleware Authentication
- Human-in-the-Loop (HITL) for AI Agents: Patterns and Best Practices
- Cordum.io — Human-in-the-Loop AI: 5 Production Patterns
- MindK — 10-Step Guide to SaaS Product Development: 2026 Edition
- Quash Bugs — How to Test AI Features: Chatbots, LLMs & Recommendations
- ForaSoft — Non-Functional Requirements: A 2026 Buyer's Playbook
- Cloudflare GitHub Actions for CI/CD
- Playwright E2E Testing Guide
Blueprint Sources
Sources used in this Blueprint
Reference material used during the research and import process. Treat implementation details as assumptions until tested in your own environment.