# GEO Agent Article Optimizer MVP Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Build the local MVP described in `docs/superpowers/specs/2026-06-16-geo-agent-article-optimizer-design.md`: paste an article, confirm facts, optimize under constraints, run QA gates, and export Markdown, Word, and JSON. **Architecture:** Use a single Next.js application with server-side route handlers for workflow execution and SQLite persistence. Keep each workflow node as a focused TypeScript module so fact extraction, optimization, QA, rewriting, and exporting can be tested independently. **Tech Stack:** Node.js 22, npm, Next.js, React, TypeScript, Tailwind CSS, `better-sqlite3`, `zod`, OpenAI-compatible SDK, `docx`, Vitest, Playwright. --- ## Environment Summary The current device is suitable for development: - macOS 26.3 on Apple M4. - 16 GB memory, 10 CPU cores. - Node.js `v22.22.3`, npm `10.9.8`. - SQLite CLI `3.51.0`. - Git, curl, jq, and Xcode command line tools are available. Missing but required or recommended: - Required project npm dependencies listed below. - One LLM credential in `.env.local`, preferably `OPENAI_API_KEY`. - Optional `pnpm`, `yarn`, `bun`, `pandoc`, and LibreOffice are not needed for this MVP. ## Dependency Set Runtime dependencies: ```bash npm install next react react-dom better-sqlite3 zod openai docx nanoid ``` Development dependencies: ```bash npm install -D typescript @types/node @types/react @types/react-dom @types/better-sqlite3 eslint eslint-config-next prettier vitest @vitejs/plugin-react jsdom playwright ``` Environment file: ```bash cat > .env.local <<'EOF' OPENAI_API_KEY=replace-with-real-key OPENAI_MODEL=gpt-4.1-mini APP_DATA_DIR=./data EOF ``` The implementation must never commit `.env.local`, `data/app.db`, or generated export files. ## Planned File Structure Create or modify these files: ```text package.json tsconfig.json next.config.ts eslint.config.mjs postcss.config.mjs tailwind.config.ts vitest.config.ts .gitignore .env.example data/.gitkeep samples/articles/*.json src/app/globals.css src/app/layout.tsx src/app/page.tsx src/app/api/jobs/route.ts src/app/api/jobs/[jobId]/confirm-fact-card/route.ts src/app/api/jobs/[jobId]/optimize/route.ts src/app/api/jobs/[jobId]/exports/[fileName]/route.ts src/components/article-input-form.tsx src/components/fact-card-editor.tsx src/components/optimized-preview.tsx src/components/qa-report-panel.tsx src/lib/db/connection.ts src/lib/db/schema.ts src/lib/db/repositories.ts src/lib/domain/types.ts src/lib/domain/validation.ts src/lib/llm/client.ts src/lib/llm/prompts.ts src/lib/workflow/input-normalizer.ts src/lib/workflow/fact-extractor.ts src/lib/workflow/article-optimizer.ts src/lib/workflow/quality-inspector.ts src/lib/workflow/targeted-rewriter.ts src/lib/workflow/exporter.ts src/lib/workflow/orchestrator.ts src/lib/workflow/__tests__/*.test.ts ``` Responsibilities: - `src/app/page.tsx`: single-screen local workflow UI. - `src/app/api/**`: server endpoints for create, confirm, optimize, and download actions. - `src/components/**`: focused UI components for the four page areas in the spec. - `src/lib/domain/**`: shared TypeScript types and Zod schemas. - `src/lib/db/**`: SQLite connection, schema initialization, and repository functions. - `src/lib/llm/**`: model client and prompts. All model calls are isolated here. - `src/lib/workflow/**`: one module per internal agent node from the spec. - `samples/articles/**`: five required risk samples for testing. ## Task 1: Scaffold The Application **Files:** - Create: `package.json` - Create: `tsconfig.json` - Create: `next.config.ts` - Create: `eslint.config.mjs` - Create: `postcss.config.mjs` - Create: `tailwind.config.ts` - Create: `vitest.config.ts` - Modify: `.gitignore` - Create: `.env.example` - Create: `src/app/layout.tsx` - Create: `src/app/globals.css` - Create: `src/app/page.tsx` - Create: `data/.gitkeep` - [ ] **Step 1: Initialize npm metadata** Use `npm init -y`, then set scripts and package metadata: ```json { "name": "geo-agent-article-optimizer", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint", "test": "vitest run", "test:watch": "vitest" } } ``` - [ ] **Step 2: Install dependencies** Run: ```bash npm install next react react-dom better-sqlite3 zod openai docx nanoid npm install -D typescript @types/node @types/react @types/react-dom @types/better-sqlite3 eslint eslint-config-next prettier vitest @vitejs/plugin-react jsdom playwright ``` Expected: `package-lock.json` is created and `npm ls --depth=0` shows the listed packages. - [ ] **Step 3: Add environment and ignore rules** `.env.example`: ```text OPENAI_API_KEY= OPENAI_MODEL=gpt-4.1-mini APP_DATA_DIR=./data ``` `.gitignore` must include: ```text node_modules/ .next/ .env .env.* !.env.example data/app.db data/exports/ ``` - [ ] **Step 4: Add initial Next.js app shell** `src/app/layout.tsx`: ```tsx import "./globals.css"; export const metadata = { title: "GEO Agent Article Optimizer", description: "Local article optimization and QA workflow", }; export default function RootLayout({ children }: { children: React.ReactNode }) { return (
{children} ); } ``` `src/app/page.tsx` initially renders a heading and four empty sections: article input, fact card, optimized result, and quality report. - [ ] **Step 5: Verify scaffold** Run: ```bash npm run build npm test ``` Expected: build succeeds. Test command may report no test files until Task 2 adds tests. - [ ] **Step 6: Commit scaffold** ```bash git add package.json package-lock.json tsconfig.json next.config.ts eslint.config.mjs postcss.config.mjs tailwind.config.ts vitest.config.ts .gitignore .env.example data/.gitkeep src/app git commit -m "chore: scaffold local optimizer app" ``` ## Task 2: Define Domain Types And Validation **Files:** - Create: `src/lib/domain/types.ts` - Create: `src/lib/domain/validation.ts` - Test: `src/lib/domain/__tests__/validation.test.ts` - [ ] **Step 1: Write validation tests** Cover: - Valid article input accepts title, body, image descriptions or links, platform, and user instructions. - Fact card with unresolved `uncertain_items` is not ready for optimization. - Confirmed fact card with empty company full name is invalid. - QA report accepts only `pass`, `warn`, or `fail`. - [ ] **Step 2: Implement shared enums and interfaces** Include: - `PublishPlatform`: `official_site`, `media_article`, `comparison_review`, `recommendation_list`. - `CheckStatus`: `pass`, `warn`, `fail`. - `QualityRuleId`: the 10 rule IDs from the spec. - `ArticleInput`, `ImageInput`, `CandidateFactCard`, `ConfirmedFactCard`, `OptimizedArticle`, `QaReport`, `QaCheck`. - [ ] **Step 3: Implement Zod schemas** Add schemas matching the interfaces. Use `.min(1)` for required user text fields and `.array(...).default([])` for list fields. - [ ] **Step 4: Verify** Run: ```bash npm test -- src/lib/domain/__tests__/validation.test.ts ``` Expected: all validation tests pass. - [ ] **Step 5: Commit** ```bash git add src/lib/domain git commit -m "feat: define optimizer domain model" ``` ## Task 3: Add SQLite Persistence **Files:** - Create: `src/lib/db/connection.ts` - Create: `src/lib/db/schema.ts` - Create: `src/lib/db/repositories.ts` - Test: `src/lib/db/__tests__/repositories.test.ts` - [ ] **Step 1: Write repository tests** Use a temporary SQLite database path. Test: - Schema initialization creates all tables. - A brand template can be inserted and fetched. - An article job can be inserted and fetched. - A confirmed fact card can be saved for a job. - Optimized article revisions increment correctly. - QA report can be saved and fetched by job/revision. - [ ] **Step 2: Implement schema** Tables: - `brand_templates` - `article_jobs` - `fact_cards` - `optimized_articles` - `qa_reports` Store structured arrays and nested objects as JSON text. Add `created_at` and `updated_at` timestamps where relevant. - [ ] **Step 3: Implement repository functions** Required functions: - `createBrandTemplate` - `listBrandTemplates` - `getBrandTemplate` - `createArticleJob` - `getArticleJob` - `saveFactCard` - `getFactCard` - `saveOptimizedArticle` - `getLatestOptimizedArticle` - `saveQaReport` - `getLatestQaReport` - [ ] **Step 4: Verify** ```bash npm test -- src/lib/db/__tests__/repositories.test.ts ``` - [ ] **Step 5: Commit** ```bash git add src/lib/db git commit -m "feat: add sqlite persistence" ``` ## Task 4: Implement Workflow Nodes With Deterministic Fallbacks **Files:** - Create: `src/lib/llm/client.ts` - Create: `src/lib/llm/prompts.ts` - Create: `src/lib/workflow/input-normalizer.ts` - Create: `src/lib/workflow/fact-extractor.ts` - Create: `src/lib/workflow/article-optimizer.ts` - Create: `src/lib/workflow/quality-inspector.ts` - Create: `src/lib/workflow/targeted-rewriter.ts` - Create: `src/lib/workflow/orchestrator.ts` - Test: `src/lib/workflow/__tests__/*.test.ts` - [ ] **Step 1: Write node tests before implementation** Tests must cover: - InputNormalizer trims whitespace and converts image lines into image inputs. - FactExtractor places missing or conflicting company facts into `uncertain_items`. - ArticleOptimizer refuses to add claims outside the confirmed fact card. - QualityInspector returns the 10 required checks. - Hard failures are produced for incomplete company names, hallucinated numeric claims, industry drift, and conflicting experience years. - TargetedRewriter edits only the failing target area. - Orchestrator stops after two failed rewrite rounds. - [ ] **Step 2: Add LLM abstraction** `src/lib/llm/client.ts` exports: - `generateJson