Task 001: dockerized monorepo foundation
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
.git
|
||||
.env
|
||||
node_modules
|
||||
**/node_modules
|
||||
**/.next
|
||||
__pycache__
|
||||
**/__pycache__
|
||||
.pytest_cache
|
||||
.ruff_cache
|
||||
.mypy_cache
|
||||
dist
|
||||
coverage
|
||||
@@ -0,0 +1,21 @@
|
||||
# Local-only defaults for Docker Compose development.
|
||||
# These values are intentionally non-production and must not be reused as real credentials.
|
||||
COMPOSE_PROJECT_NAME=ai-content-pipeline
|
||||
|
||||
FRONTEND_PORT=3000
|
||||
BACKEND_PORT=8000
|
||||
RUNNER_PORT=8010
|
||||
|
||||
POSTGRES_USER=pipeline
|
||||
POSTGRES_PASSWORD=pipeline_local
|
||||
POSTGRES_DB=pipeline
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
REDIS_PORT=6379
|
||||
|
||||
MINIO_ROOT_USER=minio_local
|
||||
MINIO_ROOT_PASSWORD=minio_local_password
|
||||
MINIO_API_PORT=9000
|
||||
MINIO_CONSOLE_PORT=9001
|
||||
OBJECT_STORAGE_BUCKET=pipeline-local
|
||||
OBJECT_STORAGE_REGION=us-east-1
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
.env
|
||||
.DS_Store
|
||||
node_modules/
|
||||
.next/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.pytest_cache/
|
||||
.ruff_cache/
|
||||
.mypy_cache/
|
||||
dist/
|
||||
coverage/
|
||||
@@ -0,0 +1,10 @@
|
||||
.PHONY: dev down smoke
|
||||
|
||||
dev:
|
||||
docker compose up --build
|
||||
|
||||
down:
|
||||
docker compose down --remove-orphans
|
||||
|
||||
smoke:
|
||||
bash tests/smoke/public-health.sh
|
||||
@@ -0,0 +1,41 @@
|
||||
# AI Content Pipeline
|
||||
|
||||
Dockerized monorepo foundation for the AI Content Pipeline.
|
||||
|
||||
## Local Development
|
||||
|
||||
Start the full stack from the repository root:
|
||||
|
||||
```sh
|
||||
make dev
|
||||
```
|
||||
|
||||
Equivalent command:
|
||||
|
||||
```sh
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
The stack exposes:
|
||||
|
||||
| Service | URL |
|
||||
| --- | --- |
|
||||
| Frontend | `http://localhost:3000` |
|
||||
| Backend | `http://localhost:8000` |
|
||||
| Runner | `http://localhost:8010` |
|
||||
| MinIO console | `http://localhost:9001` |
|
||||
|
||||
Local-only defaults are documented in `.env.example`. Copy it to `.env` only when
|
||||
you need to override local ports or service credentials.
|
||||
|
||||
## Health Smoke
|
||||
|
||||
Run the public health smoke test:
|
||||
|
||||
```sh
|
||||
make smoke
|
||||
```
|
||||
|
||||
The smoke test starts Docker Compose, checks frontend/backend/runner health
|
||||
endpoints, and verifies that the backend can connect to Postgres, Redis, and
|
||||
S3-compatible object storage.
|
||||
@@ -0,0 +1,15 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY apps/backend/requirements.txt ./requirements.txt
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY apps/backend/src ./src
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uvicorn", "src.presentation.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
@@ -0,0 +1,5 @@
|
||||
fastapi==0.115.6
|
||||
uvicorn[standard]==0.34.0
|
||||
psycopg[binary]==3.2.3
|
||||
redis==5.2.1
|
||||
boto3==1.35.90
|
||||
@@ -0,0 +1 @@
|
||||
"""Backend service package."""
|
||||
@@ -0,0 +1,3 @@
|
||||
# Application
|
||||
|
||||
Application use cases and workflow orchestration belong in this layer.
|
||||
@@ -0,0 +1 @@
|
||||
"""Application layer for use cases and orchestration."""
|
||||
@@ -0,0 +1,3 @@
|
||||
# Domain
|
||||
|
||||
Business entities, value objects, and domain rules belong in this layer.
|
||||
@@ -0,0 +1 @@
|
||||
"""Domain layer for business concepts and invariants."""
|
||||
@@ -0,0 +1,4 @@
|
||||
# Infrastructure
|
||||
|
||||
Database, cache, object storage, queue, and other external adapters belong in
|
||||
this layer.
|
||||
@@ -0,0 +1 @@
|
||||
"""Infrastructure adapters for external services."""
|
||||
@@ -0,0 +1,74 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from collections.abc import Callable
|
||||
|
||||
import boto3
|
||||
import psycopg
|
||||
from botocore.config import Config
|
||||
from redis import Redis
|
||||
|
||||
|
||||
DependencyStatus = dict[str, str]
|
||||
|
||||
|
||||
def check_dependencies() -> DependencyStatus:
|
||||
checks: dict[str, Callable[[], None]] = {
|
||||
"postgres": _check_postgres,
|
||||
"redis": _check_redis,
|
||||
"object_storage": _check_object_storage,
|
||||
}
|
||||
|
||||
results: DependencyStatus = {}
|
||||
for name, check in checks.items():
|
||||
try:
|
||||
check()
|
||||
except Exception:
|
||||
results[name] = "error"
|
||||
else:
|
||||
results[name] = "ok"
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def _check_postgres() -> None:
|
||||
dsn = _required_env("POSTGRES_DSN")
|
||||
with psycopg.connect(dsn, connect_timeout=3) as connection:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("SELECT 1")
|
||||
cursor.fetchone()
|
||||
|
||||
|
||||
def _check_redis() -> None:
|
||||
redis_url = _required_env("REDIS_URL")
|
||||
client = Redis.from_url(redis_url, socket_connect_timeout=3, socket_timeout=3)
|
||||
try:
|
||||
if client.ping() is not True:
|
||||
raise RuntimeError("Redis ping failed")
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
|
||||
def _check_object_storage() -> None:
|
||||
endpoint = _required_env("OBJECT_STORAGE_ENDPOINT")
|
||||
access_key_id = _required_env("OBJECT_STORAGE_ACCESS_KEY_ID")
|
||||
secret_access_key = _required_env("OBJECT_STORAGE_SECRET_ACCESS_KEY")
|
||||
bucket = _required_env("OBJECT_STORAGE_BUCKET")
|
||||
region = os.environ.get("OBJECT_STORAGE_REGION", "us-east-1")
|
||||
|
||||
client = boto3.client(
|
||||
"s3",
|
||||
endpoint_url=endpoint,
|
||||
aws_access_key_id=access_key_id,
|
||||
aws_secret_access_key=secret_access_key,
|
||||
region_name=region,
|
||||
config=Config(signature_version="s3v4", connect_timeout=3, read_timeout=3),
|
||||
)
|
||||
client.head_bucket(Bucket=bucket)
|
||||
|
||||
|
||||
def _required_env(name: str) -> str:
|
||||
value = os.environ.get(name)
|
||||
if not value:
|
||||
raise RuntimeError(f"Missing required environment variable: {name}")
|
||||
return value
|
||||
@@ -0,0 +1,3 @@
|
||||
# Presentation
|
||||
|
||||
HTTP routes, request/response DTOs, and API wiring belong in this layer.
|
||||
@@ -0,0 +1 @@
|
||||
"""Presentation layer for HTTP APIs."""
|
||||
@@ -0,0 +1,27 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from src.infrastructure.dependencies import check_dependencies
|
||||
|
||||
|
||||
app = FastAPI(title="AI Content Pipeline Backend")
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
def health() -> dict[str, str]:
|
||||
return {"service": "backend", "status": "ok"}
|
||||
|
||||
|
||||
@app.get("/health/dependencies")
|
||||
def dependency_health() -> JSONResponse:
|
||||
dependencies = check_dependencies()
|
||||
is_ok = all(status == "ok" for status in dependencies.values())
|
||||
|
||||
return JSONResponse(
|
||||
status_code=200 if is_ok else 503,
|
||||
content={
|
||||
"service": "backend",
|
||||
"status": "ok" if is_ok else "error",
|
||||
"dependencies": dependencies,
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
# Shared
|
||||
|
||||
Cross-layer backend primitives belong here. Keep this layer small and avoid
|
||||
placing domain rules in shared code.
|
||||
@@ -0,0 +1 @@
|
||||
"""Shared backend primitives."""
|
||||
@@ -0,0 +1,17 @@
|
||||
# Frontend Architecture
|
||||
|
||||
This app uses Next.js App Router in `src/app/` for routing. Feature-Sliced
|
||||
Design layers live next to it under `src/`:
|
||||
|
||||
```text
|
||||
src/app/
|
||||
src/processes/
|
||||
src/pages/
|
||||
src/widgets/
|
||||
src/features/
|
||||
src/entities/
|
||||
src/shared/
|
||||
```
|
||||
|
||||
`src/pages` is an FSD page-composition layer. It is not the legacy Next.js Pages
|
||||
Router; the App Router is `src/app`.
|
||||
@@ -0,0 +1,12 @@
|
||||
FROM node:22-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY apps/frontend/package.json ./package.json
|
||||
RUN npm install
|
||||
|
||||
COPY apps/frontend ./
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["npm", "run", "dev", "--", "--hostname", "0.0.0.0"]
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// This file is generated by Next.js and kept here so TypeScript works before
|
||||
// the first local `next dev` run.
|
||||
@@ -0,0 +1,6 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@pipeline/frontend",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "16.2.6",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "22.10.5",
|
||||
"@types/react": "19.0.2",
|
||||
"@types/react-dom": "19.0.2",
|
||||
"typescript": "5.7.2"
|
||||
},
|
||||
"overrides": {
|
||||
"postcss": "8.5.14"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
:root {
|
||||
color-scheme: light;
|
||||
font-family:
|
||||
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
sans-serif;
|
||||
background: #f6f7f9;
|
||||
color: #1d2733;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
min-height: 100vh;
|
||||
padding: 48px;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function GET() {
|
||||
return Response.json({ service: "frontend", status: "ok" });
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import "./globals.css";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export const metadata = {
|
||||
title: "AI Content Pipeline",
|
||||
description: "Internal editorial workflow foundation",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<main>
|
||||
<h1>AI Content Pipeline</h1>
|
||||
<p>Dockerized monorepo foundation is running.</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Entities
|
||||
|
||||
FSD layer for domain entity UI and client-side models.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Features
|
||||
|
||||
FSD layer for user-facing actions and feature logic.
|
||||
@@ -0,0 +1,4 @@
|
||||
# Pages
|
||||
|
||||
FSD page-composition layer. This is separate from the Next.js App Router
|
||||
`app/` route layer and does not enable the legacy Pages Router.
|
||||
@@ -0,0 +1,4 @@
|
||||
# Processes
|
||||
|
||||
FSD layer for cross-page user workflows. Placeholder for upcoming article
|
||||
workflow processes.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Shared
|
||||
|
||||
FSD layer for reusable frontend primitives, API clients, and UI foundations.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Widgets
|
||||
|
||||
FSD layer for composed UI blocks used by pages.
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
FROM python:3.12-slim
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY apps/runner/src ./src
|
||||
|
||||
EXPOSE 8010
|
||||
|
||||
CMD ["python", "-m", "src.presentation.main"]
|
||||
@@ -0,0 +1 @@
|
||||
"""Runner service package."""
|
||||
@@ -0,0 +1,3 @@
|
||||
# Application
|
||||
|
||||
Runner use cases, job orchestration, and command policies belong in this layer.
|
||||
@@ -0,0 +1 @@
|
||||
"""Application layer for runner use cases."""
|
||||
@@ -0,0 +1,3 @@
|
||||
# Domain
|
||||
|
||||
Runner job entities and invariants belong in this layer.
|
||||
@@ -0,0 +1 @@
|
||||
"""Domain layer for runner job concepts."""
|
||||
@@ -0,0 +1,4 @@
|
||||
# Infrastructure
|
||||
|
||||
CLI execution, workspace, filesystem, and artifact storage adapters belong in
|
||||
this layer.
|
||||
@@ -0,0 +1 @@
|
||||
"""Infrastructure adapters for runner execution."""
|
||||
@@ -0,0 +1,3 @@
|
||||
# Presentation
|
||||
|
||||
Runner HTTP routes and API wiring belong in this layer.
|
||||
@@ -0,0 +1 @@
|
||||
"""Presentation layer for runner HTTP APIs."""
|
||||
@@ -0,0 +1,33 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from http import HTTPStatus
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
|
||||
|
||||
class HealthHandler(BaseHTTPRequestHandler):
|
||||
def do_GET(self) -> None:
|
||||
if self.path != "/health":
|
||||
self.send_error(HTTPStatus.NOT_FOUND)
|
||||
return
|
||||
|
||||
body = json.dumps({"service": "runner", "status": "ok"}).encode("utf-8")
|
||||
self.send_response(HTTPStatus.OK)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
|
||||
def log_message(self, format: str, *args: object) -> None:
|
||||
return
|
||||
|
||||
|
||||
def run() -> None:
|
||||
port = int(os.environ.get("PORT", "8010"))
|
||||
server = ThreadingHTTPServer(("0.0.0.0", port), HealthHandler)
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
@@ -0,0 +1,3 @@
|
||||
# Shared
|
||||
|
||||
Cross-layer runner primitives belong here. Keep this layer small.
|
||||
@@ -0,0 +1 @@
|
||||
"""Shared runner primitives."""
|
||||
@@ -0,0 +1,106 @@
|
||||
services:
|
||||
frontend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: apps/frontend/Dockerfile
|
||||
environment:
|
||||
NEXT_TELEMETRY_DISABLED: "1"
|
||||
BACKEND_URL: http://backend:8000
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-3000}:3000"
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_started
|
||||
|
||||
backend:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: apps/backend/Dockerfile
|
||||
environment:
|
||||
POSTGRES_DSN: postgresql://${POSTGRES_USER:-pipeline}:${POSTGRES_PASSWORD:-pipeline_local}@postgres:5432/${POSTGRES_DB:-pipeline}
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
OBJECT_STORAGE_ENDPOINT: http://minio:9000
|
||||
OBJECT_STORAGE_ACCESS_KEY_ID: ${MINIO_ROOT_USER:-minio_local}
|
||||
OBJECT_STORAGE_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD:-minio_local_password}
|
||||
OBJECT_STORAGE_BUCKET: ${OBJECT_STORAGE_BUCKET:-pipeline-local}
|
||||
OBJECT_STORAGE_REGION: ${OBJECT_STORAGE_REGION:-us-east-1}
|
||||
ports:
|
||||
- "${BACKEND_PORT:-8000}:8000"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
minio-init:
|
||||
condition: service_completed_successfully
|
||||
|
||||
runner:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: apps/runner/Dockerfile
|
||||
environment:
|
||||
RUNNER_MODE: fake
|
||||
ports:
|
||||
- "${RUNNER_PORT:-8010}:8010"
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-pipeline}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-pipeline_local}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-pipeline}
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 20
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- "${REDIS_PORT:-6379}:6379"
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 20
|
||||
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
command: server /data --console-address ":9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio_local}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minio_local_password}
|
||||
ports:
|
||||
- "${MINIO_API_PORT:-9000}:9000"
|
||||
- "${MINIO_CONSOLE_PORT:-9001}:9001"
|
||||
volumes:
|
||||
- minio-data:/data
|
||||
|
||||
minio-init:
|
||||
image: minio/mc:latest
|
||||
depends_on:
|
||||
minio:
|
||||
condition: service_started
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minio_local}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minio_local_password}
|
||||
OBJECT_STORAGE_BUCKET: ${OBJECT_STORAGE_BUCKET:-pipeline-local}
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- >
|
||||
until mc alias set local http://minio:9000 "$$MINIO_ROOT_USER" "$$MINIO_ROOT_PASSWORD"; do
|
||||
sleep 1;
|
||||
done;
|
||||
mc mb --ignore-existing "local/$$OBJECT_STORAGE_BUCKET";
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
redis-data:
|
||||
minio-data:
|
||||
@@ -0,0 +1,120 @@
|
||||
# Public Health Interfaces
|
||||
|
||||
Task 001 pre-requirement contract. These interfaces are the public test surface
|
||||
for the dockerized foundation implementation. Tests must exercise only the
|
||||
Docker Compose stack and HTTP endpoints, not service internals.
|
||||
|
||||
## Local Stack
|
||||
|
||||
The local stack starts from the repository root:
|
||||
|
||||
```sh
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
The implementation must expose these local ports:
|
||||
|
||||
| Service | URL |
|
||||
| --- | --- |
|
||||
| Frontend | `http://localhost:3000` |
|
||||
| Backend | `http://localhost:8000` |
|
||||
| Runner | `http://localhost:8010` |
|
||||
|
||||
## Health Endpoints
|
||||
|
||||
### Backend Process Health
|
||||
|
||||
```http
|
||||
GET http://localhost:8000/health
|
||||
```
|
||||
|
||||
Expected response:
|
||||
|
||||
```json
|
||||
{
|
||||
"service": "backend",
|
||||
"status": "ok"
|
||||
}
|
||||
```
|
||||
|
||||
This endpoint proves that the FastAPI process is serving HTTP.
|
||||
|
||||
### Backend Connectivity Smoke
|
||||
|
||||
```http
|
||||
GET http://localhost:8000/health/dependencies
|
||||
```
|
||||
|
||||
Expected response:
|
||||
|
||||
```json
|
||||
{
|
||||
"service": "backend",
|
||||
"status": "ok",
|
||||
"dependencies": {
|
||||
"postgres": "ok",
|
||||
"redis": "ok",
|
||||
"object_storage": "ok"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This endpoint is the public smoke surface for backend connectivity to Postgres,
|
||||
Redis, and S3-compatible object storage. Implementations may add fields, but
|
||||
must preserve these keys and `ok` statuses when dependencies are reachable.
|
||||
|
||||
### Runner Health
|
||||
|
||||
```http
|
||||
GET http://localhost:8010/health
|
||||
```
|
||||
|
||||
Expected response:
|
||||
|
||||
```json
|
||||
{
|
||||
"service": "runner",
|
||||
"status": "ok"
|
||||
}
|
||||
```
|
||||
|
||||
### Frontend Health
|
||||
|
||||
```http
|
||||
GET http://localhost:3000/health
|
||||
```
|
||||
|
||||
Expected response:
|
||||
|
||||
```json
|
||||
{
|
||||
"service": "frontend",
|
||||
"status": "ok"
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture Boundaries
|
||||
|
||||
The frontend application must follow Feature-Sliced Design with these top-level
|
||||
layers:
|
||||
|
||||
```text
|
||||
app/
|
||||
processes/
|
||||
pages/
|
||||
widgets/
|
||||
features/
|
||||
entities/
|
||||
shared/
|
||||
```
|
||||
|
||||
The backend and runner services must use these top-level architecture layers:
|
||||
|
||||
```text
|
||||
domain/
|
||||
application/
|
||||
infrastructure/
|
||||
presentation/
|
||||
shared/
|
||||
```
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Docker Infrastructure
|
||||
|
||||
Local Docker Compose infrastructure for Task 001 lives at the repository root in
|
||||
`docker-compose.yml`. This directory is reserved for future service-specific
|
||||
Docker configuration, seed scripts, and local infra overrides.
|
||||
@@ -0,0 +1,4 @@
|
||||
# MinIO
|
||||
|
||||
The root Docker Compose file starts MinIO and uses the `minio-init` service to
|
||||
create the local object storage bucket declared by `OBJECT_STORAGE_BUCKET`.
|
||||
@@ -0,0 +1,3 @@
|
||||
# Postgres
|
||||
|
||||
Reserved for local Postgres initialization files and future migrations wiring.
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "ai-content-pipeline",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev:stack": "docker compose up --build",
|
||||
"smoke": "bash tests/smoke/public-health.sh"
|
||||
},
|
||||
"workspaces": [
|
||||
"apps/frontend",
|
||||
"packages/shared"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@pipeline/shared",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "5.7.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export type ServiceName = "backend" | "frontend" | "runner";
|
||||
|
||||
export type HealthResponse = {
|
||||
service: ServiceName;
|
||||
status: "ok";
|
||||
};
|
||||
|
||||
export type BackendDependencyName = "postgres" | "redis" | "object_storage";
|
||||
|
||||
export type BackendDependenciesHealthResponse = HealthResponse & {
|
||||
service: "backend";
|
||||
dependencies: Record<BackendDependencyName, "ok">;
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
export type {
|
||||
BackendDependenciesHealthResponse,
|
||||
BackendDependencyName,
|
||||
HealthResponse,
|
||||
ServiceName,
|
||||
} from "./health";
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"noEmit": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
- "apps/frontend"
|
||||
- "packages/shared"
|
||||
@@ -0,0 +1,102 @@
|
||||
# Task 001: Dockerized Monorepo Foundation
|
||||
|
||||
Development description: Create the deployable project foundation for the AI Content Pipeline with backend, frontend, runner, shared contracts, and local infrastructure booting through one Docker Compose command.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Create a monorepo layout:
|
||||
- `apps/backend` for FastAPI.
|
||||
- `apps/frontend` for Next.js.
|
||||
- `apps/runner` for the agent runner service.
|
||||
- `packages/shared` for shared schemas and generated clients.
|
||||
- `infra/docker` for local infra configuration.
|
||||
- Add `docker-compose.yml` with services for frontend, backend, runner, Postgres, Redis, and S3-compatible object storage such as MinIO.
|
||||
- Add health endpoints:
|
||||
- Backend: `GET /health`.
|
||||
- Runner: `GET /health`.
|
||||
- Frontend route: `/health`.
|
||||
- Add a root developer command such as `make dev` or `pnpm dev:stack` that starts the full stack.
|
||||
- Add `.env.example` with local-only defaults and no secrets.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- `docker compose up --build` starts the stack.
|
||||
- `GET http://localhost:<backend-port>/health` returns backend health.
|
||||
- `GET http://localhost:<runner-port>/health` returns runner health.
|
||||
- `GET http://localhost:<frontend-port>/health` returns frontend health.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [x] TDD pre-requirement: before implementation, define the public health interfaces and write one failing stack/health check test first; proceed one red-green-refactor cycle at a time and record evidence in `Result`.
|
||||
- [x] A clean checkout can build and start the stack with one documented command.
|
||||
- [x] Backend, frontend, runner, Postgres, Redis, and object storage containers start without manual setup.
|
||||
- [x] Health checks pass from outside the containers.
|
||||
- [x] Backend can connect to Postgres, Redis, and object storage using environment variables.
|
||||
- [x] No real credentials are committed.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run `docker compose up --build`.
|
||||
- Run health checks against backend, frontend, and runner.
|
||||
- Run a smoke test that verifies backend connectivity to Postgres, Redis, and object storage.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Completed; public health smoke is green.
|
||||
- TDD plan:
|
||||
- Public contract documented in `docs/public-health-interfaces.md`.
|
||||
- First vertical Red test added at `tests/smoke/public-health.sh`.
|
||||
- The test starts the stack with `docker compose -f docker-compose.yml up --build -d`.
|
||||
- The test verifies only public HTTP surfaces:
|
||||
- Backend process health: `GET http://localhost:8000/health`.
|
||||
- Runner health: `GET http://localhost:8010/health`.
|
||||
- Frontend health: `GET http://localhost:3000/health`.
|
||||
- Backend dependency smoke: `GET http://localhost:8000/health/dependencies` for Postgres, Redis, and object storage connectivity.
|
||||
- Implementation agent should make this single test green before adding the next public behavior test.
|
||||
- Red evidence:
|
||||
- Command: `bash tests/smoke/public-health.sh`.
|
||||
- Actual output:
|
||||
```text
|
||||
Starting stack with docker compose...
|
||||
open /Users/gavrilovdev/tmp/pupline/docker-compose.yml: no such file or directory
|
||||
```
|
||||
- Result: failed as expected because the Docker Compose stack and service health implementations do not exist yet.
|
||||
- Green evidence:
|
||||
- Command: `bash tests/smoke/public-health.sh`.
|
||||
- Final observed output excerpt:
|
||||
```text
|
||||
Starting stack with docker compose...
|
||||
...
|
||||
#22 106.2 found 0 vulnerabilities
|
||||
...
|
||||
pupline-frontend Built
|
||||
pupline-runner Built
|
||||
pupline-backend Built
|
||||
...
|
||||
Container pupline-backend-1 Started
|
||||
Container pupline-frontend-1 Started
|
||||
backend health ok
|
||||
runner health ok
|
||||
frontend health ok
|
||||
backend dependencies ok
|
||||
public health smoke ok
|
||||
```
|
||||
- Refactor notes:
|
||||
- Backend implemented as FastAPI with `/health` and `/health/dependencies`.
|
||||
- Runner health uses a minimal stdlib HTTP service to avoid unnecessary image dependencies for Task 001.
|
||||
- Frontend uses Next.js App Router under `apps/frontend/src/app`; FSD placeholder layers live under `apps/frontend/src/processes`, `src/pages`, `src/widgets`, `src/features`, `src/entities`, and `src/shared`.
|
||||
- Frontend pins Next.js `16.2.6` and overrides PostCSS to `8.5.14`; Docker build `npm install` reported `found 0 vulnerabilities`.
|
||||
- Verification output:
|
||||
- Command: `python3 -m py_compile apps/backend/src/presentation/main.py apps/backend/src/infrastructure/dependencies.py apps/runner/src/presentation/main.py`.
|
||||
Output: no output, exit code 0.
|
||||
- Command: `docker compose config --quiet`.
|
||||
Output: no output, exit code 0.
|
||||
- Command: `bash tests/smoke/public-health.sh`.
|
||||
Output:
|
||||
```text
|
||||
backend health ok
|
||||
runner health ok
|
||||
frontend health ok
|
||||
backend dependencies ok
|
||||
public health smoke ok
|
||||
```
|
||||
@@ -0,0 +1,53 @@
|
||||
# Task 002: Shared Domain Contracts
|
||||
|
||||
Development description: Implement the shared domain schema package that defines workflow statuses, role names, API DTOs, and validation contracts used consistently by backend, frontend, runner, and tests.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Define canonical enums for:
|
||||
- Roles: `ADMIN`, `EDITOR`.
|
||||
- Article workflow statuses from `ARTICLE_BRIEF_CREATED` through `PUBLISH_COMMIT_CREATED`.
|
||||
- Agent job statuses and error categories.
|
||||
- Claim support statuses and risk levels.
|
||||
- Publishing statuses.
|
||||
- Define shared request/response schemas for:
|
||||
- Article create/list/detail.
|
||||
- Target site config.
|
||||
- Script/config version.
|
||||
- Agent job.
|
||||
- Research artifact manifest.
|
||||
- Plan, draft, evidence, asset, review, and publish commit summaries.
|
||||
- Choose one source of truth for validation:
|
||||
- Recommended implementation: Pydantic models in backend plus generated OpenAPI client/types for frontend.
|
||||
- Shared package may hold OpenAPI schema snapshots and TypeScript types generated from backend.
|
||||
- Add schema validation tests for representative valid and invalid payloads.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Backend exposes OpenAPI JSON with the canonical contracts.
|
||||
- Frontend imports generated API types instead of duplicating DTO shapes.
|
||||
- Runner consumes backend contracts for job input/output validation.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write a failing contract test for one externally visible DTO and one invalid payload; proceed one schema at a time and record red-green evidence in `Result`.
|
||||
- [ ] Workflow status transitions use shared constants rather than string literals spread across apps.
|
||||
- [ ] Role names are exactly `ADMIN` and `EDITOR`.
|
||||
- [ ] Generated frontend types match backend OpenAPI.
|
||||
- [ ] Contract tests fail on missing required fields and invalid enum values.
|
||||
- [ ] Runner job output schemas can be validated without importing frontend code.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run backend schema tests.
|
||||
- Generate frontend API types.
|
||||
- Run a typecheck in frontend against generated types.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Task 003: Postgres Schema And Seed Data
|
||||
|
||||
Development description: Create the initial relational schema, migrations, indexes, and seed data for articles, sites, workflow state, jobs, research manifests, publishing versions, and audit events.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Add migration tooling for the FastAPI backend, such as Alembic.
|
||||
- Create tables:
|
||||
- `users`
|
||||
- `target_sites`
|
||||
- `script_config_versions`
|
||||
- `articles`
|
||||
- `boundary_questions`
|
||||
- `article_plans`
|
||||
- `plan_sections`
|
||||
- `evidence_items`
|
||||
- `claims`
|
||||
- `article_drafts`
|
||||
- `assets`
|
||||
- `workflow_events`
|
||||
- `agent_jobs`
|
||||
- `research_run_manifests`
|
||||
- `publish_commits`
|
||||
- `prompt_versions`
|
||||
- Store article/domain status in domain tables as authoritative state.
|
||||
- Store LangGraph checkpoints separately from domain tables if checkpointing is implemented in this task.
|
||||
- Seed:
|
||||
- One Admin user.
|
||||
- One Editor user.
|
||||
- One Git-backed Next target site.
|
||||
- One active publishing YAML/script config version.
|
||||
- Add indexes for article dashboard queries, job queues, workflow timeline, and site lookup by slug.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- `alembic upgrade head` creates the schema.
|
||||
- Backend repository methods can create/read seeded target sites and users.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementing migrations, write one failing integration test through repository/public backend interfaces for seeded site lookup; add further tests one behavior at a time and record red-green evidence in `Result`.
|
||||
- [ ] Migrations run cleanly on an empty Postgres database.
|
||||
- [ ] Seed data is idempotent.
|
||||
- [ ] Article/domain tables hold authoritative article statuses.
|
||||
- [ ] Script/config versions store author, timestamp, diff, rollback target, activation timestamp, YAML, and transform script.
|
||||
- [ ] Research manifests store S3 prefixes, source URLs, object keys, content hashes, artifact types, and metadata references.
|
||||
- [ ] Publish commits store repository URL, branch, commit SHA, content bundle manifest, and status.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run migration tests against a disposable Postgres database.
|
||||
- Run seed command twice and verify no duplicates.
|
||||
- Run repository integration tests.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Task 004: Auth And Admin/Editor Roles
|
||||
|
||||
Development description: Implement simple internal authentication and role-based authorization for the two v1 roles: Admin and Editor.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Add a local-demo authentication mode suitable for Docker Compose:
|
||||
- Header-based user selection for local demo, or
|
||||
- Session login with seeded users.
|
||||
- Enforce role checks at backend endpoint boundaries.
|
||||
- Role capabilities:
|
||||
- Admin can edit target sites, publishing YAML/scripts, prompt versions, and runner profiles.
|
||||
- Editor can create and run article pipelines, edit intermediate outputs, approve content, and create publish commits.
|
||||
- Add frontend role-aware navigation:
|
||||
- Admin sees site configuration and script versioning screens.
|
||||
- Editor sees pipeline execution and review screens.
|
||||
- Ensure authorization failures return stable `403` responses.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Backend identifies current user and role for each request.
|
||||
- Frontend can fetch `GET /api/me`.
|
||||
- Protected endpoints consistently allow or deny Admin/Editor actions.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing public API authorization test for an Editor attempting an Admin-only action; proceed one permission behavior at a time and record red-green evidence in `Result`.
|
||||
- [ ] `GET /api/me` returns the active user and role.
|
||||
- [ ] Admin can create/update site config and script versions.
|
||||
- [ ] Editor cannot create/update site config or script versions.
|
||||
- [ ] Editor can create articles and perform review actions.
|
||||
- [ ] Unauthorized requests are rejected consistently.
|
||||
- [ ] Frontend hides Admin-only navigation for Editors.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run backend authorization tests.
|
||||
- Run frontend role rendering tests.
|
||||
- Manually verify Admin and Editor demo sessions in Docker Compose.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Task 005: Article Intake, Dashboard, And Detail Shell
|
||||
|
||||
Development description: Build the first user-visible workflow slice where an Editor creates an article brief, sees it on the dashboard, and opens an article detail page with workflow history.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend endpoints:
|
||||
- `POST /api/articles`
|
||||
- `GET /api/articles`
|
||||
- `GET /api/articles/{article_id}`
|
||||
- Article creation requires:
|
||||
- `brief_description`
|
||||
- `target_site_id`
|
||||
- optional `content_type`
|
||||
- optional `primary_keyword`
|
||||
- Creation behavior:
|
||||
- Creates article with `ARTICLE_BRIEF_CREATED`.
|
||||
- Attaches target site.
|
||||
- Writes `ARTICLE_CREATED` workflow event.
|
||||
- Frontend screens:
|
||||
- Dashboard with article title/working title, target website, status, assigned editor, last updated, next required action, and publishing status.
|
||||
- New article form.
|
||||
- Article detail shell with status summary and timeline.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Editor submits a brief in the UI.
|
||||
- Dashboard updates with the created article.
|
||||
- Article detail page shows the workflow event timeline.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing API behavior test for creating an article and seeing `ARTICLE_BRIEF_CREATED`; add UI tests after API green and record evidence in `Result`.
|
||||
- [ ] Editor can create an article brief for a configured target site.
|
||||
- [ ] Created article appears in `GET /api/articles`.
|
||||
- [ ] Article detail includes target site summary and workflow history.
|
||||
- [ ] Article creation writes a workflow event with actor and timestamp.
|
||||
- [ ] Invalid target site returns a validation error.
|
||||
- [ ] Frontend form shows validation errors without losing typed brief text.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run backend article API tests.
|
||||
- Run frontend component/page tests for dashboard and form.
|
||||
- Run a Docker Compose smoke flow from UI brief creation to article detail.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Task 006: Site Config And Publishing Script Versioning
|
||||
|
||||
Development description: Build Admin-managed target site configuration with versioned publishing YAML and transformation scripts that can be activated, audited, and rolled back.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend endpoints:
|
||||
- `POST /api/sites`
|
||||
- `GET /api/sites`
|
||||
- `GET /api/sites/{site_id}`
|
||||
- `PATCH /api/sites/{site_id}`
|
||||
- `GET /api/sites/{site_id}/publishing-config/versions`
|
||||
- `POST /api/sites/{site_id}/publishing-config/versions`
|
||||
- `POST /api/sites/{site_id}/publishing-config/versions/{version_id}/activate`
|
||||
- `POST /api/sites/{site_id}/publishing-config/versions/{version_id}/rollback`
|
||||
- Site config fields:
|
||||
- Repository URL.
|
||||
- Production branch.
|
||||
- Content format and path templates.
|
||||
- Asset path template.
|
||||
- Frontmatter mapping.
|
||||
- Generic preview renderer selection.
|
||||
- Editorial, SEO, source, visual, and publishing rules.
|
||||
- Script/config versions store:
|
||||
- YAML config.
|
||||
- Transform script text.
|
||||
- Author.
|
||||
- Timestamp.
|
||||
- Diff from previous version.
|
||||
- Rollback target.
|
||||
- Activation timestamp.
|
||||
- No second Admin approval is required in v1.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Admin can create and activate a site publishing config version.
|
||||
- Editor can read active site config but cannot mutate it.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing Admin API test for creating a new publishing config version and one failing Editor denial test; proceed one behavior at a time and record evidence in `Result`.
|
||||
- [ ] Admin can create a target site with Git-backed publishing fields.
|
||||
- [ ] Admin can create a new YAML/script version and activate it.
|
||||
- [ ] Version creation records author, timestamp, diff, and rollback target.
|
||||
- [ ] Rollback activates a previous version and writes an audit event.
|
||||
- [ ] Editor can view active site config but cannot create, activate, or roll back versions.
|
||||
- [ ] Frontend Admin UI displays version history and active version.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run backend site config API tests.
|
||||
- Run frontend Admin site config tests.
|
||||
- Manually create, activate, and roll back a site config in Docker Compose.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Task 007: Agent Job Queue And Runner MVP
|
||||
|
||||
Development description: Implement the durable job path from backend to runner, including queued jobs, isolated workspaces, CLI command execution, log capture, output validation, retry, and cancellation basics.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend:
|
||||
- `POST /api/agent-jobs/test-codex`
|
||||
- `GET /api/agent-jobs`
|
||||
- `GET /api/agent-jobs/{job_id}`
|
||||
- `POST /api/agent-jobs/{job_id}/retry`
|
||||
- `POST /api/agent-jobs/{job_id}/cancel`
|
||||
- Queue:
|
||||
- Use Redis-backed Celery, Dramatiq, or RQ.
|
||||
- Persist `QUEUED`, `RUNNING`, `SUCCEEDED`, `FAILED`, `CANCELLED`.
|
||||
- Runner:
|
||||
- Creates one workspace per job.
|
||||
- Writes structured input files.
|
||||
- Executes allowed command.
|
||||
- Captures stdout, stderr, exit code, duration.
|
||||
- Validates output JSON against the expected schema.
|
||||
- Uploads job artifacts to object storage where appropriate.
|
||||
- Test Codex job:
|
||||
- Must support a demo-safe fake runner mode for CI/local tests.
|
||||
- Real Codex CLI execution is configurable for environments with runner auth.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Admin can enqueue a test runner job.
|
||||
- UI can show job status and logs.
|
||||
- Failed jobs can be retried where allowed.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing backend-to-fake-runner integration test through public job APIs; implement minimal queue/runner behavior to make it green and record evidence in `Result`.
|
||||
- [ ] Backend can create and persist an agent job.
|
||||
- [ ] Runner claims a queued job and marks it running.
|
||||
- [ ] Runner stores stdout, stderr, exit code, duration, and final status.
|
||||
- [ ] Runner validates output JSON and marks schema failures as `FAILED_SCHEMA_VALIDATION`.
|
||||
- [ ] Cancelled jobs do not update article domain state.
|
||||
- [ ] Retry creates a new attempt or new job with traceable parent metadata.
|
||||
- [ ] Demo stack works without real Codex credentials by using fake runner mode.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run backend/runner integration tests with fake runner.
|
||||
- Run a Docker Compose test job from Admin UI or API.
|
||||
- Verify logs and status in UI.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Task 008: Boundary Questions Loop
|
||||
|
||||
Development description: Implement generation, editing, partial saving, and submission of boundary questions before plan generation.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend endpoints:
|
||||
- `POST /api/articles/{article_id}/boundary-questions/generate`
|
||||
- `GET /api/articles/{article_id}/boundary-questions`
|
||||
- `PATCH /api/articles/{article_id}/boundary-questions/{question_id}`
|
||||
- `POST /api/articles/{article_id}/boundary-questions/submit`
|
||||
- Generate 5-10 questions covering:
|
||||
- Audience.
|
||||
- Purpose.
|
||||
- Reader outcome.
|
||||
- Depth.
|
||||
- Tone.
|
||||
- Excluded topics.
|
||||
- Primary keyword.
|
||||
- Competitor angle.
|
||||
- Evidence standard.
|
||||
- Visual expectations.
|
||||
- Use agent job path with fake runner fixtures for tests.
|
||||
- Store editable questions and answers.
|
||||
- Block plan generation until all required questions are answered and submitted.
|
||||
- Frontend:
|
||||
- Boundary questions screen.
|
||||
- Partial save.
|
||||
- Required answer validation.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Editor generates questions for an article.
|
||||
- Editor edits answers and submits them.
|
||||
- Article moves to `BOUNDARY_ANSWERS_SUBMITTED`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing API behavior test for required unanswered questions blocking submission; proceed through generate/save/submit behavior one cycle at a time and record evidence in `Result`.
|
||||
- [ ] Questions are generated from article brief plus target site config.
|
||||
- [ ] Required categories are represented.
|
||||
- [ ] Editor can save partial answers.
|
||||
- [ ] Required unanswered questions block submission.
|
||||
- [ ] Submission writes workflow event and updates article status.
|
||||
- [ ] Plan generation remains blocked until boundary answers are submitted.
|
||||
- [ ] Frontend displays required/optional state clearly.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run boundary question API tests.
|
||||
- Run frontend page tests.
|
||||
- Run a Docker Compose smoke flow from article creation to boundary answer submission.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Task 009: Plan Generation And Review Gate
|
||||
|
||||
Development description: Implement article plan generation, immutable plan versioning, direct plan edits, revision requests, and final plan approval before research.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend endpoints:
|
||||
- `POST /api/articles/{article_id}/plan/generate`
|
||||
- `GET /api/articles/{article_id}/plans`
|
||||
- `GET /api/articles/{article_id}/plans/{plan_id}`
|
||||
- `PATCH /api/articles/{article_id}/plans/{plan_id}`
|
||||
- `POST /api/articles/{article_id}/plans/{plan_id}/approve`
|
||||
- `POST /api/articles/{article_id}/plans/{plan_id}/request-revision`
|
||||
- Plan generation uses:
|
||||
- Brief.
|
||||
- Boundary answers.
|
||||
- Target site config.
|
||||
- Output schema.
|
||||
- Plan must include:
|
||||
- Title options.
|
||||
- Recommended title.
|
||||
- Reader persona.
|
||||
- Search intent.
|
||||
- Thesis.
|
||||
- At least four sections.
|
||||
- Claims to prove.
|
||||
- Evidence needs.
|
||||
- Visual needs.
|
||||
- SEO notes.
|
||||
- Risks.
|
||||
- Edits create new immutable versions rather than mutating approval-relevant content in place.
|
||||
- Research cannot start until plan is approved.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Editor generates a plan.
|
||||
- Editor edits, requests revision, or approves the plan.
|
||||
- Approved plan becomes the production contract for research.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing public API test showing research cannot start before plan approval; implement generation/review behavior in vertical cycles and record evidence in `Result`.
|
||||
- [ ] Plan generation creates version 1 and moves article to `PLAN_REVIEW_REQUIRED`.
|
||||
- [ ] Plan has at least four sections with purpose, key points, and evidence needs.
|
||||
- [ ] Direct plan edit creates a new immutable version.
|
||||
- [ ] Revision request writes workflow event and returns workflow to plan generation/revision.
|
||||
- [ ] Approval writes workflow event with actor, timestamp, and exact plan version.
|
||||
- [ ] Previous plan versions remain accessible.
|
||||
- [ ] Frontend supports approve, request revision, direct edit, section notes, source requirements, excluded sources, visual requirements, tone, audience, and SEO keyword changes.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run plan API integration tests with fake runner output.
|
||||
- Run frontend plan review tests.
|
||||
- Run a Docker Compose smoke flow through plan approval.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Task 010: Research Fetch And S3 Manifest
|
||||
|
||||
Development description: Implement explicit auditable web research with source retrieval metadata, source-section snapshots, object-storage uploads, and Postgres manifest records.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend endpoint:
|
||||
- `POST /api/articles/{article_id}/research/start`
|
||||
- `GET /api/articles/{article_id}/research`
|
||||
- Research starts only after approved plan.
|
||||
- Research uses an explicit search/fetch script, not only free-form agent browsing.
|
||||
- For discovered sources, store in object storage:
|
||||
- Relevant content sections.
|
||||
- Page metadata.
|
||||
- Search path to the article.
|
||||
- Agent/source-selection criteria.
|
||||
- Page `<head>` metadata.
|
||||
- Server IP address.
|
||||
- Domain WHOIS owner when available.
|
||||
- In Postgres store only:
|
||||
- Research run manifest.
|
||||
- S3/object keys.
|
||||
- Source URLs.
|
||||
- Content hashes.
|
||||
- Artifact types.
|
||||
- Summary metadata required for UI and validation.
|
||||
- Retention is indefinite for v1.
|
||||
- Provide deterministic fake search/fetch fixtures for tests and demo.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Editor starts research for an approved article plan.
|
||||
- UI shows research run status and discovered source summary.
|
||||
- Backend exposes manifest-linked evidence summaries.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing behavior test that starts research from an approved plan and verifies a manifest with object keys is produced; proceed one research behavior at a time and record evidence in `Result`.
|
||||
- [ ] Research cannot start before plan approval.
|
||||
- [ ] Research run creates an agent/job record and article status `RESEARCH_RUNNING`.
|
||||
- [ ] Source-section artifacts are uploaded to object storage.
|
||||
- [ ] Manifest records include object keys, hashes, URLs, artifact types, and metadata references.
|
||||
- [ ] Re-running research creates a new manifest instead of overwriting old artifacts.
|
||||
- [ ] UI can show source URL, title, domain, type, summary, retrieval timestamp, and artifact link.
|
||||
- [ ] Demo stack can run with deterministic fake research results.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run research integration tests with fake search/fetch.
|
||||
- Verify object storage contains source-section artifact files.
|
||||
- Verify Postgres manifest records point to those files.
|
||||
- Run Docker Compose research smoke flow.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Task 011: Evidence Matrix And Claim Gate
|
||||
|
||||
Development description: Build claim extraction, claim-to-evidence mapping, unsupported-claim visibility, insufficient-evidence plan revision, and high-risk unsupported-claim blocking.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend endpoints:
|
||||
- `GET /api/articles/{article_id}/evidence`
|
||||
- `PATCH /api/articles/{article_id}/evidence/{evidence_id}`
|
||||
- Claim review operations under article evidence routes.
|
||||
- Evidence matrix maps:
|
||||
- Plan sections.
|
||||
- Claims.
|
||||
- Evidence items.
|
||||
- Source quality score.
|
||||
- Support status.
|
||||
- Risk level.
|
||||
- If research cannot find enough acceptable evidence for the approved plan:
|
||||
- Move workflow to `PLAN_REVISION_REQUIRED`.
|
||||
- Do not allow drafting.
|
||||
- Show missing evidence reasons to Editor.
|
||||
- High-risk unsupported claims:
|
||||
- Are visible in review screens.
|
||||
- Block final approval until resolved.
|
||||
- Editor can approve/reject evidence and add/remove evidence manually.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Editor views evidence by section and claim.
|
||||
- Editor can filter unsupported claims.
|
||||
- Workflow refuses to draft if evidence is structurally insufficient.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing behavior test showing insufficient evidence returns the article to `PLAN_REVISION_REQUIRED`; proceed one claim/evidence behavior at a time and record evidence in `Result`.
|
||||
- [ ] Every factual claim is mapped to at least one evidence item or marked unsupported.
|
||||
- [ ] Unsupported claims are visible by section.
|
||||
- [ ] High-risk unsupported claims block final approval.
|
||||
- [ ] Insufficient acceptable evidence blocks draft production and triggers plan revision.
|
||||
- [ ] Editor can approve, reject, add, and remove evidence.
|
||||
- [ ] Evidence UI supports section and unsupported-claim filters.
|
||||
- [ ] Evidence item retains URL, title, source type, summary, quality score, retrieval timestamp, and manifest reference.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run evidence matrix API tests.
|
||||
- Run frontend evidence UI tests.
|
||||
- Run a smoke flow with both sufficient and insufficient fake research fixtures.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Task 012: Parallel Production Jobs
|
||||
|
||||
Development description: Implement parallel section scaffolding, FAQ, SEO brief, tone guide, table specs, diagram specs, and hero prompt jobs after the evidence matrix is ready.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend orchestration creates independent jobs for:
|
||||
- One section scaffold per approved plan section.
|
||||
- Hero image prompt.
|
||||
- Diagram specifications.
|
||||
- Table specifications.
|
||||
- FAQ block.
|
||||
- SEO metadata.
|
||||
- Internal link suggestions.
|
||||
- Tone guide.
|
||||
- Each section scaffold output includes:
|
||||
- `section_id`
|
||||
- `heading`
|
||||
- `draft_markdown`
|
||||
- `used_evidence_ids`
|
||||
- `unsupported_claims`
|
||||
- `suggested_visuals`
|
||||
- Failed section jobs can be retried independently.
|
||||
- No scaffold may silently introduce unsupported factual claims.
|
||||
- Use fake runner fixtures for deterministic tests and demo.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Editor starts production after evidence matrix is ready.
|
||||
- UI shows parallel job status per production artifact.
|
||||
- Editor can retry failed section jobs individually.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing behavior test that starts production and creates one job per plan section; proceed one artifact behavior at a time and record evidence in `Result`.
|
||||
- [ ] Production cannot start before evidence matrix is ready.
|
||||
- [ ] One section scaffold job is created per approved plan section.
|
||||
- [ ] Jobs run independently and can fail independently.
|
||||
- [ ] Retrying one failed section does not rerun successful sections.
|
||||
- [ ] Each scaffold lists used evidence IDs.
|
||||
- [ ] Unsupported claims introduced during scaffolding are captured and shown.
|
||||
- [ ] UI displays per-artifact job state.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run orchestration tests with fake runner.
|
||||
- Run retry behavior tests.
|
||||
- Run Docker Compose smoke flow from evidence-ready to production artifacts ready.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Task 013: Draft Assembly, Editor, And Generic Preview
|
||||
|
||||
Development description: Assemble section scaffolds into a canonical Markdown/MDX draft, provide versioned draft editing, and render a generic rich preview for editor review.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend endpoints:
|
||||
- `POST /api/articles/{article_id}/draft/assemble`
|
||||
- `GET /api/articles/{article_id}/drafts`
|
||||
- `GET /api/articles/{article_id}/drafts/{draft_id}`
|
||||
- `PATCH /api/articles/{article_id}/drafts/{draft_id}`
|
||||
- Markdown is the canonical editable draft format.
|
||||
- Draft assembly includes:
|
||||
- Title.
|
||||
- Meta title.
|
||||
- Meta description.
|
||||
- Body Markdown/MDX.
|
||||
- FAQ block when applicable.
|
||||
- Visual placeholders.
|
||||
- Evidence references.
|
||||
- Unsupported claim warnings.
|
||||
- Approval-relevant draft edits create new immutable versions.
|
||||
- Frontend includes:
|
||||
- Draft editor.
|
||||
- Generic Markdown/MDX preview.
|
||||
- Version selector/history.
|
||||
- Unsupported-claim warnings near content.
|
||||
- The preview is for editor UX and content-shape validation, not target-site build/runtime validation.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Editor assembles a draft after production artifacts are ready.
|
||||
- Editor edits Markdown and metadata.
|
||||
- Editor views a generic preview.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing behavior test that assembles a draft from successful section scaffolds through the public API; proceed one draft behavior at a time and record evidence in `Result`.
|
||||
- [ ] Draft assembly includes all planned sections in order.
|
||||
- [ ] Draft includes metadata, FAQ, visual placeholders, evidence references, and unsupported claim warnings.
|
||||
- [ ] Draft version 1 is immutable after approval-relevant edits; edits create a new version.
|
||||
- [ ] Generic Markdown/MDX preview renders headings, links, tables, images/placeholders, and FAQ.
|
||||
- [ ] Editor can compare or select draft versions.
|
||||
- [ ] Draft assembly fails clearly if required section scaffolds are missing.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run draft assembly API tests.
|
||||
- Run frontend editor/preview tests.
|
||||
- Run Docker Compose smoke flow from production artifacts to previewed draft.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,67 @@
|
||||
# Task 014: Assets And Media Library
|
||||
|
||||
Development description: Implement asset specifications, generated or uploaded asset records, object-storage file handling, approval/rejection, replacement, and a media library view.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend endpoints:
|
||||
- `POST /api/articles/{article_id}/assets/generate-specs`
|
||||
- `GET /api/articles/{article_id}/assets`
|
||||
- `PATCH /api/articles/{article_id}/assets/{asset_id}`
|
||||
- `POST /api/articles/{article_id}/assets/{asset_id}/approve`
|
||||
- Upload/replacement endpoint for asset files.
|
||||
- Asset types:
|
||||
- Hero image.
|
||||
- Inline diagram.
|
||||
- Table.
|
||||
- Flowchart.
|
||||
- Comparison matrix.
|
||||
- Architecture diagram.
|
||||
- Inline image.
|
||||
- Asset record fields:
|
||||
- Title.
|
||||
- Type.
|
||||
- Prompt or diagram/table code.
|
||||
- File URL/object key.
|
||||
- Alt text.
|
||||
- Caption.
|
||||
- Status.
|
||||
- Linked section/article.
|
||||
- Frontend:
|
||||
- Asset review page.
|
||||
- Media library.
|
||||
- Replacement upload.
|
||||
- Approval/rejection controls.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Editor reviews generated asset specs.
|
||||
- Editor approves, rejects, or replaces assets.
|
||||
- Approved assets are included in draft/publish bundle.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing public API test for approving an asset and seeing it become available to the article; proceed one asset behavior at a time and record evidence in `Result`.
|
||||
- [ ] Asset specs are generated and linked to article or section.
|
||||
- [ ] File uploads store objects in object storage and persist object keys.
|
||||
- [ ] Asset approval writes workflow event.
|
||||
- [ ] Rejected assets are excluded from publish bundle.
|
||||
- [ ] Replacement preserves audit history.
|
||||
- [ ] Media library lists article assets with status, type, title, preview/file link, alt text, and caption.
|
||||
- [ ] Draft preview uses approved asset references where available.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run asset API tests.
|
||||
- Run object-storage upload integration tests.
|
||||
- Run frontend media library tests.
|
||||
- Run smoke flow approving and replacing an asset.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Task 015: SEO And Language Review
|
||||
|
||||
Development description: Implement SEO and linguistic review jobs, issue reports, editor accept/reject/edit flows, and versioned application of accepted suggestions.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend endpoints:
|
||||
- `POST /api/articles/{article_id}/seo/review`
|
||||
- `GET /api/articles/{article_id}/seo/report`
|
||||
- `POST /api/articles/{article_id}/language/review`
|
||||
- `GET /api/articles/{article_id}/language/report`
|
||||
- endpoints to accept/reject/edit individual suggestions.
|
||||
- SEO checks:
|
||||
- Title length.
|
||||
- Meta title length.
|
||||
- Meta description length.
|
||||
- H1/H2 structure.
|
||||
- Keyword placement.
|
||||
- Internal links.
|
||||
- External citations.
|
||||
- Schema type.
|
||||
- Slug.
|
||||
- FAQ eligibility.
|
||||
- Image alt text.
|
||||
- Readability.
|
||||
- Duplicate headings.
|
||||
- Language checks:
|
||||
- Grammar.
|
||||
- Spelling.
|
||||
- Sentence length.
|
||||
- Passive voice.
|
||||
- Jargon density.
|
||||
- Brand tone.
|
||||
- Forbidden phrases.
|
||||
- Repetition.
|
||||
- Weak claims.
|
||||
- Unsupported certainty.
|
||||
- CTA consistency.
|
||||
- Accepted suggestions create a new draft version when they change approval-relevant content.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Editor runs SEO and language review.
|
||||
- Editor accepts, rejects, or edits suggestions.
|
||||
- UI shows issues with severity and locations.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing behavior test that runs an SEO review and returns a visible issue through the public API; proceed one review behavior at a time and record evidence in `Result`.
|
||||
- [ ] SEO report includes score, issues, suggested fixes, recommended slug/title, and schema JSON.
|
||||
- [ ] Language report includes severity, location, message, and suggested rewrite.
|
||||
- [ ] Editor can accept, reject, or edit each suggestion.
|
||||
- [ ] Accepted content changes create a new immutable draft version.
|
||||
- [ ] Target-site SEO and tone rules override global defaults.
|
||||
- [ ] Final review can show unresolved SEO/language issues.
|
||||
- [ ] Fake review runner produces deterministic demo reports.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run review API tests.
|
||||
- Run draft-versioning tests for accepted suggestions.
|
||||
- Run frontend review UI tests.
|
||||
- Run Docker Compose smoke flow from draft to SEO/language reports.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Task 016: Final Approval Gate
|
||||
|
||||
Development description: Implement the final review gate that prevents publishing until the editor has approved the exact draft version, assets, evidence state, metadata, and publishing settings.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend endpoints:
|
||||
- `POST /api/articles/{article_id}/final-approval`
|
||||
- `POST /api/articles/{article_id}/final-revision-request`
|
||||
- Final approval checklist:
|
||||
- Plan followed.
|
||||
- Evidence reviewed.
|
||||
- No high-risk unsupported claims remain.
|
||||
- SEO metadata approved.
|
||||
- Images/assets approved.
|
||||
- Tables and diagrams approved.
|
||||
- Internal links approved.
|
||||
- Frontmatter fields selected.
|
||||
- Content path selected.
|
||||
- Author selected.
|
||||
- Publishing mode selected.
|
||||
- Approval records:
|
||||
- Actor.
|
||||
- Timestamp.
|
||||
- Exact draft version.
|
||||
- Selected publishing settings.
|
||||
- Revision request returns workflow to `FINAL_REVISION_REQUIRED`.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Editor sees final review checklist.
|
||||
- Editor cannot approve while hard blockers remain.
|
||||
- Approved article moves to `PUBLISH_DRY_RUN_REQUIRED`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing behavior test proving high-risk unsupported claims block final approval; proceed one checklist behavior at a time and record evidence in `Result`.
|
||||
- [ ] Final approval requires an exact draft version.
|
||||
- [ ] High-risk unsupported claims block approval.
|
||||
- [ ] Missing required publishing settings block approval.
|
||||
- [ ] Unapproved required assets block approval.
|
||||
- [ ] Approval writes a workflow event with actor, timestamp, draft version, and publishing settings.
|
||||
- [ ] Revision request writes event and moves article to `FINAL_REVISION_REQUIRED`.
|
||||
- [ ] UI shows blockers and completed checklist items.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run final approval API tests.
|
||||
- Run frontend final review tests.
|
||||
- Run smoke flow showing blocked and successful approval paths.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Task 017: Git Publishing Dry Run And Commit
|
||||
|
||||
Development description: Implement the Git-backed publishing path that builds a content bundle, runs best-effort generic Markdown/MDX dry-run validation, executes Admin-defined transforms on the runner host, and commits directly to the configured production branch.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Backend endpoints:
|
||||
- `POST /api/articles/{article_id}/publishing/dry-run`
|
||||
- `POST /api/articles/{article_id}/publishing/create-commit`
|
||||
- `GET /api/articles/{article_id}/publishing/status`
|
||||
- `GET /api/articles/{article_id}/publishing/commits`
|
||||
- Content bundle includes:
|
||||
- Markdown/MDX article file.
|
||||
- Frontmatter JSON/YAML according to site mapping.
|
||||
- Referenced approved asset files.
|
||||
- Bundle manifest.
|
||||
- Publishing behavior:
|
||||
- Materialize active versioned YAML/script config into checked-out site repo workspace.
|
||||
- Run transform script directly on runner host inside site repo workspace.
|
||||
- Run generic Markdown/MDX dry-run preview validation, not target-site build.
|
||||
- Commit directly to configured production branch.
|
||||
- Rely on target repo CI/CD after push.
|
||||
- Primary done state is `PUBLISH_COMMIT_CREATED`.
|
||||
- Failure behavior:
|
||||
- Non-fast-forward push or conflict fails publish step.
|
||||
- No automatic rebase.
|
||||
- If delayed deployment verification fails, alert Admin/Editor and leave production commit in place.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Editor runs publishing dry run after final approval.
|
||||
- Editor creates publish commit after dry run passes.
|
||||
- UI shows commit SHA, branch, repository, and opportunistic deployment status.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing integration test against a temporary local Git repository proving a final-approved article can create a commit; add dry-run and failure tests one behavior at a time and record evidence in `Result`.
|
||||
- [ ] Publishing cannot dry-run before final approval.
|
||||
- [ ] Dry run fails if generic Markdown/MDX content-shape validation fails.
|
||||
- [ ] Publish commit cannot run until dry run passes.
|
||||
- [ ] Content bundle uses site-configured path templates and frontmatter mapping.
|
||||
- [ ] Active YAML/script config version is included in publish logs/manifest.
|
||||
- [ ] Publish writes commit to configured production branch in a test repository.
|
||||
- [ ] Non-fast-forward push or conflict fails without automatic rebase.
|
||||
- [ ] Publish commit record stores repository URL, branch, commit SHA, bundle manifest, and status.
|
||||
- [ ] UI clearly labels validation as best-effort content-shape validation only.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run publishing integration tests using a local bare Git repository.
|
||||
- Run generic preview validation tests.
|
||||
- Run Docker Compose smoke flow from final approval to publish commit against a demo site repo.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Task 018: Observability, Retry, Cancel, And Audit Trail
|
||||
|
||||
Development description: Build the operational layer for workflow history, job logs, redaction, retry/cancel actions, failure UI, and audit events across the full pipeline.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Workflow history records:
|
||||
- Article created.
|
||||
- Boundary questions generated.
|
||||
- Boundary answers submitted.
|
||||
- Plan generated/edited/approved.
|
||||
- Research started.
|
||||
- Evidence added/approved/rejected.
|
||||
- Draft assembled.
|
||||
- SEO/language review completed.
|
||||
- Asset approved/rejected/replaced.
|
||||
- Final approval granted.
|
||||
- Publishing dry run completed.
|
||||
- Publish commit created.
|
||||
- Delayed publish verification failed.
|
||||
- Site publishing config/script changed.
|
||||
- Job failed/retried/cancelled.
|
||||
- Failure UI shows:
|
||||
- Job type.
|
||||
- Status.
|
||||
- Error category.
|
||||
- Error message.
|
||||
- Last successful step.
|
||||
- Retry button when allowed.
|
||||
- Admin logs.
|
||||
- Sensitive values are redacted from logs before display.
|
||||
- Retry rules:
|
||||
- Plan generation manually only.
|
||||
- Research allowed.
|
||||
- Section scaffold per section.
|
||||
- SEO/language review allowed.
|
||||
- Publish commit allowed only if no publish commit exists.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- Admin and Editor see workflow timeline.
|
||||
- Admin can inspect redacted logs.
|
||||
- Allowed retry/cancel actions are available in UI.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing behavior test showing a failed job appears in article workflow history with retry eligibility; proceed one operational behavior at a time and record evidence in `Result`.
|
||||
- [ ] Article detail page shows user, system, and agent events in order.
|
||||
- [ ] Job logs are stored and displayed with sensitive values redacted.
|
||||
- [ ] Retry buttons appear only where retry is allowed.
|
||||
- [ ] Cancelling queued/running jobs prevents article state mutation.
|
||||
- [ ] Publish commit retry is blocked once a publish commit exists.
|
||||
- [ ] Admin can see detailed logs; Editor sees safe failure summary.
|
||||
- [ ] All Admin script/config changes are audit-visible with diff and rollback target.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run audit/workflow API tests.
|
||||
- Run log redaction tests.
|
||||
- Run frontend timeline/failure UI tests.
|
||||
- Run smoke flow that forces a failed fake job and retries it.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Task 019: End-To-End Demo Stack
|
||||
|
||||
Development description: Assemble and verify a complete demo-ready Docker Compose product path from article brief to Git-backed publish commit using deterministic runner/research/review fixtures and a local demo Next content repository.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Add demo fixtures:
|
||||
- Seed target site.
|
||||
- Demo Admin and Editor users.
|
||||
- Fake Codex runner outputs for questions, plan, sections, SEO, language, assets.
|
||||
- Fake research search/fetch corpus.
|
||||
- Local bare Git repository or mounted demo Next content repository as publish target.
|
||||
- Add end-to-end test path:
|
||||
- Start stack.
|
||||
- Create article.
|
||||
- Generate/submit boundary answers.
|
||||
- Generate/approve plan.
|
||||
- Run research.
|
||||
- Verify evidence matrix.
|
||||
- Run production jobs.
|
||||
- Assemble draft.
|
||||
- Review assets, SEO, and language.
|
||||
- Final approve.
|
||||
- Dry run.
|
||||
- Create publish commit.
|
||||
- Add demo documentation:
|
||||
- Setup.
|
||||
- Login/role selection.
|
||||
- Happy path.
|
||||
- Failure demo path.
|
||||
- How to inspect object storage, database state, and Git commit.
|
||||
|
||||
## Public Interface
|
||||
|
||||
- A reviewer can run one command, open the frontend, and complete the pipeline without external credentials.
|
||||
- The final demo produces a real Git commit in the configured demo repository.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TDD pre-requirement: before implementation, write one failing end-to-end smoke test for the shortest happy path through public UI/API boundaries; add failure-path checks one behavior at a time and record evidence in `Result`.
|
||||
- [ ] `docker compose up --build` starts the complete demo stack.
|
||||
- [ ] Demo requires no real Codex, search, WHOIS, cloud S3, or GitHub credentials.
|
||||
- [ ] Editor can complete the happy path from article brief to `PUBLISH_COMMIT_CREATED`.
|
||||
- [ ] Final commit contains Markdown/MDX, frontmatter, and referenced assets according to site config.
|
||||
- [ ] Object storage contains research source-section artifacts and manifests.
|
||||
- [ ] Workflow timeline shows all major actions.
|
||||
- [ ] Demo includes at least one visible failure/retry scenario.
|
||||
- [ ] README/demo guide is accurate and enough for a new developer to run the product.
|
||||
|
||||
## Verification
|
||||
|
||||
- Run the full end-to-end smoke suite.
|
||||
- Run `docker compose up --build` from a clean checkout.
|
||||
- Manually execute the documented demo path.
|
||||
- Inspect final Git commit, object storage artifacts, and workflow history.
|
||||
|
||||
## Result
|
||||
|
||||
- Status: Pending execution.
|
||||
- TDD plan: To be filled during execution.
|
||||
- Red evidence: To be filled during execution.
|
||||
- Green evidence: To be filled during execution.
|
||||
- Refactor notes: To be filled during execution.
|
||||
- Verification output: To be filled during execution.
|
||||
@@ -0,0 +1,46 @@
|
||||
# AI Content Pipeline Task Pool
|
||||
|
||||
This task pool turns `IDEA.md` into vertical, testable implementation slices that lead to a demo-ready Docker Compose product.
|
||||
|
||||
## Execution Rules
|
||||
|
||||
- Execute tasks in numeric order unless a later task is explicitly split or reprioritized.
|
||||
- Every task starts with a development description and contains concrete implementation details.
|
||||
- Every task has a TDD pre-requirement in acceptance criteria.
|
||||
- Use vertical red-green-refactor cycles: one public behavior test, minimal implementation, then repeat.
|
||||
- Do not write all tests first for a whole task.
|
||||
- Fill each task's `Result` section after execution with:
|
||||
- TDD plan.
|
||||
- Red evidence.
|
||||
- Green evidence.
|
||||
- Refactor notes.
|
||||
- Verification output.
|
||||
|
||||
## Global Implementation Assumptions
|
||||
|
||||
- v1 is internal or single-tenant.
|
||||
- Roles are only `ADMIN` and `EDITOR`.
|
||||
- Backend is FastAPI.
|
||||
- Frontend is Next.js.
|
||||
- Workflow orchestration uses LangGraph, while article/domain tables remain authoritative for product state.
|
||||
- Queue is Redis-backed Celery, Dramatiq, or RQ.
|
||||
- Postgres stores domain state and manifests, not large source snapshots.
|
||||
- Object storage stores research source-section artifacts, assets, and job artifacts.
|
||||
- Runner uses one managed Codex identity per environment, with fake-runner mode required for CI and demo.
|
||||
- Publishing target is a Git-backed Next.js content repository.
|
||||
- Publishing commits directly to the configured production branch after final approval and generic Markdown/MDX dry-run validation.
|
||||
- Target repository CI/CD owns deployment after commit.
|
||||
- Publishing validation is best-effort content-shape validation only; target-site build/runtime errors may reach production.
|
||||
- Admin-defined publishing scripts run directly on the runner host inside checked-out site repositories; Admins are trusted code operators.
|
||||
|
||||
## Demo Definition
|
||||
|
||||
The pool is complete when a reviewer can:
|
||||
|
||||
1. Run `docker compose up --build` from a clean checkout.
|
||||
2. Open the frontend.
|
||||
3. Use a demo Editor to create an article.
|
||||
4. Complete boundary questions, plan approval, research, evidence review, production, draft review, final approval, dry run, and publish commit.
|
||||
5. Inspect the final Git commit in the configured demo repository.
|
||||
6. Inspect research artifacts in object storage.
|
||||
7. Inspect workflow history and job logs in the UI.
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
COMPOSE_FILE="${ROOT_DIR}/docker-compose.yml"
|
||||
|
||||
BACKEND_URL="${BACKEND_URL:-http://localhost:8000}"
|
||||
RUNNER_URL="${RUNNER_URL:-http://localhost:8010}"
|
||||
FRONTEND_URL="${FRONTEND_URL:-http://localhost:3000}"
|
||||
WAIT_SECONDS="${WAIT_SECONDS:-60}"
|
||||
PYTHON_BIN="${PYTHON_BIN:-python3}"
|
||||
|
||||
cleanup() {
|
||||
docker compose -f "${COMPOSE_FILE}" down --remove-orphans >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
fetch_health() {
|
||||
local name="$1"
|
||||
local url="$2"
|
||||
local deadline=$((SECONDS + WAIT_SECONDS))
|
||||
local body
|
||||
|
||||
while (( SECONDS < deadline )); do
|
||||
if body="$(curl -fsS "${url}" 2>/dev/null)"; then
|
||||
printf '%s' "${body}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Timed out waiting for ${name} at ${url}" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
assert_service_health() {
|
||||
local name="$1"
|
||||
local url="$2"
|
||||
local expected_service="$3"
|
||||
local body
|
||||
|
||||
body="$(fetch_health "${name}" "${url}")"
|
||||
HEALTH_NAME="${name}" EXPECTED_SERVICE="${expected_service}" HEALTH_BODY="${body}" "${PYTHON_BIN}" - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
name = os.environ["HEALTH_NAME"]
|
||||
expected_service = os.environ["EXPECTED_SERVICE"]
|
||||
body = os.environ["HEALTH_BODY"]
|
||||
|
||||
try:
|
||||
payload = json.loads(body)
|
||||
except json.JSONDecodeError as error:
|
||||
print(f"{name}: expected JSON response, got: {body!r}", file=sys.stderr)
|
||||
raise SystemExit(1) from error
|
||||
|
||||
if payload.get("service") != expected_service:
|
||||
print(
|
||||
f"{name}: expected service={expected_service!r}, got {payload.get('service')!r}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
if payload.get("status") != "ok":
|
||||
print(f"{name}: expected status='ok', got {payload.get('status')!r}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
PY
|
||||
echo "${name} health ok"
|
||||
}
|
||||
|
||||
assert_backend_dependencies() {
|
||||
local body
|
||||
|
||||
body="$(fetch_health "backend dependencies" "${BACKEND_URL}/health/dependencies")"
|
||||
HEALTH_BODY="${body}" "${PYTHON_BIN}" - <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
body = os.environ["HEALTH_BODY"]
|
||||
required = ("postgres", "redis", "object_storage")
|
||||
|
||||
try:
|
||||
payload = json.loads(body)
|
||||
except json.JSONDecodeError as error:
|
||||
print(f"backend dependencies: expected JSON response, got: {body!r}", file=sys.stderr)
|
||||
raise SystemExit(1) from error
|
||||
|
||||
if payload.get("service") != "backend":
|
||||
print(
|
||||
f"backend dependencies: expected service='backend', got {payload.get('service')!r}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
if payload.get("status") != "ok":
|
||||
print(
|
||||
f"backend dependencies: expected status='ok', got {payload.get('status')!r}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
dependencies = payload.get("dependencies")
|
||||
if not isinstance(dependencies, dict):
|
||||
print("backend dependencies: expected dependencies object", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
|
||||
failed = [name for name in required if dependencies.get(name) != "ok"]
|
||||
if failed:
|
||||
print(
|
||||
"backend dependencies: expected ok for " + ", ".join(failed),
|
||||
file=sys.stderr,
|
||||
)
|
||||
raise SystemExit(1)
|
||||
PY
|
||||
echo "backend dependencies ok"
|
||||
}
|
||||
|
||||
cd "${ROOT_DIR}"
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "Starting stack with docker compose..."
|
||||
docker compose -f "${COMPOSE_FILE}" up --build -d
|
||||
|
||||
assert_service_health "backend" "${BACKEND_URL}/health" "backend"
|
||||
assert_service_health "runner" "${RUNNER_URL}/health" "runner"
|
||||
assert_service_health "frontend" "${FRONTEND_URL}/health" "frontend"
|
||||
assert_backend_dependencies
|
||||
|
||||
echo "public health smoke ok"
|
||||
Reference in New Issue
Block a user