# 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 (
      <html lang="zh-CN">
        <body>{children}</body>
      </html>
    );
  }
  ```

  `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<T>(input)` for structured JSON responses.
  - `generateText(input)` for prose responses.
  - `isLlmConfigured()` for UI/API warnings.

  If no API key exists, workflow modules use deterministic local fallbacks so tests and the UI remain usable.

- [ ] **Step 3: Implement workflow nodes**

  Implement the modules from the spec exactly:

  - `normalizeInput`
  - `extractCandidateFactCard`
  - `optimizeArticle`
  - `inspectQuality`
  - `rewriteFailedSections`
  - `runOptimizationWorkflow`

  Each node accepts typed input and returns typed output. No node reads from the database directly.

- [ ] **Step 4: Verify**

  ```bash
  npm test -- src/lib/workflow
  ```

- [ ] **Step 5: Commit**

  ```bash
  git add src/lib/llm src/lib/workflow
  git commit -m "feat: implement article optimization workflow"
  ```

## Task 5: Add API Routes

**Files:**

- Create: `src/app/api/jobs/route.ts`
- Create: `src/app/api/jobs/[jobId]/confirm-fact-card/route.ts`
- Create: `src/app/api/jobs/[jobId]/optimize/route.ts`
- Create: `src/app/api/jobs/[jobId]/exports/[fileName]/route.ts`
- Test: `src/app/api/__tests__/jobs.test.ts`

- [ ] **Step 1: Write route tests**

  Test:

  - `POST /api/jobs` validates input, creates a job, and returns a candidate fact card.
  - `POST /api/jobs/:jobId/confirm-fact-card` rejects unresolved uncertain items.
  - `POST /api/jobs/:jobId/optimize` rejects jobs without confirmed fact cards.
  - Successful optimize returns optimized article and QA report.
  - Export route rejects unknown filenames.

- [ ] **Step 2: Implement routes**

  Route behavior:

  - Create job: normalize input, extract candidate fact card, persist job.
  - Confirm fact card: validate user-confirmed facts, save brand template, save fact card.
  - Optimize: run orchestrator, save optimized revision, save QA report.
  - Export download: return existing generated file with correct content type.

- [ ] **Step 3: Verify**

  ```bash
  npm test -- src/app/api
  npm run build
  ```

- [ ] **Step 4: Commit**

  ```bash
  git add src/app/api
  git commit -m "feat: expose optimizer workflow api"
  ```

## Task 6: Build The Local Web UI

**Files:**

- Modify: `src/app/page.tsx`
- Create: `src/components/article-input-form.tsx`
- Create: `src/components/fact-card-editor.tsx`
- Create: `src/components/optimized-preview.tsx`
- Create: `src/components/qa-report-panel.tsx`
- Modify: `src/app/globals.css`

- [ ] **Step 1: Build Article Input form**

  Include fields:

  - Title.
  - Body.
  - Image description or image link, one per line.
  - Target platform.
  - User instructions.

  Submit calls `POST /api/jobs`.

- [ ] **Step 2: Build Fact Card editor**

  Render all fact-card fields from the spec. Disable optimization while `uncertain_items` is non-empty. Save calls `POST /api/jobs/:jobId/confirm-fact-card`.

- [ ] **Step 3: Build Optimize action and preview**

  Optimize button calls `POST /api/jobs/:jobId/optimize`. Preview displays title, summary, body markdown, image suggestions, changed sections, and content requiring user confirmation.

- [ ] **Step 4: Build QA report panel**

  Show each gate as pass, warn, or fail with evidence, reason, suggested fix, and target agent. Hard failures must visibly block export.

- [ ] **Step 5: Build export buttons**

  Enable downloads for:

  - `optimized.md`
  - `optimized.docx`
  - `qa_report.json`

  Disable export when QA has hard failures.

- [ ] **Step 6: Verify manually**

  Run:

  ```bash
  npm run dev
  ```

  Open `http://localhost:3000`, paste one sample article, confirm the fact card, optimize, review QA, and download files.

- [ ] **Step 7: Commit**

  ```bash
  git add src/app src/components
  git commit -m "feat: build local optimizer interface"
  ```

## Task 7: Implement Exporter

**Files:**

- Create: `src/lib/workflow/exporter.ts`
- Test: `src/lib/workflow/__tests__/exporter.test.ts`

- [ ] **Step 1: Write exporter tests**

  Test:

  - Markdown includes optimized title, summary, body, and image suggestions.
  - JSON export serializes the QA report.
  - Word export creates a `.docx` buffer with nonzero length.
  - Exporter writes all three files under `data/exports/job_xxx/`.

- [ ] **Step 2: Implement export functions**

  Functions:

  - `renderOptimizedMarkdown`
  - `renderQaReportJson`
  - `renderOptimizedDocx`
  - `writeJobExports`

  Use the `docx` package for the Word document. Do not use `pandoc` or LibreOffice.

- [ ] **Step 3: Integrate exporter with optimize route**

  After each successful optimization run, write the three export files and persist paths in the job result.

- [ ] **Step 4: Verify**

  ```bash
  npm test -- src/lib/workflow/__tests__/exporter.test.ts
  npm run build
  ```

- [ ] **Step 5: Commit**

  ```bash
  git add src/lib/workflow/exporter.ts src/lib/workflow/__tests__/exporter.test.ts src/app/api/jobs
  git commit -m "feat: export optimized article files"
  ```

## Task 8: Add Minimum Test Samples And End-To-End Checks

**Files:**

- Create: `samples/articles/industry-drift.json`
- Create: `samples/articles/company-name.json`
- Create: `samples/articles/title-quality.json`
- Create: `samples/articles/experience-conflict.json`
- Create: `samples/articles/image-text-mismatch.json`
- Create: `tests/e2e/mvp.spec.ts`

- [ ] **Step 1: Add five sample articles**

  Each JSON file includes:

  - `name`
  - `input`
  - `expectedHardFailures`
  - `expectedWarnings`

- [ ] **Step 2: Add Playwright smoke test**

  Test the happy path:

  - Load page.
  - Submit article input.
  - Edit and confirm fact card.
  - Run optimize.
  - See QA report.
  - Download Markdown, Word, and JSON links are present when hard failures are absent.

- [ ] **Step 3: Verify full suite**

  ```bash
  npm test
  npm run build
  npx playwright test
  ```

- [ ] **Step 4: Commit**

  ```bash
  git add samples tests package.json package-lock.json
  git commit -m "test: add optimizer mvp samples and smoke test"
  ```

## Task 9: Final Documentation And Runbook

**Files:**

- Create: `README.md`
- Create: `docs/superpowers/specs/2026-06-16-geo-agent-article-optimizer-design.md` remains unchanged unless the implementation reveals a spec correction.

- [ ] **Step 1: Write README**

  Include:

  - Project goal.
  - Local setup commands.
  - Required `.env.local` keys.
  - How to run tests.
  - How to start the local app.
  - Export file location.
  - MVP limitations from the spec.

- [ ] **Step 2: Run final verification**

  ```bash
  npm test
  npm run build
  ```

  Expected: all tests pass and production build succeeds.

- [ ] **Step 3: Commit docs**

  ```bash
  git add README.md docs/superpowers/specs/2026-06-16-geo-agent-article-optimizer-design.md
  git commit -m "docs: add local mvp runbook"
  ```

## Acceptance Mapping

- Paste title, body, image descriptions, and target platform: Task 6.
- Extract fact card and require confirmation: Tasks 2, 4, 5, 6.
- Save and reuse brand template: Task 3 and Task 5.
- Generate optimized article without changing confirmed facts: Task 4.
- Generate structured QA report for 10 gates: Task 4 and Task 6.
- Block export on hard failures: Task 4, Task 6, Task 7.
- Download Markdown and Word document: Task 7.
- Minimum five risk samples: Task 8.

## Development Order

Recommended execution order:

1. Scaffold app.
2. Domain model.
3. SQLite.
4. Workflow nodes.
5. API routes.
6. UI.
7. Exporter.
8. Samples and E2E.
9. README and final verification.

This order keeps each task independently testable and avoids building UI against unstable data contracts.

## Self-Review

- Spec coverage: all MVP scope items, page areas, workflow nodes, data model, error handling, acceptance criteria, and minimum samples are mapped to tasks.
- Placeholder scan: the plan contains no unresolved `TBD`, `TODO`, or open-ended implementation placeholders.
- Type consistency: planned names match the spec and remain stable across tasks.
- Scope check: this is one coherent local MVP. Publishing integrations, permissions, batching, direct `.docx` parsing, and complex Word templates remain excluded.
