Task 001: dockerized monorepo foundation

This commit is contained in:
2026-05-21 16:57:58 +03:00
commit d42f0c4392
83 changed files with 4236 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
.git
.env
node_modules
**/node_modules
**/.next
__pycache__
**/__pycache__
.pytest_cache
.ruff_cache
.mypy_cache
dist
coverage
+21
View File
@@ -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
View File
@@ -0,0 +1,11 @@
.env
.DS_Store
node_modules/
.next/
__pycache__/
*.pyc
.pytest_cache/
.ruff_cache/
.mypy_cache/
dist/
coverage/
+2102
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -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
+41
View File
@@ -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.
+15
View File
@@ -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"]
+5
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
"""Backend service package."""
+3
View File
@@ -0,0 +1,3 @@
# Application
Application use cases and workflow orchestration belong in this layer.
+1
View File
@@ -0,0 +1 @@
"""Application layer for use cases and orchestration."""
+3
View File
@@ -0,0 +1,3 @@
# Domain
Business entities, value objects, and domain rules belong in this layer.
+1
View File
@@ -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
+3
View File
@@ -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."""
+27
View File
@@ -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,
},
)
+4
View File
@@ -0,0 +1,4 @@
# Shared
Cross-layer backend primitives belong here. Keep this layer small and avoid
placing domain rules in shared code.
+1
View File
@@ -0,0 +1 @@
"""Shared backend primitives."""
+17
View File
@@ -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`.
+12
View File
@@ -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"]
+5
View File
@@ -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.
+6
View File
@@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
};
export default nextConfig;
+23
View File
@@ -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"
}
}
+21
View File
@@ -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;
}
+5
View File
@@ -0,0 +1,5 @@
export const dynamic = "force-dynamic";
export async function GET() {
return Response.json({ service: "frontend", status: "ok" });
}
+19
View File
@@ -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>
);
}
+8
View File
@@ -0,0 +1,8 @@
export default function HomePage() {
return (
<main>
<h1>AI Content Pipeline</h1>
<p>Dockerized monorepo foundation is running.</p>
</main>
);
}
+3
View File
@@ -0,0 +1,3 @@
# Entities
FSD layer for domain entity UI and client-side models.
+3
View File
@@ -0,0 +1,3 @@
# Features
FSD layer for user-facing actions and feature logic.
+4
View File
@@ -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.
+4
View File
@@ -0,0 +1,4 @@
# Processes
FSD layer for cross-page user workflows. Placeholder for upcoming article
workflow processes.
+3
View File
@@ -0,0 +1,3 @@
# Shared
FSD layer for reusable frontend primitives, API clients, and UI foundations.
+3
View File
@@ -0,0 +1,3 @@
# Widgets
FSD layer for composed UI blocks used by pages.
+27
View File
@@ -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"]
}
+12
View File
@@ -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"]
View File
+1
View File
@@ -0,0 +1 @@
"""Runner service package."""
+3
View File
@@ -0,0 +1,3 @@
# Application
Runner use cases, job orchestration, and command policies belong in this layer.
+1
View File
@@ -0,0 +1 @@
"""Application layer for runner use cases."""
+3
View File
@@ -0,0 +1,3 @@
# Domain
Runner job entities and invariants belong in this layer.
+1
View File
@@ -0,0 +1 @@
"""Domain layer for runner job concepts."""
+4
View File
@@ -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."""
+3
View File
@@ -0,0 +1,3 @@
# Presentation
Runner HTTP routes and API wiring belong in this layer.
+1
View File
@@ -0,0 +1 @@
"""Presentation layer for runner HTTP APIs."""
+33
View File
@@ -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()
+3
View File
@@ -0,0 +1,3 @@
# Shared
Cross-layer runner primitives belong here. Keep this layer small.
+1
View File
@@ -0,0 +1 @@
"""Shared runner primitives."""
+106
View File
@@ -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:
+120
View File
@@ -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/
```
+5
View File
@@ -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.
+4
View File
@@ -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`.
+3
View File
@@ -0,0 +1,3 @@
# Postgres
Reserved for local Postgres initialization files and future migrations wiring.
+12
View File
@@ -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"
]
}
+14
View File
@@ -0,0 +1,14 @@
{
"name": "@pipeline/shared",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
},
"scripts": {
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"typescript": "5.7.2"
}
}
+13
View File
@@ -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">;
};
+6
View File
@@ -0,0 +1,6 @@
export type {
BackendDependenciesHealthResponse,
BackendDependencyName,
HealthResponse,
ServiceName,
} from "./health";
+12
View File
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"declaration": true,
"noEmit": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts"]
}
+3
View File
@@ -0,0 +1,3 @@
packages:
- "apps/frontend"
- "packages/shared"
+102
View File
@@ -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
```
+53
View File
@@ -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.
+48
View File
@@ -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.
+57
View File
@@ -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.
+61
View File
@@ -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.
+64
View File
@@ -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.
+57
View File
@@ -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.
+67
View File
@@ -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.
+72
View File
@@ -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.
+59
View File
@@ -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.
+64
View File
@@ -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.
+46
View File
@@ -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.
+131
View File
@@ -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"