mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-09-29 16:24:22 +10:00
feat(deploy): support Vercel Hobby alongside Docker (#3541)
* feat(deploy): support Vercel Hobby alongside Docker * fix(deploy): include PDFKit runtime font assets * docs(deploy): document Vercel and Docker setup * docs(deploy): record storage persistence checks * refactor(deploy): drop scheduled staging cleanup Staging uploads are deleted after finalization and expired ones are swept on each new upload, so the Vercel cron job, its route, and CRON_SECRET are no longer needed. The Deploy with Vercel wizard now asks for two secrets. * docs(deploy): restructure Vercel guides Split the Vercel page into a how-to with its environment reference, move the large RPC staging protocol to an API reference page, and move CI deployment checks to the contributing section. Point Deploy with Vercel buttons at main. * chore: remove agent planning records and fix web app description Delete superpowers plans/specs, ADRs, issue plans, execution briefs, domain context maps, and Europass research. Describe apps/web as a TanStack Router SPA served by apps/server. * refactor(deploy): simplify Vercel support code - Share one Redis client and key namespace through @reactive-resume/db/redis for API and auth instead of a second auth-only client. - Drop the auth seeding retry; the provider already treats concurrent inserts as no-ops and deployment preparation seeds before runtime. - Detect staging support from POST /api/storage/stage (404 on Docker) instead of a separate GET probe. - Read staged bodies directly; the signed upload already caps their size. - Close per-subscription Redis connections with disconnect() alone. - Check Blob health with one list call instead of write/read/delete. - Remove redundant tsdown onlyBundle list, dead namespace fallbacks, and the conditional spread in the health status. * fix(deploy): heal stopped runs with dead owners and keep auth up without Redis - Run owners refresh a Redis heartbeat until they release their claim. Stop requests reap the run immediately when the owner has stopped heartbeating, instead of leaving the thread blocked until the 15-minute TTL reaper. - Auth and oRPC rate limiters fall back to per-instance memory limits when Redis errors, instead of rejecting every login or failing requests. * ci: allow esbuild build for Vercel CLI and register deployment deps with knip pnpm 12 fails dlx installs with ignored build scripts, so allow esbuild explicitly. The server bundle keeps @vercel/blob, ioredis, and jose external, and api/index.mjs is the Vercel Function entry. * fix(web): send buffered RPC bodies instead of teed streams Reading a request clone turned the original body into a stream, which browsers send without inspectable request data and which needs duplex mode. Send the already buffered Blob for direct requests. * fix(web): send direct RPC bodies as bytes Blob request bodies are sent as data pipes, so browser tooling cannot inspect them. Buffer the original request as an ArrayBuffer and send those bytes; this restores the e2e save assertions that match on request data.
This commit is contained in:
+18
-2
@@ -13,11 +13,17 @@ APP_URL="http://localhost:3000"
|
|||||||
# Unset or blank keeps the marketing home. Restart after changes.
|
# Unset or blank keeps the marketing home. Restart after changes.
|
||||||
# ROOT_RESUME_ID=
|
# ROOT_RESUME_ID=
|
||||||
|
|
||||||
|
# Vercel: APP_URL can be omitted; production uses VERCEL_PROJECT_PRODUCTION_URL.
|
||||||
|
|
||||||
# --- Database (PostgreSQL) ---
|
# --- Database (PostgreSQL) ---
|
||||||
# PostgreSQL connection URL. In Docker Compose, the hostname is usually `postgres`;
|
# PostgreSQL connection URL. In Docker Compose, the hostname is usually `postgres`;
|
||||||
# when running directly on your machine, `localhost` is typical.
|
# when running directly on your machine, `localhost` is typical.
|
||||||
DATABASE_URL="postgresql://postgres:postgres@postgres:5432/postgres"
|
DATABASE_URL="postgresql://postgres:postgres@postgres:5432/postgres"
|
||||||
|
|
||||||
|
# Optional direct connection for migrations (Neon: DATABASE_URL_UNPOOLED alias).
|
||||||
|
# DATABASE_MIGRATION_URL=""
|
||||||
|
# DATABASE_POOL_MAX="10"
|
||||||
|
|
||||||
# When "true", the server refuses to boot if the live database schema has drifted from
|
# When "true", the server refuses to boot if the live database schema has drifted from
|
||||||
# the migration ledger (e.g. a table dropped outside migrations). Default "false" logs
|
# the migration ledger (e.g. a table dropped outside migrations). Default "false" logs
|
||||||
# the drift loudly at startup and continues.
|
# the drift loudly at startup and continues.
|
||||||
@@ -71,7 +77,15 @@ SMTP_FROM="Reactive Resume <noreply@rxresu.me>"
|
|||||||
SMTP_SECURE="false"
|
SMTP_SECURE="false"
|
||||||
|
|
||||||
# --- Storage (optional) ---
|
# --- Storage (optional) ---
|
||||||
# If all S3 keys are disabled, the app uses local filesystem storage instead.
|
# Backend defaults to S3 when all credentials are present, otherwise local.
|
||||||
|
# Vercel defaults to private Blob. Explicit selection: local, s3, blob.
|
||||||
|
# STORAGE_BACKEND="local"
|
||||||
|
# BLOB_READ_WRITE_TOKEN=""
|
||||||
|
# BLOB_STORE_ID=""
|
||||||
|
# DEPLOYMENT_NAMESPACE="default"
|
||||||
|
# Vercel previews need isolated resources before setting ALLOW_PREVIEW_MIGRATIONS=true.
|
||||||
|
|
||||||
|
# If all S3 keys are disabled, Docker uses local filesystem storage instead.
|
||||||
# Make sure to mount this directory to a volume or the host filesystem to ensure data integrity.
|
# Make sure to mount this directory to a volume or the host filesystem to ensure data integrity.
|
||||||
# LOCAL_STORAGE_PATH overrides where local uploads/cache are written.
|
# LOCAL_STORAGE_PATH overrides where local uploads/cache are written.
|
||||||
# Defaults to /app/data in the official Docker image; in dev, defaults to <workspace>/data.
|
# Defaults to /app/data in the official Docker image; in dev, defaults to <workspace>/data.
|
||||||
@@ -86,7 +100,9 @@ S3_BUCKET="reactive-resume"
|
|||||||
S3_FORCE_PATH_STYLE="true"
|
S3_FORCE_PATH_STYLE="true"
|
||||||
|
|
||||||
# --- AI Agent Workspace (optional) ---
|
# --- AI Agent Workspace (optional) ---
|
||||||
# Required only for the authenticated /agent workspace and saved AI providers.
|
# Required for the authenticated /agent workspace and saved AI providers.
|
||||||
|
# Redis also shares rate limits, resume events, cancellation and view deduplication.
|
||||||
|
# Vercel Upstash KV_URL is accepted as an alias for REDIS_URL.
|
||||||
REDIS_URL="redis://redis:6379"
|
REDIS_URL="redis://redis:6379"
|
||||||
ENCRYPTION_SECRET="change-me-to-a-secure-agent-secret-in-production"
|
ENCRYPTION_SECRET="change-me-to-a-secure-agent-secret-in-production"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
name: Vercel compatibility
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
env:
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
artifact:
|
||||||
|
runs-on: ${{ vars.CI_RUNNER_X64 || 'ubuntu-latest' }}
|
||||||
|
timeout-minutes: 20
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:17-alpine
|
||||||
|
env:
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
ports: [5432:5432]
|
||||||
|
options: >-
|
||||||
|
--health-cmd "pg_isready -U postgres"
|
||||||
|
--health-interval 5s --health-timeout 5s --health-retries 10
|
||||||
|
env:
|
||||||
|
APP_URL: http://localhost:3000
|
||||||
|
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres
|
||||||
|
AUTH_SECRET: isolated-ci-auth-secret-32-characters
|
||||||
|
ENCRYPTION_SECRET: isolated-ci-encryption-secret-32-characters
|
||||||
|
REDIS_URL: redis://localhost:6379
|
||||||
|
STORAGE_BACKEND: blob
|
||||||
|
BLOB_READ_WRITE_TOKEN: vercel_blob_rw_ci_fake_build_only
|
||||||
|
VERCEL: "1"
|
||||||
|
VERCEL_ENV: production
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- uses: pnpm/action-setup@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version-file: .nvmrc
|
||||||
|
cache: pnpm
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
# Local project settings avoid authentication and API calls. Forks receive no cloud credentials.
|
||||||
|
- name: Build Vercel artifact against isolated PostgreSQL
|
||||||
|
run: |
|
||||||
|
mkdir -p .vercel
|
||||||
|
node --input-type=module - <<'JS'
|
||||||
|
import { writeFileSync } from 'node:fs';
|
||||||
|
writeFileSync('.vercel/project.json', JSON.stringify({
|
||||||
|
projectId: 'prj_ci', orgId: 'team_ci', projectName: 'reactive-resume-ci',
|
||||||
|
settings: { framework: null, nodeVersion: '24.x', createdAt: 0 }
|
||||||
|
}));
|
||||||
|
JS
|
||||||
|
pnpm dlx --allow-build=esbuild vercel@60.0.1 build --prod --yes --global-config "$RUNNER_TEMP/vercel-offline"
|
||||||
|
- name: Check Lambda module loading and function budget
|
||||||
|
run: |
|
||||||
|
node --no-experimental-require-module --input-type=module - <<'JS'
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { readFileSync, readdirSync } from 'node:fs';
|
||||||
|
const config = JSON.parse(readFileSync('.vercel/output/functions/api/index.func/.vc-config.json'));
|
||||||
|
assert.equal(config.runtime, 'nodejs24.x');
|
||||||
|
assert.equal(config.maxDuration, 300);
|
||||||
|
const tracedFiles = Object.keys(config.filePathMap ?? {});
|
||||||
|
assert.ok(tracedFiles.some((path) => path.endsWith('/pdfkit/js/standard-fonts/Helvetica.cjs')));
|
||||||
|
assert.ok(tracedFiles.some((path) => path.endsWith('/pdfkit/js/data/Helvetica.afm')));
|
||||||
|
for (const name of readdirSync('apps/server/dist')) {
|
||||||
|
if (name.endsWith('.mjs') && !['index.mjs', 'prepare-deployment.mjs'].includes(name)) {
|
||||||
|
await import(`./apps/server/dist/${name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { default: app } = await import('./apps/server/dist/vercel.mjs');
|
||||||
|
const response = await app.fetch(new Request('http://localhost:3000/api/storage/stage', { method: 'POST', body: '{}' }));
|
||||||
|
assert.equal(response.status, 401);
|
||||||
|
process.exit(0);
|
||||||
|
JS
|
||||||
|
|
||||||
|
live-smoke:
|
||||||
|
if: github.event_name == 'workflow_dispatch'
|
||||||
|
runs-on: ${{ vars.CI_RUNNER_X64 || 'ubuntu-latest' }}
|
||||||
|
environment: vercel-smoke
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version-file: .nvmrc
|
||||||
|
- name: Smoke-test dedicated deployment
|
||||||
|
env:
|
||||||
|
SMOKE_URL: ${{ vars.VERCEL_SMOKE_URL }}
|
||||||
|
SMOKE_AI_BASE_URL: ${{ vars.VERCEL_SMOKE_AI_BASE_URL }}
|
||||||
|
SMOKE_AI_API_KEY: ${{ secrets.VERCEL_SMOKE_AI_API_KEY }}
|
||||||
|
run: node tooling/deployment/smoke.mjs
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
.env*
|
||||||
|
!.env.example
|
||||||
|
.git
|
||||||
|
.codegraph
|
||||||
|
.superpowers
|
||||||
|
.agents
|
||||||
|
.codex
|
||||||
|
.claude
|
||||||
|
.turbo
|
||||||
|
**/node_modules
|
||||||
|
**/dist
|
||||||
|
**/coverage
|
||||||
|
**/reports
|
||||||
|
data
|
||||||
|
apps/web/data
|
||||||
|
screenshots
|
||||||
|
.vercel
|
||||||
|
.wrangler
|
||||||
|
.tanstack
|
||||||
|
.worktrees
|
||||||
|
.migration
|
||||||
|
.supermemory
|
||||||
|
.cache
|
||||||
|
tmp
|
||||||
|
temp
|
||||||
|
**/test-results
|
||||||
|
**/playwright-report
|
||||||
@@ -30,11 +30,10 @@ Boundaries: code/commits/PRs written normal.
|
|||||||
## Agent skills
|
## Agent skills
|
||||||
|
|
||||||
- Issues and specs: GitHub Issues for `reactive-resume/reactive-resume`. See `docs/agents/issue-tracker.md`.
|
- Issues and specs: GitHub Issues for `reactive-resume/reactive-resume`. See `docs/agents/issue-tracker.md`.
|
||||||
- Domain docs use a multi-context layout. See `docs/agents/domain.md`.
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Reactive Resume is a pnpm monorepo (Turborepo) with two deployable apps: `apps/web` (TanStack Start / React 19 / Vite) and `apps/server` (Hono / Node.js). The production Docker image runs a single Node.js process on port 3000; `apps/server` mounts the API/auth/MCP/static routes and serves the built web app.
|
Reactive Resume is a pnpm monorepo (Turborepo) with two deployable apps: `apps/web` (React 19 SPA with TanStack Router and Vite) and `apps/server` (Hono / Node.js). The production Docker image runs a single Node.js process on port 3000; `apps/server` mounts the API/auth/MCP/static routes and serves the built web app.
|
||||||
|
|
||||||
Internal packages are source-consumed through `package.json` export maps pointing at `src` files. Do not assume package-local `dist` output exists unless a package explicitly adds it.
|
Internal packages are source-consumed through `package.json` export maps pointing at `src` files. Do not assume package-local `dist` output exists unless a package explicitly adds it.
|
||||||
|
|
||||||
@@ -67,9 +66,10 @@ Narrow cross-cutting helpers go in `packages/utils` only after checking no domai
|
|||||||
## Web app conventions
|
## Web app conventions
|
||||||
|
|
||||||
- `apps/web/src/router.tsx` initializes router context with `queryClient`, `orpc`, `theme`, `locale`, `session`, and `flags`. Reuse route context instead of refetching these ad hoc.
|
- `apps/web/src/router.tsx` initializes router context with `queryClient`, `orpc`, `theme`, `locale`, `session`, and `flags`. Reuse route context instead of refetching these ad hoc.
|
||||||
- Builder shell: `apps/web/src/routes/builder/$resumeId`. Its nested preview route is client-only (`ssr: false`); the public resume route `apps/web/src/routes/$username/$slug.tsx` uses `ssr: "data-only"`.
|
- The web app is a client-rendered SPA. `apps/server` serves `index.html` and injects page metadata (OpenGraph, canonical, JSON-LD) in `apps/server/src/static/web.ts`; there is no React SSR.
|
||||||
- Browser-only preview code: `apps/web/src/features/resume/preview`. Public PDF viewer: `apps/web/src/features/resume/public`. Keep PDF.js/canvas/browser APIs out of SSR paths.
|
- Builder shell: `apps/web/src/routes/builder/$resumeId`. Public resume route: `apps/web/src/routes/$username/$slug.tsx`.
|
||||||
- Isomorphic oRPC client: `apps/web/src/libs/orpc/client.ts` — server calls use an in-process router client, browser calls use `/api/rpc` with credentials included.
|
- Browser-only preview code: `apps/web/src/features/resume/preview`. Public PDF viewer: `apps/web/src/features/resume/public`. Keep PDF.js/canvas code in these features, not in `packages/pdf`.
|
||||||
|
- oRPC client: `apps/web/src/libs/orpc/client.ts` calls `/api/rpc` with credentials included. `apps/web/src/libs/orpc/fetch.ts` stages large request bodies through Blob on Vercel.
|
||||||
- For React components with explicit props, use a named props type (e.g. `type FooProps = {...}` with `function Foo(props: FooProps)`) rather than inline object annotations, especially with more than one field or with generics.
|
- For React components with explicit props, use a named props type (e.g. `type FooProps = {...}` with `function Foo(props: FooProps)`) rather than inline object annotations, especially with more than one field or with generics.
|
||||||
|
|
||||||
## Package boundaries
|
## Package boundaries
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
# Domain contexts
|
|
||||||
|
|
||||||
- [Resume](packages/resume/CONTEXT.md): authored resume content and presentation concepts shared by the builder and exporters.
|
|
||||||
@@ -165,7 +165,7 @@ For detailed setup instructions, environment configuration, and self-hosting gui
|
|||||||
|
|
||||||
| Category | Technology |
|
| Category | Technology |
|
||||||
| ---------------- | ------------------------------- |
|
| ---------------- | ------------------------------- |
|
||||||
| Framework | TanStack Start (React 19, Vite) |
|
| Framework | TanStack Router (React 19, Vite) |
|
||||||
| Runtime | Node.js |
|
| Runtime | Node.js |
|
||||||
| Language | TypeScript |
|
| Language | TypeScript |
|
||||||
| Database | PostgreSQL with Drizzle ORM |
|
| Database | PostgreSQL with Drizzle ORM |
|
||||||
@@ -189,7 +189,13 @@ The full documentation lives at [docs.rxresu.me](https://docs.rxresu.me):
|
|||||||
|
|
||||||
## Self-Hosting
|
## Self-Hosting
|
||||||
|
|
||||||
Reactive Resume can be self-hosted using Docker. The stack includes:
|
Reactive Resume supports Docker and Vercel Hobby.
|
||||||
|
|
||||||
|
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Freactive-resume%2Freactive-resume&project-name=reactive-resume&repository-name=reactive-resume&env=AUTH_SECRET%2CENCRYPTION_SECRET&envDescription=Generate+two+independent+secrets+with+openssl+rand+-hex+32.+Keep+these+values+across+deployments.&envLink=https%3A%2F%2Fdocs.rxresu.me%2Fself-hosting%2Fvercel&stores=%5B%7B%22type%22%3A%22integration%22%2C%22protocol%22%3A%22storage%22%2C%22integrationSlug%22%3A%22neon%22%2C%22productSlug%22%3A%22neon%22%7D%2C%7B%22type%22%3A%22integration%22%2C%22protocol%22%3A%22storage%22%2C%22integrationSlug%22%3A%22upstash%22%2C%22productSlug%22%3A%22upstash-kv%22%7D%2C%7B%22type%22%3A%22blob%22%2C%22access%22%3A%22private%22%7D%5D)
|
||||||
|
|
||||||
|
Vercel provisions Neon PostgreSQL, private Blob storage, and Upstash Redis through its deployment wizard. Supply two persistent secrets, then deploy. See the [Vercel guide](docs/self-hosting/vercel.mdx) for setup, limits, and optional SMTP/OAuth configuration.
|
||||||
|
|
||||||
|
For Docker, the stack includes:
|
||||||
|
|
||||||
- **PostgreSQL** — Database for storing user data and resumes
|
- **PostgreSQL** — Database for storing user data and resumes
|
||||||
- **SeaweedFS** (optional) — S3-compatible storage for file uploads
|
- **SeaweedFS** (optional) — S3-compatible storage for file uploads
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from "../apps/server/dist/vercel.mjs";
|
||||||
@@ -58,6 +58,8 @@
|
|||||||
"@sindresorhus/slugify": "^3.0.1",
|
"@sindresorhus/slugify": "^3.0.1",
|
||||||
"@t3-oss/env-core": "^0.13.11",
|
"@t3-oss/env-core": "^0.13.11",
|
||||||
"@uiw/color-convert": "^2.10.3",
|
"@uiw/color-convert": "^2.10.3",
|
||||||
|
"@vercel/blob": "^2.8.0",
|
||||||
|
"@vercel/functions": "^3.9.9",
|
||||||
"ai": "^7.0.107",
|
"ai": "^7.0.107",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
"better-auth": "1.7.5",
|
"better-auth": "1.7.5",
|
||||||
@@ -70,6 +72,8 @@
|
|||||||
"fast-json-patch": "^3.1.1",
|
"fast-json-patch": "^3.1.1",
|
||||||
"fast-png": "^8.0.0",
|
"fast-png": "^8.0.0",
|
||||||
"hono": "^4.13.8",
|
"hono": "^4.13.8",
|
||||||
|
"ioredis": "^6.0.0",
|
||||||
|
"jose": "^6.2.12",
|
||||||
"jsonrepair": "^3.15.0",
|
"jsonrepair": "^3.15.0",
|
||||||
"node-html-parser": "^9.0.4",
|
"node-html-parser": "^9.0.4",
|
||||||
"nodemailer": "^10.0.10",
|
"nodemailer": "^10.0.10",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { Context } from "hono";
|
|||||||
import { isIP } from "node:net";
|
import { isIP } from "node:net";
|
||||||
import { getConnInfo } from "@hono/node-server/conninfo";
|
import { getConnInfo } from "@hono/node-server/conninfo";
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
|
import { prepareStagedBody, withStagedBody } from "@reactive-resume/api/features/storage/transport";
|
||||||
import { handleMcp } from "../mcp/handler";
|
import { handleMcp } from "../mcp/handler";
|
||||||
import { handleOpenApi } from "../openapi/handler";
|
import { handleOpenApi } from "../openapi/handler";
|
||||||
import {
|
import {
|
||||||
@@ -33,8 +34,14 @@ const getTrustedClient = (context: Context<ServerEnvironment>): string => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createApp() {
|
type AppOptions = {
|
||||||
|
serveStatic?: boolean;
|
||||||
|
trustedClient?: (request: Request) => string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createApp(options: AppOptions = {}) {
|
||||||
const app = new Hono<ServerEnvironment>();
|
const app = new Hono<ServerEnvironment>();
|
||||||
|
const client = (c: Context<ServerEnvironment>) => options.trustedClient?.(c.req.raw) ?? getTrustedClient(c);
|
||||||
|
|
||||||
app.use("/auth/*", async (c, next) => {
|
app.use("/auth/*", async (c, next) => {
|
||||||
await next();
|
await next();
|
||||||
@@ -44,15 +51,16 @@ export function createApp() {
|
|||||||
c.header("Cache-Control", "no-store");
|
c.header("Cache-Control", "no-store");
|
||||||
});
|
});
|
||||||
|
|
||||||
app.all("/api/rpc", (c) => handleRpc(c.req.raw, getTrustedClient(c)));
|
app.post("/api/storage/stage", (c) => prepareStagedBody(c.req.raw));
|
||||||
app.all("/api/rpc/*", (c) => handleRpc(c.req.raw, getTrustedClient(c)));
|
app.all("/api/rpc", (c) => withStagedBody(c.req.raw, (request) => handleRpc(request, client(c))));
|
||||||
app.all("/api/openapi", (c) => handleOpenApi(c.req.raw, getTrustedClient(c)));
|
app.all("/api/rpc/*", (c) => withStagedBody(c.req.raw, (request) => handleRpc(request, client(c))));
|
||||||
app.all("/api/openapi/*", (c) => handleOpenApi(c.req.raw, getTrustedClient(c)));
|
app.all("/api/openapi", (c) => handleOpenApi(c.req.raw, client(c)));
|
||||||
|
app.all("/api/openapi/*", (c) => handleOpenApi(c.req.raw, client(c)));
|
||||||
app.get("/api/auth/oauth", (c) => handleOAuth(c.req.raw));
|
app.get("/api/auth/oauth", (c) => handleOAuth(c.req.raw));
|
||||||
app.all("/api/auth/*", (c) => handleAuth(c.req.raw));
|
app.all("/api/auth/*", (c) => handleAuth(c.req.raw));
|
||||||
app.get("/api/health", () => handleHealth());
|
app.get("/api/health", () => handleHealth());
|
||||||
app.get("/api/resumes/:username/:slug/pdf", (c) =>
|
app.get("/api/resumes/:username/:slug/pdf", (c) =>
|
||||||
handlePublicResumePdf(c.req.raw, c.req.param("username"), c.req.param("slug"), getTrustedClient(c)),
|
handlePublicResumePdf(c.req.raw, c.req.param("username"), c.req.param("slug"), client(c)),
|
||||||
);
|
);
|
||||||
app.get("/api/resumes/:id/pdf", (c) => handleResumePdfDownload(c.req.raw, c.req.param("id")));
|
app.get("/api/resumes/:id/pdf", (c) => handleResumePdfDownload(c.req.raw, c.req.param("id")));
|
||||||
app.get("/api/uploads/*", (c) => handleUpload(c.req.raw));
|
app.get("/api/uploads/*", (c) => handleUpload(c.req.raw));
|
||||||
@@ -76,7 +84,7 @@ export function createApp() {
|
|||||||
// Must precede the static middleware: serveStatic resolves "/" to dist/index.html and would
|
// Must precede the static middleware: serveStatic resolves "/" to dist/index.html and would
|
||||||
// return it verbatim, skipping the OpenGraph/Twitter/canonical/JSON-LD injection in handleWebApp.
|
// return it verbatim, skipping the OpenGraph/Twitter/canonical/JSON-LD injection in handleWebApp.
|
||||||
app.on(["GET", "HEAD"], "/", (c) => handleWebApp(c.req.raw));
|
app.on(["GET", "HEAD"], "/", (c) => handleWebApp(c.req.raw));
|
||||||
app.use("/*", serveWebDistStatic);
|
if (options.serveStatic !== false) app.use("/*", serveWebDistStatic);
|
||||||
app.on(["GET", "HEAD"], "/*", (c) => handleWebApp(c.req.raw));
|
app.on(["GET", "HEAD"], "/*", (c) => handleWebApp(c.req.raw));
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { sql } from "drizzle-orm";
|
|||||||
import { withTimeout } from "es-toolkit";
|
import { withTimeout } from "es-toolkit";
|
||||||
import { getStorageService } from "@reactive-resume/api/features/storage";
|
import { getStorageService } from "@reactive-resume/api/features/storage";
|
||||||
import { db } from "@reactive-resume/db/client";
|
import { db } from "@reactive-resume/db/client";
|
||||||
|
import { getRedis } from "@reactive-resume/db/redis";
|
||||||
import { appVersion } from "../app-version";
|
import { appVersion } from "../app-version";
|
||||||
|
|
||||||
const HEALTHCHECK_TIMEOUT_MS = 1_500;
|
const HEALTHCHECK_TIMEOUT_MS = 1_500;
|
||||||
@@ -32,13 +33,13 @@ async function runCheck(check: () => Promise<object>): Promise<CheckResult> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function publicCheck(check: CheckResult, name: "Database" | "Storage"): CheckResult {
|
function publicCheck(check: CheckResult, name: "Database" | "Storage" | "Redis"): CheckResult {
|
||||||
if (check.status === "healthy") return check;
|
if (check.status === "healthy") return check;
|
||||||
return {
|
return {
|
||||||
status: check.status,
|
status: check.status,
|
||||||
latencyMs: check.latencyMs,
|
latencyMs: check.latencyMs,
|
||||||
error: `${name} health check failed.`,
|
error: `${name} health check failed.`,
|
||||||
...(check.type === "local" || check.type === "s3" ? { type: check.type } : {}),
|
...(check.type === "local" || check.type === "s3" || check.type === "blob" ? { type: check.type } : {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,8 +52,18 @@ async function checkDatabase() {
|
|||||||
const checkStorage = () => getStorageService().healthcheck();
|
const checkStorage = () => getStorageService().healthcheck();
|
||||||
|
|
||||||
export async function handleHealth() {
|
export async function handleHealth() {
|
||||||
const [database, storage] = await Promise.all([runCheck(checkDatabase), runCheck(checkStorage)]);
|
const redisClient = getRedis();
|
||||||
const status = [database, storage].some((check) => check.status === "unhealthy") ? "unhealthy" : "healthy";
|
const [database, storage, redis] = await Promise.all([
|
||||||
|
runCheck(checkDatabase),
|
||||||
|
runCheck(checkStorage),
|
||||||
|
redisClient
|
||||||
|
? runCheck(async () => {
|
||||||
|
await redisClient.ping();
|
||||||
|
return { status: "healthy" };
|
||||||
|
})
|
||||||
|
: undefined,
|
||||||
|
]);
|
||||||
|
const status = [database, storage, redis].some((check) => check?.status === "unhealthy") ? "unhealthy" : "healthy";
|
||||||
|
|
||||||
const checks = {
|
const checks = {
|
||||||
service: "reactive-resume",
|
service: "reactive-resume",
|
||||||
@@ -62,6 +73,7 @@ export async function handleHealth() {
|
|||||||
uptime: `${process.uptime().toFixed(2)}s`,
|
uptime: `${process.uptime().toFixed(2)}s`,
|
||||||
database: publicCheck(database, "Database"),
|
database: publicCheck(database, "Database"),
|
||||||
storage: publicCheck(storage, "Storage"),
|
storage: publicCheck(storage, "Storage"),
|
||||||
|
...(redis ? { redis: publicCheck(redis, "Redis") } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (status === "unhealthy") {
|
if (status === "unhealthy") {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export async function handlePublicResumePdf(
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const status = errorStatus(error);
|
const status = errorStatus(error);
|
||||||
|
if (status === 500) console.error("Public resume PDF generation failed", error);
|
||||||
return noStoreResponse(
|
return noStoreResponse(
|
||||||
status === 500 ? "Failed to generate public resume PDF" : "Public resume PDF unavailable",
|
status === 500 ? "Failed to generate public resume PDF" : "Public resume PDF unavailable",
|
||||||
status,
|
status,
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ vi.mock("./http/app", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
vi.mock("@reactive-resume/auth/config", () => ({
|
||||||
|
initializeAuth: async () => {
|
||||||
|
await Promise.resolve();
|
||||||
|
events.push("auth ready");
|
||||||
|
},
|
||||||
|
}));
|
||||||
vi.mock("@hono/node-server", () => ({
|
vi.mock("@hono/node-server", () => ({
|
||||||
serve: () => {
|
serve: () => {
|
||||||
events.push("server listening");
|
events.push("server listening");
|
||||||
@@ -30,6 +36,6 @@ describe("server startup", () => {
|
|||||||
const entry = await import("./index");
|
const entry = await import("./index");
|
||||||
expect(events).toEqual([]);
|
expect(events).toEqual([]);
|
||||||
await entry.main();
|
await entry.main();
|
||||||
expect(events).toEqual(["migrations complete", "auth imported", "app created", "server listening"]);
|
expect(events).toEqual(["migrations complete", "auth imported", "auth ready", "app created", "server listening"]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import { runStartupChecks } from "./startup/checks";
|
|||||||
export async function main() {
|
export async function main() {
|
||||||
await runStartupChecks();
|
await runStartupChecks();
|
||||||
|
|
||||||
// OAuth resource seeding starts when auth is imported, so load the app only
|
// Load and initialize auth only after migrations have created the provider tables.
|
||||||
// after migrations have created the provider tables.
|
|
||||||
const { createApp } = await import("./http/app");
|
const { createApp } = await import("./http/app");
|
||||||
|
const { initializeAuth } = await import("@reactive-resume/auth/config");
|
||||||
|
await initializeAuth();
|
||||||
|
|
||||||
// Safety net: Node 24 crashes the whole process on an unhandled rejection. One request's
|
// Safety net: Node 24 crashes the whole process on an unhandled rejection. One request's
|
||||||
// stray promise must not take the server down for everyone, so log and keep serving.
|
// stray promise must not take the server down for everyone, so log and keep serving.
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { initializeAuth } from "@reactive-resume/auth/config";
|
||||||
|
import { getPool } from "@reactive-resume/db/client";
|
||||||
|
import { env } from "@reactive-resume/env/server";
|
||||||
|
import { runDatabaseMigrations } from "./startup/checks";
|
||||||
|
|
||||||
|
if (process.env.VERCEL_ENV === "preview" && process.env.ALLOW_PREVIEW_MIGRATIONS !== "true") {
|
||||||
|
throw new Error(
|
||||||
|
"Preview deployment needs an isolated database. Set ALLOW_PREVIEW_MIGRATIONS=true only after connecting one.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.VERCEL === "1") {
|
||||||
|
if (env.STORAGE_BACKEND !== "blob")
|
||||||
|
throw new Error("Vercel requires private Blob storage for direct uploads. Docker supports local, S3, and Blob.");
|
||||||
|
if (!env.REDIS_URL || !env.ENCRYPTION_SECRET) throw new Error("Vercel requires Redis and ENCRYPTION_SECRET.");
|
||||||
|
}
|
||||||
|
await runDatabaseMigrations();
|
||||||
|
|
||||||
|
await initializeAuth();
|
||||||
|
await getPool().end();
|
||||||
@@ -25,32 +25,46 @@ function resolveWorkspaceFolder(folderName: string): string {
|
|||||||
throw new Error(`Could not locate ${folderName} folder relative to ${resolveFromCurrentModule(".")}`);
|
throw new Error(`Could not locate ${folderName} folder relative to ${resolveFromCurrentModule(".")}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runDatabaseMigrations() {
|
export async function runDatabaseMigrations() {
|
||||||
console.info("Running database migrations...");
|
console.info("Running database migrations...");
|
||||||
|
|
||||||
const pool = new Pool({ connectionString: env.DATABASE_URL });
|
const pool = new Pool({
|
||||||
const db = drizzle({ client: pool });
|
connectionString: env.DATABASE_MIGRATION_URL ?? env.DATABASE_URL,
|
||||||
|
max: 1,
|
||||||
|
connectionTimeoutMillis: 10_000,
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const client = await pool.connect();
|
||||||
try {
|
try {
|
||||||
await migrate(db, { migrationsFolder: resolveWorkspaceFolder("migrations") });
|
await client.query("SELECT pg_advisory_lock(721830451)");
|
||||||
console.info("Database migrations completed");
|
const db = drizzle({ client });
|
||||||
} catch (error) {
|
try {
|
||||||
console.error("Database migrations failed", { error });
|
await migrate(db, { migrationsFolder: resolveWorkspaceFolder("migrations") });
|
||||||
throw error;
|
console.info("Database migrations completed");
|
||||||
}
|
} catch (error) {
|
||||||
|
console.error("Database migrations failed", { error });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
// Post-migration verification is not a migration failure, so it gets its own log
|
// Post-migration verification is not a migration failure, so it gets its own log
|
||||||
// message. A drifted schema still lets the server boot; STRICT_SCHEMA_CHECK=true
|
// message. A drifted schema still lets the server boot; STRICT_SCHEMA_CHECK=true
|
||||||
// makes the drift fatal instead.
|
// makes the drift fatal instead.
|
||||||
try {
|
try {
|
||||||
await verifyMigratedSchema(pool);
|
await verifyMigratedSchema(client);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Database schema verification failed", { error });
|
console.error("Database schema verification failed", { error });
|
||||||
if (env.STRICT_SCHEMA_CHECK) throw error;
|
if (env.STRICT_SCHEMA_CHECK) throw error;
|
||||||
console.error(
|
console.error(
|
||||||
"Continuing with a drifted database schema; set STRICT_SCHEMA_CHECK=true to refuse startup instead.",
|
"Continuing with a drifted database schema; set STRICT_SCHEMA_CHECK=true to refuse startup instead.",
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
await client.query("SELECT pg_advisory_unlock(721830451)");
|
||||||
|
} finally {
|
||||||
|
client.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await pool.end();
|
await pool.end();
|
||||||
@@ -58,7 +72,7 @@ async function runDatabaseMigrations() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function validateLocalStoragePath() {
|
async function validateLocalStoragePath() {
|
||||||
if (env.S3_ACCESS_KEY_ID && env.S3_SECRET_ACCESS_KEY && env.S3_BUCKET) return;
|
if (env.STORAGE_BACKEND !== "local") return;
|
||||||
|
|
||||||
const dataDirectory = getLocalDataDirectory(env.LOCAL_STORAGE_PATH);
|
const dataDirectory = getLocalDataDirectory(env.LOCAL_STORAGE_PATH);
|
||||||
console.info(`Validating local storage path: ${dataDirectory}`);
|
console.info(`Validating local storage path: ${dataDirectory}`);
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { TRUSTED_IP_HEADERS } from "@reactive-resume/utils/rate-limit";
|
||||||
|
|
||||||
|
const mocks = vi.hoisted(() => ({
|
||||||
|
ipAddress: vi.fn<(request: Request) => string | undefined>(),
|
||||||
|
waitUntil: vi.fn(),
|
||||||
|
initializeAuth: vi.fn(),
|
||||||
|
attachDatabasePool: vi.fn(),
|
||||||
|
configureAgentStreamLifetime: vi.fn(),
|
||||||
|
pool: {},
|
||||||
|
getPool: vi.fn(),
|
||||||
|
createApp:
|
||||||
|
vi.fn<
|
||||||
|
(options: { serveStatic: boolean; trustedClient: (request: Request) => string }) => {
|
||||||
|
fetch: (request: Request) => Promise<Response>;
|
||||||
|
}
|
||||||
|
>(),
|
||||||
|
handle: vi.fn<(request: Request) => Promise<Response>>(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("@vercel/functions", () => ({
|
||||||
|
ipAddress: mocks.ipAddress,
|
||||||
|
waitUntil: mocks.waitUntil,
|
||||||
|
attachDatabasePool: mocks.attachDatabasePool,
|
||||||
|
}));
|
||||||
|
vi.mock("@reactive-resume/api/features/agent/streams", () => ({
|
||||||
|
configureAgentStreamLifetime: mocks.configureAgentStreamLifetime,
|
||||||
|
}));
|
||||||
|
vi.mock("@reactive-resume/auth/config", () => ({ initializeAuth: mocks.initializeAuth }));
|
||||||
|
vi.mock("@reactive-resume/db/client", () => ({ getPool: mocks.getPool }));
|
||||||
|
vi.mock("./http/app", () => ({ createApp: mocks.createApp }));
|
||||||
|
|
||||||
|
function spoofedRequest() {
|
||||||
|
return new Request("https://resume.test/api/rpc?batch=1", {
|
||||||
|
method: "POST",
|
||||||
|
body: "original RPC body",
|
||||||
|
headers: {
|
||||||
|
...Object.fromEntries(TRUSTED_IP_HEADERS.map((header) => [header, "192.0.2.66"])),
|
||||||
|
"x-forwarded-for": "192.0.2.66, 192.0.2.77",
|
||||||
|
cookie: "session=original",
|
||||||
|
authorization: "Bearer original",
|
||||||
|
"content-type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.resetModules();
|
||||||
|
vi.clearAllMocks();
|
||||||
|
mocks.getPool.mockReturnValue(mocks.pool);
|
||||||
|
mocks.handle.mockResolvedValue(new Response("handled"));
|
||||||
|
mocks.createApp.mockReturnValue({ fetch: mocks.handle });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Vercel adapter", () => {
|
||||||
|
it("registers platform lifetime hooks and disables filesystem static serving", async () => {
|
||||||
|
await import("./vercel");
|
||||||
|
expect(mocks.configureAgentStreamLifetime).toHaveBeenCalledExactlyOnceWith(mocks.waitUntil);
|
||||||
|
expect(mocks.attachDatabasePool).toHaveBeenCalledExactlyOnceWith(mocks.pool);
|
||||||
|
expect(mocks.createApp).toHaveBeenCalledExactlyOnceWith({
|
||||||
|
serveStatic: false,
|
||||||
|
trustedClient: expect.any(Function),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(["203.0.113.9", "2001:db8::9"])("replaces all spoofed IP headers with platform IP %s", async (ip) => {
|
||||||
|
mocks.ipAddress.mockReturnValue(ip);
|
||||||
|
const { default: adapter } = await import("./vercel");
|
||||||
|
const request = spoofedRequest();
|
||||||
|
expect(await (await adapter.fetch(request)).text()).toBe("handled");
|
||||||
|
expect(mocks.ipAddress).toHaveBeenCalledExactlyOnceWith(request);
|
||||||
|
const forwarded = mocks.handle.mock.calls[0]?.[0];
|
||||||
|
if (!forwarded) throw new Error("Expected forwarded request");
|
||||||
|
for (const header of TRUSTED_IP_HEADERS) {
|
||||||
|
const expected = ["x-real-ip", "x-forwarded-for"].includes(header.toLowerCase()) ? ip : null;
|
||||||
|
expect(forwarded.headers.get(header)).toBe(expected);
|
||||||
|
}
|
||||||
|
expect(mocks.createApp.mock.calls[0]?.[0].trustedClient(forwarded)).toBe(ip);
|
||||||
|
expect(forwarded.url).toBe(request.url);
|
||||||
|
expect(forwarded.method).toBe("POST");
|
||||||
|
expect(await forwarded.text()).toBe("original RPC body");
|
||||||
|
expect(forwarded.headers.get("cookie")).toBe("session=original");
|
||||||
|
expect(forwarded.headers.get("authorization")).toBe("Bearer original");
|
||||||
|
expect(forwarded.headers.get("content-type")).toBe("application/json");
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([undefined, "", "invalid-ip", "203.0.113.9, 192.0.2.66"])(
|
||||||
|
"clears attacker headers when platform IP is missing or invalid: %s",
|
||||||
|
async (ip) => {
|
||||||
|
mocks.ipAddress.mockReturnValue(ip);
|
||||||
|
const { default: adapter } = await import("./vercel");
|
||||||
|
await adapter.fetch(spoofedRequest());
|
||||||
|
const forwarded = mocks.handle.mock.calls[0]?.[0];
|
||||||
|
if (!forwarded) throw new Error("Expected forwarded request");
|
||||||
|
for (const header of TRUSTED_IP_HEADERS) expect(forwarded.headers.has(header)).toBe(false);
|
||||||
|
expect(mocks.createApp.mock.calls[0]?.[0].trustedClient(forwarded)).toBe("unknown");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { isIP } from "node:net";
|
||||||
|
import { attachDatabasePool, ipAddress, waitUntil } from "@vercel/functions";
|
||||||
|
import { configureAgentStreamLifetime } from "@reactive-resume/api/features/agent/streams";
|
||||||
|
import { initializeAuth } from "@reactive-resume/auth/config";
|
||||||
|
import { getPool } from "@reactive-resume/db/client";
|
||||||
|
import { TRUSTED_IP_HEADERS } from "@reactive-resume/utils/rate-limit";
|
||||||
|
import { createApp } from "./http/app";
|
||||||
|
|
||||||
|
configureAgentStreamLifetime(waitUntil);
|
||||||
|
attachDatabasePool(getPool());
|
||||||
|
const app = createApp({
|
||||||
|
serveStatic: false,
|
||||||
|
trustedClient: (request) => request.headers.get("x-real-ip") ?? "unknown",
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async fetch(request: Request) {
|
||||||
|
await initializeAuth();
|
||||||
|
const ip = ipAddress(request);
|
||||||
|
const headers = new Headers(request.headers);
|
||||||
|
for (const name of TRUSTED_IP_HEADERS) headers.delete(name);
|
||||||
|
headers.delete("x-real-ip");
|
||||||
|
if (ip && isIP(ip)) {
|
||||||
|
headers.set("x-real-ip", ip);
|
||||||
|
headers.set("x-forwarded-for", ip);
|
||||||
|
}
|
||||||
|
return app.fetch(new Request(request, { headers }));
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -8,8 +8,35 @@ const rootPackageJson = JSON.parse(readFileSync(new URL("../../package.json", im
|
|||||||
version?: string;
|
version?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Lambda disables require(ESM) and uses stricter CJS export detection than standalone Node.
|
||||||
|
const bundledInteropPackages = new Set([
|
||||||
|
"@uiw/color-convert",
|
||||||
|
"@babel/runtime",
|
||||||
|
"sanitize-html",
|
||||||
|
"htmlparser2",
|
||||||
|
"domhandler",
|
||||||
|
"domutils",
|
||||||
|
"domelementtype",
|
||||||
|
"dom-serializer",
|
||||||
|
"entities",
|
||||||
|
"deepmerge",
|
||||||
|
"escape-string-regexp",
|
||||||
|
"is-plain-object",
|
||||||
|
"parse-srcset",
|
||||||
|
"postcss",
|
||||||
|
"nanoid",
|
||||||
|
"picocolors",
|
||||||
|
"source-map-js",
|
||||||
|
"launder",
|
||||||
|
"dayjs",
|
||||||
|
]);
|
||||||
|
|
||||||
const shouldExternalizeThirdParty = (id: string) => {
|
const shouldExternalizeThirdParty = (id: string) => {
|
||||||
if (id.startsWith("@reactive-resume/")) return false;
|
const packageName = id
|
||||||
|
.split("/")
|
||||||
|
.slice(0, id.startsWith("@") ? 2 : 1)
|
||||||
|
.join("/");
|
||||||
|
if (id.startsWith("@reactive-resume/") || bundledInteropPackages.has(packageName)) return false;
|
||||||
if (id.startsWith("@/") || id.startsWith(".") || id.startsWith("/") || id.startsWith("\0")) return false;
|
if (id.startsWith("@/") || id.startsWith(".") || id.startsWith("/") || id.startsWith("\0")) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -33,7 +60,9 @@ const promptAssetsPlugin: TsdownPlugin = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
entry: { index: "src/index.ts" },
|
entry: { index: "src/index.ts", vercel: "src/vercel.ts", "prepare-deployment": "src/prepare-deployment.ts" },
|
||||||
|
// Keep import.meta.url-based asset lookup adjacent to the entrypoints.
|
||||||
|
outputOptions: { chunkFileNames: "[name]-[hash].mjs" },
|
||||||
format: "esm",
|
format: "esm",
|
||||||
platform: "node",
|
platform: "node",
|
||||||
target: "node24",
|
target: "node24",
|
||||||
@@ -47,7 +76,7 @@ export default defineConfig({
|
|||||||
suppressWarnings: [/dynamic import will not move module into another chunk/],
|
suppressWarnings: [/dynamic import will not move module into another chunk/],
|
||||||
outExtensions: () => ({ js: ".mjs" }),
|
outExtensions: () => ({ js: ".mjs" }),
|
||||||
deps: {
|
deps: {
|
||||||
alwaysBundle: [/^@reactive-resume\//],
|
alwaysBundle: [/^@reactive-resume\//, ...bundledInteropPackages],
|
||||||
neverBundle: shouldExternalizeThirdParty,
|
neverBundle: shouldExternalizeThirdParty,
|
||||||
},
|
},
|
||||||
plugins: [promptAssetsPlugin],
|
plugins: [promptAssetsPlugin],
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { createORPCClient, onError } from "@orpc/client";
|
|||||||
import { RPCLink } from "@orpc/client/fetch";
|
import { RPCLink } from "@orpc/client/fetch";
|
||||||
import { BatchLinkPlugin } from "@orpc/client/plugins";
|
import { BatchLinkPlugin } from "@orpc/client/plugins";
|
||||||
import { createTanstackQueryUtils } from "@orpc/tanstack-query";
|
import { createTanstackQueryUtils } from "@orpc/tanstack-query";
|
||||||
|
import { rpcFetch } from "./fetch";
|
||||||
|
|
||||||
const getRpcUrl = () => {
|
const getRpcUrl = () => {
|
||||||
if (typeof window === "undefined") return "http://localhost:3000/api/rpc";
|
if (typeof window === "undefined") return "http://localhost:3000/api/rpc";
|
||||||
@@ -13,7 +14,7 @@ const getRpcUrl = () => {
|
|||||||
export const client: RouterClient<typeof router> = createORPCClient(
|
export const client: RouterClient<typeof router> = createORPCClient(
|
||||||
new RPCLink({
|
new RPCLink({
|
||||||
url: getRpcUrl(),
|
url: getRpcUrl(),
|
||||||
fetch: (request, init) => fetch(request, { ...init, credentials: "include" }),
|
fetch: rpcFetch,
|
||||||
plugins: [
|
plugins: [
|
||||||
new BatchLinkPlugin({
|
new BatchLinkPlugin({
|
||||||
mode: "streaming",
|
mode: "streaming",
|
||||||
@@ -32,7 +33,7 @@ export const client: RouterClient<typeof router> = createORPCClient(
|
|||||||
export const streamClient: RouterClient<typeof router> = createORPCClient(
|
export const streamClient: RouterClient<typeof router> = createORPCClient(
|
||||||
new RPCLink({
|
new RPCLink({
|
||||||
url: getRpcUrl(),
|
url: getRpcUrl(),
|
||||||
fetch: (request, init) => fetch(request, { ...init, credentials: "include" }),
|
fetch: rpcFetch,
|
||||||
interceptors: [
|
interceptors: [
|
||||||
onError((error) => {
|
onError((error) => {
|
||||||
if (error instanceof DOMException && error.name === "AbortError") return;
|
if (error instanceof DOMException && error.name === "AbortError") return;
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const fetchMock = vi.fn<typeof fetch>();
|
||||||
|
const rpcUrl = "https://resume.test/api/rpc/storage/uploadFile?batch=1";
|
||||||
|
const contentType = "multipart/form-data; boundary=original-boundary";
|
||||||
|
const largeBody = new Uint8Array(5 * 1024 * 1024).fill(173);
|
||||||
|
|
||||||
|
function queueStaging() {
|
||||||
|
fetchMock
|
||||||
|
.mockResolvedValueOnce(Response.json({ id: "upload-1", url: "https://blob.test/signed-put" }))
|
||||||
|
.mockResolvedValueOnce(new Response("uploaded"))
|
||||||
|
.mockResolvedValueOnce(new Response("rpc-result"));
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.resetModules();
|
||||||
|
fetchMock.mockReset();
|
||||||
|
vi.stubGlobal("fetch", fetchMock);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => vi.unstubAllGlobals());
|
||||||
|
|
||||||
|
describe("RPC fetch", () => {
|
||||||
|
it("sends small requests directly with their body and headers", async () => {
|
||||||
|
fetchMock.mockResolvedValue(new Response("ok"));
|
||||||
|
const { rpcFetch } = await import("./fetch");
|
||||||
|
await rpcFetch(rpcUrl, { method: "POST", body: "original bytes", headers: { "x-example": "preserved" } });
|
||||||
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||||
|
const [url, sent = {}] = fetchMock.mock.calls[0] ?? [];
|
||||||
|
expect(url).toBe(rpcUrl);
|
||||||
|
// Plain bytes (not a stream or Blob) keep the body inspectable and avoid duplex streaming.
|
||||||
|
expect(sent.body).toBeInstanceOf(ArrayBuffer);
|
||||||
|
expect(new TextDecoder().decode(sent.body as ArrayBuffer)).toBe("original bytes");
|
||||||
|
expect(new Headers(sent.headers).get("x-example")).toBe("preserved");
|
||||||
|
expect(sent.credentials).toBe("include");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uploads large wire bytes to Blob, then sends a tiny reference to the original RPC", async () => {
|
||||||
|
queueStaging();
|
||||||
|
const { rpcFetch } = await import("./fetch");
|
||||||
|
const result = await rpcFetch(
|
||||||
|
new Request(rpcUrl, {
|
||||||
|
method: "POST",
|
||||||
|
body: largeBody,
|
||||||
|
headers: { "content-type": contentType, "x-example": "preserved" },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(await result.text()).toBe("rpc-result");
|
||||||
|
expect(fetchMock).toHaveBeenCalledTimes(3);
|
||||||
|
const [prepareUrl, preparation] = fetchMock.mock.calls[0] ?? [];
|
||||||
|
expect(prepareUrl).toBe("/api/storage/stage");
|
||||||
|
expect(JSON.parse(preparation?.body as string)).toEqual({
|
||||||
|
path: "/api/rpc/storage/uploadFile?batch=1",
|
||||||
|
contentType,
|
||||||
|
size: largeBody.length,
|
||||||
|
});
|
||||||
|
const [uploadUrl, upload] = fetchMock.mock.calls[1] ?? [];
|
||||||
|
expect(uploadUrl).toBe("https://blob.test/signed-put");
|
||||||
|
expect(upload?.method).toBe("PUT");
|
||||||
|
expect(Buffer.from(upload?.body as ArrayBuffer).equals(Buffer.from(largeBody))).toBe(true);
|
||||||
|
const [finalUrl, finalRequest] = fetchMock.mock.calls[2] ?? [];
|
||||||
|
expect(finalUrl).toBe(rpcUrl);
|
||||||
|
expect(finalRequest?.body).toBeUndefined();
|
||||||
|
expect(finalRequest?.credentials).toBe("include");
|
||||||
|
const headers = new Headers(finalRequest?.headers);
|
||||||
|
expect(headers.get("x-resume-staged-body")).toBe("upload-1");
|
||||||
|
expect(headers.get("content-type")).toBe(contentType);
|
||||||
|
expect(headers.get("x-example")).toBe("preserved");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sends large requests directly once staging is unavailable on Docker", async () => {
|
||||||
|
fetchMock.mockResolvedValueOnce(new Response("Not Found", { status: 404 })).mockResolvedValue(new Response("ok"));
|
||||||
|
const { rpcFetch } = await import("./fetch");
|
||||||
|
await rpcFetch(rpcUrl, { method: "POST", body: largeBody });
|
||||||
|
await rpcFetch(rpcUrl, { method: "POST", body: largeBody });
|
||||||
|
expect(fetchMock).toHaveBeenCalledTimes(3);
|
||||||
|
for (const call of [fetchMock.mock.calls[1], fetchMock.mock.calls[2]]) {
|
||||||
|
const [url, sent = {}] = call ?? [];
|
||||||
|
expect(url).toBe(rpcUrl);
|
||||||
|
expect(Buffer.from(sent.body as ArrayBuffer).equals(Buffer.from(largeBody))).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not dispatch the RPC when preparation fails", async () => {
|
||||||
|
fetchMock.mockResolvedValueOnce(new Response("Unavailable", { status: 503 }));
|
||||||
|
const { rpcFetch } = await import("./fetch");
|
||||||
|
await expect(
|
||||||
|
rpcFetch(rpcUrl, { method: "POST", body: largeBody, headers: { "content-type": contentType } }),
|
||||||
|
).rejects.toThrow();
|
||||||
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
let stagingUnavailable = false;
|
||||||
|
|
||||||
|
/** Large RPC bodies bypass the hosting ingress limit while retaining their original wire format. */
|
||||||
|
export async function rpcFetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> {
|
||||||
|
const request = new Request(input, { ...init, credentials: "include" });
|
||||||
|
if (request.method !== "POST") return fetch(request);
|
||||||
|
// Buffer as bytes: a teed stream needs duplex mode, and a Blob body is not visible to DevTools/CDP.
|
||||||
|
const body = await request.arrayBuffer();
|
||||||
|
const sendDirect = () =>
|
||||||
|
fetch(request.url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: request.headers,
|
||||||
|
body,
|
||||||
|
credentials: "include",
|
||||||
|
signal: request.signal,
|
||||||
|
});
|
||||||
|
if (stagingUnavailable || body.byteLength < 3 * 1024 * 1024) return sendDirect();
|
||||||
|
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const prepared = await fetch("/api/storage/stage", {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
signal: request.signal,
|
||||||
|
headers: { "content-type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
path: `${url.pathname}${url.search}`,
|
||||||
|
contentType: request.headers.get("content-type"),
|
||||||
|
size: body.byteLength,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
// Docker has no staging endpoint; send large bodies directly from now on.
|
||||||
|
if (prepared.status === 404) {
|
||||||
|
stagingUnavailable = true;
|
||||||
|
return sendDirect();
|
||||||
|
}
|
||||||
|
if (!prepared.ok) throw new Error(`Could not prepare upload (${prepared.status}). Please retry.`);
|
||||||
|
const stage = (await prepared.json()) as { id: string; url: string };
|
||||||
|
const uploaded = await fetch(stage.url, {
|
||||||
|
method: "PUT",
|
||||||
|
body,
|
||||||
|
headers: { "content-type": "application/octet-stream" },
|
||||||
|
signal: request.signal,
|
||||||
|
});
|
||||||
|
if (!uploaded.ok) throw new Error(`Upload failed (${uploaded.status}). Please retry.`);
|
||||||
|
const headers = new Headers(request.headers);
|
||||||
|
headers.set("x-resume-staged-body", stage.id);
|
||||||
|
headers.delete("content-length");
|
||||||
|
return fetch(request.url, { method: request.method, headers, credentials: "include", signal: request.signal });
|
||||||
|
}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# Internal issue audit, reviewed in the repository and pull request.
|
|
||||||
superpowers/plans/2026-09-05-open-issue-audit.md
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# ADR 0001: Workspace Boundaries
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
Accepted
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
Reactive Resume had package and app code arranged mostly by technical layer. Some server runtime code imported files from the web app source tree, resume-domain behavior lived in generic utilities, API implementation was split across package-root routers/services/helpers, and browser PDF preview code sat near React PDF generation code.
|
|
||||||
|
|
||||||
That made debugging harder because a feature's route, service, domain behavior, tests, and runtime adapter could be spread across unrelated folders. It also made package boundaries implicit, so regressions such as app-to-app source imports were easy to reintroduce.
|
|
||||||
|
|
||||||
## Decision
|
|
||||||
|
|
||||||
Use a domain-first monorepo structure with executable boundaries.
|
|
||||||
|
|
||||||
- Keep deployable apps in `apps/web` and `apps/server`.
|
|
||||||
- Keep runtime and domain capabilities in focused internal packages.
|
|
||||||
- Source-consume internal packages through package export maps.
|
|
||||||
- Forbid cross-workspace private `src` imports and repository-path imports.
|
|
||||||
- Use `packages/api/src/features/*` for API features instead of root technical-layer folders.
|
|
||||||
- Use explicit browser/server package subpaths for runtime-specific code.
|
|
||||||
- Enforce package direction with `turbo boundaries`.
|
|
||||||
- Enforce source-path import rules with Biome `noRestrictedImports` and the local GritQL plugin in `tooling/grit/no-cross-workspace-src-imports.grit`.
|
|
||||||
|
|
||||||
## Consequences
|
|
||||||
|
|
||||||
New code needs an owner before it gets a folder. That adds a little up-front friction, but it makes debugging paths predictable.
|
|
||||||
|
|
||||||
Feature-owned API modules can still share code, but shared code needs a named capability and an intentional package export.
|
|
||||||
|
|
||||||
The Turbo tag set is coarse by design. It blocks the current high-risk edges first: package-to-app imports, server-to-browser runtime imports, and universal/domain packages depending on server/app layers. More granular rules can be added as package roles settle.
|
|
||||||
|
|
||||||
The root shared Vitest config remains an intentionally ignored boundary edge for now. Moving it behind a package export is a separate test-infrastructure cleanup.
|
|
||||||
|
|
||||||
## Rejected Alternatives
|
|
||||||
|
|
||||||
Keep the old technical-layer API layout: rejected because it kept feature behavior split across routers, services, and helpers.
|
|
||||||
|
|
||||||
Move every web feature into packages: rejected because route-owned UI and browser-only behavior are easier to evolve inside the web app until they are genuinely reusable.
|
|
||||||
|
|
||||||
Put PDF.js viewer code in `packages/pdf`: rejected because `packages/pdf` owns React PDF generation, while PDF.js viewer/canvas behavior is browser UI.
|
|
||||||
|
|
||||||
Use documentation-only boundaries: rejected because the previous issue was not lack of intent; it was lack of executable enforcement.
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
# ADR 0002: Agent AI SDK Adoption
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
Proposed
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
The `/agent` workspace (and the builder's in-resume assistant) is a single `ToolLoopAgent` loop in `packages/api/src/features/agent/service.ts` with four tools, resumable Redis streams, Postgres-persisted `UIMessage` history, and snapshot-based undo.
|
|
||||||
|
|
||||||
LangGraph was evaluated and rejected: everything it would add (loop control, persistence, resumability, interrupts) is already built here on the AI SDK. This work instead adopts more of the AI SDK v7 surface (`ai@7.0.66`, already installed) to fix real defects and add the features it makes cheap, plus shadcn's `@shadcn/helpers` ai-sdk fixture library (a deterministic scripted `ChatTransport` for `useChat` — a testing harness, not a runtime HITL implementation) for backend-free UI tests.
|
|
||||||
|
|
||||||
Defects driving this:
|
|
||||||
|
|
||||||
1. Unbounded context growth — every turn replays the full thread, including every full-resume `read_resume` dump; long threads will exceed model context and permanently break.
|
|
||||||
2. Stale-resume editing — `apply_resume_patch` returns only metadata; subsequent JSON Patch array indexes can silently target wrong items after removes/moves.
|
|
||||||
3. Crash-unsafe runs — patches commit immediately but the assistant message persists only in `onFinish`; process death orphans applied edits; a stuck `activeRunId` (no reaper; `activeRunStartedAt` written, never read) permanently CONFLICTs the thread.
|
|
||||||
4. Client-trusted cancellation — `messages.stop` persists a client-authored `partialMessage`.
|
|
||||||
5. Weak validation — `isUiMessage` checks three fields; arbitrary `parts` get persisted and replayed.
|
|
||||||
6. No run guards — no timeout, `maxRetries`, or `maxOutputTokens` on the run.
|
|
||||||
7. Invisible tool activity — `tool-read_resume`/`read_attachment`/`web_search`/`dynamic-tool` parts render as nothing; the patch card is a raw `JSON.stringify` dump; zero token-usage visibility.
|
|
||||||
8. Latent bug found during design: on an `ask_user_question` continuation, `toUIMessageStream({originalMessages})` continues the existing assistant message (same `uiMessage.id`) but `persistMessage` is insert-only, producing duplicate rows sharing one message id. Fixed by the Phase 1 upsert; regression-tested.
|
|
||||||
|
|
||||||
Verified API surface from published `ai@7.0.66` and `@ai-sdk/react@4.0.69` typings and docs: `prepareStep` and `pruneMessages`; tool-level `needsApproval`, call-level `toolApproval`, `experimental_toolApprovalSecret` (HMAC-signs approval requests); UIMessage tool-part states `approval-requested`/`approval-responded`/`output-denied`; `lastAssistantMessageIsCompleteWithApprovalResponses`; `useChat().addToolApprovalResponse`; `validateUIMessages`/`safeValidateUIMessages`; `repairToolCall`; tool `inputExamples` plus `addToolInputExamplesMiddleware`; `messageMetadata` on `toUIMessageStream` (plus `messageMetadataSchema` on `useChat`); `onStepEnd`; `ToolLoopAgentSettings` extends `LanguageModelCallOptions` so `timeout`/`maxOutputTokens`/`maxRetries` are valid in the constructor; `smoothStream` passed as `agent.stream({ experimental_transform })`; `useChat` `throttle`; `isStepCount`; `InferAgentUIMessage`; `LanguageModelUsage` with cache read/write detail; `totalUsage` on finish events.
|
|
||||||
|
|
||||||
Constraints the implementation must respect:
|
|
||||||
|
|
||||||
- `getAgentModel` silently switches to the OpenAI Responses API for direct-OpenAI web-search models — every model-facing change is QA'd on both a Responses and a chat-completions provider.
|
|
||||||
- Abort reasons must be `DOMException(label, "AbortError")`; `AbortSignal.timeout()`/`AbortSignal.any()` produce `TimeoutError` and would crash the resumable-stream pump, so the run timeout uses `setTimeout` plus `controller.abort(abortReason("RUN_TIMEOUT"))`.
|
|
||||||
- The `activeRunControllers` map is single-process; multi-process abort (Redis pub/sub) is explicitly out of scope.
|
|
||||||
- `service.test.ts` mocks the whole `"ai"` module with a closed factory and scripts DB queries positionally; the test harness is migrated first (Phase 1.0).
|
|
||||||
- Shared contracts live in `packages/ai` (runtime-universal per ADR 0001): zod-only runtime, `import type` from `"ai"` only, with `ai` added to devDependencies.
|
|
||||||
- No new environment variables anywhere in this plan (the approval secret derives from `ENCRYPTION_SECRET`), so `turbo.json` `globalEnv` stays untouched.
|
|
||||||
- `resumeService.patchInTransaction` already accepts `expectedUpdatedAt` and throws `RESUME_VERSION_CONFLICT`; the agent tool just passes it.
|
|
||||||
- Legacy persisted rows must never hard-fail: validation happens only at the network boundary (schema-less), and the metadata schema is loose and optional.
|
|
||||||
- Lingui macros for all new user-facing copy; Base UI `render` prop (no Radix `asChild`); `cn` from `@reactive-resume/utils/style`; toasts via `toast.add`.
|
|
||||||
|
|
||||||
## Decision
|
|
||||||
|
|
||||||
Adopt the AI SDK v7 surface in three phases on top of the existing loop, rather than introducing a graph framework.
|
|
||||||
|
|
||||||
### Phase 1 — correctness
|
|
||||||
|
|
||||||
Order: 1.0 → 1.1 → 1.2 → 1.3 → 1.4 → 1.5 → 1.6 (spine: 1.3 → 1.4 → 1.5; 1.6 last because it changes model-visible behavior).
|
|
||||||
|
|
||||||
- **1.0 Test-harness prep (no behavior change).** Replace the closed `vi.mock("ai")` factory with a spread-actual factory so pure helpers (`isStepCount`, `safeValidateUIMessages`, `pruneMessages`, `JsonToSseTransformStream`) stay real and only the scripted seams (`convertToModelMessages`, `ToolLoopAgent`) stay mocked. Adopt the rule that new DB queries on the send path go into separate injectable modules (the `runs.ts` pattern) and are module-mocked wholesale. Gate: suite green with zero source changes.
|
|
||||||
- **1.1 Run guards, snapshot consistency, misc.** `createAgent` adds `maxOutputTokens: 8_192`, `maxRetries: 2`, `timeout: 120_000` (per provider request). Whole-run wall clock: after run claim, `setTimeout(() => controller.abort(abortReason("RUN_TIMEOUT")), 600_000)`, handle stored in a module map keyed by runId and cleared in `cleanupActiveRun`. `applyResumePatch` passes `expectedUpdatedAt` to `patchInTransaction` and rethrows `RESUME_VERSION_CONFLICT` as a recoverable tool error. `readAttachment` uses a named `MAX_ATTACHMENT_TEXT_CHARS` constant.
|
|
||||||
- **1.2 Real message validation at the send boundary.** Keep `isUiMessage` as the sync oRPC gate; inside `messages.send`, run `safeValidateUIMessages({ messages: [input.message] })` before `claimActiveAgentRun` and return BAD_REQUEST on failure. Deliberately schema-less so provider-echoed parts pass and replayed history is never re-validated.
|
|
||||||
- **1.3 Crash-safe incremental persistence (draft row).** New `messages-persistence.ts` (injectable DB): `applyStepToUiMessage` (pure fold), `insertDraftAssistantMessage`, `upsertAssistantUiMessage` (by row id, then by `uiMessage->>'id'`, then insert), `deleteDraftIfEmpty`. The service pre-generates the response message id, inserts a draft before `agent.stream`, folds and upserts on `onStepEnd` (≤30 writes/run), and upserts the SDK's authoritative message with `completed|canceled` in `onFinish`. `apply_resume_patch` threads `toolCallId` through and sets `agentAction.messageId` at INSERT time. The upsert also fixes the duplicate-row continuation bug (defect 8).
|
|
||||||
- **1.4 Server-side cancellation.** `messages.stop` drops `partialMessage` persistence; the body aborts with `abortReason("USER_STOPPED")`, clears the timeout handle, and clears the run claim. Partial content persists server-side via `onFinish({isAborted: true})`. The router keeps `partialMessage` in the input schema for one release (deprecated, ignored).
|
|
||||||
- **1.5 Stale-run reaper.** `STALE_AGENT_RUN_TTL_MS = 15 * 60_000` (greater than the run timeout, so live runs always die by their own timeout first; deliberately TTL-only and multi-replica-safe). Wired at server boot, lazily in `messages.send` before the CONFLICT throw, and at the top of `threads.get`. Reap conditionally clears run columns and flips `streaming` draft rows to `canceled`, appending synthetic `tool-apply_resume_patch` parts rebuilt from action rows so replayed history stays provider-valid. Applied actions stay applied — they are real committed edits, individually revertable.
|
|
||||||
- **1.6 Context growth: fresh-document patch output plus pruning.** `applyResumePatch` returns the full post-patch document plus `changedPaths`, with a tool description telling the model to base further patches on it. New pure `context.ts`: `AGENT_CONTEXT_TOKEN_BUDGET = 40_000`, `estimateTokenCount` (chars/4), `pruneAgentModelContext`. `createAgent` gains `prepareStep` wiring so pruning runs every loop step.
|
|
||||||
|
|
||||||
Pruning tiers (all pure, wired via `prepareStep`):
|
|
||||||
|
|
||||||
- Tier 0, always: supersede resume snapshots — every `read_resume` result and every patch result's embedded `resume` except the last in the conversation becomes a stub note. A stale snapshot is actively harmful (shifted indexes), so this runs even in short threads; exactly one full snapshot survives, positioned where the model last acted.
|
|
||||||
- Tier 1, over budget: strip reasoning from all but the last assistant message.
|
|
||||||
- Tier 2, still over: collapse oldest tool call/result pairs (never orphan one side — several BYOK gateways reject unpaired tool messages) into one-line stubs, excluding the last assistant message, unresolved question/approval parts, and the surviving snapshot.
|
|
||||||
- Tier 3, last resort: attachment parts on non-latest user messages become stubs teaching the `read_attachment` recovery path.
|
|
||||||
- Never pruned: instructions, latest user message, last assistant message, unresolved interactive parts.
|
|
||||||
|
|
||||||
Full-document patch output was chosen over a `prepareStep`-injected "current resume" reminder because a reminder re-sends a snapshot every step, defeats provider prompt caching, and splits authority into two places; a summarized view invites hallucination and forces a `read_resume` round-trip per edit against a 30-step cap. Output plus Tier 0 bounds cost: N patches do not produce N surviving snapshots.
|
|
||||||
|
|
||||||
### Phase 2 — human-in-the-loop approvals
|
|
||||||
|
|
||||||
Order: 2.1 contracts → 2.2 migration/endpoint → 2.3 tool+secret → 2.4 merge/continuation → 2.5 UI → 2.6 tests.
|
|
||||||
|
|
||||||
- **2.1 Shared typed tool contracts in `packages/ai`.** New `agent-tool-contracts.ts` with zod schemas for tool inputs/outputs and a loose all-optional message metadata schema, plus `AgentTools`/`AgentUIMessage` types. `"ai"` goes in devDependencies, `import type` only; zod stays the only runtime import.
|
|
||||||
- **2.2 Migration plus thread setting.** `reviewPatches: boolean, default false` on `agentThread` (the plan's only migration), a `threads.update` procedure, and the flag in `toThreadSummary`. Auto-apply stays the default.
|
|
||||||
- **2.3 `needsApproval` plus HMAC secret.** `getAgentToolApprovalSecret()` derives `sha256(ENCRYPTION_SECRET + ":agent-tool-approval")` — domain-separated from the AES key, no new env var, and deterministic so a signature minted at halt verifies at continuation even across a restart. `buildAgentTools` gains `requirePatchApproval`; `createAgent` reads `thread.reviewPatches` and passes `experimental_toolApprovalSecret`.
|
|
||||||
- **2.4 Merge generalization plus continuation.** Extract the tool-response merge into `messages-merge.ts` as `mergeClientToolResponses`, handling both `ask_user_question` outputs and `approval-responded` parts in one pass. `{0,0}` maps to BAD_REQUEST, `{0,>0}` to CONFLICT("already handled") before claiming a run.
|
|
||||||
- **2.5 Client: approval UI, composed auto-send, `useConfirm`.** `sendAutomaticallyWhen` composes `lastAssistantMessageIsCompleteWithToolCalls` with `lastAssistantMessageIsCompleteWithApprovalResponses`. New `patch-approval-card.tsx` (web feature, not `packages/ui`) renders `approval-requested` with Approve/Deny plus optional reason, and `output-denied` as a muted declined card. A "Review edits" toggle lands in the thread menu. `window.confirm` is replaced with the existing `useConfirm()`.
|
|
||||||
- **2.6 Approval-flow tests with `@shadcn/helpers`.** Component tests for the card, plus a small harness wiring `useChat` to the scripted transport to exercise the composed auto-send and approval state machine with no backend. `AgentChat` is not refactored to accept a transport prop just for tests.
|
|
||||||
|
|
||||||
Approval flow end-to-end: the halt leaves no active run (identical lifecycle to today's `ask_user_question` halt); the client resubmits the last assistant message byte-for-byte through the existing transport; the merge copies approval-response fields onto the stored, signed request part matched by `toolCallId` plus approval id, so a client cannot substitute a forged request; the continuation run replays history, the fresh agent derives the same secret, the signature verifies, and the SDK executes (or denies) the call, streaming into the same message id via the 1.3 upsert — which is load-bearing, so 1.3 ships before 2.4. Idempotency ladder: UI disables buttons on state flip → concurrent sends race the atomic `claimActiveAgentRun` → post-completion resubmits match `alreadyResolved` and CONFLICT before any run is claimed → conflicting approved values are BAD_REQUEST.
|
|
||||||
|
|
||||||
### Phase 3 — visibility and polish
|
|
||||||
|
|
||||||
- **3.1 Usage metadata.** `messageMetadata` on `toUIMessageStream` records `{usage, model}` on finish; it round-trips in the `uiMessage` jsonb with no migration. The client renders a muted per-message token footer and a per-thread aggregate; legacy rows without metadata render fine.
|
|
||||||
- **3.2 Render the invisible parts plus a real patch card.** New generic `tool-part-card.tsx` (collapsed icon/label/state card with expandable input/output) for `read_resume`/`read_attachment`/`web_search`/`dynamic-tool`; the patch card gains human-readable operation rows with raw JSON demoted to a nested `details` block; consecutive `source-url` parts group into one sources block.
|
|
||||||
- **3.3 Streaming performance.** `useChat({throttle: 50})`, memoized markdown/message components, and `smoothStream({chunking: "word"})` via `experimental_transform`.
|
|
||||||
- **3.4 Tool-input robustness.** `inputExamples` on `apply_resume_patch` plus `addToolInputExamplesMiddleware`; a `repairToolCall` callback (`repair.ts`) that runs `jsonrepair`, strips `/data` prefixes and section shortcuts, and re-validates against the shared schema, falling back to the SDK re-ask on `null`. `normalizeAgentResumePatchOperations` stays as last-line defense; the instructions blob shrinks accordingly.
|
|
||||||
- **3.5 Structured logging plus provider options.** One JSON line per step (`agent.step`) and per tool execution (`agent.tool`) — greppable structured console, no OpenTelemetry dependency. Optionally `anthropic.cacheControl: ephemeral` on the instructions block.
|
|
||||||
|
|
||||||
### Crash-safety design: draft-row upsert over reconciliation-on-read
|
|
||||||
|
|
||||||
The failure that matters is a patch committed plus process death before `onFinish`. Reconciliation would synthesize an assistant message from action rows, but a synthesized message has no model-authored text and no tool call/result pairing that replays validly. The draft row records the real transcript step-by-step (≤30 single-row jsonb upserts per run); a patch executes within a step, so the orphan window shrinks from "entire run" to milliseconds. It also subsumes server-side cancellation partials, `agentAction.messageId` at INSERT time, and the duplicate-row continuation bug. The reaper covers the remaining sliver by appending synthetic tool parts from action rows during reap.
|
|
||||||
|
|
||||||
## Consequences
|
|
||||||
|
|
||||||
- Long threads stop growing without bound; exactly one resume snapshot survives in model context, and patch results carry the fresh document so array indexes never go stale.
|
|
||||||
- Runs are bounded (per-request timeout, whole-run wall clock, output-token cap, retry cap) and crash-safe (draft-row persistence, boot/lazy/read-path reaping); cancellation is server-authored.
|
|
||||||
- Users can opt threads into edit review; approvals are HMAC-signed server-side and survive restarts.
|
|
||||||
- Tool activity, token usage, and patch contents become visible in the UI; streaming is smoother and cheaper to render.
|
|
||||||
- The send path gains real message validation without breaking legacy rows.
|
|
||||||
- Test coverage moves toward pure, mock-free modules; the `"ai"` mock keeps real helpers.
|
|
||||||
- An approval continuation costs a second `send` against the in-process rate limit (20/min) — acceptable, noted for tuning.
|
|
||||||
|
|
||||||
Out of scope, documented as follow-ups: multi-process cancellation via Redis pub/sub; OpenTelemetry; removing the deprecated `partialMessage` input (next release); the legacy non-agent `chat()` in `ai/service.ts`; thread-title generation via a cheap model call.
|
|
||||||
|
|
||||||
## Rejected Alternatives
|
|
||||||
|
|
||||||
LangGraph: rejected — loop control, persistence, resumability, and interrupts are already built here on the AI SDK; a graph framework would add a dependency and a second orchestration model without removing any existing code.
|
|
||||||
|
|
||||||
`prepareStep`-injected "current resume" reminder instead of full-document patch output: rejected — re-sends a snapshot every step, defeats provider prompt caching, and splits document authority.
|
|
||||||
|
|
||||||
Reconciliation-on-read instead of draft-row persistence: rejected — synthesized messages replay invalidly against providers that require call/result pairing.
|
|
||||||
|
|
||||||
Random per-boot approval secret: rejected — it would strand pending approvals across restarts; the deterministic derivation from `ENCRYPTION_SECRET` keeps signatures verifiable.
|
|
||||||
|
|
||||||
New environment variable for the approval secret: rejected — derivation avoids env, `turbo.json`, and deployment churn.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# ADR-0003: Keep PostgreSQL separate from the application image
|
|
||||||
|
|
||||||
Reactive Resume does not provide an all-in-one image embedding PostgreSQL. The maintainer rejected that packaging direction on 2026-09-05 because it provides no benefit worth supporting; keep the database lifecycle separate and address setup convenience through existing Compose/Unraid onboarding.
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Domain Docs
|
|
||||||
|
|
||||||
How engineering skills consume this repository’s domain documentation.
|
|
||||||
|
|
||||||
## Before exploring, read these
|
|
||||||
|
|
||||||
- **`CONTEXT-MAP.md`** at repository root. It points to context-specific `CONTEXT.md` files. Read each context relevant to current work.
|
|
||||||
- **`docs/adr/`** for system-wide decisions touching current area.
|
|
||||||
- Context-scoped ADR directories referenced by `CONTEXT-MAP.md`.
|
|
||||||
|
|
||||||
If any file does not exist, proceed silently. Do not flag absence or suggest creating it upfront. `/domain-modeling` creates domain documents lazily when terminology or decisions become settled.
|
|
||||||
|
|
||||||
## File structure
|
|
||||||
|
|
||||||
This repository uses a multi-context layout:
|
|
||||||
|
|
||||||
```text
|
|
||||||
/
|
|
||||||
├── CONTEXT-MAP.md
|
|
||||||
├── docs/adr/ ← system-wide decisions
|
|
||||||
├── apps/
|
|
||||||
│ └── <context>/
|
|
||||||
│ └── CONTEXT.md
|
|
||||||
└── packages/
|
|
||||||
└── <context>/
|
|
||||||
├── CONTEXT.md
|
|
||||||
└── docs/adr/ ← context-specific decisions
|
|
||||||
```
|
|
||||||
|
|
||||||
`CONTEXT-MAP.md` is authoritative for context boundaries. Not every app or package needs a `CONTEXT.md`; create one only when it represents a meaningful domain context.
|
|
||||||
|
|
||||||
## Use glossary vocabulary
|
|
||||||
|
|
||||||
When output names a domain concept—in issue titles, refactor proposals, hypotheses, or test names—use terms defined in relevant `CONTEXT.md`. Do not drift to explicitly avoided synonyms.
|
|
||||||
|
|
||||||
Missing terminology signals either language foreign to project or genuine domain-model gap. Reconsider first; otherwise note gap for `/domain-modeling`.
|
|
||||||
|
|
||||||
## Flag ADR conflicts
|
|
||||||
|
|
||||||
If output contradicts existing ADR, surface conflict explicitly instead of silently overriding:
|
|
||||||
|
|
||||||
> _Contradicts ADR-0007 (event-sourced orders), but worth reopening because…_
|
|
||||||
@@ -3,7 +3,7 @@ title: "Project architecture"
|
|||||||
description: "How the Reactive Resume monorepo is laid out, the runtime boundaries between the web and server apps, and the package ownership model."
|
description: "How the Reactive Resume monorepo is laid out, the runtime boundaries between the web and server apps, and the package ownership model."
|
||||||
---
|
---
|
||||||
|
|
||||||
Reactive Resume is a pnpm/Turborepo monorepo. The product runs as one deployed Node.js process, while the source is split into a full-stack web app, a server adapter, and focused internal packages.
|
Reactive Resume is a pnpm/Turborepo monorepo. Docker runs one Node.js process. Vercel serves static assets through its CDN and uses a Node.js Function for the same Hono application. Both targets share the web app, API, authentication, renderers, and database schema.
|
||||||
|
|
||||||
Internal packages are source-consumed through their `package.json` export maps. Import package subpaths, not another workspace's private `src` files.
|
Internal packages are source-consumed through their `package.json` export maps. Import package subpaths, not another workspace's private `src` files.
|
||||||
|
|
||||||
@@ -31,11 +31,11 @@ flowchart TD
|
|||||||
Server --> MCP
|
Server --> MCP
|
||||||
API --> PDFServer
|
API --> PDFServer
|
||||||
API --> DB["packages/db"]
|
API --> DB["packages/db"]
|
||||||
API --> Storage["File system or S3-compatible storage"]
|
API --> Storage["Local disk, S3, or private Vercel Blob"]
|
||||||
DB --> Postgres["PostgreSQL"]
|
DB --> Postgres["PostgreSQL"]
|
||||||
```
|
```
|
||||||
|
|
||||||
`apps/web` owns the TanStack Start experience. `apps/server` owns the production Hono process and mounts RPC, auth, OpenAPI, MCP, static uploads, schema JSON, and the built web app.
|
`apps/web` owns the React SPA with TanStack Router and Vite. `apps/server` owns the Hono application and mounts RPC, auth, OpenAPI, MCP, static uploads, schema JSON, and the built web app.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ flowchart TD
|
|||||||
|
|
||||||
| Workspace | Ownership |
|
| Workspace | Ownership |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `apps/web` | TanStack Start routes, web features, browser PDF.js preview/viewer code, PWA setup, oRPC browser client |
|
| `apps/web` | TanStack Router routes, web features, browser PDF.js preview/viewer code, PWA setup, oRPC browser client |
|
||||||
| `apps/server` | Hono route composition, production HTTP adapters, MCP transport, OpenAPI/well-known handlers, static file serving, startup checks |
|
| `apps/server` | Hono route composition, production HTTP adapters, MCP transport, OpenAPI/well-known handlers, static file serving, startup checks |
|
||||||
| `packages/api` | oRPC procedures and feature-owned business behavior under `src/features/*` |
|
| `packages/api` | oRPC procedures and feature-owned business behavior under `src/features/*` |
|
||||||
| `packages/auth` | Better Auth config, auth helpers, and exported auth types |
|
| `packages/auth` | Better Auth config, auth helpers, and exported auth types |
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
title: "Deployment checks"
|
||||||
|
description: "How CI verifies the Vercel build artifact, and how to run the deployment smoke test against Vercel or Docker."
|
||||||
|
---
|
||||||
|
|
||||||
|
The **Vercel compatibility** workflow (`.github/workflows/vercel.yml`) has two jobs.
|
||||||
|
|
||||||
|
## Artifact build
|
||||||
|
|
||||||
|
Runs on every pull request and every push to `main`. It needs no Vercel account and no secrets, so fork pull requests run it safely.
|
||||||
|
|
||||||
|
The job:
|
||||||
|
|
||||||
|
1. Starts an isolated PostgreSQL service.
|
||||||
|
2. Writes a local `.vercel/project.json` and runs `vercel build --prod` offline, with placeholder Blob credentials. This also applies migrations to the isolated database.
|
||||||
|
3. Checks the generated Function:
|
||||||
|
- runtime is `nodejs24.x` and `maxDuration` is `300`;
|
||||||
|
- PDFKit standard font files are included in the traced files;
|
||||||
|
- every emitted server chunk loads with `--no-experimental-require-module`, which matches the Vercel runtime;
|
||||||
|
- the Vercel entrypoint answers a request.
|
||||||
|
|
||||||
|
If a new server dependency fails the module-loading check, add it to `bundledInteropPackages` in `apps/server/tsdown.config.ts`.
|
||||||
|
|
||||||
|
## Live smoke test
|
||||||
|
|
||||||
|
Runs only when started manually (`workflow_dispatch`). It uses the `vercel-smoke` GitHub environment and runs `tooling/deployment/smoke.mjs` against `VERCEL_SMOKE_URL`.
|
||||||
|
|
||||||
|
<Warning>
|
||||||
|
Point the smoke test only at a dedicated test installation. It creates an account, a public resume, and files, then deletes them.
|
||||||
|
</Warning>
|
||||||
|
|
||||||
|
The script checks health, public pages, signup, resume CRUD, public PDF rendering, a 10 MiB upload and download, and one-time use of staged requests.
|
||||||
|
|
||||||
|
To also check a 25 MiB agent attachment, configure a deterministic OpenAI-compatible test provider that serves the model `smoke-model`:
|
||||||
|
|
||||||
|
| Name | Kind | Value |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `VERCEL_SMOKE_URL` | Variable | Test installation origin |
|
||||||
|
| `VERCEL_SMOKE_AI_BASE_URL` | Variable | Test provider base URL |
|
||||||
|
| `VERCEL_SMOKE_AI_API_KEY` | Secret | Test provider API key |
|
||||||
|
|
||||||
|
No paid AI model is needed.
|
||||||
|
|
||||||
|
## Run the smoke test locally
|
||||||
|
|
||||||
|
Against a local Docker installation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
SMOKE_URL=http://localhost:3000 node tooling/deployment/smoke.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `SMOKE_AI_BASE_URL` and `SMOKE_AI_API_KEY` to include the attachment check.
|
||||||
@@ -156,7 +156,7 @@ The scripts you will use most during development:
|
|||||||
```
|
```
|
||||||
reactive-resume/
|
reactive-resume/
|
||||||
├── apps/
|
├── apps/
|
||||||
│ ├── web/ # TanStack Start routes, web features, and browser UI
|
│ ├── web/ # TanStack Router routes, web features, and browser UI
|
||||||
│ └── server/ # Hono production server, HTTP adapters, static serving
|
│ └── server/ # Hono production server, HTTP adapters, static serving
|
||||||
├── packages/
|
├── packages/
|
||||||
│ ├── api/ # oRPC features and business behavior
|
│ ├── api/ # oRPC features and business behavior
|
||||||
|
|||||||
+8
-1
@@ -97,6 +97,7 @@
|
|||||||
"group": "Integrations",
|
"group": "Integrations",
|
||||||
"pages": [
|
"pages": [
|
||||||
"guides/using-the-api",
|
"guides/using-the-api",
|
||||||
|
"guides/large-rpc-requests",
|
||||||
"guides/using-the-patch-api",
|
"guides/using-the-patch-api",
|
||||||
"guides/using-the-mcp-server",
|
"guides/using-the-mcp-server",
|
||||||
"guides/using-ai",
|
"guides/using-ai",
|
||||||
@@ -142,6 +143,7 @@
|
|||||||
"group": "Self-Hosting",
|
"group": "Self-Hosting",
|
||||||
"pages": [
|
"pages": [
|
||||||
"self-hosting/docker",
|
"self-hosting/docker",
|
||||||
|
"self-hosting/vercel",
|
||||||
"self-hosting/kubernetes",
|
"self-hosting/kubernetes",
|
||||||
"self-hosting/examples",
|
"self-hosting/examples",
|
||||||
"self-hosting/sso",
|
"self-hosting/sso",
|
||||||
@@ -150,7 +152,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "Contributing",
|
"group": "Contributing",
|
||||||
"pages": ["contributing/architecture", "contributing/development", "contributing/translations"]
|
"pages": [
|
||||||
|
"contributing/architecture",
|
||||||
|
"contributing/development",
|
||||||
|
"contributing/deployment-checks",
|
||||||
|
"contributing/translations"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group": "Community",
|
"group": "Community",
|
||||||
|
|||||||
@@ -1,307 +0,0 @@
|
|||||||
# Approved issue plans execution ledger
|
|
||||||
|
|
||||||
Coordinator-owned record for plans approved in PR #3455. Completed, mergeable PRs merge as soon as publication gates
|
|
||||||
pass. Issue comments and state changes are recorded when explicitly directed by maintainer.
|
|
||||||
|
|
||||||
## Run metadata
|
|
||||||
|
|
||||||
- Planning source: PR #3455, bootstrap head `a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; final head
|
|
||||||
`86e200a4dae0f421d1c96bbd3a2e1a0a6899a959`, merged as `42527ad83bdc5720bd76eef9c9da102384f21f6f`
|
|
||||||
- Implementation source: `origin/main`, head `7a98f6662ffc6fd5a1a7281c30ab3829fe3722ec` at bootstrap
|
|
||||||
- Coordinator branch: `codex/issue-execution-ledger`
|
|
||||||
- Started: 2026-09-05, Europe/Berlin
|
|
||||||
- Merge policy changed: 2026-09-06; maintainer authorized immediate merge of complete, mergeable PRs
|
|
||||||
- Current integrated main evidence: `778fd4b7d9a2de07852f32d87713c4a6187cf4d4` after twenty-eight approved
|
|
||||||
plan-execution merges plus planning PR #3455, ledger PR #3456, and geometry CI regression fix PR #3484
|
|
||||||
- Completion snapshot before this finalization update: 35/35 plan units terminal; all 63 inventory issues accounted for;
|
|
||||||
6 inventory issues closed and 57 open; 59 repository issues open including excluded residuals #2828 and #3246;
|
|
||||||
zero executable plan units and zero open PRs
|
|
||||||
- Status values: `pending`, `diagnosing`, `implementing`, `reviewing`, `published`, `merged`, `no-change`, `blocked`,
|
|
||||||
`declined`, `skipped`
|
|
||||||
- Evidence rule: each terminal disposition needs current source/GitHub proof, focused tests or reproduction evidence, and
|
|
||||||
independent review when code changed.
|
|
||||||
|
|
||||||
## Dependency and ownership rules
|
|
||||||
|
|
||||||
- Every implementation unit starts from refreshed `origin/main` unless this ledger names a true stacked dependency.
|
|
||||||
- One active owner per overlapping source file. Rich-text ownership coordinates units 16/19; renderer ownership coordinates
|
|
||||||
12/13/14/17/18/27/30/31; section/schema/layout ownership coordinates 20–24/31/32; image ownership coordinates 06/15/25;
|
|
||||||
template ownership coordinates 26/28/29/34.
|
|
||||||
- Units 24 and 32 have shared-file exclusion, not a preset stacked dependency; stack only if current implementation proves
|
|
||||||
unit 32 needs schema or interfaces introduced by unit 24. Units 30/31 wait for relevant renderer baselines. Unit 33 stops
|
|
||||||
after reference research and concrete visual proposal until explicit visual approval.
|
|
||||||
- Worker reports separate verified facts from uncertainty and include reproduction, first failing boundary, exact commit,
|
|
||||||
tests run, skipped gates, risks, issue coverage, and PR state.
|
|
||||||
- Audit disposition mapping: ready, documentation-only, or a split with an executable unit maps to `pending` until dispatch;
|
|
||||||
active execution maps to `implementing`; diagnostic-only maps to `diagnosing` while evidence work runs and `blocked` when
|
|
||||||
only external evidence remains; already fixed maps to `no-change`; blocked maps to `blocked`; declined maps to `declined`.
|
|
||||||
For split outcomes, record executable and blocked portions separately in validity/evidence fields.
|
|
||||||
|
|
||||||
## Units
|
|
||||||
|
|
||||||
| Unit | Issues | Status / current validity | Owner | Worktree / branch | Base → head | Dependencies | Evidence | Tests | PR | Next action | Blockers |
|
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
||||||
| 01 account login/recovery | #3166, #3164, #3078, #3046, #2897, #2837 | blocked | audit `task_855fbee0a803` | `codex-audit-backend-01-06` | `7a98f6662` → no source change | none | zero drift; live issues remain split; #3046/#3078 have merged #3095 candidate only | auth 42, email 6, focused/full API/server suites and affected typechecks/boundaries passed in audit | — | select exact auth/mail/API boundary only after current sanitized trace | current cloud digest, provider/account method, request/status trace, controlled mailbox |
|
|
||||||
| 02 hosted v4 recovery | #3181, #2760 | merged partial unit | implementation/review chain complete | `codex/issue-3181-recovery-procedure` | `7a98f6662` → `325d1fcd1` → merge `549135bb3` | #2760 identity branch depends on 01 evidence | Unicode format-character IDs rejected across all manifest fields; bot cleanup preserved; scanner extraction and relaxed canonicalization rejected with independent evidence; four hosted threads replied/resolved | fresh coordinator 83 comparator, 381 API, 21 auth tests plus gates green; exhaustive Unicode/mutation rereview clean; all exact-head hosted checks green; approved, mergeable, zero unresolved threads | [#3460](https://github.com/reactive-resume/reactive-resume/pull/3460) | retain external recovery gate | real recovery still needs verified owner, snapshot, mapping, private delivery |
|
|
||||||
| 03 MCP registration | #3398, #3153 | blocked; historical source fix present | audit `task_855fbee0a803` | `codex-audit-backend-01-06` | `7a98f6662` → no source change | coordinate DB/startup with 05 | merged #3421 (`fe9b59e`) is present on main and current source schema/startup/auth contracts match plan, but deployed behavior is not verified | #3421 hosted checks successful; audit auth/server/API/DB tests, typechecks, boundaries passed | #3421 merged before run | obtain deployed digest/log correlation, then rerun exact Codex/Claude DCR → consent → PKCE → MCP flow | deployed digest/log correlation and exact client retest unavailable; source no-change evidence cannot prove deployment |
|
|
||||||
| 04 AI provider compatibility | #2732, #2766, #2723, #2708 | blocked | audit `task_855fbee0a803` | `codex-audit-backend-01-06` | `7a98f6662` → no source change | serialize edits to AI service | zero drift; historical #2708 Responses path and #2766 structured Test path absent; remaining tuples differ | focused API 96 and full API suites/typecheck/boundaries passed in audit | — | select wire/state/import/base-path unit only after exact current tuple fails | provider/model/base URL/path/version/action/error tuple and allowed endpoint unavailable |
|
|
||||||
| 05 AI provider migrations | #3152 | blocked | audit `task_855fbee0a803` | `codex-audit-backend-01-06` | `7a98f6662` → no source change | coordinate DB/startup with 03 | zero drift; table schema, migration, startup ordering, Docker copy path coherent; `42P01` historical cause unresolved | audit API/server/DB tests, typechecks, boundaries passed; no real PostgreSQL migration fixture | — | choose config/packaging/startup/migration repair only after disposable reproduction | affected image layout/digest, working directory, DB schema/search path/journal, fresh+upgrade DB fixture |
|
|
||||||
| 06 image storage delivery | #2684, #2778 | blocked | audit `task_855fbee0a803` | `codex-audit-backend-01-06` | `7a98f6662` → no source change | coordinate 12/15/25 renderer/image fixtures | #2684 ACL cause fixed by merged #3432 (`35cecf9`); #2778 Browserless path obsolete; current topology unproved | #3432 hosted checks successful; audit storage/upload/PDF tests, typechecks, boundaries passed | #3432 merged before run | deployment retest plus shared real-encoding/backend/render fixture before repair | deployed digest; disposable S3/Garage/SeaweedFS/MinIO/proxy and browser/server raster matrix unavailable |
|
|
||||||
| 07 AIO deployment | #2722 | docs merged; feature declined | implementation/review chain complete | `codex/issue-2722-postgres-docs` | `7a98f6662` → `171637526` → merge `ee52636c1` | none | image and repository update flows now separate through correct `reactive-resume`/`reactive_resume` log commands; clean rereview | fresh Compose/DB/docs coordinator gates and all exact-head hosted checks green; approved; zero unresolved threads | [#3457](https://github.com/reactive-resume/reactive-resume/pull/3457) | documentation complete; #2722 disposition declined | AIO implementation explicitly declined; optional Unraid host smoke unavailable |
|
|
||||||
| 08 root public resume | #2669 | merged partial unit | implementation/review chain complete | `codex/issue-2669-root-public-resume` | `38832014b` → `576fad5ef` → merge `744eaa902` | none | optional server-only root mapping is public-only, preserves password identity, rejects hostile target/Host input, and owns canonical/static metadata safely; independent review clean | 1,392 implementation tests plus disabled/enabled production E2E; post-main focused 123 tests and type/lint gates green | [#3470](https://github.com/reactive-resume/reactive-resume/pull/3470) | retain broader custom-domain/TLS issue scope | feature remains narrower than arbitrary domain/TLS registry |
|
|
||||||
| 09 external version backup | #2705 | merged | implementation/review chain complete | `codex/issue-2705-git-backup-docs` | `7a98f6662` → `4ffdd96bd` → merge `772bf1452` | none | clean round-4 rereview; command-site prose requires unused output; replies document selected revision and fresh-file fixes | fresh coordinator 152 tests plus Markdown/diff/scope and all exact-head hosted checks green; approved; zero unresolved threads | [#3458](https://github.com/reactive-resume/reactive-resume/pull/3458) | complete | DB integration/E2E unavailable; no sync/remote/whole-account restore |
|
|
||||||
| 10 legacy link routing | #2836 | skipped by maintainer | implementation/review chain complete before cancellation | `codex/issue-2836-retired-link-notices` | `7a98f6662` → hosted `3eac878d` | none | implementation was technically reviewed, then maintainer rejected product scope due redirect-lifecycle and error-handling overhead | PR closed unmerged; issue note records not-planned rationale | [#3463](https://github.com/reactive-resume/reactive-resume/pull/3463) | no further implementation | explicitly out of plan; branch/worktree retained for auditability |
|
|
||||||
| 11 job search policy | #3010 | merged | implementation/review chain complete | `codex/issue-3010-jsearch-docs` | `7a98f6662` → `1d0397884` → merge `8c5804ed0` | none | two valid wording comments fixed; independent full-diff re-review found no issues; replies carry commit evidence | 52 focused tests plus Markdown/link/diff/base/scope and all exact-head hosted checks green; approved; zero unresolved threads | [#3459](https://github.com/reactive-resume/reactive-resume/pull/3459) | complete | real provider/browser optional gate unavailable; removal motive unknown |
|
|
||||||
| 12 preview/export failures | #3323, #3290, #3033, #3007, #2609 | blocked corrective work; diagnostic-ready | audit `task_1c6582ccdeae` | `codex-audit-rendering-12-19` | `7a98f6662` → no source change | renderer ownership; share observation harness with 18 | zero drift; staged-preview/error-boundary baselines remain; reports cross persistence/font/PDF/viewer/deployment boundaries | audit focused web/PDF suites, affected typechecks, boundaries, build passed | — | isolate PT Sans, browser/config, and template-selection boundaries; no shared fix | exact JSON/output/browser/config/current reproduction missing |
|
|
||||||
| 13 font/glyph/spacing | #3249, #3159, #3147, #3093, #3089, #2988 | blocked corrective work; diagnostic-ready | audit `task_1c6582ccdeae` | `codex-audit-rendering-12-19` | `7a98f6662` → no source change | serialize font sources with 14/27 | merged metrics/cache/Unicode fixes present and passing; residuals require per-font/per-symptom fixtures | audit focused PDF/font suites, affected typechecks, boundaries, build passed | — | retain regressions; create residual unit only from exact failing font fixture | missing Ropa Sans/source strings/font hashes/before-after artifacts |
|
|
||||||
| 14 RTL export layout | #3275 | blocked corrective work; diagnostic-ready | audit `task_1c6582ccdeae` | `codex-audit-rendering-12-19` | `7a98f6662` → no source change | serialize renderer/fonts with 13/27 | merged canvas-direction fix remains; broader shaping/bidi cause unproved | audit RTL/preview suites, affected typechecks, boundaries, build passed | — | run controlled same-bytes export matrix against approved shaping oracle | exact JSON/font/version and known-good reference absent |
|
|
||||||
| 15 picture fitting/style | #3168, #3088, #2794, #2782 | 15A merged; other causes blocked | implementation/review/finalization chain complete | `codex/issue-2782-picture-fit` | `7a98f6662` → `6e071ebcd` → merge `ab67831e4` | coordinate 06/25; schema/PDF owner exclusion | clean independent exact-head rereview; five threads resolved; fresh CodeRabbit approval; schema/retry fixes valid; rejected requests documented | focused tests/typechecks/boundaries/build/Biome green; exact-head hosted E2E 35/35 and all code checks green; Mintlify parser failure proven pre-existing; normal merge required no bypass | [#3461](https://github.com/reactive-resume/reactive-resume/pull/3461) | partial unit complete | delivery/centering issues still lack source revisions/assets/expected crop |
|
|
||||||
| 16 imported table borders | #3196 | merged | implementation/review chain complete | `codex/issue-3196-editable-tables` | `7a98f6662` → `b170290e9` → merge `999cd618c` | rich-text owner; Plan 19 stacked from reviewed head | table grammar/grid and link semantics now fail closed when Tiptap cannot round-trip; borderless PDF test is color-independent; final focused rereview clean | focused/broad web/PDF tests, typechecks, boundaries, build, narrow Biome, authenticated E2E, independent review, and hosted checks green | [#3464](https://github.com/reactive-resume/reactive-resume/pull/3464) | complete | historical visual equivalence lacks reporter HTML/JSON but feature contract is approved |
|
|
||||||
| 17 list/skill pagination | #2751, #3040 | blocked corrective work; diagnostic-ready | audit `task_1c6582ccdeae` | `codex-audit-rendering-12-19` | `7a98f6662` → no source change | renderer owner shared with 13/14/18 | merged ordered-marker/wrap/level-gap fixes present; current residual not reproduced | audit focused PDF suites, typecheck, boundaries, build passed | — | retain controls; add fix only from exact failing list/skill fixture | #2751 source absent; #3040 current fixture no longer proves residual clipping |
|
|
||||||
| 18 preview/export geometry | #2683 | diagnostic merged; runtime fix blocked | implementation/review/remediation/rereview chain complete | `codex/issue-2683-preview-export-geometry` | `cdb7bdd2f` → `ce372b54b` → `5f5dca844` → merge `11d619d3d` | share output-boundary harness with 12; viewer owner exclusion | opt-in production-path E2E verifies exact disposable DB identity, persisted source/revision and export target, positive pages/ink/sentinels, measured zoom scale, stable transform/clip geometry, all formats/margins/fit/overflow, and DPR1/2; synthetic matrix did not reproduce mismatch | dedicated PostgreSQL/production-server E2E 2/2; web 949, PDF 1,073, two typechecks, collection, Biome, boundaries 1,452, diff hygiene; seven review findings remediated and focused rereview clean | [#3483](https://github.com/reactive-resume/reactive-resume/pull/3483) | retain #2683 pending original JSON/browser fixture; no runtime change from negative synthetic evidence | exact reporter JSON/PDF/browser/DPR/zoom/page settings absent; no measured first divergence |
|
|
||||||
| 19 literal rich-text whitespace | #3397 | merged | implementation/review/remediation chain complete | `codex/issue-3397-literal-whitespace` | `38832014b` → `2f6942fb7` → merge `ea97de5ec` | Plan 16 merged; coordinate DOCX/PDF/editor with 31 | literal whitespace preserved through editor transforms, clipboard/table normalization, PDF layout/textkit geometry, and DOCX; final bare `TD`/`TH` gap fixed | post-main web 102, PDF 106, DOCX 20, three typechecks, Biome, diff green; dedicated Chromium E2E 2/2 | [#3472](https://github.com/reactive-resume/reactive-resume/pull/3472) | complete | direct Word/LibreOffice visual unavailable; XML/four-space contract verified |
|
|
||||||
| 20 section restoration | #3378, #3265, #2921 | 20A merged for #2921; residuals split | implementation/review chain complete | `codex/issue-2921-hidden-section-recovery` | `7a98f6662` → `792d7f8e2` → merge `5850230f8` | 20A before 21/31; placement semantics before 29 | collapsed navigation reopens real accordion before deferred row focus/scroll; final independent review found no issues | focused tests, typechecks, boundaries, build, PO checks, diff gates, authenticated E2E, and all hosted checks green; approved/mergeable/clean | [#3462](https://github.com/reactive-resume/reactive-resume/pull/3462) | begin dependent Plan 21 when owner slot permits | #3378/#3265 need version/action/sanitized JSON; defensive fix cannot close them |
|
|
||||||
| 21 section heading visibility | #3060 | merged | implementation/review chain complete | `codex/issue-3060-section-heading-visibility` | `2a4a1583b` → `3ce4321cb` → merge `368858a56` | 20A and 34 merged; unblocks 23B and 31 | Q1–Q3 implemented across schema, builder menus, Move-to defaults, PDF/DOCX visual omission, accessibility labels, generated references, and recovery fixtures | full 19-task suite; post-main schema 103, PDF 125, DOCX 15, web 30; four typechecks, boundaries, Biome, Markdown, diff; independent review clean | [#3477](https://github.com/reactive-resume/reactive-resume/pull/3477) | complete; issue closed by merge | no remaining approved scope |
|
|
||||||
| 22 skill keyword presentation | #2785 | merged | implementation/review/remediation chain complete | `codex/issue-2785-skill-keyword-layout` | `38832014b` → `3c06c3b0b` → merge `870388192` | must precede/serialize 34 | built-in/custom inline/list schema, menus, PDF, DOCX, accessible HTML, import, locales, generated docs, and recovery hashes updated; both independent reviews clean | 1,280 affected tests and build; post-main 80 focused tests; remediation tooling 97/97, type/lint/diff green | [#3469](https://github.com/reactive-resume/reactive-resume/pull/3469) | complete; Plan 34 dispatched | no product gate |
|
|
||||||
| 23 pagination controls | #3350, #3090 | 23A and diagnostic 23B merged; 23C blocked | implementation/review/remediation chain complete | `codex/issue-3350-item-pagination` | `368858a56` → `f2769dce5` → merge `5e8284e49` | possible 23C requires renderer-safe fallback | deterministic physical-page matrix asserts every token exactly once and separates authored pages from physical overflow; installed renderer proves `wrap=false` truncates oversized items, so no unsafe control shipped | 11 focused pagination tests, PDF typecheck, Biome, boundaries, Markdown, diff; independent review gaps remediated | [#3478](https://github.com/reactive-resume/reactive-resume/pull/3478) | retain item-control issue pending safe fallback | oversized keep-together fallback is lossy; widow/orphan UI remains deferred |
|
|
||||||
| 24 date layout | #3155, #2841 | characterization merged; behavior blocked | implementation/review/remediation chain complete | `codex/issue-3155-date-layout-characterization` | `66c25efe1` → `77a549988` → merge `cdb7bdd2f` | 24B needs explicit numeric geometry; mutual exclusion with 32 | deterministic executable JSON/PNG coordinate/raster baselines cover all Q7/custom sections, nested roles, Chikorita/Ditto LTR/RTL, all 15 templates, and #2841 controls; no runtime behavior changed | 14 focused tests, PDF typecheck, Biome, boundaries, Markdown, diff after current-main integration; independent findings remediated | [#3480](https://github.com/reactive-resume/reactive-resume/pull/3480) | retain issues pending width units/bounds/default visual target | numeric width/bounds/default geometry cannot be invented |
|
|
||||||
| 25 experience company logos | #3379 | blocked implementation; contract-design-ready | audit `task_47ed7eb02d48` | `codex-audit-builder-20-34` | `7a98f6662` → no source change | 25A → 25B; coordinate 06/15 | current generic uploader has eager deletion and no reference counting/ownership validation; unsafe to reuse unchanged | audit relevant package suites/typechecks/boundaries passed | — | define asset ownership, accepted types, retention/orphan/copy/undo contract and tests | storage ownership/lifetime contract requires new decision |
|
|
||||||
| 26 secondary color | #3373 | inventory-ready; implementation blocked | audit `task_47ed7eb02d48` | `codex-audit-builder-20-34` | `7a98f6662` → no source change | 26A → 26B; serialize semantic docs with 28 | theme has three roles; primary consumers are not classified decorative vs semantic | audit schema/PDF suites and typechecks/boundaries passed | — | enumerate primary consumers and approve decorative truth table | consumer classification/visual contract not selected |
|
|
||||||
| 27 offline fonts | #3377 | diagnostic merged; production resolver blocked | implementation/review/remediation chain complete | `codex/issue-3377-offline-font-diagnostic` | `2a4a1583b` → `397d9e43b` → merge `f783908b0` | 27A informs 30A; 27B needs external gate | opt-in four-surface cold-network fixture, deterministic marker crops, tested blank/tofu classification, enforced browser download, and bounded administrator-hosted manifest evidence; no production behavior changed | helper 8, fonts 55, PDF 35, targeted TypeScript, Playwright collection, Biome, boundaries, Markdown, diff; independent findings remediated | [#3479](https://github.com/reactive-resume/reactive-resume/pull/3479) | retain issue pending host-level cold-server proof | production resolver remains blocked until controlled restart/egress evidence and asset-hosting design are proven |
|
|
||||||
| 28 basics custom styles | #3137 | merged partial unit | implementation/review/remediation complete | `codex/issue-3137-semantic-css-diagnostics` | `b85d285b6` → `28c933b55` → merge `38832014b` | serialize semantic docs/tests with 26 | gradients remain unsupported; false-positive recognition and direct-LINK assertions fixed; both threads resolved and CodeRabbit approved | main-integrated package gates plus remediation resume 47/47, PDF 10/10, typechecks, Biome, and diff green; unrelated baseline E2E flakes documented | [#3468](https://github.com/reactive-resume/reactive-resume/pull/3468) | retain residual issue until reporter names remaining failure | reporter has not named remaining failure; keep issue open |
|
|
||||||
| 29 Onyx profile header | #2812 | blocked | audit `task_47ed7eb02d48` | `codex-audit-builder-20-34` | `7a98f6662` → no source change | placement semantics 20; accessibility order 31 | historical Onyx-only PR obsolete; no persisted generic placement owner exists | audit relevant suites/typechecks/boundaries passed | — | choose generic placement capability and produce one-page/overflow visual contract | new persisted-interface choice plus visual contract required |
|
|
||||||
| 30 ATS export evaluation | #2845 | diagnostic merged; preset not warranted | implementation/review/remediation/rereview chain complete | `codex/issue-2845-ats-export-evaluation` | `f783908b0` → `f89873f08` → `d17e188b0` → merge `10eb3bdbc` | current renderer/font diagnostics informed 30A; 30B requires measured material deficiency | occurrence-aware grouping, complete visible/link and hidden-channel coverage, portable JSZip DOCX extraction, and positive numbering fidelity now measure existing exports; two-column PDF and DOCX recover 106/106 expected occurrences, full-width PDF 105/106, and DOCX telephone-target omission is explicit | evaluator 108/108, four affected typechecks, Biome, boundaries 1,117, diff hygiene; independent review found five gaps and focused rereview found one final label gap, all remediated | [#3482](https://github.com/reactive-resume/reactive-resume/pull/3482) | retain #2845 pending vendor-side evidence; do not add preset from current measurements | no vendor accuracy claim; local extraction evidence shows no material deficiency warranting preset |
|
|
||||||
| 31 document accessibility | #2844 | HTML residual merged; public/export audit blocked | implementation/review/remediation chain complete | `codex/issue-2844-accessibility` | `cdb7bdd2f` → `acd2a9cfe` → merge `3e62a1d60` | public viewer requires duplicate-announcement/manual gate | builder mirror now has valid H3/H4 hierarchy and safe recursive rich-text list/mark/link semantics while preserving hidden/unplaced/Q3/order rules; no public/PDF/DOCX changes or conformance claim | focused 15, full web 949, web typecheck, Biome, boundaries; independent empty-summary/blank-company findings remediated | [#3481](https://github.com/reactive-resume/reactive-resume/pull/3481) | retain #2844 for public/PDF/DOCX/manual evidence | dedicated E2E environment and manual screen-reader transcript unavailable |
|
|
||||||
| 32 section date sorting | #2725 | merged | implementation/review chain complete | `codex/issue-2725-one-shot-sort` | `7a98f6662` → `5c07409ae` → merge `b85d285b6` | shared-file exclusion with 20–24; behavior independent of 24 | pure stable one-shot Experience/Education sort implemented with exact unresolved IDs, undo/persistence, and locked-state coverage; independent review found no issues | focused/full tests, typechecks, boundaries, build, catalogs, authenticated E2E, review, and hosted gates green | [#3465](https://github.com/reactive-resume/reactive-resume/pull/3465) | complete | broader autosort/preferences/custom/role behavior remains out of scope |
|
|
||||||
| 33 Europass template | #2689 | research merged; template blocked at approval gate | implementation/review chain complete for 33A | `codex/issue-2689-europass-research` | `2a4a1583b` → `57f2c30` → merge `578cb496a` | 33A → explicit visual approval → possible 33B | official-source mapping, canonical SVG direction, one-page/overflow/comparison artifacts, and corrected full-resolution geometry independently reviewed; no renderer code | XML/bounds/source mapping and 2480×3508 visual rereview passed | [#3475](https://github.com/reactive-resume/reactive-resume/pull/3475) | await explicit approval of proposed visual/product direction before 33B | canonical SVG direction, neutral naming, fluency labels, chronology gutter/date wrapping, and photo-free defaults require maintainer approval |
|
|
||||||
| 34 Gengar skill layout | #2611 | merged | implementation/review chain complete | `codex/issue-2611-gengar-skill-layout` | `870388192` → `f2186d4f7` → merge `2a4a1583b` | 22 merged; shared Skills renderer serialized before 21 | Gengar-only capability restores name → rating → proficiency → keywords in visual and semantic trees; other templates unchanged; independent review clean | 51 focused PDF tests, PDF typecheck, boundaries, Biome, diff green after current-main integration | [#3473](https://github.com/reactive-resume/reactive-resume/pull/3473) | complete | no historical screenshot parity claim |
|
|
||||||
| 35 resume import errors | #2768 | merged adjacent-fix unit | implementation/review chain complete | `codex/issue-2768-import-reproduction` | `38832014b` → `ae219f3d9` → merge `a6057abd7` | none | synthetic matrix proved and fixed empty-MIME rejection, v4 misclassification, and opaque offline-PDF messages; independent review found no product blocker; historical failure remains unproved | focused 26 web + 144 import; web typecheck/build; boundaries/Biome; E2E 13/13 plus repeats 12/12; post-main 158 tests/typecheck/Biome/diff green | [#3471](https://github.com/reactive-resume/reactive-resume/pull/3471) | retain historical needs-info issue | reporter artifact/version/error/steps absent; keep issue open |
|
|
||||||
|
|
||||||
## Active orchestration
|
|
||||||
|
|
||||||
| Scope | Task / dispatch | Owner worktree | State | Deliverable |
|
|
||||||
| --- | --- | --- | --- | --- |
|
|
||||||
| Plans 01–06 | `task_855fbee0a803` / `ctx_949458744061` | `codex-audit-backend-01-06` | complete; worker released | zero drift; plans 01/04/05/06 blocked on named evidence; plan 03 no-change; plan 02 synthetic unit ready |
|
|
||||||
| Plans 07–11, 35 | `task_9f27829ca50f` / `ctx_11f7d7cf6d17` | `codex-audit-backend-07-11-35` | complete; worker release pending | 07/09/10/11 ready after named brief corrections; 08 needs engineering scope amendment; 35 diagnostic-only |
|
|
||||||
| Plans 12–19 | `task_1c6582ccdeae` / `ctx_795fced595ef` | `codex-audit-rendering-12-19` | complete; worker release pending | 15A/16/19 implementation-ready; remaining causes split into diagnostics with named evidence gates |
|
|
||||||
| Plans 20–34 | `task_47ed7eb02d48` / `ctx_50d8257459ab` | `codex-audit-builder-20-34` | complete; worker release pending | ready: 20A, 21, 22, 23A, 28 diagnostics, 32, 34; remaining plans split at evidence/design gates |
|
|
||||||
| Plan 07 implementation | `task_aee702e37eae` / `ctx_ed134b1d79ce` | `codex/issue-2722-postgres-docs` | implementation complete; worker released | commit `e37b73566`; two-file docs change; implementation report complete |
|
|
||||||
| Plan 09 implementation | `task_e450ea143bfa` / `ctx_3b575cf1d5c7` | `codex/issue-2705-git-backup-docs` | implementation complete; worker released | commit `d4ef67113`; two-file docs change and synthetic local-Git validation |
|
|
||||||
| Plan 11 implementation | `task_58d76423d364` / `ctx_20d4adf43908` | `codex/issue-3010-jsearch-docs` | implementation complete; worker released | commit `cb011841c`; three-file docs change and validation report |
|
|
||||||
| Plan 02 synthetic recovery | `task_bae016c4d1f2` / `ctx_303f5d1f1031` | `codex/issue-3181-recovery-procedure` | implementation complete; worker released | commit `3f53deca8`; pure comparator, safeguards docs, TDD evidence |
|
|
||||||
| Plan 02 independent review | `task_46be9a1a4d82` / `ctx_e9f651d38a65` | same Plan 02 worktree | complete; changes required | false no-op, target-state contradiction, v4 wording overclaim |
|
|
||||||
| Plan 07 independent review | `task_b29de2cd974c` / `ctx_46d7f32205a1` | same Plan 07 worktree | complete; changes required | high: all-services pull could cross PostgreSQL major version |
|
|
||||||
| Plan 07 review fix | `task_8ea6cfc76ef4` / `ctx_0184e1101bca` | same Plan 07 worktree | complete | commit `b61ca1609`; app-only update recipe and separately pinned database upgrade path |
|
|
||||||
| Plan 02 review fix | `task_5fe67c42d856` / `ctx_28ed501db631` | same Plan 02 worktree | complete | commit `6af21121b`; strict validation, target invariant, exact v5-only documentation |
|
|
||||||
| Plan 07 re-review | `task_90a9a6b63782` / `ctx_2f1def9119d6` | same Plan 07 worktree | complete; no findings | full-diff verification after update-safety fix |
|
|
||||||
| Plan 09 independent review | `task_129b49e32bc7` / `ctx_68b13c76cc72` | same Plan 09 worktree | complete; changes required | embedded-letter JSON contradiction; template-checkpoint overclaim |
|
|
||||||
| Plan 09 review fix | `task_8aa48854e7da` / `ctx_5aa2007ec136` | same Plan 09 worktree | complete | commit `6931d2cc4`; correct rendered-export scope and throttled template snapshot wording |
|
|
||||||
| Plan 02 re-review | `task_5bffa8e386f2` / `ctx_57fe2ac671f8` | same Plan 02 worktree | complete; changes required | original object still serialized before validation; boxed string reproduced false no-op |
|
|
||||||
| Plan 02 second review fix | `task_eb3b8c09aef2` / `ctx_3d3e5f7d2496` | same Plan 02 worktree | complete; worker released | commit `8607a5138`; original-form validation plus boxed-string/custom-`toJSON` regressions |
|
|
||||||
| Plan 02 final re-review | `task_ae1f7e591826` / `ctx_475ef901639c` | same Plan 02 worktree | complete; changes required; worker released | P1 truthy gate bypass plus executable/non-JSON object input identity |
|
|
||||||
| Plan 02 contract hardening | `task_baf33a609db0` / `ctx_48534ddcf89f` | same Plan 02 worktree | complete; worker released | commit `00855fa16`; serialized request-only API, strict envelope, 36-case adversarial coverage |
|
|
||||||
| Plan 02 final independent review | `task_5b02aa026038` / `ctx_d16058d89c7c` | same Plan 02 worktree | complete; changes required; worker released | P1 duplicate-member safety bypass; P2 whitespace/control-only manifest identifiers |
|
|
||||||
| Plan 02 hardening round 4 | `task_5b56f07b5a2c` / `ctx_e7c63529b719` | same Plan 02 worktree | complete; worker released | commit `4125074f9`; 59 comparator tests plus duplicate-member and unsafe-ID adversarial gates green |
|
|
||||||
| Plan 02 independent review round 5 | `task_a2b0562df7aa` / `ctx_bc5056c8978c` | same Plan 02 worktree | complete; no findings; pushed; worker released | exact head `4125074f9`; full diff, 54 independent assertions, fresh coordinator verification, PR #3460 |
|
|
||||||
| Plan 02 autofix review | `task_796f15a35422` / `ctx_c581be24021a` | same Plan 02 worktree | complete; changes required; worker released | bot export cleanup safe; valid P2 Unicode Cf ID gap; three Codacy suggestions rejected/already satisfied with evidence |
|
|
||||||
| Plan 02 hosted review fix | `task_1d56264e5f15` / `ctx_2a1a0a6af73e` | same Plan 02 worktree | complete; worker released | commit `325d1fcd1`; reject and document Unicode format-character IDs, add all-field regressions, preserve strict comparator contract |
|
|
||||||
| Plan 02 hosted fix rereview | `task_51b92554b36b` / `ctx_08bc7a78dd61` | same Plan 02 worktree | complete; no findings; worker released | full diff, all four hosted dispositions, 1,410 Unicode combinations, and mutation probes approved exact local head `325d1fcd1` |
|
|
||||||
| Plan 02 hosted fix publication | coordinator | same Plan 02 worktree | complete; monitor only | exact head `325d1fcd1`; four evidence replies posted, all four threads resolved, all hosted checks green, approved and mergeable |
|
|
||||||
| Plan 09 re-review | `task_1bd82b008a77` / `ctx_ca2a0176b52f` | same Plan 09 worktree | complete; no findings; worker released | full diff and both factual corrections verified before PR #3458 |
|
|
||||||
| Plan 09 hosted review follow-up | `task_a44e374822b4` / `ctx_89e6769df19d` | same Plan 09 worktree | complete; changes required; worker released | selected revision prints correctly but no importable file is created |
|
|
||||||
| Plan 09 hosted review fix round 2 | `task_8605c30ca032` / `ctx_2e0aa7be1312` | same Plan 09 worktree | complete; worker released | commit `9dd218ba1`; non-destructive recovered-file command plus synthetic proof |
|
|
||||||
| Plan 09 final hosted re-review | `task_435249ae4c23` / `ctx_5fd78d0eeccd` | same Plan 09 worktree | complete; changes required; worker released | low: fixed `recovered-resume.json` name silently overwrites an existing local recovery file |
|
|
||||||
| Plan 09 review fix round 3 | `task_9e6fdbe67fb7` / `ctx_9087e895192f` | same Plan 09 worktree | complete; worker released | commit `4ffdd96bd`; require unused output filename, preserve existing sentinel, 152 tests and docs gates green |
|
|
||||||
| Plan 09 rereview round 4 | `task_4af7ea4cfb3f` / `ctx_98b5a07f33d4` | same Plan 09 worktree | complete; no findings; pushed; worker released | exact head `4ffdd96bd`; clean full-diff review and fresh coordinator 152-test/docs verification |
|
|
||||||
| Plan 11 independent review | `task_7d5d4c1417a9` / `ctx_17ee05faf9e7` | same Plan 11 worktree | complete; changes required | attachment-format overclaim and duplicated patch/restore guidance |
|
|
||||||
| Plan 11 review fix | `task_351a96e90b69` / `ctx_e398c9128731` | same Plan 11 worktree | complete; worker released | commit `e05977007`; attachment promise removed and adjacent guidance deduplicated |
|
|
||||||
| Plan 11 re-review | `task_86a66b578544` / `ctx_3bb8017e8676` | same Plan 11 worktree | complete; no findings; worker released | exact head `e05977007`; 52 focused tests and full diff verified before PR #3459 |
|
|
||||||
| Plan 11 hosted review follow-up | `task_a9d8b3e0a8a2` / `ctx_6b03ba8f389c` | same Plan 11 worktree | no findings; pushed; worker released | commit `1d03978`; sample wording plus heading/UI terminology alignment; all hosted checks green and both threads resolved |
|
|
||||||
| Plan 15A implementation | `task_557902c8adef` / `ctx_db6fae1b75c4` | `codex/issue-2782-picture-fit` | complete; worker released | commit `d891afd66`; explicit cover/contain contract across schema/editor/preview/PDF |
|
|
||||||
| Plan 15A independent review | `task_d4f4841355e2` / `ctx_467f293cf443` | same Plan 15A worktree | complete; changes required; worker released | medium: raster checks did not pin fit geometry; low: class coupling and duplicate props type |
|
|
||||||
| Plan 15A review fix | `task_5a4afecb7d0d` / `ctx_43faac4f25eb` | same Plan 15A worktree | complete; worker released | commit `6145d5a59`; all three findings fixed; full tests/build and authenticated raster E2E green |
|
|
||||||
| Plan 15A final rereview | `task_2b45ae3b4e98` / `ctx_695a139cd0b9` | same Plan 15A worktree | complete; no findings; worker released | exact head `6145d5a59`; full 70-file behavior, mutation-sensitive geometry, computed CSS, compatibility, scope, full suites, build, and E2E approved before PR #3461 |
|
|
||||||
| Plan 15A hosted review | `task_06bedd03c57a` / `ctx_f3a9297b252e` | same Plan 15A worktree | complete; two changes required; terminal transferred | accepted published-schema optionality and failed same-file retry; rejected metadata flag semantics and Codacy/style warnings; full report at `.orchestration/plan-15a-hosted-review.md` |
|
|
||||||
| Plan 15A hosted review fix | `task_855387612fac` / `ctx_3e9f9c80147d` | same Plan 15A worktree | complete; pushed; worker released | commit `6e071ebcd`; public schema/OpenAPI omit required `picture.fit` while runtime output remains required/defaulted; failed Contain upload clears input for same-file retry; accepted threads auto-resolved |
|
|
||||||
| Plan 15A hosted rereview | `task_1116a06f2543` / `ctx_636533e5113e` | same Plan 15A worktree | complete; no code findings; terminal transferred | exact `6e071ebcd` approved; E2E 35/35; Mintlify failure traced to pre-existing generated MDX comment marker and partial-deploy parser behavior |
|
|
||||||
| Plan 15A hosted finalization | `task_01693dcf5e94` / `ctx_de7c5995049d` | same Plan 15A worktree | complete; worker released | all five threads resolved; CodeRabbit withdrew metadata finding and freshly approved exact head; no stale-review dismissal, code, merge, or issue mutation |
|
|
||||||
| Plan 07 hosted review follow-up | `task_6e9bc84ca2d4` / `ctx_e31d0e345240` | `codex/issue-2722-postgres-docs` | no findings; pushed; worker released | commit `a7b8c4c`; five valid comments fixed, `--no-deps` removal rejected; six replies and zero unresolved threads |
|
|
||||||
| Plan 07 late hosted fix | `task_6d0545ee43b1` / `ctx_1d6a1e36def3` | same Plan 07 worktree | complete; worker released | commit `171637526`; separate image/repository build and log paths; Compose/docs gates green |
|
|
||||||
| Plan 07 late fix rereview | `task_8ff818f3e95a` / `ctx_b73be2273e01` | same Plan 07 worktree | complete; no findings; pushed; worker released | exact head `171637526`; full-diff/Compose review and fresh coordinator gates green; hosted checks rerunning |
|
|
||||||
| Plan 16 implementation | `task_f3d05a6ebb86` / `ctx_686ba93fc8af` | `codex/issue-3196-editable-tables` | complete; worker released | commit `83aca184e`; atomic editable supported tables, lossless unsupported fallback, PDF border geometry, persistence/E2E |
|
|
||||||
| Plan 16 independent review | `task_e52d01cff964` / `ctx_63c00e7abb56` | same Plan 16 worktree | complete; changes required; worker released | P1 unsupported descendant markup can normalize destructively; P2 conversion is still HTML and E2E does not prove unrelated save persistence; orchestration report accepted by brief |
|
|
||||||
| Plan 16 review fix | `task_7b7368639ff8` / `ctx_ba9ed045764f` | same Plan 16 worktree | complete; worker released | commit `d0cca949f`; fail-closed unsupported grammar, removal of out-of-scope conversion UX, and persisted unrelated-edit E2E proof; all requested gates green |
|
|
||||||
| Plan 16 final rereview | `task_fac7a8d1cf90` / `ctx_b5d8f7f10380` | same Plan 16 worktree | complete; two P1 findings; terminal transferred | truncated `<table` marker fails open; invalid align/colwidth/span/indent/style values can pass name-only allowlist and normalize destructively |
|
|
||||||
| Plan 16 preservation fix | `task_d21a8f07917c` / `ctx_71f2eee5ec8d` | same Plan 16 worktree | complete; terminal transferred | commit `02b8b1587`; fail-closed marker accounting and declarative value validators with 34 exact-byte tests; full gates green |
|
|
||||||
| Plan 16 branch hygiene | `task_fad9ffd13a36` / `ctx_b6f270801828` | same Plan 16 worktree | complete; worker released | commit `c3f0dde76`; all tracked orchestration reports removed; six-file final diff, full-range diff check, Biome, and Markdownlint clean |
|
|
||||||
| Plan 16 publication rereview | `task_939fe21be440` / `ctx_dca9372dac03` | same Plan 16 worktree | complete; three findings; worker released | two P1 Tiptap round-trip gaps in invalid table geometry and href-less anchors; P2 borderless PDF color blind spot |
|
|
||||||
| Plan 16 publication fix | `task_7e846e42ddab` / `ctx_000a540aff73` | same Plan 16 worktree | complete; worker released | commit `b170290e9`; all three findings fixed with focused 70 web and 11 PDF tests; generated artifacts removed |
|
|
||||||
| Plan 16 focused fix rereview | `task_6b49f917cd3d` / `ctx_a29bed0272bf` | same Plan 16 worktree | complete; no findings; worker released | exact three-file fix diff approved before PR #3464 |
|
|
||||||
| Plan 20A implementation | `task_4237c974827a` / `ctx_00770ded7b0d` | `codex/issue-2921-hidden-section-recovery` in worktree `issue-3378-hidden-section-recovery` | complete; worker released | commit `0ec054df7`; #2921-only inventory/recovery UI, navigation, unit coverage, and authenticated PDF E2E |
|
|
||||||
| Plan 20A independent review | `task_be0db32b205d` / `ctx_ab43d98bf4aa` | same Plan 20A worktree | complete; two linked P2 findings; terminal transferred | collapsed accordion navigation returns before target mounts; unit test manually retained impossible collapsed row and missed defect |
|
|
||||||
| Plan 20A collapsed-navigation fix | `task_580f83d5f46d` / `ctx_63ceb289dcb7` | same Plan 20A worktree | complete; worker released | commit `792d7f8e2`; reopen real Base UI accordion before deferred row lookup/focus/scroll; lifecycle RED/GREEN and full gates passed |
|
|
||||||
| Plan 20A final rereview | `task_6e43715b86de` / `ctx_4a87a2476608` | same Plan 20A worktree | complete; no findings; worker released | exact head `792d7f8e2`; full diff, lifecycle navigation, persistence/PDF E2E, accessibility, and strict #2921-only scope approved before PR #3462 |
|
|
||||||
| Plan 10 implementation | `task_fd191afc86ba` / `ctx_5100e7419c11` | `codex/issue-2836-retired-link-notices` | complete; worker released | commits `b33962e3f` and `58d2972c7`; prospective 90-day owner-only retired-link attempts with migration, privacy limits, owner UI/docs, full tests, and disposable E2E |
|
|
||||||
| Plan 10 independent review | `task_124a094afdf0` / `ctx_092876c252c5` | same Plan 10 worktree | complete; two Spec findings; terminal transferred | P1 concurrent cross-resume reuse race reproduced in PostgreSQL; P2 required failed-rename rollback proof absent; no Standards finding |
|
|
||||||
| Plan 10 transaction fix | `task_ce5381b8974b` / `ctx_45490fb447e1` | same Plan 10 worktree | complete; worker retained by user takeover | commit `81bdb868d`; live acquisition reordered; PostgreSQL concurrency regression green |
|
|
||||||
| Plan 10 focused rereview | `task_dd9678d7784a` / `ctx_d270e086a77f` | same Plan 10 worktree | complete; one P2; worker released | code fix approved; occupied-target case did not prove rollback after successful mutation |
|
|
||||||
| Plan 10 rollback proof/final rereview | `task_050a59b459d6`, `task_19860273bc0c` | same Plan 10 worktree | complete; no findings; workers released | commit `6ac2edb1b`; forced post-update PostgreSQL capture failure proves rollback; mutation test and cleanup independently approved before PR #3463 |
|
|
||||||
| Plan 32 implementation | `task_547d2dbc1520` / `ctx_a0f1ac5fb10b` | `codex/issue-2725-one-shot-sort` | complete; worker released | commits `3e9d57174` and hygiene `1031dc9a8`; one-shot Experience/Education date sort, targeted warning, undo/persistence/lock coverage |
|
|
||||||
| Plan 32 independent review | `task_0903351a51d2` / `ctx_537efdb21d35` | same Plan 32 worktree | complete; no findings; worker released | exact final head approved after focused behavior/typecheck/catalog/diff review; PR #3465 merged as `b85d285b6` |
|
|
||||||
| Plan 23A implementation/review | `task_fd704c2e7a5d`, `task_2cb223b6dbb9` | `codex/issue-3090-authored-page-guidance` | complete; PR #3467 merged | review-fix `322bed31c`; latest-main integration head `412da288f`; all 14 hosted threads resolved; relevant E2E passed; merge `0fbeeeb4c` |
|
|
||||||
| Plan 19 implementation | `task_d9237449193e` / `ctx_656a3cd4360a` | `codex/issue-3397-literal-whitespace` | complete; worker released | exact integrated head `6d9341425`; full local gates and PostgreSQL E2E green |
|
|
||||||
| Plan 19 independent review | `task_a138df5923b5` / `ctx_d1537f68e074` | same Plan 19 worktree | complete; changes requested | one P1 PDF leading-whitespace failure plus four P2 editor transform, clipboard, and tab-display failures |
|
|
||||||
| Plan 19 review remediation | `task_5afe88028c7c` / `ctx_a1a01a86bac7` | same Plan 19 worktree | complete; worker released | commit `4ad9de6e0`; all five findings fixed; web 100, PDF 133, DOCX 20, Chromium E2E 2/2, type/lint/boundary gates green |
|
|
||||||
| Plan 19 focused rereview | `task_709b405c7f26` / `ctx_6e5d11ab901c` | same Plan 19 worktree | complete; one P2; worker released | original five seams and patch-package integrity approved; bare `TD`/`TH` clipboard text can still collapse accepted whitespace |
|
|
||||||
| Plan 19 table-cell remediation | `task_5d88bb692ded` / `ctx_f47937fd0b56` | same Plan 19 worktree | complete; worker released | commit `2a55bebdc`; supported bare `TD`/`TH` normalized to marked paragraphs; post-main gates green; PR #3472 merged |
|
|
||||||
| Plan 22 implementation | `task_69bd20897491` | `codex/issue-2785-skill-keyword-layout` | complete | exact head `d87176ef4`; 75 files including 55 catalogs; 1,280 affected tests and full build green |
|
|
||||||
| Plan 22 independent review | `task_546ed601aa62` / `ctx_e423055f28ee` | same Plan 22 worktree | complete; worker released | no Standards or Spec findings; fresh 80 focused tests and five typechecks green |
|
|
||||||
| Plan 22 generated-fixture rereview | collaboration `/root/review_plan22_refresh` | same Plan 22 worktree | complete; no findings | generated docs exactly match schema; all four recovery hashes independently recomputed; focused tooling/type/lint/diff gates green |
|
|
||||||
| Plan 28 diagnostics/review | `task_e8b11d71f5ec`, `task_30f17b58c95a` | `codex/issue-3137-semantic-css-diagnostics` | complete; PR #3468 merged | independent review clean; hosted review found two focused test-quality defects; fixes approved and merged as `38832014b` |
|
|
||||||
| Plan 28 hosted remediation | `task_5fa47e8b49ca` | same Plan 28 worktree | complete | head `28c933b55`; narrow gradient-recognition and direct-LINK assertion fixes pushed; threads resolved; approved |
|
|
||||||
| Plan 08 implementation | `task_3aecece76616` / `ctx_db909cf711e4` | `codex/issue-2669-root-public-resume` | complete; worker released | commit `5401c08a5`; 1,392 tests plus enabled/disabled production E2E green; merged current main at review head `576fad5ef` |
|
|
||||||
| Plan 08 independent review | `task_eed414e9890e` / `ctx_6fa84066d77e` | same Plan 08 worktree | complete; worker released | no Standards or Spec findings; focused risk tests and broad/E2E evidence approved before PR #3470 |
|
|
||||||
| Plan 35 reproduction | `task_6efe013f350a` / `ctx_5f174f7a941d` | `codex/issue-2768-import-reproduction` | complete; worker released | commit `df8d5f334`; three deterministic adjacent fixes with 13/13 E2E and 12/12 repeated-case evidence; historical case remains unproved |
|
|
||||||
| Plan 35 independent review | `task_e417d3d5d084` / `ctx_e4b1ec8ae401` | same Plan 35 worktree | complete; no product blocker; worker released | two evidence-label limits recorded; 1,127 emitted JS/MJS artifacts removed without deleting review evidence; current main integrated and PR #3471 opened |
|
|
||||||
| Plan 33A research | `task_0ed3877760c1` / `ctx_7e91c8f1d077` | `codex/issue-2689-europass-research` | complete; worker released | commit `2de5b6240`; official-source mapping plus one-page/overflow/comparison artifacts; no renderer code |
|
|
||||||
| Plan 33A independent review | `task_55fd536909d2` / `ctx_ac573ee1d4ad` | same Plan 33A worktree | complete; two P1 visual findings; worker released | overflow page 1 date/title collision and page 2 right-edge clipping; research/mapping/source-safety checks otherwise clean |
|
|
||||||
| Plan 33A visual remediation | `task_e5a239df7684` / `ctx_fd5ff59ddc46` | same Plan 33A worktree | complete; worker released | commit `57f2c30`; date/title collision and right-edge text/URL clipping corrected; XML/mapping/bounds gates green |
|
|
||||||
| Plan 33A visual rereview | `task_dbf4b61a1ada` / `ctx_4ba872ef6b5c` | same Plan 33A worktree | complete; no findings; worker released | full-resolution 2480×3508 raster inspection confirmed both corrected geometry seams; XML bounds and source mapping clean; PR #3475 merged as `578cb496a` |
|
|
||||||
| Plan 34 implementation/review | `task_422be45068f7`, `task_e7e35921c6be` | `codex/issue-2611-gengar-skill-layout` | complete; PR #3473 merged | implementation `b337d8147`, current-main head `f2186d4f7`, independent review clean, merge `2a4a1583b` |
|
|
||||||
| Plan 21 implementation | `task_b7cc0bb3ec69` / `ctx_23c399d315ec` | `issue-3060-section-heading-visibility` | complete; worker released | commit `eeb9e09c4`; full suite, affected typechecks, boundaries, Biome, diff, and pre-commit gates green |
|
|
||||||
| Plan 21 independent review | `task_c115a5436782` / `ctx_681b494ae3ed` | same Plan 21 worktree | complete; no findings; worker released; PR #3477 merged | current main integrated; focused post-main gates green; merge `368858a56` |
|
|
||||||
| Plan 27A font diagnostic | `task_4244667977dd` / `ctx_448dd7709bbc` | `issue-3377-offline-font-diagnostic` | focused rereview complete; findings; worker released | server blocker/evidence corrections approved; duplicate marker source can mis-map crop and browser-PDF raster failures can be swallowed |
|
|
||||||
| Plan 27A raster fix round 2 | `task_c491972c35cc` / `ctx_c636c67853bf` | same Plan 27A worktree | complete; worker released | commit `c8a10b3d3`; split/duplicate marker mapping, isolated raster crops, and post-download evidence enforcement; final review found one broader download false-pass |
|
|
||||||
| Plan 27A final remediation | `task_98d303694a36` / `ctx_5d93d3ca0d36` | same Plan 27A worktree | complete; worker released; PR #3479 merged | commit `313cfab63`; successful browser download required and raw browser measurements use tested Node classifier; merge `f783908b0` |
|
|
||||||
| Imported-table E2E baseline repair | `task_c5a99a1210d5` / `ctx_4a7956dbe266` | `codex/fix-imported-table-raster-ci` | complete; worker released; PR #3476 merged | commit `6f274496d`; independent review clean; exact 17/12 table topology retained while unrelated stale-stroke endPath is excluded; merge `66c25efe1` |
|
|
||||||
| Plan 23B item pagination | `task_8224e5a09e22`, review `task_f3a10c98a230`, remediation `task_985f488f65e1` | `codex/issue-3350-item-pagination` | complete; workers released; PR #3478 merged | every token asserted exactly once; authored/physical pages separated; lossy oversized `wrap=false` blocker retained; merge `5e8284e49` |
|
|
||||||
| Plan 24A date characterization | `task_45f004efc2bc`, review `task_1b18f7c40ea3`, remediation `task_69caca11a49e` | `codex/issue-3155-date-layout-characterization` | complete; workers released; PR #3480 merged | executable deterministic JSON/PNG baselines; exact Meowth exception; raw PDFs omitted; merge `cdb7bdd2f` |
|
|
||||||
| Plan 31 accessibility | implementation `task_f03773b6531d`, review `task_02b2a71db0f6`, remediation `task_75034a30305e` | `codex/issue-2844-accessibility` | complete; workers released; PR #3481 merged | H3/H4 plus safe rich-text semantics; 1,155 reviewer-emitted JS artifacts removed; public/export/manual gates retained; merge `3e62a1d60` |
|
|
||||||
| Plan 30 export evaluation | implementation `task_8ccb73b62e79`; review `task_62e7e0f678f3`; remediation `task_328f1a7caa2c`; rereview `task_27bfa7e77312`; final fix `task_57d3967893fb` | `codex/issue-2845-ats-export-evaluation` | complete; all workers released; PR #3482 merged | commits `f89873f08`, `0e5994f24`, `d17e188b0`; 108 tests and focused rereview evidence; merge `10eb3bdbc` |
|
|
||||||
| Plan 18 geometry diagnostic | implementation `task_938999fe6ae1`; review `task_a71aca971d4c`; remediation `task_4ddf17ed5fea`; rereview `task_e6b96ad69cd7` | `codex/issue-2683-preview-export-geometry` | complete; all workers released; PR #3483 merged | commits `ce372b54b`, `5f5dca844`, integration `25e044c86`; full dedicated matrix 2/2 and focused rereview clean; merge `11d619d3d` |
|
|
||||||
| Final completion audit | `task_81402f5f945f` / `ctx_1b430f809130` | ledger plus planning checkout | complete; worker released | exact 35-plan/63-issue coverage; found Unit 03/07 disposition wording and PR #3475 head metadata gaps; corrected before ledger publication |
|
|
||||||
| Completion correction rereview | `task_66f7c09eea8b` / `ctx_649722e31975` | ledger worktree | complete; worker released; no findings | independently confirmed all three audit corrections, sequential Units 01–35, and exact 63-issue coverage |
|
|
||||||
| Plan 10 planning disposition review | `task_070925cc66a5` / `ctx_4d34e328c0d9` | planning PR #3455 checkout | complete; findings; worker released | found approval/execution wording that still included declined Plan 10; inventory remained 35 plans/63 issues |
|
|
||||||
| Plan 10 planning disposition rereview | `task_34a5d6723d99` / `ctx_a4e34a8d5b89` | same planning checkout | complete; no findings; worker released | confirmed README/DECISIONS/archived-plan authority corrections; PR #3455 merged as `42527ad83` |
|
|
||||||
| Geometry E2E CI regression fix | `task_d77e8890f112` / `ctx_603cdc257add` | `codex/fix-geometry-e2e-opt-in` | complete; worker released; PR #3484 merged | commit `26f2360cf`; ordinary run 2 skipped, opt-in collection 2 tests, exact database guard retained; merge `778fd4b7d` |
|
|
||||||
| Geometry E2E fix review | `task_f045a60ebea9` / `ctx_b6df79925977` | same regression-fix worktree | complete; no findings; worker released | unusable database URL still produced 2 skipped before fixtures; Biome and diff checks clean |
|
|
||||||
|
|
||||||
## Existing PR and residual accounting
|
|
||||||
|
|
||||||
| Item | Live state | Exact head | Evidence / checks | Next action |
|
|
||||||
| --- | --- | --- | --- | --- |
|
|
||||||
| PR #3453 | merged 2026-09-06 | `ccd111da894cf7d44cc3dee06c937f70d91fef24` → merge `a4bdc54b2` | exact-head hosted checks green and approved before merge | complete |
|
|
||||||
| PR #3454 | merged 2026-09-06 | `80b0d3ab02cc4292f8a4514db8c2516adc1f9dc3` → merge `2e711fd14` | exact-head hosted checks green and approved before merge | complete |
|
|
||||||
| PR #3455 | merged 2026-09-06 | `86e200a4dae0f421d1c96bbd3a2e1a0a6899a959` → merge `42527ad83` | final Plan 10 not-planned disposition independently rereviewed; planning inventory remains 35 plans/63 issues | complete |
|
|
||||||
| PR #3456 | merged 2026-09-06 | `138f3bbd1219f0a82f1a617ddcde2797e70b029c` → merge `981d7581f` | final audit corrections independently rereviewed; exact 35-plan/63-issue coverage | complete |
|
|
||||||
| PR #3461 | merged 2026-09-06 | `6e071ebcd60c5e31a5b7bcc48f8a24c7c491e787` → merge `ab67831e4` | all code checks/E2E green and approved; unrelated Mintlify failure documented; normal merge, no bypass | complete |
|
|
||||||
| PR #3462 | merged 2026-09-06 | `792d7f8e2677d442536fecd450b4e9dbbddd94ee` → merge `5850230f8` | exact-head hosted checks green and approved before merge | complete |
|
|
||||||
| PR #3463 | closed unmerged 2026-09-06 | `3eac878d68df48b85a6d6dd9eed37bd8df6b3e6d` | maintainer explicitly skipped retired-link implementation; not-planned rationale posted to #2836 | no further action; preserve branch |
|
|
||||||
| PR #3464 | merged 2026-09-06 | `b170290e96b411bce46873b64b345e7d5b8e2fa1` → merge `999cd618c` | independent review and exact-head hosted checks green | complete |
|
|
||||||
| PR #3465 | merged 2026-09-06 | `5c07409ae035549cd743b702b99b2959cb25e183` → merge `b85d285b6` | independent review; refreshed tooling 97/97, resume 75/75, web 29/29; hosted checks green and approved | complete |
|
|
||||||
| PR #3466 | merged 2026-09-06 | `43e6de7e4591b87f211f9ace28363204bbbe30f8` → merge `695cdb851` | four stale recovery hashes independently recomputed after picture-fit default changed canonical bytes; focused 83/83, tooling 97/97, typecheck/Biome/diff green | complete; monitor hosted E2E |
|
|
||||||
| PR #3467 | merged 2026-09-06 | `412da288f0164be8c52d7ca51404d0bd2a5880b7` → merge `0fbeeeb4c` | pagination assertion fixed; 14 threads resolved; relevant authored-page E2E passed; unrelated baseline flakes documented; normal merge, no bypass | complete; keep #3350 out of scope |
|
|
||||||
| PR #3468 | merged 2026-09-06 | `28c933b555de7aeed9c18b11745b10f2b108eca9` → merge `38832014b` | focused remediation approved; two threads resolved; semantic checks green; unrelated baseline flakes documented; normal merge, no bypass | partial unit complete; keep #3137 open |
|
|
||||||
| PR #3469 | merged 2026-09-06 | `3c06c3b0bbe85ff346e38bdecbc24ee2630f8642` → merge `870388192` | feature review and focused generated-fixture rereview clean; exact failed tooling gate fixed 97/97; normal merge, no bypass | complete |
|
|
||||||
| PR #3470 | merged 2026-09-06 | `576fad5ef8c1a622fd78245e0c0c26c24c08d662` → merge `744eaa902` | current main integrated; independent review clean; local enabled/disabled production E2E and focused gates green; normal merge, no bypass | partial unit complete; keep #2669 open |
|
|
||||||
| PR #3471 | merged 2026-09-06 | `ae219f3d911a259d0a7a4ce26505060d4956232e` → merge `a6057abd7` | current main integrated; independent review found no product blocker; post-main 158 focused tests, web typecheck, Biome, and diff green; normal merge | adjacent fixes complete; keep #2768 open |
|
|
||||||
| PR #3472 | merged 2026-09-06 | `2f6942fb7299770a231c863b6b3a4e99c4538fad` → merge `ea97de5ec` | current main integrated twice; final 102 web tests, web typecheck, Biome, and diff green after independent-review remediation; normal merge | complete; #3397 closed |
|
|
||||||
| PR #3473 | merged 2026-09-06 | `f2186d4f7495804670c6973b9454578280e461a7` → merge `2a4a1583b` | current main integrated; independent review clean; 51 focused PDF tests, PDF typecheck, Biome, boundaries, and diff green; normal merge | complete; #2611 closed |
|
|
||||||
| PR #3474 | merged 2026-09-06 | `ec0a18fd8b5cd0b9b1cc7ee31a12d2d783eb4ffd` → merge `97f34b7cc` | Codacy findings from #3472 removed: clipboard HTML now parsed with DOMParser and new LRM/RLM literals use escapes; 102 web + 44 PDF tests, two typechecks, Biome, diff green | static-analysis follow-up complete |
|
|
||||||
| PR #3475 | merged 2026-09-06 | exact head `46b24581d29b26c6be70cba719adf9ea9251bb56` (`57f2c30` remediation) → merge `578cb496a` | independent full-resolution visual rereview passed after collision and clipping remediation; research/XML/bounds/source-mapping gates clean | research complete; issue #2689 remains open pending explicit visual/product approval before renderer implementation |
|
|
||||||
| PR #3476 | merged 2026-09-06 | `6f274496d` → merge `66c25efe1` | repeated hosted false positive reproduced from artifact; two consecutive dedicated E2E runs, focused/full gates, independent review clean; hosted attempt 2 passed | imported-table CI baseline repaired; no production behavior change |
|
|
||||||
| PR #3477 | merged 2026-09-06 | `3ce4321cb` → merge `368858a56` | full implementation suite, post-main focused tests/typechecks/static gates, independent review clean; hosted attempt 2 passed | complete; #3060 closed |
|
|
||||||
| PR #3478 | merged 2026-09-06 | `f2769dce5` → merge `5e8284e49` | 11 focused pagination tests, PDF typecheck, Biome, boundaries, Markdown, diff; independent review gaps remediated; hosted E2E rerun attempt 2 failed unrelated baseline `picture-rendering` Contain persistence while changed diagnostics stayed outside that path | diagnostic complete; keep #3350 open pending renderer-safe fallback |
|
|
||||||
| PR #3479 | merged 2026-09-06 | `397d9e43b` → merge `f783908b0` | helper 8, fonts 55, PDF 35, targeted TypeScript, Playwright collection, Biome, boundaries, Markdown, diff; independent false-pass findings remediated; hosted E2E rerun attempt 2 failed unrelated baseline autosave timing and section-recovery state | diagnostic complete; keep #3377 open pending controlled cold-server egress proof |
|
|
||||||
| PR #3480 | merged 2026-09-06 | `77a549988` → merge `cdb7bdd2f` | 14 focused tests, PDF typecheck, Biome, boundaries, Markdown, diff; independent artifact findings remediated | characterization complete; keep #3155/#2841 open pending numeric visual geometry |
|
|
||||||
| PR #3481 | merged 2026-09-06 | `acd2a9cfe` → merge `3e62a1d60` | focused 15, full web 949, typecheck, Biome, boundaries; independent heading findings remediated; hosted E2E rerun attempt 2 failed unrelated baseline `picture-rendering` Contain persistence | HTML residual complete; keep #2844 open for public/PDF/DOCX/manual evidence |
|
|
||||||
| PR #3482 | merged 2026-09-06 | `d17e188b0` → merge `10eb3bdbc`; autofix `f447f429a` | evaluator 108/108, four affected typechecks, Biome, boundaries 1,117; five initial review gaps and one focused-rereview label gap remediated; hosted E2E, CodeRabbit, and Greptile passed; Codacy alerts are non-secret synthetic token equality and fixed internal XML attribute-name construction; autofix export narrowing landed on main before its head-ref publication failure | diagnostic complete; keep #2845 open pending vendor-side evidence; no ATS preset justified |
|
|
||||||
| PR #3483 | merged 2026-09-06 | `25e044c86` → merge `11d619d3d` | dedicated PostgreSQL/production-server geometry E2E 2/2; web 949, PDF 1,073, typechecks, collection, Biome, boundaries 1,452; seven review findings remediated and focused rereview clean; hosted baseline later exposed missing default-off test gating | diagnostic complete; keep #2683 open pending original fixture/browser evidence; CI regression fixed by #3484 |
|
|
||||||
| PR #3484 | merged 2026-09-06 | `26f2360cf` → merge `778fd4b7d` | default-off Playwright 2 skipped; opt-in collection 2 tests; web/PDF typechecks, Biome, boundaries 1,117; independent review clean; exact disposable DB guard unchanged | CI regression complete; no product-scope change |
|
|
||||||
| Residual #2828 | pending product direction | — | stale whole-document concurrent-tab overwrite reproduced in Chromium/PostgreSQL; excluded from 63 | account for separately; do not implement conflict UI until product policy selected |
|
|
||||||
| Residual #3246 | outside approved package | — | open repository issue excluded by `plans/inventory.json`; no approved implementation unit | retain outside this run |
|
|
||||||
|
|
||||||
## Rulings and blockers log
|
|
||||||
|
|
||||||
- 2026-09-05 — No rulings yet. Approved Q1–Q12 and blanket directions are binding inputs, not coordinator rulings.
|
|
||||||
- 2026-09-06 — Plan 16 preservation hardening uses one declarative element-rule registry with conservative value
|
|
||||||
validators and explicit zero-table failure. Render-time Tiptap normalization was rejected because it adds editor work
|
|
||||||
and fragile canonicalization exceptions; unknown or ambiguous markup remains exact-byte read-only.
|
|
||||||
- 2026-09-06 — Publication throughput policy: require one independent pre-publication review, run focused risk-based gates,
|
|
||||||
then push and open PR immediately. Hosted CI runs concurrently under coordinator monitoring. Repeat review only for a
|
|
||||||
concrete finding and limit it to the changed seam; workers do not poll hosted state or repeat unchanged full suites.
|
|
||||||
- 2026-09-06 — Maintainer changed integration policy: merge complete, mergeable PRs immediately. Twenty-eight approved
|
|
||||||
plan-execution PRs were merged without admin bypass. Plan 10 retired-link work was explicitly rejected as disproportionate redirect and
|
|
||||||
error-handling overhead; PR #3463 closed unmerged and rationale recorded on issue #2836.
|
|
||||||
- 2026-09-06 — Plan 22 orchestration metadata initially named issue #3060. Live issue revalidation corrected ownership to
|
|
||||||
#2785 and branch `codex/issue-2785-skill-keyword-layout` before publication; #3060 remains reserved for Plan 21.
|
|
||||||
- 2026-09-06 — Plan 35 publishes three deterministic adjacent fixes without claiming the historical cloud failure fixed.
|
|
||||||
Issue #2768 remains open/needs-info because reporter fixture, exact error, version, browser, and steps remain absent.
|
|
||||||
- 2026-09-06 — Codacy security findings on Plan 19 were addressed in an immediate follow-up rather than accepted as
|
|
||||||
detached-parser false positives: DOMParser removes the flagged assignment and Unicode escapes remove new invisible controls.
|
|
||||||
- 2026-09-06 — Plan 33A research is merged without renderer code. Plan 33B remains behind explicit visual/product approval
|
|
||||||
for canonical SVG direction, neutral naming, supplied fluency labels, chronology gutter/date wrapping, and photo-free defaults.
|
|
||||||
- 2026-09-06 — Hosted baseline E2E for merged PR #3483 exposed that destructive geometry diagnostics threw when their
|
|
||||||
opt-in flag was absent. PR #3484 added suite-level default-off skips without changing the matrix or database safety guard.
|
|
||||||
|
|
||||||
## Publication log
|
|
||||||
|
|
||||||
- Planning package: PR [#3455](https://github.com/reactive-resume/reactive-resume/pull/3455), merged as
|
|
||||||
`42527ad83bdc5720bd76eef9c9da102384f21f6f` after final Plan 10 not-planned corrections and independent rereview.
|
|
||||||
- Coordinator ledger: PR [#3456](https://github.com/reactive-resume/reactive-resume/pull/3456), merged as
|
|
||||||
`981d7581f5ccb7eb02177f7f449a20fe5aee3976` after completion audit and focused rereview.
|
|
||||||
- Final ledger refresh: PR [#3485](https://github.com/reactive-resume/reactive-resume/pull/3485) records the terminal
|
|
||||||
execution snapshot; its own eventual merge commit is intentionally left to GitHub state to avoid a self-referential update.
|
|
||||||
- Merged 2026-09-06 after exact-head revalidation: #3453 (`a4bdc54b2`), #3454 (`2e711fd14`),
|
|
||||||
#3457 (`ee52636c1`), #3458 (`772bf1452`), #3459 (`8c5804ed0`), #3460 (`549135bb3`),
|
|
||||||
#3462 (`5850230f8`), #3461 (`ab67831e4`), #3464 (`999cd618c`), #3466 (`695cdb851`), and
|
|
||||||
#3465 (`b85d285b6`), #3467 (`0fbeeeb4c`), #3468 (`38832014b`), #3469 (`870388192`), and
|
|
||||||
#3470 (`744eaa902`), #3471 (`a6057abd7`), #3472 (`ea97de5ec`), #3473 (`2a4a1583b`), and
|
|
||||||
#3474 (`97f34b7cc`), #3475 (`578cb496a`), #3476 (`66c25efe1`), #3477 (`368858a56`), and
|
|
||||||
#3478 (`5e8284e49`), #3479 (`f783908b0`), #3480 (`cdb7bdd2f`), #3481 (`3e62a1d60`), and
|
|
||||||
#3482 (`10eb3bdbc`), #3483 (`11d619d3d`), and CI regression fix #3484 (`778fd4b7d`).
|
|
||||||
- Plan 10: PR [#3463](https://github.com/reactive-resume/reactive-resume/pull/3463), closed unmerged by maintainer direction;
|
|
||||||
issue [#2836 comment](https://github.com/reactive-resume/reactive-resume/issues/2836#issuecomment-5556080394) records not-planned rationale.
|
|
||||||
- Plan 16: PR [#3464](https://github.com/reactive-resume/reactive-resume/pull/3464), merged as
|
|
||||||
`999cd618cb2e54826aa860c298c7114045f8ebdd` after hosted checks passed.
|
|
||||||
- Plan 32: PR [#3465](https://github.com/reactive-resume/reactive-resume/pull/3465), merged as
|
|
||||||
`b85d285b69843612e9d7f0ab802248982e7bf0ea` after latest-main integration and hosted approval.
|
|
||||||
- Recovery hash hotfix: PR [#3466](https://github.com/reactive-resume/reactive-resume/pull/3466), merged as
|
|
||||||
`695cdb851431a0fe7a17b05bbd9630129fdd0759`; canonical fixtures refreshed after Plan 15A changed default bytes.
|
|
||||||
- Plan 23A: PR [#3467](https://github.com/reactive-resume/reactive-resume/pull/3467), merged as
|
|
||||||
`0fbeeeb4c48993333384e4d31a0950ab8bf91b01`; relevant E2E passed and unrelated baseline flakes were documented.
|
|
||||||
- Plan 28: PR [#3468](https://github.com/reactive-resume/reactive-resume/pull/3468), merged as
|
|
||||||
`38832014b969580d217be268257040ffc4f95ff6`; gradients remain unsupported and issue #3137 stays open.
|
|
||||||
- Plan 22: PR [#3469](https://github.com/reactive-resume/reactive-resume/pull/3469), merged as
|
|
||||||
`870388192e38a34e70ba036027b20e3ec789dba0`; generated references and recovery hashes were refreshed and rereviewed.
|
|
||||||
- Plan 08: PR [#3470](https://github.com/reactive-resume/reactive-resume/pull/3470), merged as
|
|
||||||
`744eaa902eeb90ea0473cf84549d71f51e7beab5`; broader custom-domain/TLS issue scope remains open.
|
|
||||||
- Plan 35: PR [#3471](https://github.com/reactive-resume/reactive-resume/pull/3471), merged as
|
|
||||||
`a6057abd7951a05c47b4785114f3e5eff670066e`; historical issue #2768 remains open/needs-info.
|
|
||||||
- Plan 19: PR [#3472](https://github.com/reactive-resume/reactive-resume/pull/3472), merged as
|
|
||||||
`ea97de5ec4e9adced7e83d6ff02e208727961887`; issue #3397 closed.
|
|
||||||
- Plan 34: PR [#3473](https://github.com/reactive-resume/reactive-resume/pull/3473), merged as
|
|
||||||
`2a4a1583be097290906a1252045c57e73b78b1a9`; issue #2611 closed.
|
|
||||||
- Plan 19 static-analysis follow-up: PR [#3474](https://github.com/reactive-resume/reactive-resume/pull/3474), merged as
|
|
||||||
`97f34b7ccda73eb8d767205741d68465cbc1c0c1`.
|
|
||||||
- Plan 33A: PR [#3475](https://github.com/reactive-resume/reactive-resume/pull/3475), merged as
|
|
||||||
`578cb496aa326751dd2ab1bea53db868daa35908`; issue #2689 remains open at its explicit visual/product gate.
|
|
||||||
- Imported-table E2E repair: PR [#3476](https://github.com/reactive-resume/reactive-resume/pull/3476), merged as
|
|
||||||
`66c25efe18775f91640e66d1b2c6868bc70f0b55`; exact Plan 16 table topology remains enforced.
|
|
||||||
- Plan 21: PR [#3477](https://github.com/reactive-resume/reactive-resume/pull/3477), merged as
|
|
||||||
`368858a56fc9c3152b540c39829908e2c3ea04c5`; issue #3060 closed.
|
|
||||||
- Plan 23B: PR [#3478](https://github.com/reactive-resume/reactive-resume/pull/3478), merged as
|
|
||||||
`5e8284e49fcca1fc0d56660205872038bd307c84`; issue #3350 remains open because installed renderer clips oversized
|
|
||||||
non-wrapping items.
|
|
||||||
- Plan 27A: PR [#3479](https://github.com/reactive-resume/reactive-resume/pull/3479), merged as
|
|
||||||
`f783908b0e0054869e4ecdae3f4cad75402e2fd5`; issue #3377 remains open pending controlled cold-server egress proof.
|
|
||||||
- Plan 24A: PR [#3480](https://github.com/reactive-resume/reactive-resume/pull/3480), merged as
|
|
||||||
`cdb7bdd2fe06d491eb57f8c1aa93fa48011acdb5`; #3155/#2841 remain open pending numeric date-column geometry.
|
|
||||||
- Plan 31 HTML residual: PR [#3481](https://github.com/reactive-resume/reactive-resume/pull/3481), merged as
|
|
||||||
`3e62a1d604041e3769a7fc8f5b2ec8224d0663a4`; #2844 remains open for public/export/manual accessibility evidence.
|
|
||||||
- Plan 30 diagnostic: PR [#3482](https://github.com/reactive-resume/reactive-resume/pull/3482), merged as
|
|
||||||
`10eb3bdbc77a903a792f1e6bfa7cba0db9dea130`; #2845 remains open pending vendor-side evidence, and current measurements do not justify a preset.
|
|
||||||
- Plan 18 diagnostic: PR [#3483](https://github.com/reactive-resume/reactive-resume/pull/3483), merged as
|
|
||||||
`11d619d3d9c7da87bb38463147a7aac4bc35b092`; #2683 remains open pending original JSON/browser evidence, and no synthetic mismatch justified a runtime change.
|
|
||||||
- Geometry E2E CI regression: PR [#3484](https://github.com/reactive-resume/reactive-resume/pull/3484), merged as
|
|
||||||
`778fd4b7d9a2de07852f32d87713c4a6187cf4d4`; ordinary baseline runs skip both destructive diagnostics unless explicitly opted in.
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# Address second independent review: plan 02
|
|
||||||
|
|
||||||
Read `.orchestration/plan-02-rereview.md`, pinned approved plan 02, current `AGENTS.md`, RTK, and applicable
|
|
||||||
receiving-code-review/TDD/documentation skills. Reproduce the remaining finding before editing.
|
|
||||||
|
|
||||||
Use strict TDD. Add failing regressions first, then make the smallest conservative fix within the original four-file scope:
|
|
||||||
|
|
||||||
- For non-string object input, validate the supplied object in its original form before any serialization. Schema-invalid
|
|
||||||
objects must return a deterministic blocked manifest and must never produce `no-op`.
|
|
||||||
- Cover at least a boxed string (`new String("")`) that serializes to a valid primitive and a schema-invalid object with a
|
|
||||||
custom `toJSON`. Assert the latter is not executed before validation when the input is invalid.
|
|
||||||
- Keep JSON-text behavior: parse text, validate the parsed value exactly, and retain raw-versus-parsed normalization checks.
|
|
||||||
- Preserve target presence invariants, strict source/target validation, deterministic hashes and reasons, pure/non-networked/
|
|
||||||
non-writing behavior, and existing documentation claims. Change docs only if implementation makes a current sentence false.
|
|
||||||
|
|
||||||
Do not access private data, add DB/filesystem writes, implement legacy conversion, widen scope, or rewrite prior reports.
|
|
||||||
Run focused RED/GREEN tests, tooling typecheck, relevant API/auth checks, boundaries, narrow Biome/Markdown when applicable,
|
|
||||||
diff and four-file scope gates. Add a normal follow-up commit.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-02-review-fix-round2.md` with reproduction, RED/GREEN, exact commit/files, commands/results,
|
|
||||||
skipped gates, and remaining risks. Do not push/open PR/merge/mutate issues/spawn subagents. Final response at most ten lines.
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
# Address third independent review: plan 02
|
|
||||||
|
|
||||||
Read `.orchestration/plan-02-rereview-round3.md`, pinned approved plan 02, current `AGENTS.md`, RTK, and applicable
|
|
||||||
receiving-code-review/TDD/documentation skills. Reproduce both findings before editing.
|
|
||||||
|
|
||||||
Use strict TDD. Narrow comparator public input contract to one serialized JSON request string. This is approved engineering
|
|
||||||
correction: object-envelope inputs are no longer accepted. A non-string runtime argument must be rejected immediately,
|
|
||||||
before any property access, schema parsing, serialization, accessor call, proxy trap, or caller method. Keep returned
|
|
||||||
manifest deterministic and add an explicit stable invalid-input reason/identity convention where needed.
|
|
||||||
|
|
||||||
After parsing the primitive string with `JSON.parse`, strictly validate complete envelope before hashing:
|
|
||||||
|
|
||||||
- non-empty string case/source IDs and either null or non-empty string target ID;
|
|
||||||
- literal booleans for all three safety flags; named gate failures still apply only to valid `false` values, while truthy
|
|
||||||
non-booleans are invalid input;
|
|
||||||
- required JSON-compatible source/target values with target presence invariant retained;
|
|
||||||
- no unknown envelope keys, executable/non-JSON values, NaN/Infinity, or lossy envelope normalization.
|
|
||||||
|
|
||||||
Parsed JSON creates inert data; retain exact current-v5 resume validation, raw-vs-schema canonical equality checks,
|
|
||||||
deterministic SHA-256 hashes, no-op/export-copy semantics, and pure/no-network/no-write/no-output behavior. Update migration
|
|
||||||
docs to say comparator accepts a serialized comparison request only, not object arguments.
|
|
||||||
|
|
||||||
RED regressions must cover each reported bypass: string `"false"` for each safety flag; numeric/empty IDs; top-level
|
|
||||||
accessor/proxy objects with zero getter/trap calls; schema-valid changing getters if passed as object; malformed request
|
|
||||||
JSON/NaN; distinct non-JSON-versus-null identity; all prior boxed-string/custom-`toJSON`/template/target mismatch cases.
|
|
||||||
Adapt success tests to serialized request input. Prefer a small strict Zod input schema if available through public package
|
|
||||||
exports; avoid custom recursive proxy detection because narrowing to JSON text removes that surface.
|
|
||||||
|
|
||||||
Run focused RED/GREEN tests, tooling typecheck, relevant API/auth checks and typechecks, boundaries, narrow Biome/Markdown,
|
|
||||||
static import, diff/four-file scope gates. Add normal follow-up commit. Write `.orchestration/plan-02-review-fix-round3.md`
|
|
||||||
with reproductions, RED/GREEN, exact commit/files, commands/results, skipped gates, risks. Do not push/open PR/merge/mutate
|
|
||||||
issues/spawn subagents. Final response at most ten lines.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Address independent review: plan 02
|
|
||||||
|
|
||||||
Read `.orchestration/plan-02-review.md`, pinned approved plan 02, current `AGENTS.md`, RTK, and applicable
|
|
||||||
receiving-code-review/TDD/documentation skills. Verify each finding with direct probes before editing. Three findings are
|
|
||||||
provisionally accepted: lossy schema fallback can yield false no-op, target ID/data states can contradict, and migration docs
|
|
||||||
overstate raw v4 JSON support.
|
|
||||||
|
|
||||||
Use strict TDD. Add failing regressions first, then smallest conservative fixes within original four-file scope:
|
|
||||||
|
|
||||||
- A source with schema-invalid value that current Zod `.catch` would normalize to target must never return `no-op`. Validate
|
|
||||||
without accepting lossy coercion/default mutation, or hash validated raw canonical input while explicitly detecting and
|
|
||||||
blocking lossy schema changes. Prefer safe false-block/export over false no-op. Cover source and target variants.
|
|
||||||
- Enforce target presence invariant: target data and target resume ID are either both absent or both present. Encode a
|
|
||||||
discriminated input contract where practical and keep runtime validation for untyped callers. Both mismatch directions
|
|
||||||
return deterministic blocked manifest with named reason; no contradictory target hash/ID.
|
|
||||||
- Clarify migration guide: only JSON text already conforming exactly to current v5 resume-data schema is accepted; raw v4
|
|
||||||
exports are unsupported; comparator performs no conversion; historical converter review remains separate prerequisite.
|
|
||||||
- Add sentence that hashes prove content equality only, never ownership/source authenticity/recipient identity.
|
|
||||||
|
|
||||||
Preserve pure/non-networked/non-writing behavior and deterministic manifest. Do not access private data, add DB/filesystem
|
|
||||||
writes, implement legacy conversion, widen scope, or rewrite prior reports. Run focused RED/GREEN tests, tooling typecheck,
|
|
||||||
relevant API/auth checks, boundaries, narrow Biome/Markdown, diff and four-file scope gates. Add normal follow-up commit.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-02-review-fix.md` with probes, RED/GREEN, exact commit/files, commands/results, skipped gates, and
|
|
||||||
remaining risks. Do not push/open PR/merge/mutate issues/spawn subagents. Final response at most ten lines.
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# Plan 07 late hosted-review fix
|
|
||||||
|
|
||||||
Work in `/Users/amruth/orca/workspaces/reactive-resume/issue-2722-postgres-docs` on
|
|
||||||
`codex/issue-2722-postgres-docs`. Current remote/local head should be
|
|
||||||
`a7b8c4cc5754c1249534d0ed8993e67e57e2cb87`; PR #3457 stays open and unmerged.
|
|
||||||
|
|
||||||
Read current instructions, pinned approved Plan 07, all implementation/review reports, complete `origin/main...HEAD`
|
|
||||||
diff, and live PR thread `PRRT_kwDODuah5s6fnaBa` / comment `3942253797`. Revalidate current base/head/checks first.
|
|
||||||
Run root Intent inventory before edit. Do not spawn subagents.
|
|
||||||
|
|
||||||
Finding: numbered update flow is correctly introduced as image-quickstart-only, and repository alternative already uses
|
|
||||||
`reactive_resume`; however repository users following that alternative can continue to image-only step 4 and run logs
|
|
||||||
against nonexistent `reactive-resume`. Make path separation unmistakable and provide correct repository log command
|
|
||||||
`docker compose logs -f reactive_resume` adjacent to its build update. Preserve image commands and app-only `--no-deps`
|
|
||||||
policy; do not widen into dependency lifecycle or PostgreSQL upgrade changes.
|
|
||||||
|
|
||||||
Use minimal docs edit. Validate both Compose service names/commands against current files, run Compose dry-runs,
|
|
||||||
PostgreSQL/docs gates proportionate to changed claim, Markdown/link/command/diff/exact-scope checks. Commit locally with
|
|
||||||
normal message. Do not push, reply, resolve thread, mutate issue, or merge. Write
|
|
||||||
`.orchestration/plan-07-hosted-review-fix-round2.md` and send worker_done.
|
|
||||||
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# Address independent review: plan 07
|
|
||||||
|
|
||||||
Read `.orchestration/plan-07-review.md`, approved plan 07 from pinned planning head, current `AGENTS.md`, RTK, and applicable
|
|
||||||
documentation/receiving-code-review skills. Verify finding against current diff and runtime files. Finding is provisionally
|
|
||||||
accepted: current update recipe pulls all Compose services while `postgres:latest` can cross major versions, contradicting
|
|
||||||
separate-upgrade guidance.
|
|
||||||
|
|
||||||
Edit only `docs/self-hosting/docker.mdx`. Make normal app update path pull and recreate only `reactive-resume`; do not pull or
|
|
||||||
recreate PostgreSQL as part of app update. Direct database updates to separately chosen major-pinned image/provider upgrade
|
|
||||||
procedure with backup/restore verification. Preserve existing app migration explanation and two-service topology. Do not add
|
|
||||||
unsafe generic PostgreSQL commands, Compose/runtime changes, or new scope.
|
|
||||||
|
|
||||||
Rerun focused update/PostgreSQL `rg`, Compose config, two-doc Markdown lint, internal-link inspection, `git diff --check`,
|
|
||||||
and name-only scope. Amend or add a normal follow-up commit; do not rewrite reviewer report. Write
|
|
||||||
`.orchestration/plan-07-review-fix.md` with exact commit, change, commands/results, skipped gates, and remaining risk. Do not
|
|
||||||
push, open PR, merge, mutate issues, or spawn subagents. Final response at most ten lines.
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# Address plan 09 hosted-review follow-up finding
|
|
||||||
|
|
||||||
Read `.orchestration/plan-09-hosted-review-rereview.md`, pinned approved plan 09, current `AGENTS.md`, RTK, and applicable
|
|
||||||
receiving-code-review/documentation skills. Verify finding in a disposable two-revision local repository before editing.
|
|
||||||
|
|
||||||
Make smallest one-guide correction: add explicit plain-Git command that saves selected earlier `resume.json` revision to
|
|
||||||
a new importable filename such as `recovered-resume.json`, without overwriting current `resume.json`. Direct following
|
|
||||||
dashboard instruction to that recovered file. Preserve selected-commit log/show workflow, privacy warning, import-as-new
|
|
||||||
behavior, no remote/push/global config/sync/destructive replacement, and two-doc base scope.
|
|
||||||
|
|
||||||
Rerun synthetic two-revision workflow and prove selected earlier content exists in recovered file while current
|
|
||||||
`resume.json` remains current. Run 152 focused tests, Markdown lint, forbidden-command/link/diff/scope gates. Commit normal
|
|
||||||
follow-up. Write `.orchestration/plan-09-hosted-review-fix-round2.md` with verification, exact commit/files, commands/results,
|
|
||||||
skips, risks. Do not push/merge/resolve threads/mutate issues/spawn subagents. Final response at most ten lines.
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# Address independent review: plan 09
|
|
||||||
|
|
||||||
Read `.orchestration/plan-09-review.md`, pinned plan 09, current `AGENTS.md`, RTK, and applicable
|
|
||||||
receiving-code-review/documentation skills. Verify two findings against current source before editing; both are provisionally
|
|
||||||
accepted.
|
|
||||||
|
|
||||||
Edit only two approved guide files:
|
|
||||||
|
|
||||||
- In export guide, scope “excludes cover letter sections” to rendered PDF/DOCX/Markdown output and state JSON retains
|
|
||||||
embedded cover-letter custom sections. Keep distinction from independent cover-letter JSON.
|
|
||||||
- In version-history guide, do not claim template changes create independent checkpoints. Describe template changes as
|
|
||||||
ordinary editing covered by throttled snapshots; retain accurate explicit import/AI/API/restore checkpoint statements.
|
|
||||||
|
|
||||||
Do not alter local Git workflow, add runtime code, promise whole-account restore/sync, or widen scope. Rerun focused source
|
|
||||||
checks, API/import/schema/web tests if wording relies on them, Markdown lint, synthetic Git workflow as needed,
|
|
||||||
`git diff --check`, and two-file scope gate. Add normal follow-up commit. Write `.orchestration/plan-09-review-fix.md` with
|
|
||||||
exact commit/files, verified source facts, commands/results, skipped gates, and risks. Do not push/open PR/merge/mutate
|
|
||||||
issues/spawn subagents. Final response at most ten lines.
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# Address independent review: plan 11
|
|
||||||
|
|
||||||
Read `.orchestration/plan-11-review.md`, pinned approved plan 11, current `AGENTS.md`, RTK, and applicable
|
|
||||||
receiving-code-review/documentation skills. Verify both findings against current source before editing.
|
|
||||||
|
|
||||||
Make smallest documentation-only corrections within existing three-file scope:
|
|
||||||
|
|
||||||
- Remove unqualified attachment promise from changelog. Prefer saying users can paste a job description there; keep exact
|
|
||||||
supported attachment details in guide where current MIME/direct-file qualifications already exist.
|
|
||||||
- Remove adjacent duplication of review/restore/rollback guidance in `using-ai-agent.mdx`. Keep concise workflow step and
|
|
||||||
point to existing `Review patches` section, retaining full behavior explanation only once.
|
|
||||||
|
|
||||||
Preserve verified history, current provider capability boundary, isolated AI Draft behavior, no invented removal motive,
|
|
||||||
and no promise of paid JSearch restoration. Run focused source probes, Markdown lint, relevant targeted tests if claims
|
|
||||||
changed, link/diff/three-file scope gates. Add normal follow-up commit.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-11-review-fix.md` with finding verification, exact commit/files, commands/results, skipped gates,
|
|
||||||
and remaining risks. Do not push/open PR/merge/mutate issues/spawn subagents. Final response at most ten lines.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Fix review findings: Plan 16 editable imported tables
|
|
||||||
|
|
||||||
Work only in `/Users/amruth/orca/workspaces/reactive-resume/issue-3196-editable-tables` from exact head
|
|
||||||
`83aca184e4eeb3a9ded36c1f222adf695ef6ca98`. Read current repository instructions, pinned approved Plan 16,
|
|
||||||
implementation report, and `.orchestration/plan-16-review.md` completely. Revalidate `origin/main`, issue #3196, and
|
|
||||||
overlap before editing. Run root Intent inventory and load matching local skills. No subagents.
|
|
||||||
|
|
||||||
Fix every review finding with TDD while preserving existing supported-table behavior:
|
|
||||||
|
|
||||||
1. Make unsupported-table detection fail closed before destructive Tiptap normalization. Conservatively reject any cell
|
|
||||||
descendant element/attribute/structure that cannot round-trip through configured editor schema. Cover at least a nested
|
|
||||||
`section` with `aria-label`, multiple `tbody` groups, malformed/repaired table markup, and other representative
|
|
||||||
unrepresented descendants. Ordinary edits, prop updates, lock/keyboard paths, and cancel must preserve exact original
|
|
||||||
HTML bytes for these cases. Do not widen unsafe HTML support.
|
|
||||||
2. Follow pinned Plan 16: destructive conversion UX is outside this repair. Remove the `Convert to editable text` action
|
|
||||||
and `usePrompt` path entirely rather than inventing raw-text or normalized-HTML semantics. Test that unsupported content
|
|
||||||
exposes only an accessible read-only notice and no conversion affordance or ordinary interaction can overwrite it.
|
|
||||||
3. Strengthen authenticated E2E so an unrelated Basics-name edit proves a real save transition and survives reload before
|
|
||||||
table assertions. Avoid accepting a stale pre-existing Saved status.
|
|
||||||
|
|
||||||
Also preserve #3438 cases, supported 2x3 editing/paste/undo/redo/persistence, CSS precedence, PDF border geometry,
|
|
||||||
dependency minimality, SSR/accessibility, and package boundaries. Keep product commit scope to intended implementation,
|
|
||||||
tests, dependency/lockfile, and mandated orchestration report; do not push, publish, reply, or mutate issues.
|
|
||||||
|
|
||||||
Run focused and broad web/PDF tests, affected typechecks, boundaries, frozen install, production build, narrow non-writing
|
|
||||||
Biome, diff/scope gates, and dedicated isolated authenticated E2E. Commit fixes locally. Write findings resolved, RED/GREEN
|
|
||||||
evidence, exact head, test results, and remaining limits to `.orchestration/plan-16-review-fix.md`; send `worker_done`.
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# Implement plan 02 synthetic recovery procedure
|
|
||||||
|
|
||||||
Read first:
|
|
||||||
|
|
||||||
1. Entire approved plan from local planning checkout only when its HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use pinned `git show` fallback below.
|
|
||||||
2. Audit evidence: `/Users/amruth/orca/workspaces/reactive-resume/codex-audit-backend-01-06/.orchestration/revalidate-backend-01-06.md`, plan 02 section.
|
|
||||||
3. Current worktree `AGENTS.md`, referenced issue/domain instructions, relevant ADRs, `/Users/amruth/.codex/RTK.md`.
|
|
||||||
4. `/Users/amruth/.agents/skills/test-driven-development/SKILL.md`, its `writing-good-tests.md` reference,
|
|
||||||
`/Users/amruth/.agents/skills/karpathy-guidelines/SKILL.md`, and
|
|
||||||
`/Users/amruth/.agents/skills/documentation-writer/SKILL.md`.
|
|
||||||
|
|
||||||
Portable plan fallback: fetch PR #3455 and use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/02-hosted-v4-account-recovery.md`.
|
|
||||||
|
|
||||||
Implement only synthetic, non-networked recovery support. This is partial support for #3181, not production recovery; #2760
|
|
||||||
remains a separate diagnostic. Do not access private/production data, spawn subagents, touch coordinator ledger/other
|
|
||||||
worktrees, merge, push, create PR, or mutate issues.
|
|
||||||
|
|
||||||
Required execution:
|
|
||||||
|
|
||||||
- Confirm clean worktree; fetch current `origin/main`; rename branch `codex/issue-3181-recovery-procedure`; verify exact base;
|
|
||||||
run plan drift command; fetch live #3181/#2760 evidence; run root intent discovery.
|
|
||||||
- Modify only `docs/self-hosting/migration.mdx`, `docs/guides/accessing-the-previous-version.mdx`, and new
|
|
||||||
`tooling/recovery/compare-resume.ts` plus `tooling/recovery/compare-resume.test.ts`. Stop if another file is truly required
|
|
||||||
and report before broadening.
|
|
||||||
- TDD tooling strictly: write focused behavior test, run it and capture expected missing-feature failure, implement minimum,
|
|
||||||
rerun green, then refactor. Tests must exercise real function and hand-derived outcomes/hashes; no tautological helpers or
|
|
||||||
mock assertions.
|
|
||||||
- Tool accepts already-exported JSON/current resume-shaped data only; no database URL or writes. Produce deterministic dry-run
|
|
||||||
manifest with synthetic IDs, source/target hashes, and outcome `no-op`, `export-copy`, or `blocked`. Cover identical,
|
|
||||||
old-only/divergent, owner-unverified/mapping-missing, unavailable snapshot, invalid source/target JSON, determinism, and no
|
|
||||||
input mutation. Use current schema/default exports through package public exports. Do not invent legacy fields or convert v4.
|
|
||||||
- Docs specify per-owner case record, source snapshot time, owner verification, target ID, content hash, proposed outcome,
|
|
||||||
default private export, separate copy/no overwrite, hosted-vs-self-hosted authority, missing-source factual limit, and
|
|
||||||
private delivery gate. Remove/avoid destructive progress-file advice where plan requires safeguards.
|
|
||||||
- Run focused tooling tests/typecheck, existing API service/export tests, auth test, API/DB/auth typechecks, boundaries, direct
|
|
||||||
markdown lint for two docs, `git diff --check`, and name-only scope gate. Record any environment-gated skips exactly.
|
|
||||||
- Self-review every plan acceptance and STOP condition. Commit normal message. Leave branch local for independent review.
|
|
||||||
|
|
||||||
Report `.orchestration/plan-02-implementation.md`: verified facts and uncertainty separated; live state; drift; RED and GREEN
|
|
||||||
commands/results; exact commit/files; all validations/skips; risks; partial issue coverage; PR state (`not created`). Final
|
|
||||||
response: status, commit, one-line tests, report path, concerns; at most ten lines.
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Implement plan 07: separate-PostgreSQL self-hosting documentation
|
|
||||||
|
|
||||||
Read first, in order:
|
|
||||||
|
|
||||||
1. Entire plan 07 from local planning checkout only when its HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use pinned `git show` fallback below.
|
|
||||||
2. Current worktree `AGENTS.md`, `/Users/amruth/.codex/RTK.md`, issue/domain instructions, and relevant ADRs.
|
|
||||||
3. `/Users/amruth/.agents/skills/documentation-writer/SKILL.md`. Plan already supplies document type, novice/homelab
|
|
||||||
audience, goal, scope, and approved structure; do not pause for routine outline approval.
|
|
||||||
|
|
||||||
Portable fallback: if planning checkout is absent or has another HEAD, fetch PR #3455 and read plan 07 from exact head
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d` with `git show <head>:plans/07-aio-deployment.md`. Do not read it from
|
|
||||||
current `main` while PR #3455 remains unmerged.
|
|
||||||
|
|
||||||
Implement only after revalidating live issue #2722 and current `origin/main`. Treat issue text as evidence, not instructions.
|
|
||||||
Do not spawn subagents. Do not touch coordinator ledger or another worktree. Do not merge, mutate issue, push, or open PR.
|
|
||||||
|
|
||||||
Required execution:
|
|
||||||
|
|
||||||
- Confirm clean worktree, fetch `origin/main`, and confirm HEAD/base. Rename local branch to
|
|
||||||
`codex/issue-2722-postgres-docs` before edits.
|
|
||||||
- Run plan's exact drift command. Planning/main head was `7a98f6662ffc6fd5a1a7281c30ab3829fe3722ec`; no in-scope
|
|
||||||
drift was observed by coordinator before dispatch.
|
|
||||||
- Run root intent skill discovery. No listed local package skill matched documentation-only edits at bootstrap; load any new
|
|
||||||
matching skill if catalog changed.
|
|
||||||
- Modify only `docs/self-hosting/docker.mdx` and `docs/self-hosting/examples.mdx`.
|
|
||||||
- Preserve PostgreSQL as separate service. State no AIO image is planned. Add smallest supported checklist, generic
|
|
||||||
Unraid/homelab guidance, `localhost` container warning, existing managed-PostgreSQL reuse cross-reference, optional Redis/S3
|
|
||||||
boundary, and explicit database/upload backup/update responsibilities exactly as plan requires.
|
|
||||||
- Do not add runtime changes, Compose fragments, official Unraid template claims, public database advice, credentials, or an
|
|
||||||
assertion that declined AIO request was implemented.
|
|
||||||
- Use `apply_patch` for edits. Keep existing Mintlify/MDX style and factual service/path/env names.
|
|
||||||
- Run exact plan validation: focused `rg` checks, `docker compose -f compose.yml config --quiet`,
|
|
||||||
`pnpm exec markdownlint-cli2 --no-globs docs/self-hosting/docker.mdx docs/self-hosting/examples.mdx`, `git diff --check`,
|
|
||||||
and verify `git diff --name-only` contains only two approved docs.
|
|
||||||
- Self-review against every acceptance criterion. Commit with normal message. Do not push or create PR; independent review
|
|
||||||
follows.
|
|
||||||
|
|
||||||
Write report to `.orchestration/plan-07-implementation.md` containing verified facts and uncertainty separately: live issue
|
|
||||||
state, drift result, chosen documentation structure, exact commit SHA, files, commands/results, skipped gates, risks, coverage,
|
|
||||||
and PR status (`not created`). Final response: status, commit, one-line validation summary, report path, concerns; no more than
|
|
||||||
ten lines.
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
# Implement plan 09: user-controlled Git backup documentation
|
|
||||||
|
|
||||||
Read entire approved plan first from local planning checkout only when its HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use pinned `git show` fallback below. Then read current
|
|
||||||
worktree `AGENTS.md`, referenced domain/issue instructions, relevant ADRs, `/Users/amruth/.codex/RTK.md`, and
|
|
||||||
`/Users/amruth/.agents/skills/documentation-writer/SKILL.md`. Plan supplies document type, audience, goal, scope, and approved
|
|
||||||
structure; do not pause for routine outline approval. Portable fallback: fetch PR #3455 and use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/09-external-version-backup.md`.
|
|
||||||
|
|
||||||
Do not spawn subagents. Do not touch coordinator ledger or another worktree. Do not merge, mutate issues, push, or create PR.
|
|
||||||
|
|
||||||
Required execution:
|
|
||||||
|
|
||||||
- Confirm clean worktree, fetch current `origin/main`, rename branch `codex/issue-2705-git-backup-docs`, run exact drift
|
|
||||||
command, fetch live #2705 body/comments/state/linked PRs, and run root intent discovery before edits.
|
|
||||||
- Revalidate current export shapes and history semantics from exact source/tests. Stop if exporter lacks required document
|
|
||||||
types or synthetic restore loses content; do not broaden into runtime fixes.
|
|
||||||
- Modify only `docs/guides/exporting-your-resume.mdx` and
|
|
||||||
`docs/guides/undoing-changes-and-version-history.mdx`.
|
|
||||||
- Document single-resume JSON, independent cover-letter JSON, and account archive differences; stable filenames; image URL
|
|
||||||
availability; private-data/repository-visibility warning; local-only Git workflow; non-destructive import-as-new recovery;
|
|
||||||
rolling in-app versions versus owner-managed Git. Correct planning prose before publication: every user-facing code block
|
|
||||||
must use ordinary `git init`, `git add -- ...`, `git diff`, `git commit`, and `git show`. Never publish agent-only
|
|
||||||
`rtk proxy git` commands. No automatic sync, credentials, remote URL, `git push`, whole-account restore promise, or new
|
|
||||||
product UI.
|
|
||||||
- Use only synthetic data. Run API export/version tests specified by plan. Validate single-resume import round-trip using
|
|
||||||
existing synthetic fixtures/tests or a bounded disposable test; never use private data.
|
|
||||||
- Execute command sequence in `mktemp -d`, staging only `resume.json` and `cover-letter.json`; show changed visible field in
|
|
||||||
`git diff`. Executor shell may wrap validation with `rtk proxy`, but copied documentation commands must remain plain Git.
|
|
||||||
Do not modify global Git config if identity missing; record limitation.
|
|
||||||
- Run plan's focused `rg`, markdown lint, `git diff --check`, and two-file name-only gate. Self-review every acceptance item.
|
|
||||||
- Commit with normal message. Leave branch local for independent review.
|
|
||||||
|
|
||||||
Write report `.orchestration/plan-09-implementation.md`: verified facts vs uncertainty, live state, drift, exact commit/files,
|
|
||||||
commands/results, fixture details, skipped gates, risks, issue coverage, PR status (`not created`). Final response: status,
|
|
||||||
commit, one-line tests, report path, concerns; at most ten lines.
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# Implement plan 10: prospective retired-link attempt notices
|
|
||||||
|
|
||||||
Read approved plan 10 from local planning checkout only when HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/10-legacy-link-routing.md`. Read current `AGENTS.md`, RTK,
|
|
||||||
issue/domain/DB migration guidance, ADRs, and applicable brainstorming/TDD skills. Approved prospective direction and routine
|
|
||||||
limits bind; do not ask them again. Run root Intent inventory and load matching local skill before edits. Do not spawn
|
|
||||||
subagents, touch ledger, mutate issues, merge, push, or create PR.
|
|
||||||
|
|
||||||
Start clean from refreshed `origin/main`, rename branch `codex/issue-2836-retired-link-notices`, revalidate issue 2836/open
|
|
||||||
PRs/current source, and run exact drift. Historical cause remains unknown: reference issue without closing keyword. Scope is
|
|
||||||
future slug changes under unchanged current username, 90 days, newest 50 per resume, aggregate owner-only attempts, safe 404;
|
|
||||||
no redirects, email/push, backfill, username history, visitor identity, or historical recovery.
|
|
||||||
|
|
||||||
Strict TDD, one coherent migration/API/UI/docs/E2E unit:
|
|
||||||
|
|
||||||
- Add additive `resume_retired_link` table: generated ID, cascading owner/resume FKs, retired username/slug/timestamp,
|
|
||||||
aggregate attempt count and nullable last attempt. Unique username+slug, resume+retired index. No IP, UA, email, content.
|
|
||||||
Generate one migration via repository workflow against disposable PostgreSQL only; review SQL/snapshot for no drops/rewrites.
|
|
||||||
- Add pure/service tests before code for old-path capture inside existing locked slug transaction, unchanged slug no-op,
|
|
||||||
rollback atomicity, prune expired and beyond newest 50, live-path reuse removal, same-owner reuse, current-route priority,
|
|
||||||
renamed username/deleted/private/expired/competing route denial.
|
|
||||||
- On no-current-row lookup only, recognize valid retired path, best-effort increment outside rolled-back NOT_FOUND transaction,
|
|
||||||
and always return original indistinguishable 404. Owner excluded. Separate one-hour dedup with hard 50,000 active-entry cap:
|
|
||||||
prune expired then evict oldest. Unknown probes allocate nothing. Count failure remains 404 and never increments views.
|
|
||||||
- Protected owner listing only: verify resume ownership, lazy expiry prune, at most 50 sanitized newest-first records. Preserve
|
|
||||||
existing statistics response shapes. Another owner/missing resume denied.
|
|
||||||
- Owner Statistics UI: empty state omitted; recorded paths show aggregate count/last attempt and explicit prospective
|
|
||||||
90-day/50-path/same-username limits. No visitor data or notification toggle. Lingui messages and focused DOM tests.
|
|
||||||
- Public sharing guide documents exact limits. No public route response change needed.
|
|
||||||
- E2E with disposable accounts/DB: rename first→second, anonymous old 404 increments once with dedup, live new path view
|
|
||||||
independent, owner old-path excluded, cross-owner denied, private/deleted/expired no leak, live reuse wins/removes retired
|
|
||||||
attribution. Correct audit gate: run `public-sharing.spec.ts`, not unrelated dashboard `resume-views.spec.ts`.
|
|
||||||
|
|
||||||
Use `.env.retired-links-test.local` only if it is clearly disposable; never production DB. Run RED/GREEN DB/API/web tests,
|
|
||||||
migration generate/apply fresh and populated upgrade, DB/API/web typechecks, translation extraction, boundaries, build,
|
|
||||||
focused E2E plus public-sharing, narrow non-writing Biome, and diff/migration/scope review. Disclose/inspect write-capable
|
|
||||||
`pnpm check`. Stop on unreliable transaction attribution, uniqueness drift, migration uncertainty, or privacy leak. Commit
|
|
||||||
locally; no push/PR before independent review.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-10-implementation.md`: live/drift state, exact migration/base, facts vs uncertainty, RED/GREEN,
|
|
||||||
commit/files, tests/results, skipped DB/E2E gates, privacy/transaction risks, partial issue coverage, PR `not created`. Final
|
|
||||||
response at most ten lines.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Implement plan 11: JSearch removal and tailoring documentation
|
|
||||||
|
|
||||||
Read entire approved plan first from local planning checkout only when its HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use pinned `git show` fallback below. Then read current worktree
|
|
||||||
`AGENTS.md`, referenced issue/domain instructions, relevant ADRs, `/Users/amruth/.codex/RTK.md`, and
|
|
||||||
`/Users/amruth/.agents/skills/documentation-writer/SKILL.md`. Plan supplies document type, audience, goal, scope, and approved
|
|
||||||
structure; do not pause for routine outline approval. Portable fallback: fetch PR #3455 and use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/11-job-search-policy.md`.
|
|
||||||
|
|
||||||
Do not spawn subagents. Do not touch coordinator ledger or another worktree. Do not merge, mutate issues, push, or create PR.
|
|
||||||
|
|
||||||
Required execution:
|
|
||||||
|
|
||||||
- Confirm clean worktree, fetch current `origin/main`, rename branch `codex/issue-3010-jsearch-docs`, run exact drift command,
|
|
||||||
fetch live #3010 body/comments/state/linked PRs, and run root intent discovery before edits.
|
|
||||||
- Verify release history, current job-search redirect, agent tools, provider/model capability policy, and attachment UI before
|
|
||||||
wording claims. Stop if release history contradicts removal attribution or documented UI steps do not exist.
|
|
||||||
- Modify only `docs/changelog/index.mdx`, `docs/guides/using-ai-agent.mdx`, and
|
|
||||||
`docs/guides/ai-agent-tools.mdx`.
|
|
||||||
- Add factual v5.1.0 migration note, current controlled tailoring workflow using plan's exact synthetic job description, review
|
|
||||||
and undo guidance, pasted/attached-content behavior, and live-search capability distinction. No unverified removal motive,
|
|
||||||
paid JSearch restoration, stale model list, provider credentials, or promise that chat is equivalent structured search.
|
|
||||||
- Run exact API tool/capability tests and plan's focused `rg`, markdown lint, `git diff --check`, and three-file name-only gate.
|
|
||||||
Record real-provider/browser workflow as optional unavailable validation when not run.
|
|
||||||
- Self-review every acceptance criterion. Commit with normal message. Leave branch local for independent review.
|
|
||||||
|
|
||||||
Write report `.orchestration/plan-11-implementation.md`: verified facts vs uncertainty, live state, drift, exact commit/files,
|
|
||||||
commands/results, skipped gates, risks, issue coverage, PR status (`not created`). Final response: status, commit, one-line
|
|
||||||
tests, report path, concerns; at most ten lines.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Implement plan 15A: opt-in picture cover/contain
|
|
||||||
|
|
||||||
Read entire approved plan from local planning checkout only when its HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use pinned portable fallback:
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/15-picture-fitting-and-style.md`. Read current `AGENTS.md`,
|
|
||||||
RTK, issue/domain instructions, ADRs, and applicable skills. Run root Intent inventory and load matching local skill before
|
|
||||||
source edits. Do not spawn subagents. Do not touch ledger, mutate issues, merge, push, or create PR.
|
|
||||||
|
|
||||||
Start from refreshed `origin/main`, require clean worktree, rename branch `codex/issue-2782-picture-fit`, revalidate live
|
|
||||||
issue 2782 and all open implementation PRs, and run plan drift check. Stop on overlapping implementation or contradicted picture
|
|
||||||
contract. Historical #3168/#3088/#2794 causes remain outside this implementation and must not be claimed fixed.
|
|
||||||
|
|
||||||
Strict TDD and bounded scope:
|
|
||||||
|
|
||||||
- First add failing schema compatibility and picture-fit geometry/UI tests. Record exact RED output before implementation.
|
|
||||||
- Add `fit: z.enum(["cover", "contain"]).catch("cover")` to picture schema and `fit: "cover"` to defaults/required
|
|
||||||
fixtures. Old and invalid JSON parse as cover; contain round-trips. No DB migration.
|
|
||||||
- Add named Cover/Contain control through existing form/draft path. Cover retains crop flow. Contain uploads selected full file
|
|
||||||
via existing endpoint without cropped-canvas construction. Preserve validation, cancel/error/locked behavior, save/reload,
|
|
||||||
and undo. Explain that switching cannot restore pixels already cropped; no asset history or new endpoint.
|
|
||||||
- Make sidebar preview and shared PDF renderer consume selected fit. Preserve frame/aspect/border/shadow/rotation and normal
|
|
||||||
Semantic CSS precedence. Inspect every template image consumer; change only paths that bypass shared fit.
|
|
||||||
- Use synthetic marked square/landscape/portrait images. Assert contain retains all edges and centers within one pixel; cover
|
|
||||||
preserves old crop geometry; test border/shadow branches and semantic `object-fit: cover` override.
|
|
||||||
- Add/extend authenticated synthetic E2E for full-image upload, persistence, JSON/browser/server PDF parity when environment
|
|
||||||
supports it. Never use reporter/private assets.
|
|
||||||
|
|
||||||
Run focused schema/web/PDF tests, affected typechecks, boundaries, full build, and plan E2E against dedicated disposable DB.
|
|
||||||
Use narrow non-writing Biome inspection; if `pnpm check` runs, disclose it is write-capable and inspect diff. Run
|
|
||||||
`git diff --check` and scope/name-only checks. Commit locally with normal message; leave for independent review.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-15a-implementation.md`: verified facts vs uncertainty, live/drift state, RED/GREEN evidence, exact
|
|
||||||
commit/files, tests/results, skipped gates, visual/raster evidence, risks, issue coverage, PR `not created`. Final response at
|
|
||||||
most ten lines.
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
# Implement plan 16: editable imported rich-text tables
|
|
||||||
|
|
||||||
Read entire approved plan from local planning checkout only when its HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use pinned portable fallback:
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/16-imported-table-borders.md`. Read current `AGENTS.md`, RTK,
|
|
||||||
issue/domain instructions, ADRs, and applicable skills, including test-driven-development. Run root Intent inventory and load
|
|
||||||
matching local skill before source edits. Do not spawn subagents. Do not touch ledger, mutate issues, merge, push, or open PR.
|
|
||||||
|
|
||||||
Start from refreshed `origin/main`, require clean worktree, rename branch `codex/issue-3196-editable-tables`, revalidate live
|
|
||||||
issue 3196 and open PRs, and run exact drift check. Preserve merged #3438. Historical screenshot equivalence remains unverified
|
|
||||||
without source, but Q11 independently approves supported table editing.
|
|
||||||
|
|
||||||
Implement one atomic TDD unit across editor/HTML/PDF:
|
|
||||||
|
|
||||||
- Add failing `rich-input.table` tests first and record exact RED output: supported 2x3 table parses as structured Tiptap
|
|
||||||
JSON; mount emits no change; named-cell edit affects one cell; undo/redo and remount retain rows/cells/text; HTML reimports
|
|
||||||
equivalently. Include colspan/rowspan, multiple paragraphs, inline marks, paste, and unrelated prop updates.
|
|
||||||
- Register native Tiptap table/row/header/cell support with smallest required dependencies and frozen lockfile changes.
|
|
||||||
Preserve supported widths/spans/borders and existing `emitUpdate: false` behavior.
|
|
||||||
- Detect unsupported structured markup before destructive normalization. Retain exact original HTML, expose accessible read-only
|
|
||||||
notice, and prevent ordinary edits from overwriting it. Cover locked, keyboard, reopen, and cancellation behavior. Stop if
|
|
||||||
implementation needs broader HTML security policy or arbitrary editor extensions.
|
|
||||||
- Keep explicit CSS precedence and borderless tables borderless. Legacy HTML `border` mapping is outside initial scope unless
|
|
||||||
an exact fixture proves it is first failure; then stop and report fork rather than widening silently.
|
|
||||||
- Extend actual PDF integration: assert six cell coordinates plus exact horizontal/vertical border operators and fixed-DPI
|
|
||||||
pixels for supported CSS borders in legacy/semantic modes. Unsupported fallback must remain lossless. Text-only assertion
|
|
||||||
cannot pass.
|
|
||||||
- Add focused synthetic import E2E covering edit, undo/redo, save/reload, unrelated edit, browser/server PDF. No private data.
|
|
||||||
|
|
||||||
Run initial RED, focused web/PDF GREEN suites including existing #3438 test, web/PDF typechecks, boundaries, full build, and
|
|
||||||
plan E2E against dedicated disposable DB. Use narrow non-writing Biome; disclose/inspect any write-capable `pnpm check`.
|
|
||||||
Run `git diff --check` and scope inspection. Commit locally with normal message; no push/PR before independent review.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-16-implementation.md`: verified facts vs uncertainty, live/drift state, exact RED/GREEN evidence,
|
|
||||||
dependency/lockfile changes, commit/files, commands/results, skipped gates, risks, historical issue limitation, PR `not
|
|
||||||
created`. Final response at most ten lines.
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# Implement plan 19: scoped literal rich-text whitespace
|
|
||||||
|
|
||||||
Read approved plan 19 from local planning checkout only when HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/19-literal-rich-text-whitespace.md`. Read current `AGENTS.md`,
|
|
||||||
RTK, issue/domain guidance, ADRs, applicable TDD skills, and completed Plan 16 implementation/review reports. This unit must
|
|
||||||
start from or rebase onto reviewed Plan 16 head because both own rich editor/HTML seams; never implement concurrently. Run
|
|
||||||
root Intent inventory and load matching local skill before edits. Do not spawn subagents, touch ledger, mutate issues, merge,
|
|
||||||
push, or create PR.
|
|
||||||
|
|
||||||
Require clean dependent worktree, revalidate issue 3397/open PRs/current main and Plan 16 head, rename branch
|
|
||||||
`codex/issue-3397-literal-whitespace`, and record exact stacked base. Preserve paragraph indentation, Unicode-space fixes, and
|
|
||||||
table-cell support. Approved persisted marker is `data-resume-whitespace="preserve"`; unmarked legacy HTML must remain
|
|
||||||
unchanged. No global whitespace mode, NBSP substitution, code-block feature, or tab-key redesign.
|
|
||||||
|
|
||||||
Strict TDD, atomic web/PDF/DOCX contract:
|
|
||||||
|
|
||||||
- Record current GREEN characterization. Add failing marked paragraph/heading tests while existing unmarked ASCII collapse,
|
|
||||||
pretty-printed import, Unicode spaces, indentation, lists, quotes, Enter/Shift+Enter, and table regressions remain green.
|
|
||||||
- Newly authored blocks and blocks receiving text-input/paste mark preservation; mount, prop update, and unmarked legacy import
|
|
||||||
do not mark or emit saves. Exact leading/interior/trailing spaces and tab codepoints survive save/remount, undo/redo,
|
|
||||||
paragraph↔heading, list transitions without data loss, marks, line breaks, RTL, and supported Plan 16 table cells.
|
|
||||||
- Scope editor display behavior to marked nodes. Preserve unsupported-content channel from Plan 16.
|
|
||||||
- PDF preserves only marked node-local whitespace. One tab adds exactly four ordinary-space advances; two add eight,
|
|
||||||
independent of current x. Spaces remain breakable; narrow content never disappears. Do not disable dependency collapse
|
|
||||||
globally; patch CJS/ESM and frozen install only if no smaller neutral adapter exists.
|
|
||||||
- DOCX emits preserved-space representation and the same logical four-space tab contract; verify XML plus rendered geometry
|
|
||||||
when claiming visual width.
|
|
||||||
- Add synthetic authenticated E2E: type/paste, save/reload, JSON/PDF/DOCX export, exact codepoints and output geometry. No
|
|
||||||
private content.
|
|
||||||
|
|
||||||
Run focused web/PDF/DOCX tests including Plan 16 table regressions, affected typechecks, boundaries, full build, E2E against
|
|
||||||
dedicated DB, narrow non-writing Biome, and diff/scope gates. Disclose/inspect any write-capable `pnpm check`. Commit locally;
|
|
||||||
no push/PR before independent review.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-19-implementation.md`: exact dependency/base, live/drift state, facts vs uncertainty, RED/GREEN,
|
|
||||||
commit/files, tests/results, skipped visual/E2E gates, risks, issue coverage, PR `not created`. Final response at most ten lines.
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# Implement plan 20A: compact hidden-section recovery
|
|
||||||
|
|
||||||
Read approved plan 20 from local planning checkout only when HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/20-section-restoration.md`. Read current `AGENTS.md`, RTK,
|
|
||||||
issue/domain guidance, relevant ADRs, and applicable brainstorming/TDD/frontend skills. Plan supplies approved UX direction;
|
|
||||||
do not pause for routine product choices. Run root Intent inventory and load matching local skill before edits. Do not spawn
|
|
||||||
subagents, touch ledger, mutate issues, merge, push, or create PR.
|
|
||||||
|
|
||||||
Start clean from refreshed `origin/main`, rename branch `codex/issue-2921-hidden-section-recovery`, revalidate live issues
|
|
||||||
2921/3378/3265 and open PRs, and run exact plan drift check. This unit implements 20A for verified issue 2921. Reporter
|
|
||||||
forensics for 3378/3265 stay open. Do not claim missing/deleted content recovery or implement Layout placement in this unit.
|
|
||||||
|
|
||||||
Strict TDD, exact scope:
|
|
||||||
|
|
||||||
- First add failing pure tests for known printable section inventory: built-ins, summary, real custom sections; hidden and
|
|
||||||
placement locations independent; duplicate/later-page/sidebar locations; unknown IDs excluded; picture/basics/UI-only
|
|
||||||
custom container excluded; no mutation. Implement proposed `getSectionAvailability` in `@reactive-resume/resume` with an
|
|
||||||
explicit export. Include validated placement operation only if required by plan's shared helper contract, but do not expose
|
|
||||||
20B UI or auto-place anything.
|
|
||||||
- Add failing DOM tests then compact Hidden sections UI. Keep Picture/Basics normal. Remove full editor panels only for hidden
|
|
||||||
printable sections. List built-in, summary, and individual custom sections by effective localized title. Show changes only
|
|
||||||
existing hidden flag through `useUpdateResumeData`; retain content, item order, and all saved layout IDs.
|
|
||||||
- Preserve custom-section editor container behavior when only some custom children are hidden. Hidden-but-unplaced Show must
|
|
||||||
not choose placement. Sidebar icon navigation must focus/open recovery entry. Controls need accessible names, keyboard
|
|
||||||
behavior, locked-state disablement, and undo semantics.
|
|
||||||
- Use named props types and existing UI primitives. New strings through Lingui workflow. No schema, PDF, importer, reset,
|
|
||||||
deletion, title-default, or layout-placement semantics changes.
|
|
||||||
- Add authenticated synthetic E2E: hide built-in/summary/custom, save/reload, compact entries present, PDF text absent; Show,
|
|
||||||
same layout reference/output returns; undo/redo and locked state. Do not use reporter data.
|
|
||||||
|
|
||||||
Record initial RED and final GREEN. Run resume/web focused tests including existing visibility/menu/navigation regressions,
|
|
||||||
affected typechecks, boundaries, full build, and focused E2E against dedicated disposable DB. Use narrow non-writing Biome;
|
|
||||||
disclose/inspect any write-capable `pnpm check`. Run diff/scope checks. Commit locally; no push/PR before independent review.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-20a-implementation.md`: live/drift state, facts vs uncertainty, RED/GREEN evidence, exact commit
|
|
||||||
and files, tests/results, skipped gates, risks, issue-specific coverage, PR `not created`. Final response at most ten lines.
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# Implement plan 23A: authored-page versus overflow guidance
|
|
||||||
|
|
||||||
Read approved plan 23 from local planning checkout only when HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/23-pagination-controls.md`. Read current `AGENTS.md`, RTK,
|
|
||||||
issue/domain guidance, ADRs, and applicable brainstorming/TDD/frontend skills. Plan and Q10 supply approved direction; do not
|
|
||||||
pause for routine wording/layout choices. Run root Intent inventory and load matching local skill before edits. Do not spawn
|
|
||||||
subagents, touch ledger, mutate issues, merge, push, or create PR.
|
|
||||||
|
|
||||||
Start clean from refreshed `origin/main`, rename branch `codex/issue-3090-authored-page-guidance`, revalidate issues 3090 and
|
|
||||||
3350 plus open PRs, and run plan drift check. This is only 23A/Q10 guidance for issue 3090. Do not add item keep-together,
|
|
||||||
widow/orphan controls, renderer-generated page records, schema changes, or claim issue 3350 fixed.
|
|
||||||
|
|
||||||
TDD and bounded implementation:
|
|
||||||
|
|
||||||
- Add failing Layout UI test with one authored page and multiple physical-render-page evidence/stub. Guidance must identify
|
|
||||||
authored pages versus automatic PDF overflow, name existing `Move to` → `New Page` and full-width controls accurately, and
|
|
||||||
make clear physical overflow pages are not separately saved/editable.
|
|
||||||
- Add concise, accessible guidance at owning Layout pages surface using existing UI primitives and Lingui strings. Link or
|
|
||||||
focus existing controls only if current component contracts support it without new state. Preserve existing warning and
|
|
||||||
avoid duplicative copy.
|
|
||||||
- Prove rendering guidance does not mutate `metadata.layout.pages`, add page records, change section assignment, or alter PDF
|
|
||||||
behavior. Cover keyboard/accessibility and locked state where relevant.
|
|
||||||
- Extend synthetic Azurill case: automatic overflow retains all content; manually authored second full-width page remains an
|
|
||||||
independent saved layout choice. Do not promise independent styling of physical overflow.
|
|
||||||
|
|
||||||
Record initial RED then GREEN. Run focused Layout/page tests, relevant PDF pagination regression if touched by test harness,
|
|
||||||
web typecheck, boundaries, full build, focused E2E if existing infrastructure can observe guidance without private data,
|
|
||||||
Lingui extraction/catalog checks, narrow non-writing Biome, `git diff --check`, and scope review. Commit locally; no push/PR
|
|
||||||
before independent review.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-23a-implementation.md`: live/drift state, facts vs uncertainty, RED/GREEN, exact commit/files,
|
|
||||||
tests/results, skipped gates, risks, partial issue coverage, PR `not created`. Final response at most ten lines.
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
# Implement plan 32: one-shot chronological section sort
|
|
||||||
|
|
||||||
Read approved plan 32 from local planning checkout only when HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/32-section-date-sorting.md`. Read current `AGENTS.md`, RTK,
|
|
||||||
issue/domain guidance, ADRs, and applicable brainstorming/TDD skills. Approved direction fixes behavior; do not ask routine
|
|
||||||
interaction questions. Run root Intent inventory and load matching local skill before edits. Do not spawn subagents, touch
|
|
||||||
ledger, mutate issues, merge, push, or create PR.
|
|
||||||
|
|
||||||
Start clean from refreshed `origin/main`, rename branch `codex/issue-2725-one-shot-sort`, revalidate live issue 2725 and open
|
|
||||||
PRs, and run exact drift. Ensure no active owner touches same section menus. Plan 24 is presentation-only; do not stack unless
|
|
||||||
current source truly requires its interface. This increment is one-shot Experience/Education only, not autosort or full issue
|
|
||||||
closure.
|
|
||||||
|
|
||||||
Strict TDD and contract:
|
|
||||||
|
|
||||||
- First characterize existing `parsePeriod` for numeric/localized/ongoing/year-only/reversed/blank/bare-Present/prose/equal
|
|
||||||
values without changing parser or ATS behavior. Record nullable cases.
|
|
||||||
- Add failing tests for pure `sortSectionItemsByPeriod(items, locale): { items, unresolvedIds }` in `packages/resume` and
|
|
||||||
an intentional public export. New array, original item objects/content/IDs unchanged, identical multiset, no input mutation.
|
|
||||||
- Total order: ongoing first by descending start; known-ended by descending end then start; stable ties. Mixed precision uses
|
|
||||||
internal `[year, month ?? 0]`; never persists fabricated dates. Missing known endpoint ranks after known. Unresolved and
|
|
||||||
reversed entries remain stable at end; return their exact IDs. Cover transitivity, determinism/repeat invocation,
|
|
||||||
empty/single, localized months, year-only, bare Present, blank, prose, reversed, and stable equal ranges.
|
|
||||||
- Add existing-menu one-shot action only for built-in Experience/Education through one `useUpdateResumeData` draft mutation.
|
|
||||||
One undo restores exact order; save/reload retains chosen order; later edits never resort. Locked state disables action.
|
|
||||||
Concise Lingui notice identifies only affected entries in current section, preferably safe title or ID semantics supported by
|
|
||||||
current notification patterns; do not expose unrelated resume content.
|
|
||||||
- No schema setting, persistent autosort, Date.parse, free-text rewrite, role/custom-section sort, or implicit render/save sort.
|
|
||||||
|
|
||||||
Record RED and GREEN. Run period/helper tests, focused web menu/undo tests, resume/web typechecks, boundaries, build, Lingui
|
|
||||||
catalog checks, and synthetic authenticated persistence E2E if feasible. Use narrow non-writing Biome; disclose/inspect any
|
|
||||||
write-capable `pnpm check`. Run diff/scope checks. Commit locally; no push/PR before independent review.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-32-implementation.md`: live/drift state, facts vs uncertainty, RED/GREEN, exact commit/files,
|
|
||||||
tests/results, skipped gates, risks, partial issue coverage, PR `not created`. Final response at most ten lines.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Independent rereview: Plan 02 hosted feedback fix
|
|
||||||
|
|
||||||
Review only in `/Users/amruth/orca/workspaces/reactive-resume/issue-3181-recovery-procedure`.
|
|
||||||
Exact target head: `325d1fcd1e2ea3744896c1688c6f6c0bfc3dd5ce`. Do not edit tracked files, commit, push, reply,
|
|
||||||
resolve threads, publish, merge, or mutate issues.
|
|
||||||
|
|
||||||
Read current repository instructions, pinned approved Plan 02, prior review reports, hosted-review fix report, full
|
|
||||||
`origin/main...HEAD` diff, live PR #3460, and all four unresolved hosted threads. Refresh base and verify exact head.
|
|
||||||
Run root Intent inventory and load matching review skill if any; no subagents.
|
|
||||||
|
|
||||||
Review Standards and Spec independently. Verify especially:
|
|
||||||
|
|
||||||
- `caseId`, `sourceResumeId`, and non-null `targetResumeId` reject all Unicode control (`Cc`) and format (`Cf`)
|
|
||||||
characters, including embedded and format-only U+200B, U+2066, U+202E, and U+FEFF values;
|
|
||||||
- safe accepted identifiers remain byte-preserving and unnormalized;
|
|
||||||
- strict current-v5 canonical equality and duplicate-member scanner remain fail-closed;
|
|
||||||
- autofix cleanup is retained and no scope expansion or contract weakening occurred;
|
|
||||||
- migration guidance exactly matches executable validation and does not imply raw-v4 conversion or real recovery;
|
|
||||||
- Codacy scanner-complexity and scanner-coverage comments are non-actionable or already satisfied, and relaxing
|
|
||||||
canonical equality would violate approved direction;
|
|
||||||
- all 83 comparator cases and independent adversarial probes are mutation-sensitive enough to catch removal or partial
|
|
||||||
application of the `Cf` guard.
|
|
||||||
|
|
||||||
Run focused comparator/API/auth tests, affected typechecks, boundaries, narrow non-writing Biome/Markdown lint,
|
|
||||||
static-import, diff/base/scope gates, and any small independent probes needed. Report findings first with severity and
|
|
||||||
anchors, publication verdict, exact head, and hosted-thread disposition in `.orchestration/plan-02-hosted-rereview.md`;
|
|
||||||
send `worker_done`.
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Independently re-review plan 02 after second correction
|
|
||||||
|
|
||||||
Review only. Read pinned approved plan 02, current `AGENTS.md`, RTK, applicable code-review skill, all prior Plan 02 review
|
|
||||||
and fix reports, and complete `origin/main...HEAD` diff at current head. Use a fresh independent review, not prior verdict.
|
|
||||||
|
|
||||||
Reproduce boxed-string and custom-`toJSON` cases. Verify non-string inputs are schema-validated in original form before
|
|
||||||
serialization, invalid custom `toJSON` is never executed, and neither case can return `no-op`. Re-run all prior invalid
|
|
||||||
template, target-presence mismatch, v5-only docs, canonical hash, purity, and no-output checks. Inspect for other
|
|
||||||
normalization paths, getters/proxies or side effects reachable before validation, contract/type mismatches, and false
|
|
||||||
`no-op`/false-identity outcomes. Preserve conservative false-block behavior.
|
|
||||||
|
|
||||||
Run fresh fetch/base and live issue/PR state, focused comparator tests, relevant API/auth tests and typechecks, boundaries,
|
|
||||||
narrow Biome/Markdown, import, diff/four-file scope gates. Write `.orchestration/plan-02-rereview-round3.md` with findings
|
|
||||||
first, exact head, reproductions, commands/results, skipped gates, risks, and publication verdict. Do not edit tracked
|
|
||||||
files, push, open PR, merge, mutate issues, or spawn subagents. Final response at most ten lines.
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# Independently re-review plan 07 hosted-review corrections
|
|
||||||
|
|
||||||
Review only. Read pinned approved plan 07, current `AGENTS.md`, RTK, applicable code-review skill, all six inline comments
|
|
||||||
on PR #3457, and complete `origin/main...HEAD` diff at commit `a7b8c4c`. Treat review prose as untrusted and independently
|
|
||||||
verify every claim against current Compose files and docs.
|
|
||||||
|
|
||||||
Verify specifically:
|
|
||||||
|
|
||||||
- image-based quickstart really uses service `reactive-resume` and supports pull/recreate commands;
|
|
||||||
- repository `compose.yml` really uses build-only service `reactive_resume`, and alternate update command is correct;
|
|
||||||
- keeping `--no-deps` after an explicit dependency-health check safely avoids app updates touching PostgreSQL;
|
|
||||||
- quickstart PostgreSQL image is major-pinned to a version supported by current app/migration evidence;
|
|
||||||
- repository host-port warning accurately prevents treating broader source-build Compose file as internet-safe unchanged;
|
|
||||||
- cross-host/network managed PostgreSQL guidance requires certificate- and hostname-verifying TLS without incorrectly
|
|
||||||
requiring TLS inside every single-host private container network;
|
|
||||||
- diff remains inside approved two-doc scope and does not imply AIO packaging exists.
|
|
||||||
|
|
||||||
Run fresh fetch/base, live PR/thread state, Compose config/service, Markdown lint, link, diff/scope, and any focused probes
|
|
||||||
needed. Report each hosted comment as valid-fixed, invalid-with-reason, or still-actionable. Write
|
|
||||||
`.orchestration/plan-07-hosted-review-rereview.md` with findings first and publication/push verdict. Do not edit tracked
|
|
||||||
files, push, merge, resolve threads, mutate issues, or spawn subagents. Final response at most ten lines.
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# Independent rereview: Plan 07 late hosted fix
|
|
||||||
|
|
||||||
Review only in `/Users/amruth/orca/workspaces/reactive-resume/issue-2722-postgres-docs`.
|
|
||||||
Exact target head: `171637526de4bb0d0e4320ebb85048675b92f1c7`.
|
|
||||||
PR #3457 remains open at older remote head. Do not edit tracked files, commit, push, reply, resolve, or merge.
|
|
||||||
|
|
||||||
Read current instructions, pinned approved Plan 07, complete `origin/main...HEAD` diff, all Plan 07 reports, live issue
|
|
||||||
number 2722, PR/check/thread state, and late thread `PRRT_kwDODuah5s6fnaBa`. Refresh base and verify target head.
|
|
||||||
|
|
||||||
Independently verify image quickstart and repository source-build update paths are unmistakably separate through log
|
|
||||||
inspection. Both paths must use correct service names; image path retains pull/up/log commands for `reactive-resume`;
|
|
||||||
repository path uses build/up/log commands for `reactive_resume`, no pull, app-only `--no-deps`, and no dependency or
|
|
||||||
PostgreSQL lifecycle widening.
|
|
||||||
|
|
||||||
Run both Compose config validations and dry-runs, focused DB test, Markdown/link/command/diff/exact-scope gates. Review
|
|
||||||
full diff for standards and approved-plan compliance. Write `.orchestration/plan-07-hosted-review-rereview-round2.md`
|
|
||||||
with findings first and publication verdict, then send worker_done. No subagents.
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Independently re-review plan 09 hosted-review correction
|
|
||||||
|
|
||||||
Review only. Read pinned approved plan 09, current `AGENTS.md`, RTK, applicable code-review skill, prior Plan 09 review/fix/
|
|
||||||
rereview reports, and both inline comments on PR #3458. Review complete `origin/main...HEAD` diff at current head.
|
|
||||||
|
|
||||||
Verify duplicated hosted finding is resolved: instructions list commits affecting `resume.json`, use a user-selected commit
|
|
||||||
reference rather than `HEAD`, and still recover by saving selected JSON then importing as a new resume. In a disposable
|
|
||||||
local repository with at least two committed resume versions, prove `git log --oneline -- resume.json` identifies both and
|
|
||||||
`git show <selected>:resume.json` returns chosen earlier content. Ensure user-facing commands remain plain Git with no RTK,
|
|
||||||
remote, credentials, push, global config, sync, destructive replacement, or whole-account restore promise.
|
|
||||||
|
|
||||||
Revalidate all previously corrected export/cover-letter/version-history claims, exact two-doc scope, fresh base/live PR
|
|
||||||
state, focused tests, Markdown lint, diff/scope. Write `.orchestration/plan-09-hosted-review-rereview.md` with findings
|
|
||||||
first, exact head, commands/results, skipped gates, risks, and push/thread-resolution verdict. Do not edit tracked files,
|
|
||||||
push, merge, resolve threads, mutate issues, or spawn subagents. Final response at most ten lines.
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Independently re-review plan 09 after recovered-file fix
|
|
||||||
|
|
||||||
Review only. Read pinned approved plan 09, current `AGENTS.md`, RTK, applicable code-review skill, all Plan 09 review/fix
|
|
||||||
reports, and complete `origin/main...HEAD` diff at current head.
|
|
||||||
|
|
||||||
Reproduce full documented Git workflow in fresh disposable two-revision repository. Verify path-filtered log identifies
|
|
||||||
both revisions, selected commit inspection is correct, explicit save command creates importable recovered JSON containing
|
|
||||||
earlier content, tracked current `resume.json` remains current and unmodified, and dashboard prose points to recovered
|
|
||||||
file/import-as-new path. Assess output-filename clobber risk against wording; report if still misleading.
|
|
||||||
|
|
||||||
Revalidate all prior export, cover-letter, history, privacy, image, no-sync/no-remote/no-destructive-replacement claims;
|
|
||||||
fresh base/live PR/thread state; 152 focused tests; Markdown/link/forbidden-command/diff/two-doc scope gates. Write
|
|
||||||
`.orchestration/plan-09-rereview-round3.md` with findings first, exact head, commands/results, skips, risks, and push/thread-
|
|
||||||
resolution verdict. Do not edit tracked files, push, merge, resolve threads, mutate issues, or spawn subagents. Final
|
|
||||||
response at most ten lines.
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# Independent rereview: Plan 09 round 4
|
|
||||||
|
|
||||||
Review only. Assigned worktree:
|
|
||||||
`/Users/amruth/orca/workspaces/reactive-resume/issue-2705-git-backup-docs`.
|
|
||||||
|
|
||||||
Target local head: `4ffdd96bdda668513e26051a52c971d546e01bff`.
|
|
||||||
PR #3458 remote head remains older. Do not edit tracked files, commit, push, resolve threads, or merge.
|
|
||||||
|
|
||||||
Read current instructions, pinned approved Plan 09, full `origin/main...HEAD` diff, implementation/fix reports, and every
|
|
||||||
prior review report. Revalidate current `origin/main`, live issue #2705, PR #3458, checks, and unresolved threads.
|
|
||||||
|
|
||||||
Primary acceptance: revised recovery prose must require a fresh/unused output filename at command site; following it must
|
|
||||||
preserve an existing sentinel output, create a valid importable earlier revision under a distinct name, and leave tracked
|
|
||||||
`resume.json` unchanged. Confirm all prior factual corrections and all nine approved commands remain coherent.
|
|
||||||
|
|
||||||
Run 152 focused tests, disposable two-revision recovery workflow, Markdown/link/command/forbidden-command/diff/scope
|
|
||||||
gates. Review complete diff for standards and approved-plan compliance. Report findings first with severity and anchors.
|
|
||||||
Write `.orchestration/plan-09-rereview-round4.md`, then send worker_done with publication verdict. No subagents.
|
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Independently re-review plan 11 hosted-review corrections
|
|
||||||
|
|
||||||
Review only. Read pinned approved plan 11, current `AGENTS.md`, RTK, applicable code-review skill, all Plan 11 reports,
|
|
||||||
and two inline comments on PR #3459. Review complete `origin/main...HEAD` diff at current head.
|
|
||||||
|
|
||||||
Verify both hosted wording corrections are coherent: user-facing example uses accessible “sample” terminology without
|
|
||||||
weakening fictional-data safety, and section/link text “Review edits and patches” aligns with exact **Review edits** UI
|
|
||||||
label while still covering patch inspection and restore. Verify anchor resolution and no stale `#review-patches` links.
|
|
||||||
Revalidate previous attachment, history, provider capability, isolated AI Draft, patch/restore, scope, and no-invented-
|
|
||||||
motive findings.
|
|
||||||
|
|
||||||
Run fresh base/live PR/thread state, relevant source probes and focused tests, Markdown/link/diff/three-doc scope gates.
|
|
||||||
Write `.orchestration/plan-11-hosted-review-rereview.md` with findings first, exact head, commands/results, skipped gates,
|
|
||||||
risks, and push/thread-resolution verdict. Do not edit tracked files, push, merge, resolve threads, mutate issues, or spawn
|
|
||||||
subagents. Final response at most ten lines.
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Independently re-review plan 11 after corrections
|
|
||||||
|
|
||||||
Review only. Read pinned approved plan 11, current `AGENTS.md`, RTK, applicable code-review skill,
|
|
||||||
`.orchestration/plan-11-review.md`, and `.orchestration/plan-11-review-fix.md`. Review complete `origin/main...HEAD` diff,
|
|
||||||
not only follow-up commit.
|
|
||||||
|
|
||||||
Verify both prior findings are fully resolved: changelog no longer promises arbitrary attachment-based tailoring, and
|
|
||||||
workflow no longer duplicates adjacent patch review/restore/rollback guidance. Revalidate history, redirect, provider
|
|
||||||
capability boundary, supported supplied-description paths, isolated AI Draft behavior, patch review/restore semantics,
|
|
||||||
three-doc scope, and absence of invented removal motive or JSearch restoration promise.
|
|
||||||
|
|
||||||
Run fresh fetch/base and live issue/PR state checks, source probes, relevant focused tests, Markdown lint, link/diff/scope
|
|
||||||
gates. Write `.orchestration/plan-11-rereview.md` with findings first, exact head, commands/results, skipped gates, risks,
|
|
||||||
and publication verdict. Do not edit tracked files, push, open PR, merge, mutate issues, or spawn subagents. Final response
|
|
||||||
at most ten lines.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Independent rereview: Plan 15A picture fit
|
|
||||||
|
|
||||||
Review only in `/Users/amruth/orca/workspaces/reactive-resume/issue-2782-picture-fit`.
|
|
||||||
Exact target head: `6145d5a5925373287b87dfaa30ab675ebc84e105`.
|
|
||||||
No PR or remote branch exists. Do not edit tracked files, commit, push, publish, merge, or mutate issues.
|
|
||||||
|
|
||||||
Read current instructions, pinned approved Plan 15, complete `origin/main...HEAD` diff, implementation/review/fix reports,
|
|
||||||
live issue #2782 and open PR overlap. Refresh base and verify target head. Review all 70-file behavior, not only fix commit.
|
|
||||||
|
|
||||||
Independently verify every prior finding:
|
|
||||||
|
|
||||||
- Cover raster pins legacy centered crop geometry for landscape, portrait, and square control with explicit retained/cropped
|
|
||||||
edges and symmetric bounds;
|
|
||||||
- Contain asserts expected fitted bitmap dimensions and centering within one pixel, including border/shadow branches;
|
|
||||||
- mutation sensitivity proves wrong object-position or scale fails tests;
|
|
||||||
- sidebar and Playwright assert computed `object-fit`, not Tailwind class presence;
|
|
||||||
- props type duplication is removed without weakening named-props convention.
|
|
||||||
|
|
||||||
Reconfirm Cover default, Contain original-file upload, schema/import compatibility, autosave/undo/error/cancel/lock flows,
|
|
||||||
all-template shared PDF consumption, semantic CSS precedence, locale/docs/reference completeness, exact issue #2782 scope,
|
|
||||||
and no claims for other Plan 15 issues.
|
|
||||||
|
|
||||||
Run focused and full affected suites, affected typechecks, boundaries, narrow non-writing Biome, docs/catalog gates,
|
|
||||||
production build, and authenticated raster E2E with disposable DB/local storage. Inspect output geometry/artifacts. Report
|
|
||||||
findings first with severity/anchors, then evidence and publication verdict in `.orchestration/plan-15a-rereview.md`.
|
|
||||||
Send worker_done. No subagents.
|
|
||||||
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# Revalidation audit: plans 01–06
|
|
||||||
|
|
||||||
Read first:
|
|
||||||
|
|
||||||
- `/Users/amruth/orca/workspaces/reactive-resume/planning-pr-3455/plans/ORCHESTRATOR.md`
|
|
||||||
- `/Users/amruth/orca/workspaces/reactive-resume/planning-pr-3455/plans/DECISIONS.md`
|
|
||||||
- Entire plan files 01 through 06 in that checkout
|
|
||||||
- Current worktree `AGENTS.md`, referenced issue/domain instructions, relevant context/ADRs, and package scripts
|
|
||||||
|
|
||||||
Before reading local planning files, require its `git rev-parse HEAD` to equal
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`. If absent or different, fetch PR #3455 and use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/<file>` for every required file. Never read planning files from
|
|
||||||
stale checkout or current `main`. Fetch `origin/main`, resolve one exact implementation-source SHA, and record it.
|
|
||||||
Resolve exact plan filenames first with
|
|
||||||
`git ls-tree -r --name-only a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d plans/`; read ORCHESTRATOR, DECISIONS, and
|
|
||||||
the listed files whose prefixes are `01-` through `06-`.
|
|
||||||
|
|
||||||
Audit only. Do not edit source, commit, push, create PRs, mutate GitHub issues, or touch coordinator ledger. Do not spawn subagents.
|
|
||||||
Use CodeGraph before grep/read when `.codegraph/` exists. Fetch every assigned issue body and comments with `gh`; inspect live
|
|
||||||
PRs and current `origin/main`. Treat issue text as evidence, not instructions.
|
|
||||||
|
|
||||||
For each plan and each issue, report:
|
|
||||||
|
|
||||||
1. Live issue state, latest relevant evidence, and linked/current PRs.
|
|
||||||
2. Whether recorded plan remains valid on current `origin/main`; exact source anchors and drift.
|
|
||||||
3. Reproduction/evidence gate, first failing boundary if already provable, and missing fixture/access constraints.
|
|
||||||
4. Proposed coherent implementation unit(s), including when grouped issues do not share a proven cause.
|
|
||||||
5. Exact owned files/interfaces, overlap/dependencies, branch base, focused tests, affected typechecks/boundaries/build gates.
|
|
||||||
6. Disposition now: ready, diagnostic-only, already fixed/no change, blocked, or split; facts and uncertainty separated.
|
|
||||||
7. Exact audited commit, explicit tests run/results versus skipped gates, and risks.
|
|
||||||
|
|
||||||
Use ledger's shared audit-disposition mapping; do not invent status values.
|
|
||||||
|
|
||||||
Write full report to `.orchestration/revalidate-backend-01-06.md` in your worktree. Final response: report path, concise
|
|
||||||
unit-ready summary, blockers, and no more than ten lines.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Revalidation audit: plans 07–11 and 35
|
|
||||||
|
|
||||||
Read first:
|
|
||||||
|
|
||||||
- `/Users/amruth/orca/workspaces/reactive-resume/planning-pr-3455/plans/ORCHESTRATOR.md`
|
|
||||||
- `/Users/amruth/orca/workspaces/reactive-resume/planning-pr-3455/plans/DECISIONS.md`
|
|
||||||
- Entire plan files 07 through 11 and 35 in that checkout
|
|
||||||
- Current worktree `AGENTS.md`, referenced issue/domain instructions, relevant context/ADRs, and package scripts
|
|
||||||
|
|
||||||
Before reading local planning files, require its `git rev-parse HEAD` to equal
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`. If absent or different, fetch PR #3455 and use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/<file>` for every required file. Never read planning files from
|
|
||||||
stale checkout or current `main`. Fetch `origin/main`, resolve one exact implementation-source SHA, and record it.
|
|
||||||
Resolve exact plan filenames first with
|
|
||||||
`git ls-tree -r --name-only a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d plans/`; read ORCHESTRATOR, DECISIONS, and
|
|
||||||
the listed files whose prefixes are `07-` through `11-` plus `35-`.
|
|
||||||
|
|
||||||
Audit only. Do not edit source, commit, push, create PRs, mutate GitHub issues, or touch coordinator ledger. Do not spawn
|
|
||||||
subagents. Use CodeGraph before grep/read when `.codegraph/` exists. Fetch every assigned issue body/comments and inspect
|
|
||||||
live PRs/current `origin/main`. Q12 and blanket-approved scoped directions are binding.
|
|
||||||
|
|
||||||
For each plan and each issue, report live state/PRs, current-code validity and anchors, reproduction or documentation evidence,
|
|
||||||
coherent unit split, exact owned files, dependencies, tests/checks, blockers, and disposition. Distinguish declined AIO from
|
|
||||||
documentation improvements; avoid cosmetic fix claims. For import errors, require reproduction before parser/dialog changes.
|
|
||||||
Separate verified facts from uncertainty. Record exact audited commit, first failing boundary, explicit tests run/results
|
|
||||||
versus skipped gates, and risks. Use ledger's shared audit-disposition mapping; do not invent status values.
|
|
||||||
|
|
||||||
Write full report to `.orchestration/revalidate-backend-07-11-35.md` in your worktree. Final response: report path, concise
|
|
||||||
unit-ready summary, blockers, and no more than ten lines.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Revalidation audit: plans 20–34
|
|
||||||
|
|
||||||
Read first:
|
|
||||||
|
|
||||||
- `/Users/amruth/orca/workspaces/reactive-resume/planning-pr-3455/plans/ORCHESTRATOR.md`
|
|
||||||
- `/Users/amruth/orca/workspaces/reactive-resume/planning-pr-3455/plans/DECISIONS.md`
|
|
||||||
- Entire plan files 20 through 34 in that checkout
|
|
||||||
- Current worktree `AGENTS.md`, referenced domain instructions, context/ADRs, and package scripts
|
|
||||||
|
|
||||||
Before reading local planning files, require its `git rev-parse HEAD` to equal
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`. If absent or different, fetch PR #3455 and use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/<file>` for every required file. Never read planning files from
|
|
||||||
stale checkout or current `main`.
|
|
||||||
Resolve exact plan filenames first with
|
|
||||||
`git ls-tree -r --name-only a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d plans/`; read ORCHESTRATOR, DECISIONS, and
|
|
||||||
the listed files whose prefixes are `20-` through `34-`.
|
|
||||||
|
|
||||||
Audit only. Do not edit source, commit, push, create PRs, mutate GitHub issues, or touch coordinator ledger. Do not spawn
|
|
||||||
subagents. Use CodeGraph first only when `.codegraph/` exists. Otherwise inspect exact-head source with `git show`, `rg`, and
|
|
||||||
direct reads; record CodeGraph unavailability and limitation. Fetch every issue body/comments and live PRs/current main.
|
|
||||||
|
|
||||||
For each plan and issue, report live state/PRs, source validity/drift, reproduction/evidence gates, coherent cause-based unit
|
|
||||||
split, exact owned files/interfaces, overlap/dependency graph, visual/rendered assertions, focused tests/typechecks/boundaries/
|
|
||||||
build gates, blockers, and disposition. Q1–Q10 plus blanket approvals bind. Identify partial implementations already on main.
|
|
||||||
Plan 30/31 depend on renderer baselines. Plan 33 stops after official-reference research plus concrete visual proposal pending
|
|
||||||
future visual approval. Separate verified facts from uncertainty.
|
|
||||||
|
|
||||||
Write full report to `.orchestration/revalidate-builder-20-34.md` in your worktree. Final response: report path, concise
|
|
||||||
unit-ready summary, blockers, and no more than ten lines.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Revalidation audit: plans 12–19
|
|
||||||
|
|
||||||
Read first:
|
|
||||||
|
|
||||||
- `/Users/amruth/orca/workspaces/reactive-resume/planning-pr-3455/plans/ORCHESTRATOR.md`
|
|
||||||
- `/Users/amruth/orca/workspaces/reactive-resume/planning-pr-3455/plans/DECISIONS.md`
|
|
||||||
- Entire plan files 12 through 19 in that checkout
|
|
||||||
- Current worktree `AGENTS.md`, referenced domain instructions, context/ADRs, and package scripts
|
|
||||||
|
|
||||||
Before reading local planning files, require its `git rev-parse HEAD` to equal
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`. If absent or different, fetch PR #3455 and use
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/<file>` for every required file. Never read planning files from
|
|
||||||
stale checkout or current `main`.
|
|
||||||
Resolve exact plan filenames first with
|
|
||||||
`git ls-tree -r --name-only a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d plans/`; read ORCHESTRATOR, DECISIONS, and
|
|
||||||
the listed files whose prefixes are `12-` through `19-`.
|
|
||||||
|
|
||||||
Audit only. Do not edit source, commit, push, create PRs, mutate GitHub issues, or touch coordinator ledger. Do not spawn
|
|
||||||
subagents. Use CodeGraph first only when `.codegraph/` exists. Otherwise inspect pinned exact-head source with `git show`,
|
|
||||||
`rg`, and direct reads; record CodeGraph unavailability and limitation. Fetch every issue body/comments and live PRs/main.
|
|
||||||
|
|
||||||
For each plan and issue, report live state/PRs, source validity/drift, exact first-boundary reproduction, available/missing
|
|
||||||
fixtures, coherent cause-based unit split, owned files/interfaces, overlap map across 12–19 and units 27/30/31, exact visual
|
|
||||||
or raster/content assertions, focused tests/typechecks/boundaries/build gates, blockers, and disposition. Preserve existing
|
|
||||||
verified fixes. Q11 makes editable rich-text tables selected behavior; plan 19 whitespace direction is approved. Separate
|
|
||||||
verified facts from uncertainty.
|
|
||||||
|
|
||||||
Write full report to `.orchestration/revalidate-rendering-12-19.md` in your worktree. Final response: report path, concise
|
|
||||||
unit-ready summary, blockers, and no more than ten lines.
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# Independent review: Plan 02 autofix head
|
|
||||||
|
|
||||||
Review only in `/Users/amruth/orca/workspaces/reactive-resume/issue-3181-recovery-procedure`.
|
|
||||||
Exact target/PR #3460 head: `6c47439358aa624f459b519f6d51ba19bbde97c0`.
|
|
||||||
Do not edit tracked files, commit, push, merge, mutate issues, or access private recovery data.
|
|
||||||
|
|
||||||
Read current instructions, pinned Plan 02, complete `origin/main...HEAD` diff, all Plan 02 reports, and live PR #3460
|
|
||||||
checks/threads. Verify bot commit `4125074f9..6c4743935` removes only unused exports for internal outcome/reason aliases and
|
|
||||||
does not change public `RecoveryComparisonInput`, `RecoveryManifest`, comparator behavior, consumer compatibility, or
|
|
||||||
approved scope. Inspect current package exports/imports and any potential external tooling use.
|
|
||||||
|
|
||||||
Run 59 comparator tests, tooling typecheck, static import, API/auth focused suites, affected typechecks, boundaries, narrow
|
|
||||||
Biome/Markdown, diff/scope gates, plus targeted compile probes showing intended public types remain usable. Review complete
|
|
||||||
diff for standards/spec regressions. Write `.orchestration/plan-02-autofix-review.md` with findings first and exact-head
|
|
||||||
publication verdict, then send worker_done. No subagents.
|
|
||||||
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# Final independent review: plan 02 serialized recovery comparator
|
|
||||||
|
|
||||||
Review only. Read pinned approved plan 02, current `AGENTS.md`, RTK, applicable code-review skill, every Plan 02 report,
|
|
||||||
and complete `origin/main...00855fa1667b35502ec66d609a603716d647466d` four-file diff. Start fresh; prior churn is not evidence of correctness.
|
|
||||||
|
|
||||||
Reproduce every prior P1/P2 bypass. Verify primitive-string guard runs before any object access/trap/getter/method; parsed
|
|
||||||
envelope validation is exact, non-coercing, finite-JSON-only, rejects unknown/missing keys and invalid/empty IDs/flag types;
|
|
||||||
valid false gates retain named reasons; target invariant and invalid source/target order are conservative; invalid manifests
|
|
||||||
are fresh and deterministic. Audit recursive validation/canonicalization for false identity, normalization, mutation,
|
|
||||||
exceptions, stack/size behavior appropriate to local synthetic tooling, and hash determinism. Ensure serialized-request
|
|
||||||
docs match actual API and do not claim raw-v4 support or real recovery.
|
|
||||||
|
|
||||||
Run fresh base/live issue/PR state, all 36 comparator tests plus independent adversarial probes, API/auth tests/typechecks,
|
|
||||||
boundaries, narrow Biome/Markdown, import/no-output/diff/four-file scope. Review Standards and Spec axes, including whether
|
|
||||||
596-line tool/test diff remains proportionate and maintainable for approved procedure.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-02-final-review.md` with findings first and file/line evidence, exact head, commands/results,
|
|
||||||
skips, risks, and publication verdict. Do not edit tracked files, push, open PR, merge, mutate issues, or spawn subagents.
|
|
||||||
Final response at most ten lines.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Independent review: Plan 02 round 5
|
|
||||||
|
|
||||||
Review only. Worktree:
|
|
||||||
`/Users/amruth/orca/workspaces/reactive-resume/issue-3181-recovery-procedure`.
|
|
||||||
Exact target head: `4125074f99ad91c161d8a9437259465d4b7f933b`.
|
|
||||||
No PR exists. Do not edit tracked files, commit, push, publish, mutate issues, or perform private recovery.
|
|
||||||
|
|
||||||
Read current instructions, pinned approved Plan 02, full `origin/main...HEAD` diff, every implementation/fix/review report,
|
|
||||||
and live issues #3181/#2760 plus branch/PR state. Refresh `origin/main` and verify target head before review.
|
|
||||||
|
|
||||||
Review standards and spec. Independently adversarially verify:
|
|
||||||
|
|
||||||
- duplicate JSON member rejection at every depth, both orders, escaped-equivalent names, arrays/objects, and supported
|
|
||||||
serialized source/target resume strings before gates, schema comparison, or hashing;
|
|
||||||
- lexical scanner correctness for valid JSON escapes, primitives, nested structures, malformed input, deep input, and no
|
|
||||||
executable-object access;
|
|
||||||
- all safety flags remain literal booleans and fail closed;
|
|
||||||
- all three manifest IDs reject blank and Unicode control-containing strings while preserving accepted IDs verbatim;
|
|
||||||
- fresh invalid manifests, deterministic stable hashes, exact-envelope contract, docs, and four-file scope;
|
|
||||||
- proportionality and maintainability of complete implementation.
|
|
||||||
|
|
||||||
Run 59 comparator tests, independent probes, 54 API tests, 21 auth tests, affected typechecks, boundaries, narrow
|
|
||||||
Biome/Markdown, import/diff/scope gates. Report findings first with severity and exact anchors. Write
|
|
||||||
`.orchestration/plan-02-review-round5.md`; send worker_done with publication verdict. No subagents.
|
|
||||||
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Independent review: plan 02 synthetic recovery
|
|
||||||
|
|
||||||
Review only; do not edit, commit, push, open PR, mutate issues, or spawn subagents. Read current worktree `AGENTS.md`, RTK,
|
|
||||||
issue/domain guidance, code-review/receiving-code-review/testing skills, implementation report
|
|
||||||
`.orchestration/plan-02-implementation.md`, and approved plan 02. Trust local planning checkout only when HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise read
|
|
||||||
`git show a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d:plans/02-hosted-v4-account-recovery.md`.
|
|
||||||
|
|
||||||
Fetch current `origin/main` and GitHub issue/PR state. Confirm reviewed branch/commit and inspect complete
|
|
||||||
`origin/main...HEAD` diff. Treat prior report as a claim to verify. Review standards and approved-plan fidelity, especially:
|
|
||||||
|
|
||||||
- Comparator is pure, deterministic, non-networked, non-writing, current-schema-only, and never implies owner/source
|
|
||||||
authenticity from hashes. Validate no import-time effects or accidental sensitive logging.
|
|
||||||
- Manifest contracts and tests correctly cover identical, old-only, divergent, owner/mapping/source blocks, malformed input,
|
|
||||||
determinism, immutability, and stable IDs/hashes. Seek false positives and weak self-fulfilling tests.
|
|
||||||
- Docs distinguish hosted operator authority from self-hosted authority, require owner verification/mapping/private delivery,
|
|
||||||
forbid overwrite/default public exposure, state no-source limits, and make no v4 conversion/recovery promise.
|
|
||||||
- Scope contains exactly four approved files. No private data or destructive recovery steps.
|
|
||||||
|
|
||||||
Rerun focused tooling test/typecheck, relevant API/auth tests, affected typechecks, boundaries, narrow Biome/Markdown checks,
|
|
||||||
and `git diff --check`; rerun broader tests only where a finding needs proof. Record skipped gates. Findings first, ordered by
|
|
||||||
severity with exact file/line and concrete evidence. If none, state `No findings` and residual risks.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-02-review.md` with reviewed SHA/base, findings, commands/results, skipped gates, risks, and verdict
|
|
||||||
`ready for publication` or `changes required`. Final response at most ten lines.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Independent review: plan 07 self-hosting documentation
|
|
||||||
|
|
||||||
Review only; do not edit, commit, push, open PR, mutate issues, or spawn subagents. Read current worktree `AGENTS.md`, RTK,
|
|
||||||
issue/domain guidance, code-review/documentation skills, implementation report `.orchestration/plan-07-implementation.md`,
|
|
||||||
and approved plan 07. Trust local planning checkout only when HEAD equals
|
|
||||||
`a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use pinned `git show` for `plans/07-aio-deployment.md`.
|
|
||||||
|
|
||||||
Fetch current `origin/main` and live issue/PR state. Confirm reviewed branch/commit and inspect complete
|
|
||||||
`origin/main...HEAD` diff. Verify every runtime/config claim against current Dockerfile, Compose, env validation/example, and
|
|
||||||
startup code. Review approved scope and reader safety:
|
|
||||||
|
|
||||||
- State supported one-app-container plus separate PostgreSQL topology and no planned AIO image without claiming issue 2722
|
|
||||||
implemented or closed.
|
|
||||||
- Smallest checklist and generic Unraid/homelab guidance use exact current service/path/port/env facts, warn that container
|
|
||||||
`localhost` is wrong for PostgreSQL, and never expose DB publicly or assert official Unraid support.
|
|
||||||
- Managed PostgreSQL reuse, optional Redis/S3 boundaries, database/upload backups, container updates, and PostgreSQL major
|
|
||||||
upgrades are accurate, non-duplicative, cross-linked, and safe for novice operators.
|
|
||||||
- Diff contains only two approved docs and retains existing MDX structure/links.
|
|
||||||
|
|
||||||
Rerun focused `rg`, Compose config, Markdown lint, link inspection, and `git diff --check`. Record unavailable Unraid/Mintlify
|
|
||||||
checks as residual gates, not success. Findings first, ordered by severity with exact file/line and evidence. If none, state
|
|
||||||
`No findings` and residual risks.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-07-review.md` with reviewed SHA/base, findings, commands/results, skipped gates, risks, and verdict
|
|
||||||
`ready for publication` or `changes required`. Final response at most ten lines.
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# Independent review: plan 09 local Git backup documentation
|
|
||||||
|
|
||||||
Review only; do not edit, commit, push, open PR, mutate issues, or spawn subagents. Read current worktree `AGENTS.md`, RTK,
|
|
||||||
issue/domain guidance, code-review/documentation skills, `.orchestration/plan-09-implementation.md`, and approved plan 09.
|
|
||||||
Trust local planning checkout only when HEAD equals `a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use pinned `git show`
|
|
||||||
for `plans/09-external-version-backup.md`.
|
|
||||||
|
|
||||||
Fetch current `origin/main` and live issue/PR state. Confirm reviewed commit and inspect full `origin/main...HEAD` diff. Verify
|
|
||||||
claims against current export/import/version source and tests, then review:
|
|
||||||
|
|
||||||
- Correctly distinguish single-resume JSON, embedded cover-letter sections, independent cover-letter JSON, and account
|
|
||||||
archive. Account archive must not be presented as single-resume import or whole-account restore.
|
|
||||||
- User code uses plain local Git commands only: targeted `git add --`, inspect diff, commit, show. No `rtk`, remote URL,
|
|
||||||
credentials, `git push`, global Git config mutation, or automatic sync.
|
|
||||||
- Restore is import-as-new and non-destructive. Filenames stable. Images described as URL references with availability risk.
|
|
||||||
Private-data and repository-visibility warning is prominent and actionable.
|
|
||||||
- Existing rolling history comparison is accurate and non-duplicative. Scope contains exactly two approved docs.
|
|
||||||
- Synthetic validation/report evidence is reproducible; note database/E2E skips accurately and inspect whether retained temp
|
|
||||||
path creates any repository or privacy risk.
|
|
||||||
|
|
||||||
Rerun focused API/import/schema/web tests, Markdown lint, disposable local-Git sequence using synthetic data, and diff/scope
|
|
||||||
checks where practical. Record skipped DB/E2E gates. Findings first, severity-ordered with file/line and evidence. If none,
|
|
||||||
state `No findings` plus residual risks.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-09-review.md` with reviewed SHA/base, findings, commands/results, skipped gates, risks, and verdict
|
|
||||||
`ready for publication` or `changes required`. Final response at most ten lines.
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# Independent review: plan 11 JSearch/current tailoring documentation
|
|
||||||
|
|
||||||
Review only; do not edit, commit, push, open PR, mutate issues, or spawn subagents. Read current `AGENTS.md`, RTK,
|
|
||||||
issue/domain guidance, code-review/documentation skills, `.orchestration/plan-11-implementation.md`, and approved plan 11.
|
|
||||||
Trust local planning checkout only when HEAD equals `a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`; otherwise use pinned `git show`
|
|
||||||
for `plans/11-job-search-policy.md`.
|
|
||||||
|
|
||||||
Fetch current `origin/main` and live issue/PR state. Inspect complete diff and verify every historical/runtime/UI claim against
|
|
||||||
Git history and current source/tests:
|
|
||||||
|
|
||||||
- v5.1.0 removal timing of JSearch/RapidAPI Job Listings is factual; removal motive remains unknown.
|
|
||||||
- Legacy settings redirect, current Integrations/provider setup, supplied job-description tailoring, attachments, review edits,
|
|
||||||
patch inspection, and Restore labels/workflow match current UI/source.
|
|
||||||
- Provider-native live web search is clearly capability/provider/model-dependent and not equated with structured JSearch
|
|
||||||
results. Unsupported setups can use pasted/attached content without implied live search.
|
|
||||||
- No stale model list, paid API restoration, credentials, quotas, unsupported attachment promise, unverified motive, or issue
|
|
||||||
closing claim. Prose minimal and non-duplicative. Diff exactly three docs.
|
|
||||||
|
|
||||||
Rerun agent/capability tests, Git history/source checks, Markdown lint, links, diff/scope gates. Optional real provider/browser
|
|
||||||
flow remains a named skip. Findings first, severity-ordered with exact file/line/evidence; otherwise `No findings` plus risks.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-11-review.md` with reviewed SHA/base, findings, commands/results, skipped gates, risks, and verdict
|
|
||||||
`ready for publication` or `changes required`. Final response at most ten lines.
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# Independent hosted review: Plan 15A picture fitting
|
|
||||||
|
|
||||||
Review only in `/Users/amruth/orca/workspaces/reactive-resume/issue-2782-picture-fit` and live PR #3461.
|
|
||||||
Exact target head: `6145d5a5925373287b87dfaa30ab675ebc84e105`. Do not edit tracked files, commit, push, reply,
|
|
||||||
resolve threads, merge, or mutate issues.
|
|
||||||
|
|
||||||
Read current repository instructions, pinned approved Plan 15, implementation and review reports, full
|
|
||||||
`origin/main...HEAD` diff, issue #2782, live PR checks/reviews, and every current review thread. Refresh base and verify
|
|
||||||
exact head. Run root Intent inventory and load matching review skill if any; no subagents.
|
|
||||||
|
|
||||||
Adjudicate hosted feedback independently. Current Codacy threads note:
|
|
||||||
|
|
||||||
- Contain uploads intentionally bypass cropping to preserve original image; users can only access crop flow in Cover.
|
|
||||||
- `PictureFitField` could map over fit-option metadata instead of declaring two buttons.
|
|
||||||
|
|
||||||
Determine whether either is a real Standards or approved-Spec defect. Verify selected-mode behavior, accessible labels,
|
|
||||||
autosave/lock behavior, original-file preservation, warning copy, test mutation sensitivity, and whether refactoring would
|
|
||||||
improve correctness rather than merely alter style. Inspect any CodeRabbit or later threads that exist at review time.
|
|
||||||
|
|
||||||
Run focused web/schema/PDF tests and narrow non-writing formatting/diff gates as needed. Report findings first with
|
|
||||||
severity and anchors, exact-head/check state, each thread disposition, and publication verdict in
|
|
||||||
`.orchestration/plan-15a-hosted-review.md`; send `worker_done`.
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Independently review plan 15A picture fitting
|
|
||||||
|
|
||||||
Review only. Read pinned approved plan 15, current `AGENTS.md`, RTK, applicable code-review skill, implementation report,
|
|
||||||
and complete `origin/main...d891afd667dc571dcee642d54f851f6bde45fad8` diff. Revalidate live issue/PR/base state.
|
|
||||||
|
|
||||||
Review Standards and Spec axes. Verify:
|
|
||||||
|
|
||||||
- schema/default/sample/v4 import compatibility defaults missing/invalid fit to Cover without corrupting data;
|
|
||||||
- Cover retains current crop dialog, cancel/error/locked/autosave/undo behavior; Contain uploads full selected file through
|
|
||||||
existing validated storage path and never implies already-cropped pixels can be restored;
|
|
||||||
- sidebar and every PDF template route through one shared fit contract; semantic CSS precedence and borders/shadows stay
|
|
||||||
correct; Cover output remains backward compatible;
|
|
||||||
- controls are named, accessible, localized through correct catalog workflow, and generated docs/skill schema match source;
|
|
||||||
- tests assert behavior rather than implementation, raster tolerances are meaningful, E2E does not add brittle global
|
|
||||||
state, hardcoded local assumptions, unsafe cleanup, production-only dependencies, or a hidden network requirement;
|
|
||||||
- exact issue #2782 scope; no claims for #3168/#3088/#2794 and no unrelated generated artifacts.
|
|
||||||
|
|
||||||
Run fresh base/live checks, focused schema/web/PDF/import tests, affected typechecks, boundaries, narrow Biome/catalog/docs,
|
|
||||||
diff/scope. Inspect E2E source and run dedicated raster E2E when disposable DB/ports are safely available; otherwise state
|
|
||||||
exact gate. Do not accept implementation report as proof without independent commands/probes.
|
|
||||||
|
|
||||||
Write `.orchestration/plan-15a-review.md` with findings first and file/line evidence, exact head, commands/results, skips,
|
|
||||||
risks, and publication verdict. Do not edit tracked files, push, open PR, merge, mutate issues, or spawn subagents. Final
|
|
||||||
response at most ten lines.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Independent review: Plan 16 editable imported tables
|
|
||||||
|
|
||||||
Review only in `/Users/amruth/orca/workspaces/reactive-resume/issue-3196-editable-tables`.
|
|
||||||
Exact target head: `83aca184e`. Do not edit tracked files, commit, push, publish, merge, or mutate issues.
|
|
||||||
|
|
||||||
Read current instructions, pinned approved Plan 16, implementation report, full `origin/main...HEAD` diff, live issue
|
|
||||||
issue #3196, merged #3438, open PR overlap, and relevant editor/PDF/import domain docs. Refresh base and verify exact head.
|
|
||||||
Run root Intent inventory and load matching review skill if any; no subagents.
|
|
||||||
|
|
||||||
Review Standards and Spec. Independently verify:
|
|
||||||
|
|
||||||
- supported 2x3 tables parse as structured Tiptap nodes; mount and unrelated prop updates emit no destructive change;
|
|
||||||
- named-cell edit, paste, spans, multiple paragraphs, inline marks, undo/redo, save/reload, and HTML round-trip remain lossless;
|
|
||||||
- unsupported markup is detected before normalization, exact original HTML is preserved, accessible read-only notice works,
|
|
||||||
ordinary/locked/keyboard/reopen/cancel flows cannot overwrite it, and confirmed plain-text conversion is explicit;
|
|
||||||
- CSS declaration precedence, widths/spans/borders and borderless behavior hold without unsafe HTML widening;
|
|
||||||
- PDF tests prove six cell coordinates, exact border operators, and fixed-DPI pixels in legacy/semantic modes;
|
|
||||||
- synthetic import E2E covers editor persistence, unrelated edit, browser/server PDF and cleanup;
|
|
||||||
- Tiptap dependency/lockfile delta is minimal, compatible, licensed, and boundary-safe;
|
|
||||||
- no regression to #3438, non-table rich input, SSR, accessibility, or package ownership;
|
|
||||||
- `.orchestration/plan-16-implementation.md` presence in product commit is intentional or report as scope hygiene finding.
|
|
||||||
|
|
||||||
Run RED-evidence sanity review; focused table/indent/PDF suites plus broader affected web/PDF tests, typechecks, boundaries,
|
|
||||||
frozen install, build, narrow non-writing Biome, diff/scope gates, and dedicated DB/local-storage E2E. Report findings first
|
|
||||||
with severity/anchors and publication verdict in `.orchestration/plan-16-review.md`; send worker_done.
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# Independent review: Plan 20A hidden-section recovery
|
|
||||||
|
|
||||||
Review only in `/Users/amruth/orca/workspaces/reactive-resume/issue-3378-hidden-section-recovery` on branch
|
|
||||||
`codex/issue-2921-hidden-section-recovery`. Exact target head:
|
|
||||||
`0ec054df70e6f445557bd3a54a0d62686586b3de`. Do not edit tracked files, commit, push, publish, merge, or mutate issues.
|
|
||||||
|
|
||||||
Read current repository instructions, pinned approved Plan 20, implementation report, full `origin/main...HEAD` diff,
|
|
||||||
live issues #2921/#3378/#3265, open PR overlap, and builder/resume-domain guidance. Refresh base and verify exact head.
|
|
||||||
Run root Intent inventory and load matching review skill if any; no subagents.
|
|
||||||
|
|
||||||
Review Standards and Spec independently. Verify especially:
|
|
||||||
|
|
||||||
- pure `getSectionAvailability` inventory covers all printable built-ins, Summary, and real custom sections while excluding
|
|
||||||
Picture, Basics, UI-only custom container, and unknown IDs; placement and hidden state remain independent; duplicate,
|
|
||||||
later-page, and sidebar locations work without mutating data;
|
|
||||||
- compact recovery UI replaces full editor panels only for hidden printable sections; effective localized titles, custom
|
|
||||||
child behavior, navigation/focus, keyboard access, locked disablement, and undo/redo are correct;
|
|
||||||
- Show changes only the existing hidden flag, preserving content, item order, and byte-equivalent layout arrays; an
|
|
||||||
unplaced hidden section remains unplaced and no 20B placement behavior appears;
|
|
||||||
- custom-section editor container stays usable when only some custom children are hidden;
|
|
||||||
- package export, ownership, named props, SSR, Lingui catalog/source handling, and accessibility follow repository rules;
|
|
||||||
- tests are mutation-sensitive and authenticated E2E proves save/reload, compact entries, PDF absence/restoration, exact
|
|
||||||
authored layout preservation, undo/redo, locked state, and cleanup without accepting stale Saved state;
|
|
||||||
- PR scope must reference #2921 only and must not claim recovery for #3378/#3265 or deleted content.
|
|
||||||
|
|
||||||
Run focused resume/web tests plus relevant existing visibility/menu/navigation regressions, affected typechecks, boundaries,
|
|
||||||
production build, narrow non-writing Biome, Lingui/diff/scope gates, and isolated authenticated E2E if practical. Report
|
|
||||||
findings first with severity/anchors and publication verdict in `.orchestration/plan-20a-review.md`; send `worker_done`.
|
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
---
|
||||||
|
title: "Large RPC requests"
|
||||||
|
description: "Reference for the staged-body protocol that lets RPC requests larger than the hosting request-body limit reach Reactive Resume on Vercel."
|
||||||
|
---
|
||||||
|
|
||||||
|
Vercel limits Function request bodies to 4.5 MB. On Vercel installations, RPC requests larger than that are uploaded to private Blob staging first. The server then restores the original request and runs it with the normal authorization, validation, and quota checks.
|
||||||
|
|
||||||
|
The web app uses this protocol automatically for request bodies of 3 MiB or more. Docker installations do not need it.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
| Item | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| Applies to | `POST /api/rpc/...` only |
|
||||||
|
| Does not apply to | REST (`/api/openapi`) and MCP. Their bodies stay subject to the 4.5 MB limit. |
|
||||||
|
| Maximum staged size | 160 MiB of serialized request bytes, including base64 and RPC framing |
|
||||||
|
| Reference lifetime | Upload URL: 5 minutes. Staging reference: 10 minutes. |
|
||||||
|
| Use count | One. A reference is consumed when a finalization request passes the user and path checks, whether the RPC call then succeeds or fails. |
|
||||||
|
| Rate limit | 30 staging requests per user per minute |
|
||||||
|
|
||||||
|
## Protocol
|
||||||
|
|
||||||
|
### 1. Prepare
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /api/storage/stage
|
||||||
|
Content-Type: application/json
|
||||||
|
x-api-key: YOUR_API_KEY
|
||||||
|
|
||||||
|
{ "path": "/api/rpc/storage/uploadFile", "contentType": "multipart/form-data; boundary=...", "size": 10485861 }
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `path` | string | Pathname and query string of the original RPC request. Must start with `/api/rpc`. |
|
||||||
|
| `contentType` | string | `Content-Type` header of the original request, including any multipart boundary. |
|
||||||
|
| `size` | integer | Exact byte length of the serialized original body. |
|
||||||
|
|
||||||
|
Authenticate with a session cookie, an `x-api-key` header, or an OAuth bearer token. Browsers must send an `Origin` header that matches the application origin.
|
||||||
|
|
||||||
|
Response `200`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "id": "3f2b9c1e-6a0d-4a57-9d0a-3c1f7b8e2d44", "url": "https://..." }
|
||||||
|
```
|
||||||
|
|
||||||
|
The endpoint returns `404` when the installation does not support staging, for example on Docker. Send the original request unchanged in that case.
|
||||||
|
|
||||||
|
### 2. Upload
|
||||||
|
|
||||||
|
```http
|
||||||
|
PUT <url from step 1>
|
||||||
|
Content-Type: application/octet-stream
|
||||||
|
|
||||||
|
<exact serialized body bytes>
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not send application credentials to this URL. The body must be exactly `size` bytes.
|
||||||
|
|
||||||
|
### 3. Finalize
|
||||||
|
|
||||||
|
Send the original request with an empty body and the staging reference header:
|
||||||
|
|
||||||
|
```http
|
||||||
|
POST /api/rpc/storage/uploadFile
|
||||||
|
x-api-key: YOUR_API_KEY
|
||||||
|
x-resume-staged-body: 3f2b9c1e-6a0d-4a57-9d0a-3c1f7b8e2d44
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the same `path` and the same user as in step 1. The server replaces the body with the staged bytes, sets `Content-Type` to the stored `contentType`, and returns the normal RPC response.
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Step | Cause |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `400` | Prepare | Invalid JSON, `path` outside `/api/rpc`, or `size` above the maximum. |
|
||||||
|
| `400` | Finalize | Malformed reference, non-`POST` request, or staged object missing. |
|
||||||
|
| `401` | Prepare, finalize | Not authenticated, or `Origin` does not match. |
|
||||||
|
| `403` | Finalize | Reference belongs to another user or another path. |
|
||||||
|
| `404` | Prepare | Staging not available on this installation. |
|
||||||
|
| `409` | Finalize | Reference used by a parallel request. |
|
||||||
|
| `410` | Finalize | Reference expired or already used. |
|
||||||
|
| `413` | Finalize | Uploaded byte count differs from `size`. |
|
||||||
|
| `429` | Prepare | Rate limit exceeded. |
|
||||||
|
| `503` | Prepare | Redis unavailable. |
|
||||||
|
|
||||||
|
A reference cannot be retried. If a finalization response is lost, check the result of the mutation before you stage and send it again.
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
# Europass research and visual proposal — Plan 33A
|
|
||||||
|
|
||||||
Status: **research complete; visual and naming approval pending**. No renderer, template ID, gallery entry,
|
|
||||||
schema change, or import/export integration is included. This proposal does not resolve issue #2689.
|
|
||||||
|
|
||||||
## Review package
|
|
||||||
|
|
||||||
- [Reference comparison](artifacts/reference-comparison.svg): analytical redraw of the selected official
|
|
||||||
reference beside the mapped proposal. The redraw excludes branding and source-specific recruitment text.
|
|
||||||
- [One-page proposal](artifacts/one-page.svg): canonical static layout with exact synthetic field sources.
|
|
||||||
- Overflow proposal: [page 1](artifacts/overflow-1.svg), [page 2](artifacts/overflow-2.svg),
|
|
||||||
[page 3](artifacts/overflow-3.svg). These are one document, not three template options.
|
|
||||||
- [Generated concept](artifacts/concept-one-page.png): supplementary visual exploration, not the field or
|
|
||||||
geometry contract. The SVGs take precedence.
|
|
||||||
- [Field mapping and behavior](mapping.md), [synthetic fixture records](synthetic-fixtures.json), and
|
|
||||||
[generation provenance](artifacts/PROVENANCE.md).
|
|
||||||
|
|
||||||
Open SVGs directly in a browser. They contain their text and geometry, require no application server,
|
|
||||||
and load no external images, fonts or scripts. A local sans-serif font is sufficient. The JSON contains
|
|
||||||
two complete ResumeData objects under `onePage` and `overflow`; neither is an official Europass file.
|
|
||||||
|
|
||||||
## Authority and repository revalidation
|
|
||||||
|
|
||||||
Access/revalidation date: **2026-09-06**. Research base: `b85d285b69843612e9d7f0ab802248982e7bf0ea`.
|
|
||||||
Planning authority: commit `a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d`,
|
|
||||||
[Plan 33](https://github.com/reactive-resume/reactive-resume/blob/a2557b2ad40e06e1e63eb655f286e6a78fe6bf0d/plans/33-europass-template.md)
|
|
||||||
and its recorded decisions. Approval covers this research/proposal unit; concrete visual approval remains open.
|
|
||||||
|
|
||||||
Read-only `gh issue view 2689` confirmed the [issue](https://github.com/reactive-resume/reactive-resume/issues/2689)
|
|
||||||
is open, last updated 2026-08-16. Its request for a built-in option is relevant; assertions about compulsory
|
|
||||||
formats are not adopted. The linked community converter remains third-party work, not an import contract.
|
|
||||||
|
|
||||||
The complete open-PR list returned four PRs: [#3455](https://github.com/reactive-resume/reactive-resume/pull/3455)
|
|
||||||
(planning), [#3456](https://github.com/reactive-resume/reactive-resume/pull/3456) (execution ledger),
|
|
||||||
[#3467](https://github.com/reactive-resume/reactive-resume/pull/3467) (authored-page guidance), and
|
|
||||||
[#3468](https://github.com/reactive-resume/reactive-resume/pull/3468) (stylesheet diagnostics).
|
|
||||||
None proposed a Europass renderer. Pagination guidance is adjacent work; no shared source is changed here.
|
|
||||||
|
|
||||||
The schema still has 15 template IDs and no Europass ID. The plan's targeted drift check from `7a98f6662`
|
|
||||||
found only changes to an unrelated rich-text table integration test. Current ResumeData and shared filtering
|
|
||||||
were read directly; no `.codegraph/` directory was present. Intent inventory returned
|
|
||||||
“No intent-enabled packages found,” so no matching local package skill was available to load.
|
|
||||||
|
|
||||||
## Official evidence register
|
|
||||||
|
|
||||||
All sources below were accessed on **2026-09-06**. Only EU/Europass first-party material informs external
|
|
||||||
claims. Copyright attribution for summarized Europass information: **© European Union**. The independent
|
|
||||||
synthetic examples and diagrams are adaptations for discussion, not official Europass output.
|
|
||||||
|
|
||||||
| ID | Source | Supported finding and limit |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| S1 | [Create your Europass CV](https://europass.europa.eu/en/create-europass-cv) | Current guidance offers a choice of designs and selected profile content. It recommends clear, tailored descriptions and reverse chronology. Its photograph advice is guidance, not evidence of a universal required field. No single fixed layout is specified here. |
|
|
||||||
| S2 | [Europass FAQ, page 1](https://europass.europa.eu/en/faq?page=1) | CV sections can be added, removed, moved and renamed; several templates exist. Current saving guidance specifies Europass PDF. Information reuse is allowed with source acknowledgement. This is not proof of a logo licence or compatibility of third-party PDFs with the editor. |
|
|
||||||
| S3 | [Europass FAQ, page 2](https://europass.europa.eu/en/faq?page=2) | The profile FAQ says name and surname are mandatory and users choose other sections. Initial web extraction failed; a direct HTTPS fetch subsequently verified the live page text. This concerns the profile, not a separately tested CV-editor validation contract. |
|
|
||||||
| S4 | [EEAS-hosted Europass CV form](https://www.eeas.europa.eu/sites/default/files/documents/2025/Europass-cv-en%20template_0.pdf) | Stable public two-page blank form, inspected as PDF and raster. It contains a legacy © European Union 2002–2018 footer and recruitment-specific text. Its 2025 URL does not make its design a current universal specification. Headings are optional in this form. This is the selected visual reference. |
|
|
||||||
| S5 | [Europass terms of use](https://europass.europa.eu/en/node/2161) | Terms govern platform use and prohibit misleading content and intellectual-property violations. No explicit third-party template-branding permission was found in this page. |
|
|
||||||
| S6 | [European Commission legal notice](https://commission.europa.eu/legal-notice_en) | The general policy permits reuse of covered EU-owned website content with credit and indication of changes, but excludes protected names, logos and other industrial-property material. It is not a blanket licence for every asset hosted by another EU institution. |
|
|
||||||
|
|
||||||
### Reference selection and current guidance
|
|
||||||
|
|
||||||
S4 provides an inspectable visual anchor: a narrow label/date area at reading start, wider detail area,
|
|
||||||
blue headings and rules, employment and education entries, a language matrix, and running page metadata.
|
|
||||||
The comparison redraw records these observations; measurements in the proposal are our design decisions.
|
|
||||||
We do not redistribute the original PDF or its mark. Reviewers can open the first-party PDF through S4.
|
|
||||||
|
|
||||||
S1/S2 are the current behavioral guidance. They support flexible content and multiple layouts; they do not
|
|
||||||
establish S4 as today's sole layout. The proposed document therefore takes the chronology structure from
|
|
||||||
S4 while adapting it to existing data and the current guidance. No official live-editor CV was exported
|
|
||||||
or uploaded, and no account or personal data was used.
|
|
||||||
|
|
||||||
An EEA search result for historic CV instructions redirected to the agency's general About page when
|
|
||||||
opened. That result is not used as evidence. Historic XML documentation also surfaced but is not used to
|
|
||||||
promise current export formats or interoperability. No secondary-source claims were adopted.
|
|
||||||
|
|
||||||
## Material visual and reuse decisions
|
|
||||||
|
|
||||||
Recommended working title: **European chronology**. Do not register a product ID yet. “Europass” is the
|
|
||||||
research topic; use as a public template name remains an explicit maintainer decision with an unresolved
|
|
||||||
rights basis. The design contains no Europass wordmark, EU flag, emblem, endorsement, official copyright
|
|
||||||
footer, or interoperability badge. General website information reuse does not settle branding rights.
|
|
||||||
|
|
||||||
The proposed default is A4, white background, dark blue `#1e5580`, charcoal `#20262b`, 36 pt side margins,
|
|
||||||
a 128 pt label/date area and 16 pt gap, with details beginning at x = 180 pt. Body text is 10.5 pt sans-serif,
|
|
||||||
headings are 10 pt bold, and the name is 23 pt. The line and color choices are proposals, not official tokens.
|
|
||||||
The static one-page drawing uses more deliberate section spacing than the dense overflow study.
|
|
||||||
|
|
||||||
Deviations requiring approval:
|
|
||||||
|
|
||||||
1. An independent unbranded design, with neutral provisional name, instead of an exact official clone.
|
|
||||||
2. A chronology gutter for dates and headings, coordinated with Plan 24 rather than introducing a new
|
|
||||||
date-column schema here. Empty date cells keep alignment; periods are never parsed or normalized.
|
|
||||||
3. Plain language/fluency text instead of the five-dimensional language grid. Numeric levels are not CEFR
|
|
||||||
evidence. Skill ratings are hidden by the fixture's existing design setting.
|
|
||||||
4. No compulsory photo or personal attributes. Photo-free examples reserve no blank frame. Optional photo
|
|
||||||
placement and mixed-script examples still need a separate visual pass before renderer approval.
|
|
||||||
5. Existing section titles and custom sections remain available. The concept does not force a fixed set of
|
|
||||||
official headings or discard additional resume sections.
|
|
||||||
6. Continuation pages retain only a small name/running header and page number, with content flowing below.
|
|
||||||
The synthetic approval watermark is an artifact annotation, not proposed resume output.
|
|
||||||
|
|
||||||
## Unresolved claims and handoff gate
|
|
||||||
|
|
||||||
- Permission to ship the Europass name, logo, exact branded template or official-looking footer is **not
|
|
||||||
established**. No such permission is inferred from S2 or S6. If maintainers require branding or an exact
|
|
||||||
clone, resolve rights and reference choice before any implementation.
|
|
||||||
- The chosen legacy structure is documented, but whether it satisfies the request for a current built-in
|
|
||||||
Europass option needs maintainer review of these actual artifacts. A current editor design would be a
|
|
||||||
different reference-selection task, not a silent replacement.
|
|
||||||
- Mandatory CV-editor fields, exact language availability, and export embedding details were not validated
|
|
||||||
interactively. Official pages vary in language counts; this proposal makes no count or import promise.
|
|
||||||
- Structured CEFR dimensions, mother-tongue flags and qualification-framework levels are absent from
|
|
||||||
current ResumeData. They are omissions, not guessed values. See the complete mapping.
|
|
||||||
- Renderer pagination, links, fonts, RTL shaping, tagged accessibility and machine extraction are untested
|
|
||||||
for this proposal. Static artifacts are not evidence that a future PDF renderer implements those behaviors.
|
|
||||||
- This task stops at the planned concrete-design gate. Approval must identify the canonical SVG set,
|
|
||||||
public naming/branding choice and accepted data omissions; it cannot be inferred from general plan approval.
|
|
||||||
|
|
||||||
Follow-up implementation remains conditional. It would require the plan's schema, semantic manifest,
|
|
||||||
gallery, renderer, pagination, extraction, localization, typecheck, build and boundary checks after the
|
|
||||||
maintainer approves the design. No implementation, push, PR creation, issue comment or issue closure occurred here.
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
# Visual artifact provenance
|
|
||||||
|
|
||||||
Created 2026-09-06 for Plan 33A. All biographical content, organizations, qualifications and achievements are
|
|
||||||
synthetic. `example.invalid` links are intentionally nonresolving test data. No personal files, CVs, account
|
|
||||||
data or real portrait were used.
|
|
||||||
|
|
||||||
## Canonical static artifacts
|
|
||||||
|
|
||||||
`one-page.svg` and `overflow-1.svg` through `overflow-3.svg` are editable static drawings with text traced
|
|
||||||
to [fixture](../synthetic-fixtures.json) paths using `data-source`. They are research artifacts, not a resume
|
|
||||||
renderer or template implementation. SVG source is the reproducible artifact; raster previews are optional.
|
|
||||||
|
|
||||||
`reference-comparison.svg` juxtaposes an original analytical schematic with the one-page SVG. The schematic
|
|
||||||
describes the selected [official EEAS reference](https://www.eeas.europa.eu/sites/default/files/documents/2025/Europass-cv-en%20template_0.pdf),
|
|
||||||
© European Union, accessed 2026-09-06. It is not a screenshot or faithful reproduction, omits the mark and
|
|
||||||
recruitment-specific content, and labels the legacy provenance. Original reference remains on its official host.
|
|
||||||
|
|
||||||
The static drawings use sans-serif text and editable geometry. Browser rendering may select a different
|
|
||||||
installed font. They are intentionally independent of the app, and include no network resource dependencies.
|
|
||||||
An isolated PyMuPDF environment was used to rasterize the drawings for visual inspection. No raster screenshot
|
|
||||||
is evidence of Reactive Resume's PDF behavior.
|
|
||||||
|
|
||||||
To regenerate a raster from the canonical SVG without application dependencies:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
uv run --with pymupdf python - <<'PY'
|
|
||||||
from pathlib import Path
|
|
||||||
import pymupdf
|
|
||||||
|
|
||||||
source = Path("docs/research/europass/artifacts/one-page.svg")
|
|
||||||
image = pymupdf.open("svg", source.read_bytes())
|
|
||||||
pdf = pymupdf.open("pdf", image.convert_to_pdf())
|
|
||||||
pdf[0].get_pixmap(matrix=pymupdf.Matrix(2, 2)).save("one-page-review.png")
|
|
||||||
PY
|
|
||||||
```
|
|
||||||
|
|
||||||
## Supplementary image generation
|
|
||||||
|
|
||||||
`concept-one-page.png` was generated with the built-in image generation tool. No CLI/API fallback was used.
|
|
||||||
The selected output was copied into this repository; review does not require the tool's private output folder.
|
|
||||||
It is a visual exploration only. Its name is positioned farther right, rules span both columns, some typography
|
|
||||||
and spacing differ, and it adds minor presentational punctuation/“area” wording. These differences are not
|
|
||||||
approved specification changes. The mapped SVGs and fixture take precedence over generated pixels.
|
|
||||||
|
|
||||||
Exact prompt:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Use case: productivity-visual. Asset type: static CV design proposal, one portrait A4 page, straight-on flat white document, no desk or mockup shadows. Independent unbranded European chronological CV concept, not an official document. Exact synthetic content only. Header at upper right of broad content column: Alex Marin in large dark blue sans serif, below it Research coordinator; below: Brussels, Belgium | alex.marin@example.invalid. Narrow reading-start column for dates and section labels (about 27 percent), broad content column 73 percent, thin blue horizontal section rules. Airy white page, charcoal 10.5pt-equivalent body, dark blue uppercase section headings. No photograph and no placeholder box. Sections exact text: ABOUT ME: Research coordinator building clear public-service guidance. WORK EXPERIENCE: date 2023 – Present, title Research coordinator, organisation Civic Atlas Lab, Brussels, Belgium. Two bullets: Coordinated multilingual guidance reviews. Published accessible research summaries. EDUCATION AND TRAINING: date 2019 – 2022, title BA Social Research, organisation Northbridge Institute, area Public policy. LANGUAGE SKILLS: English — Native; French — B2 (self-assessed). SKILLS: Research — Advanced; Interviews, synthesis, documentation. PROJECTS: date 2025, title Open Guidance Map; Mapped public learning resources. OTHER ACTIVITIES: Community workshops and peer mentoring. Footer small: SYNTHETIC DESIGN PROPOSAL · NOT APPROVED, with 1 / 1 at right. No logos, EU flag, stars, Europass wordmark, compliance claims, nationality, date of birth, QR codes, extra personal facts, ratings or CEFR matrix. All text legible. This is concept direction only; deterministic separate artifacts will govern exact mapping.
|
|
||||||
```
|
|
||||||
|
|
||||||
This image was inspected for recognizable text, missing sections, branding and private-data leakage. All seven
|
|
||||||
intended sections are present and no official mark or portrait appears. No image-generation claim is used as
|
|
||||||
evidence about official guidance, reuse rights, application behavior or field support.
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 941 KiB |
@@ -1,44 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="595.28pt" height="841.89pt" viewBox="0 0 595.28 841.89" role="img" aria-labelledby="title desc"><title id="title">One-page visual proposal</title><desc id="desc">Synthetic ResumeData illustration. Static layout, not renderer output. See mapping.md.</desc><g font-family="Helvetica"><rect width="595.28" height="841.89" fill="white"/>
|
|
||||||
<text font-family="sans-serif" x="180" y="62" font-size="23" fill="#1e5580" font-weight="700" data-source="basics.name">Alex Marin</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="88" font-size="10.5" fill="#20262b" font-weight="400" data-source="basics.headline">Research coordinator</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="109" font-size="10.5" fill="#20262b" font-weight="400" data-source="basics.location">Brussels, Belgium</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="126" font-size="10.5" fill="#20262b" font-weight="400" data-source="basics.email">alex.marin@example.invalid</text>
|
|
||||||
<text font-family="sans-serif" x="36" y="168" font-size="10" fill="#1e5580" font-weight="700" data-source="summary.title">ABOUT ME</text>
|
|
||||||
<path d="M180 165 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="180" y="190" font-size="10.5" fill="#20262b" font-weight="400" data-source="summary.content">Research coordinator building clear public-service guidance.</text>
|
|
||||||
<text font-family="sans-serif" x="36" y="231" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.experience.title">WORK EXPERIENCE</text>
|
|
||||||
<path d="M180 228 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="36" y="254" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].period">2023 – Present</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="254" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.experience.items[0].position">Research coordinator</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="272" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].company">Civic Atlas Lab</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="289" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].location">Brussels, Belgium</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="309" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].description">• Coordinated multilingual guidance reviews.</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="326" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].description">• Published accessible research summaries.</text>
|
|
||||||
<text font-family="sans-serif" x="36" y="367" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.education.title">EDUCATION AND</text>
|
|
||||||
<text font-family="sans-serif" x="36" y="380" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.education.title">TRAINING</text>
|
|
||||||
<path d="M180 364 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="36" y="401" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.education.items[0].period">2019 – 2022</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="401" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.education.items[0].degree">BA Social Research</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="419" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.education.items[0].school">Northbridge Institute</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="436" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.education.items[0].area">Public policy</text>
|
|
||||||
<text font-family="sans-serif" x="36" y="477" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.languages.title">LANGUAGE SKILLS</text>
|
|
||||||
<path d="M180 474 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="180" y="500" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.languages.items[0].language">English</text>
|
|
||||||
<text font-family="sans-serif" x="270" y="500" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.languages.items[0].fluency">Native</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="521" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.languages.items[1].language">French</text>
|
|
||||||
<text font-family="sans-serif" x="270" y="521" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.languages.items[1].fluency">B2 (self-assessed)</text>
|
|
||||||
<text font-family="sans-serif" x="36" y="562" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.skills.title">SKILLS</text>
|
|
||||||
<path d="M180 559 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="180" y="585" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.skills.items[0].name">Research</text>
|
|
||||||
<text font-family="sans-serif" x="270" y="585" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.skills.items[0].proficiency">Advanced</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="606" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.skills.items[0].keywords">Interviews, synthesis, documentation</text>
|
|
||||||
<text font-family="sans-serif" x="36" y="647" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.projects.title">PROJECTS</text>
|
|
||||||
<path d="M180 644 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="36" y="670" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].period">2025</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="670" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.projects.items[0].name">Open Guidance Map</text>
|
|
||||||
<text font-family="sans-serif" x="180" y="690" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">Mapped public learning resources.</text>
|
|
||||||
<text font-family="sans-serif" x="36" y="731" font-size="10" fill="#1e5580" font-weight="700" data-source="customSections[0].title">OTHER ACTIVITIES</text>
|
|
||||||
<path d="M180 728 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="180" y="754" font-size="10.5" fill="#20262b" font-weight="400" data-source="customSections[0].items[0].content">Community workshops and peer mentoring.</text>
|
|
||||||
<text font-family="sans-serif" x="36" y="808" font-size="8" fill="#56636d" font-weight="400" data-source="proposal.annotation">SYNTHETIC DESIGN PROPOSAL · NOT APPROVED</text>
|
|
||||||
<text font-family="sans-serif" x="526" y="808" font-size="8" fill="#56636d" font-weight="400" data-source="proposal.annotation">1 / 1</text></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 6.6 KiB |
@@ -1,50 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="595.28pt" height="841.89pt" viewBox="0 0 595.28 841.89" role="img" aria-labelledby="title desc"><title id="title">overflow visual proposal, page 1</title><desc id="desc">Synthetic ResumeData illustration. Static pagination, not renderer output. See mapping.md.</desc><g font-family="Helvetica"><rect width="595.28" height="841.89" fill="white"/>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="59.00" font-size="23" fill="#1e5580" font-weight="700" data-source="basics.name">Alexandra Noor Marin-López</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="86.00" font-size="10.5" fill="#1e5580" font-weight="400" data-source="basics.headline">Research coordinator for multilingual public-service information</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="103.70" font-size="10.5" fill="#20262b" font-weight="400" data-source="basics.location">Brussels, Belgium</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="121.40" font-size="10.5" fill="#20262b" font-weight="400" data-source="basics.email">alex.marin@example.invalid</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="139.10" font-size="10.5" fill="#20262b" font-weight="400" data-source="basics.website.label">Research portfolio</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="156.80" font-size="10.5" fill="#20262b" font-weight="400" data-source="basics.customFields[0].text">Available for cross-border project work</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="190.50" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.profiles.title">PROFILES</text>
|
|
||||||
<path d="M180 187.50 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="210.50" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.profiles.items[0].network">Portfolio</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="228.20" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.profiles.items[0].username">alexandra</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="245.90" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.profiles.items[0].website.label">Selected writing</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="282.60" font-size="10" fill="#1e5580" font-weight="700" data-source="summary.title">ABOUT ME</text>
|
|
||||||
<path d="M180 279.60 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="302.60" font-size="10.5" fill="#20262b" font-weight="400" data-source="summary.content">Research coordinator building clear public-service guidance.</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="333.30" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.experience.title">WORK EXPERIENCE</text>
|
|
||||||
<path d="M180 330.30 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="353.30" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].period">2023 – Present</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="353.30" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.experience.items[0].position">Research coordinator</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="371.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].company">Civic Atlas Lab</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="388.70" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].location">Brussels, Belgium</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="406.40" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].description">Supported a distributed team producing accessible guidance for community</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="421.10" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].description">learning programmes.</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="439.80" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].roles[0].period">September 2024 –</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="454.50" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].roles[0].period">Present</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="469.20" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].roles[0].period">(part-time)</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="439.80" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.experience.items[0].roles[0].position">Programme lead</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="483.90" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].roles[0].description">Led editorial planning across four working languages.</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="502.60" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].roles[1].period">January 2023 – August</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="517.30" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].roles[1].period">2024</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="502.60" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.experience.items[0].roles[1].position">Research associate</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="532.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].roles[1].description">Built an evidence catalogue for public learning services.</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="557.70" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[1].period">Autumn 2021 – Summer</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="572.40" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[1].period">2022</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="557.70" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.experience.items[1].position">Research assistant</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="575.40" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[1].company">Open Learning Observatory</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="593.10" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[1].location">Leuven, Belgium</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="610.80" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[1].description">Collected public documentation and organised findings for volunteer reviewers.</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="648.50" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.education.title">EDUCATION AND</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="661.50" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.education.title">TRAINING</text>
|
|
||||||
<path d="M180 645.50 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="680.50" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.education.items[0].period">2019 – 2022</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="680.50" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.education.items[0].degree">BA Social Research</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="698.20" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.education.items[0].school">Northbridge Institute</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="715.90" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.education.items[0].area">Public policy</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="733.60" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.education.items[0].grade">Distinction</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="751.30" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.education.items[0].location">Leuven, Belgium</text>
|
|
||||||
|
|
||||||
<text font-family="sans-serif" x="36.00" y="808.00" font-size="8" fill="#56636d" font-weight="400" data-source="proposal.annotation">SYNTHETIC DESIGN PROPOSAL · NOT APPROVED</text>
|
|
||||||
<text font-family="sans-serif" x="526.00" y="808.00" font-size="8" fill="#56636d" font-weight="400" data-source="proposal.annotation">1 / 3</text></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 8.4 KiB |
@@ -1,45 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="595.28pt" height="841.89pt" viewBox="0 0 595.28 841.89" role="img" aria-labelledby="title desc"><title id="title">overflow visual proposal, page 2</title><desc id="desc">Synthetic ResumeData illustration. Static pagination, not renderer output. See mapping.md.</desc><g font-family="Helvetica"><rect width="595.28" height="841.89" fill="white"/>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="42.00" font-size="11" fill="#1e5580" font-weight="700" data-source="basics.name">Alexandra Noor Marin-López</text>
|
|
||||||
<text font-family="sans-serif" x="420.00" y="42.00" font-size="9" fill="#1e5580" font-weight="400" data-source="proposal.annotation">Continuation</text>
|
|
||||||
<path d="M36 52 H559" stroke="#c2cdd5"/><text font-family="sans-serif" x="36" y="76" font-size="10" fill="#1e5580" data-source="proposal.annotation">EDUCATION</text><text font-family="sans-serif" x="36" y="89" font-size="10" fill="#1e5580" data-source="proposal.annotation">CONTINUED</text><text font-family="sans-serif" x="180" y="76" font-size="10.5" data-source="sections.education.items[0].description">Studied research methods, plain-language writing and collaborative</text><text font-family="sans-serif" x="180" y="90.7" font-size="10.5" data-source="sections.education.items[0].description">service design.</text>
|
|
||||||
|
|
||||||
<text font-family="sans-serif" x="36.00" y="153.70" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.projects.title">PROJECTS</text>
|
|
||||||
<path d="M180 150.70 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="173.70" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].period">2025</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="173.70" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.projects.items[0].name">Open Guidance Map</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="191.40" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">Mapped public learning resources.</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="210.10" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">Reviewed regional catalogues with volunteers, comparing the</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="224.80" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">clarity of eligibility, learning outcomes and application</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="239.50" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">guidance. Each record includes an attributed source and an</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="254.20" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">editorial review date.</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="272.90" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">Documented ambiguous wording without guessing the</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="287.60" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">provider’s intent. Workshop participants tested revised</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="302.30" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">navigation labels and contributed examples of questions</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="317.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">that the directory should answer.</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="335.70" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">Prepared a maintenance guide so future editors can update</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="350.40" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">links, preserve context and flag missing evidence. The guide</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="365.10" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">separates direct source statements from editorial</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="379.80" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">interpretations.</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="405.50" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.projects.items[1].name">Undated community handbook</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="423.20" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[1].website.url">https://example.invalid/learning/resources/</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="437.90" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[1].website.url">community-handbook/review-notes</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="455.60" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[1].description">Collected workshop notes without inventing a project date.</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="493.30" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.languages.title">LANGUAGE SKILLS</text>
|
|
||||||
<path d="M180 490.30 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="513.30" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.languages.items[0].language">English</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="531.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.languages.items[0].fluency">Native</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="555.70" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.languages.items[1].language">French</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="573.40" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.languages.items[1].fluency">B2 (self-assessed)</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="610.10" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.skills.title">SKILLS</text>
|
|
||||||
<path d="M180 607.10 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="630.10" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.skills.items[0].name">Research</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="647.80" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.skills.items[0].proficiency">Advanced</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="665.50" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.skills.items[0].keywords">Interviews, synthesis, documentation</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="702.20" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.awards.title">AWARDS</text>
|
|
||||||
<path d="M180 699.20 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="722.20" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.awards.items[0].date">2025</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="722.20" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.awards.items[0].title">Community research recognition</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="739.90" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.awards.items[0].awarder">Civic Learning Forum</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="757.60" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.awards.items[0].description">Recognised collaborative documentation work.</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="808.00" font-size="8" fill="#56636d" font-weight="400" data-source="proposal.annotation">SYNTHETIC DESIGN PROPOSAL · NOT APPROVED</text>
|
|
||||||
<text font-family="sans-serif" x="526.00" y="808.00" font-size="8" fill="#56636d" font-weight="400" data-source="proposal.annotation">2 / 3</text></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 8.2 KiB |
@@ -1,32 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="595.28pt" height="841.89pt" viewBox="0 0 595.28 841.89" role="img" aria-labelledby="title desc"><title id="title">overflow visual proposal, page 3</title><desc id="desc">Synthetic ResumeData illustration. Static pagination, not renderer output. See mapping.md.</desc><g font-family="Helvetica"><rect width="595.28" height="841.89" fill="white"/>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="42.00" font-size="11" fill="#1e5580" font-weight="700" data-source="basics.name">Alexandra Noor Marin-López</text>
|
|
||||||
<text font-family="sans-serif" x="420.00" y="42.00" font-size="9" fill="#1e5580" font-weight="400" data-source="proposal.annotation">Continuation</text>
|
|
||||||
<path d="M36 52 H559" stroke="#c2cdd5"/>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="76.00" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.certifications.title">CERTIFICATIONS</text>
|
|
||||||
<path d="M180 73.00 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="96.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.certifications.items[0].date">2024</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="96.00" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.certifications.items[0].title">Accessible writing workshop</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="113.70" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.certifications.items[0].issuer">Open Training Collective</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="166.00" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.publications.title">PUBLICATIONS</text>
|
|
||||||
<path d="M180 163.00 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="186.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.publications.items[0].date">2025</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="186.00" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.publications.items[0].title">A practical guide to learning directories</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="203.70" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.publications.items[0].publisher">Community Methods Review</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="240.40" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.volunteer.title">VOLUNTEERING</text>
|
|
||||||
<path d="M180 237.40 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="260.40" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.volunteer.items[0].period">2022 – Present</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="260.40" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.volunteer.items[0].organization">Neighbourhood Learning Circle</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="278.10" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.volunteer.items[0].location">Brussels, Belgium</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="295.80" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.volunteer.items[0].description">Facilitated peer mentoring sessions.</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="333.50" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.interests.title">INTERESTS</text>
|
|
||||||
<path d="M180 330.50 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="353.50" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.interests.items[0].name">Walking</text>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="371.20" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.interests.items[0].keywords">Urban history, public spaces</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="407.90" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.references.title">REFERENCES</text>
|
|
||||||
<path d="M180 404.90 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="427.90" font-size="10.5" fill="#1e5580" font-weight="700" data-source="sections.references.items[0].name">Available upon request</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="464.60" font-size="10" fill="#1e5580" font-weight="700" data-source="customSections[0].title">OTHER ACTIVITIES</text>
|
|
||||||
<path d="M180 461.60 H559" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="180.00" y="484.60" font-size="10.5" fill="#20262b" font-weight="400" data-source="customSections[0].items[0].content">Community workshops and peer mentoring.</text>
|
|
||||||
<text font-family="sans-serif" x="36.00" y="808.00" font-size="8" fill="#56636d" font-weight="400" data-source="proposal.annotation">SYNTHETIC DESIGN PROPOSAL · NOT APPROVED</text>
|
|
||||||
<text font-family="sans-serif" x="526.00" y="808.00" font-size="8" fill="#56636d" font-weight="400" data-source="proposal.annotation">3 / 3</text></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 4.9 KiB |
@@ -1,88 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1320" height="1150" viewBox="0 0 1320 1150" role="img" aria-labelledby="title desc"><title id="title">Official reference structure and independent proposal</title><desc id="desc">Left is an analytical schematic of the legacy EEAS-hosted Europass form, not a reproduction. Right is the synthetic proposal. See README.md for source and reuse limits.</desc><rect width="1320" height="1150" fill="#edf1f4"/>
|
|
||||||
<text x="40" y="45" font-family="sans-serif" font-size="26" fill="#20262b">REFERENCE → PROPOSAL</text>
|
|
||||||
<text x="40" y="77" font-family="sans-serif" font-size="16" fill="#20262b">Plan 33A · research only · synthetic content · visual approval pending</text>
|
|
||||||
<text x="40" y="119" font-family="sans-serif" font-size="19" fill="#20262b">Observed legacy structure — analytical schematic</text>
|
|
||||||
<text x="680" y="119" font-family="sans-serif" font-size="19" fill="#20262b">European chronology — mapped proposal</text>
|
|
||||||
<rect x="40" y="140" width="595" height="842" fill="white"/>
|
|
||||||
<text x="70" y="178" font-family="sans-serif" font-size="13" fill="#20262b">Legacy source: EEAS PDF, © European Union</text>
|
|
||||||
<text x="70" y="199" font-family="sans-serif" font-size="13" fill="#20262b">2002–2018 document; 2025 hosting path</text>
|
|
||||||
<text x="70" y="227" font-family="sans-serif" font-size="12" fill="#56636d">Brand mark intentionally not reproduced</text>
|
|
||||||
<text x="70" y="280" font-family="sans-serif" font-size="13" fill="#1e5580">Personal information</text>
|
|
||||||
<path d="M245 275 H605" stroke="#1e5580"/>
|
|
||||||
<text x="245" y="304" font-family="sans-serif" font-size="13" fill="#20262b">Name and contacts</text>
|
|
||||||
<text x="70" y="395" font-family="sans-serif" font-size="13" fill="#1e5580">Work experience</text>
|
|
||||||
<path d="M245 390 H605" stroke="#1e5580"/>
|
|
||||||
<text x="245" y="419" font-family="sans-serif" font-size="13" fill="#20262b">Position · employer · responsibilities</text>
|
|
||||||
<text x="70" y="423" font-family="sans-serif" font-size="12" fill="#20262b">Date range</text>
|
|
||||||
<text x="70" y="510" font-family="sans-serif" font-size="13" fill="#1e5580">Education and training</text>
|
|
||||||
<path d="M245 505 H605" stroke="#1e5580"/>
|
|
||||||
<text x="245" y="534" font-family="sans-serif" font-size="13" fill="#20262b">Qualification · institution · subjects</text>
|
|
||||||
<text x="70" y="538" font-family="sans-serif" font-size="12" fill="#20262b">Date range</text>
|
|
||||||
<text x="70" y="625" font-family="sans-serif" font-size="13" fill="#1e5580">Personal skills</text>
|
|
||||||
<path d="M245 620 H605" stroke="#1e5580"/>
|
|
||||||
<text x="245" y="649" font-family="sans-serif" font-size="13" fill="#20262b">Mother tongue; five language dimensions</text>
|
|
||||||
<rect x="245" y="674" width="70" height="42" fill="none" stroke="#b1bcc4"/>
|
|
||||||
<text x="252" y="700" font-family="sans-serif" font-size="12" fill="#20262b">1</text>
|
|
||||||
<rect x="315" y="674" width="70" height="42" fill="none" stroke="#b1bcc4"/>
|
|
||||||
<text x="322" y="700" font-family="sans-serif" font-size="12" fill="#20262b">2</text>
|
|
||||||
<rect x="385" y="674" width="70" height="42" fill="none" stroke="#b1bcc4"/>
|
|
||||||
<text x="392" y="700" font-family="sans-serif" font-size="12" fill="#20262b">3</text>
|
|
||||||
<rect x="455" y="674" width="70" height="42" fill="none" stroke="#b1bcc4"/>
|
|
||||||
<text x="462" y="700" font-family="sans-serif" font-size="12" fill="#20262b">4</text>
|
|
||||||
<rect x="525" y="674" width="70" height="42" fill="none" stroke="#b1bcc4"/>
|
|
||||||
<text x="532" y="700" font-family="sans-serif" font-size="12" fill="#20262b">5</text>
|
|
||||||
<text x="245" y="739" font-family="sans-serif" font-size="12" fill="#20262b">No conversion from a single numeric rating</text>
|
|
||||||
<text x="70" y="790" font-family="sans-serif" font-size="13" fill="#1e5580">Other skills</text>
|
|
||||||
<path d="M245 785 H605" stroke="#1e5580"/>
|
|
||||||
<text x="245" y="814" font-family="sans-serif" font-size="13" fill="#20262b">Communication · organisation · job skills</text>
|
|
||||||
<text x="70" y="943" font-family="sans-serif" font-size="12" fill="#56636d">Structure only; source-specific recruitment text omitted</text>
|
|
||||||
<g><title id="proposal-title">One-page visual proposal</title><desc id="proposal-desc">Synthetic ResumeData illustration. Static layout, not renderer output. See mapping.md.</desc><g font-family="Helvetica"><rect x="680" y="140" width="595.28" height="841.89" fill="white"/>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="202.00" font-size="23" fill="#1e5580" font-weight="700" data-source="basics.name">Alex Marin</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="228.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="basics.headline">Research coordinator</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="249.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="basics.location">Brussels, Belgium</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="266.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="basics.email">alex.marin@example.invalid</text>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="308.00" font-size="10" fill="#1e5580" font-weight="700" data-source="summary.title">ABOUT ME</text>
|
|
||||||
<path d="M860 305.00 H1239" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="330.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="summary.content">Research coordinator building clear public-service guidance.</text>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="371.00" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.experience.title">WORK EXPERIENCE</text>
|
|
||||||
<path d="M860 368.00 H1239" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="394.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].period">2023 – Present</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="394.00" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.experience.items[0].position">Research coordinator</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="412.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].company">Civic Atlas Lab</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="429.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].location">Brussels, Belgium</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="449.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].description">• Coordinated multilingual guidance reviews.</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="466.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.experience.items[0].description">• Published accessible research summaries.</text>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="507.00" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.education.title">EDUCATION AND</text>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="520.00" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.education.title">TRAINING</text>
|
|
||||||
<path d="M860 504.00 H1239" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="541.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.education.items[0].period">2019 – 2022</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="541.00" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.education.items[0].degree">BA Social Research</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="559.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.education.items[0].school">Northbridge Institute</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="576.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.education.items[0].area">Public policy</text>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="617.00" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.languages.title">LANGUAGE SKILLS</text>
|
|
||||||
<path d="M860 614.00 H1239" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="640.00" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.languages.items[0].language">English</text>
|
|
||||||
<text font-family="sans-serif" x="950.00" y="640.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.languages.items[0].fluency">Native</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="661.00" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.languages.items[1].language">French</text>
|
|
||||||
<text font-family="sans-serif" x="950.00" y="661.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.languages.items[1].fluency">B2 (self-assessed)</text>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="702.00" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.skills.title">SKILLS</text>
|
|
||||||
<path d="M860 699.00 H1239" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="725.00" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.skills.items[0].name">Research</text>
|
|
||||||
<text font-family="sans-serif" x="950.00" y="725.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.skills.items[0].proficiency">Advanced</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="746.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.skills.items[0].keywords">Interviews, synthesis, documentation</text>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="787.00" font-size="10" fill="#1e5580" font-weight="700" data-source="sections.projects.title">PROJECTS</text>
|
|
||||||
<path d="M860 784.00 H1239" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="810.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].period">2025</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="810.00" font-size="10.5" fill="#20262b" font-weight="700" data-source="sections.projects.items[0].name">Open Guidance Map</text>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="830.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="sections.projects.items[0].description">Mapped public learning resources.</text>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="871.00" font-size="10" fill="#1e5580" font-weight="700" data-source="customSections[0].title">OTHER ACTIVITIES</text>
|
|
||||||
<path d="M860 868.00 H1239" stroke="#1e5580" stroke-width="0.8"/>
|
|
||||||
<text font-family="sans-serif" x="860.00" y="894.00" font-size="10.5" fill="#20262b" font-weight="400" data-source="customSections[0].items[0].content">Community workshops and peer mentoring.</text>
|
|
||||||
<text font-family="sans-serif" x="716.00" y="948.00" font-size="8" fill="#56636d" font-weight="400" data-source="proposal.annotation">SYNTHETIC DESIGN PROPOSAL · NOT APPROVED</text>
|
|
||||||
<text font-family="sans-serif" x="1206.00" y="948.00" font-size="8" fill="#56636d" font-weight="400" data-source="proposal.annotation">1 / 1</text></g></g>
|
|
||||||
|
|
||||||
<text x="40" y="1022" font-family="sans-serif" font-size="17" fill="#20262b">Keep: chronological gutter, blue section rules, broad content column.</text>
|
|
||||||
<text x="40" y="1054" font-family="sans-serif" font-size="17" fill="#20262b">Change: neutral identity, optional personal fields, plain-text fluency, user-authored sections.</text>
|
|
||||||
<text x="40" y="1086" font-family="sans-serif" font-size="15" fill="#20262b">Current Europass offers several layouts; this legacy structure is not a universal current specification.</text>
|
|
||||||
<text x="40" y="1120" font-family="sans-serif" font-size="13" fill="#20262b">Reference URL, access date, exact field mapping and unresolved reuse decisions: ../README.md and ../mapping.md</text>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,133 +0,0 @@
|
|||||||
# Existing ResumeData to proposed visual fields
|
|
||||||
|
|
||||||
Read against `b85d285b69843612e9d7f0ab802248982e7bf0ea`. The source of truth is
|
|
||||||
[data.ts](../../../packages/schema/src/resume/data.ts), with existing defaults in
|
|
||||||
[default.ts](../../../packages/schema/src/resume/default.ts) and visibility behavior in
|
|
||||||
[shared filtering](../../../packages/pdf/src/templates/shared/filtering.ts).
|
|
||||||
This document proposes presentation; it adds no fields or renderer behavior.
|
|
||||||
|
|
||||||
Both fixture records were validated against the current Zod `resumeDataSchema`. The checked-in generated
|
|
||||||
`packages/schema/schema.json` rejects the current `picture.fit` field; it is stale relative to `data.ts` at
|
|
||||||
this revision. That unrelated generated-schema mismatch is recorded, not repaired in this research unit.
|
|
||||||
|
|
||||||
The canonical SVG text nodes carry `data-source` paths into the matching object in
|
|
||||||
[synthetic-fixtures.json](synthetic-fixtures.json). Wrapped text uses the same path on each line.
|
|
||||||
Section titles are shown in uppercase in these English drawings; source strings retain their original case.
|
|
||||||
`proposal.annotation` means review metadata, not ResumeData: synthetic watermark, continuation labels and
|
|
||||||
page numbers. The repeated running name still comes from `basics.name`.
|
|
||||||
|
|
||||||
## Header and shared fields
|
|
||||||
|
|
||||||
| Proposed display | Existing path | Rule / absence behavior |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| Name and running name | `basics.name` | Preserve full name; do not split first/last names or reorder culturally. Wrap long names. Empty name renders no invented label. |
|
|
||||||
| Professional headline | `basics.headline` | Optional text, wraps within the detail width. It is not an official occupational code. |
|
|
||||||
| Contact location | `basics.location` | One free string; no parsing into country/address. Do not invent a full postal address. |
|
|
||||||
| Email and phone | `basics.email`, `basics.phone` | Omit empty values and separators. Phone is empty in both fixtures; no number is fabricated. Future renderer uses existing link handling. |
|
|
||||||
| Personal website | `basics.website.url`, `.label` | Nonempty URL uses label when provided, URL otherwise. Overflow displays “Research portfolio.” SVGs illustrate text only; hyperlink behavior needs implementation checks. |
|
|
||||||
| Other personal detail | `basics.customFields[].text`, `.link`, `.icon` | Display user-authored text/link in order. Overflow shows availability. No semantic inference from text. There is no per-field hidden flag; empty text yields no visual row. |
|
|
||||||
| Optional photo | `picture.url`, `.hidden`, `.fit`, `.size`, `.rotation`, `.aspectRatio`, border/shadow fields | Both fixtures have no URL and `hidden: true`; no frame is displayed. Future visible photo uses existing upload/picture contract and preserves cover/contain choice. Suggested reading-start header frame is not visually approved. Failed image handling needs future verification. |
|
|
||||||
| Section heading | `summary.title`, `sections.<type>.title`, `customSections[].title` | Use authored title; empty title uses existing localized fallback. Do not replace custom titles with fixed Europass labels. |
|
|
||||||
| Section presence/order | `metadata.layout.pages[].main`, `.sidebar`, `sections.*.hidden`, `customSections[].hidden`, `summary.hidden` | Respect authored page/column order and visibility. Missing-from-layout is distinct from hidden. Fixtures choose one authored full-width page; physical overflow is illustrated separately. |
|
|
||||||
| Date gutter | Item `.period` or `.date` | Verbatim free text, including role periods. Wrap to fixed gutter; keep undated details aligned. No automatic chronological sort. The fixture itself is authored newest-first. |
|
|
||||||
| Rich description | Item `.description`, summary `.content`, custom summary item `.content` | Preserve supported paragraphs, lists, emphasis and links. These drawings only exercise paragraphs/lists. No HTML tags printed; no arbitrary HTML discarded by proposed renderer. Unsupported-rich-text handling remains implementation work. |
|
|
||||||
| Item website | Item `.website.url`, `.label`, `.inlineLink` | Preserve URL target; label or raw URL as display text. Inline-link preference must remain honored in future renderer. Fixture links all use `inlineLink: false`. |
|
|
||||||
| Icons and colors | Section `.icon`; profile/skill/interest `.icon`, `.iconColor`; `metadata.page.hideIcons`, `.hideSectionIcons` | Default proposal is quiet/text-led. Fixture icons are absent or suppressed. Nondefault icon settings are not demonstrated and must be tested before implementation approval. |
|
|
||||||
|
|
||||||
## Built-in sections
|
|
||||||
|
|
||||||
Each `items[]` entry also has `id` and `hidden`; identifiers never print. A section with no visible valid
|
|
||||||
items is omitted with its heading and rule. The table lists every content field, including blank optional fields.
|
|
||||||
|
|
||||||
| Proposed section | Paths under `sections.<type>.items[]` | Presentation / fixture coverage |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| Profiles | `profiles`: `network`, `username`, `website`, `icon`, `iconColor` | Network, user name, website label. Present only in overflow. Keep within section, do not duplicate in header. |
|
|
||||||
| Work experience | `experience`: `position`, `company`, `location`, `period`, `description`, `website`, `roles[]` | Position then organization/location, period in gutter. Overflow retains overall title/tenure and both individual roles. |
|
|
||||||
| Individual experience role | `experience.roles[]`: `id`, `position`, `period`, `description` | Role heading, independent period, description. No role hidden flag exists. Existing shared filtering excludes blank-position roles; parent visibility still applies. |
|
|
||||||
| Education and training | `education`: `degree`, `school`, `area`, `grade`, `location`, `period`, `description`, `website` | Qualification then institution, subject, supplied grade and location. Overflow includes all optional text fields except website. Grade is not an EQF level. |
|
|
||||||
| Projects | `projects`: `name`, `period`, `description`, `website` | Named project, date gutter, rich narrative. Overflow adds a long multi-paragraph project and an undated project with raw long URL. |
|
|
||||||
| Language skills | `languages`: `language`, `fluency`, `level` | Show language and supplied fluency verbatim. English/Native and French/B2 (self-assessed) are synthetic user-entered strings. No conversion from `level` to fluency or CEFR. |
|
|
||||||
| Skills | `skills`: `name`, `proficiency`, `level`, `keywords`, `icon`, `iconColor` | Show name, proficiency, comma-separated keywords. Keyword strings/order retained. Levels are intentionally hidden via existing `metadata.design.level.type: hidden`; no proficiency inferred. |
|
|
||||||
| Awards | `awards`: `title`, `awarder`, `date`, `description`, `website` | Title, awarder, supplied date, description. Overflow only. |
|
|
||||||
| Certifications | `certifications`: `title`, `issuer`, `date`, `description`, `website` | Title, issuer and supplied date; empty description/link omitted. Overflow only. No accreditation claim. |
|
|
||||||
| Publications | `publications`: `title`, `publisher`, `date`, `description`, `website` | Title, publisher and supplied date. Overflow only. No DOI field invented. |
|
|
||||||
| Volunteering | `volunteer`: `organization`, `location`, `period`, `description`, `website` | Organization, place, period and description. There is no dedicated role-title field; do not infer one. Overflow only. |
|
|
||||||
| Interests | `interests`: `name`, `keywords`, `icon`, `iconColor` | Name and keyword text, with configured icon behavior. Overflow only. |
|
|
||||||
| References | `references`: `name`, `position`, `phone`, `description`, `website` | Existing user-authored “Available upon request” string in overflow; no contact is invented. There is no structured reference-email field. |
|
|
||||||
| About me | `summary.content`, `.title`, `.hidden` | Existing summary supports optional narrative. Present in both records. This is separate from experience descriptions. |
|
|
||||||
|
|
||||||
## Custom sections, omitted and unsupported fields
|
|
||||||
|
|
||||||
| Concern | Existing representation | Decision |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| Custom typed sections | `customSections[]`: `id`, `type`, shared section fields, `items[]` matching type | Same mapping as the matching built-in type. Preserve own title and authored layout ID. All 12 built-in types are structurally available as custom types; this fixture demonstrates a custom summary, not every custom variant. |
|
|
||||||
| Other activities | Custom `type: summary`, `items[].content` | Both records display community workshops and peer mentoring. No fabricated employer or dates. |
|
|
||||||
| Cover letter | Custom `type: cover-letter`, `items[].recipient`, `.content` | Supported by current model but outside this CV visual proposal; fixtures contain none. A future template must preserve existing cover-letter behavior or explicitly gate selection; silently dropping a visible cover letter is not acceptable. |
|
|
||||||
| Structured nationality, birth date/place, sex/gender, full address components, second phone/fax, driving licence | No dedicated typed fields in `basics` | Omitted from examples, not required. A user can author free text in `customFields`, but that is not a typed equivalent or automatic mapping. Never infer from names, photos, location or language. |
|
|
||||||
| Mother-tongue boolean or multiple structured native languages | `languages[].fluency` is free text only | No separate mother-tongue group inferred from “Native.” Keep supplied text alongside language. |
|
|
||||||
| Five CEFR dimensions: listening, reading, spoken interaction, spoken production, writing | No fields | Omit official reference matrix. A scalar `level` or single fluency text cannot populate it faithfully. |
|
|
||||||
| Language certificate linked to particular language/dimension | General certifications exist, no structured association | Keep as separate certification if user authored one. Do not guess association. |
|
|
||||||
| EQF/NQF/ISCED level, qualification IDs, occupation codes, business sector | No dedicated typed fields | Omit unless user explicitly wrote free text in existing description/area/custom content. No equivalence inferred from degree or grade. |
|
|
||||||
| Attachments, signed declaration, consent block, official Europass identifiers, XML payload, verification QR | No corresponding proposed CV field | Omitted. Existing website links do not establish attached documents, signatures or official interoperability. |
|
|
||||||
| Official branding/copyright footer | Not ResumeData | Omitted; reuse permission not established. Source attribution belongs in research note, not masquerading as official CV provenance. |
|
|
||||||
| Notes | `metadata.notes` | Never print. Overflow contains `PRIVATE_NOTES_SENTINEL` to make accidental disclosure detectable. |
|
|
||||||
|
|
||||||
## Visibility, empty content and layout controls
|
|
||||||
|
|
||||||
Existing filtering is the behavioral baseline: skip hidden sections/items, title-backed entries without their
|
|
||||||
required primary title, and blank-position experience roles. Those primary fields are `network`, `company`,
|
|
||||||
`school`, `name` (projects/skills/interests/references), `language`, `title` (awards/certifications/publications)
|
|
||||||
and `organization`. A missing optional field alone must not hide an otherwise valid item.
|
|
||||||
|
|
||||||
Summary visibility currently checks nonempty trimmed content, not semantic emptiness of HTML. Do not claim
|
|
||||||
that `<p></p>` is already filtered correctly; future proposal tests must include it and settle any desired
|
|
||||||
change separately. The fixtures use populated summaries and empty item arrays, not malformed imported data.
|
|
||||||
|
|
||||||
Overflow includes a hidden experience item, a hidden custom section and private notes. Their `HIDDEN_*` and
|
|
||||||
`PRIVATE_NOTES_*` tokens must not occur in any visual artifact. One-page empty sections do not render headings.
|
|
||||||
Separators and rules belong to visible content only. Blank date cells stay blank, never show “Present” or a dash.
|
|
||||||
|
|
||||||
`columns`, `keepTogether`, `startOnNewPage`, `skills.layout`, authored pages/full-width/sidebar settings,
|
|
||||||
typography, colors, page format/margins/gaps, hyphenation and style rules already exist. The fixture explicitly
|
|
||||||
chooses a full-width A4 page, single-column sections, ordinary flow and no style rules. It retains the valid
|
|
||||||
existing `metadata.template: onyx` only to remain schema-compatible: importing this fixture currently selects
|
|
||||||
Onyx, **not this proposal**. No new template ID, date-width option or stylesheet behavior is encoded.
|
|
||||||
|
|
||||||
## Pagination and long text proposal
|
|
||||||
|
|
||||||
One-page and overflow SVGs are manual, deterministic illustrations. They do not exercise `packages/pdf`.
|
|
||||||
The three overflow drawings illustrate one authored page flowing onto physical pages, not editable per-page
|
|
||||||
overflow settings. No fixed page count or exact future line break is promised.
|
|
||||||
|
|
||||||
Dates wrap inside a 128 pt reading-start gutter; detail starts remain aligned. Keep entry title with its first
|
|
||||||
detail line, and section heading with its first item. Allow a long entry to continue across pages without
|
|
||||||
truncation or repeated body text. In the study, the education description continues on page 2 as a complete
|
|
||||||
two-line paragraph; a continuation label is review metadata. Page 3 carries remaining sections without shrinking
|
|
||||||
font size to force two pages. Repeated running names/page numbers are intentional, not duplicated body content.
|
|
||||||
|
|
||||||
Long names, headings, free-text dates, paragraphs and URLs must wrap; do not ellipsize, parse or normalize them.
|
|
||||||
The overflow contains an accented long name, wrapped role dates and undated project. Long unbroken URL glyphs
|
|
||||||
need break opportunities while retaining the original link target. Future renderer tests must add a single
|
|
||||||
entry longer than a page, bullets across breaks, manual page starts, nondefault columns and keep-together
|
|
||||||
settings. These cases are specified here but not established by the static study.
|
|
||||||
|
|
||||||
## Localization, RTL, photo and accessibility review limits
|
|
||||||
|
|
||||||
Use `metadata.page.locale` for existing translated fallback headings; preserve user-authored text, date strings
|
|
||||||
and titles. Do not translate content or uppercase text in languages where that harms meaning or shaping.
|
|
||||||
The proposal's uppercase English labels are a visual choice, not a localization algorithm. Font selection and
|
|
||||||
fallback should stay with existing PDF font registration, not be bundled into this research.
|
|
||||||
|
|
||||||
RTL proposal: put label/date gutter at reading start (right), mirror alignment and optional photo placement,
|
|
||||||
keep email/URL/phone runs in their natural direction, and preserve logical source order. Current model has no
|
|
||||||
new direction field here; reuse the existing locale/direction contract after validating it. Arabic/Hebrew shaping,
|
|
||||||
mixed-direction punctuation, CJK glyphs and translated long headings need their own rendered fixtures before
|
|
||||||
implementation approval. The Latin-script drawings do not prove RTL or full localization coverage.
|
|
||||||
|
|
||||||
No synthetic portrait was needed for the photo-free design. Before implementing the optional-photo variation,
|
|
||||||
review a visible-photo artifact using nonprivate synthetic imagery, both cover and contain, and long names
|
|
||||||
beside the frame. No stock/person photo or user upload was used in this package.
|
|
||||||
|
|
||||||
Keep semantic section labels and logical extraction order even if visual arrangement changes. Tagged PDF,
|
|
||||||
screen-reader behavior, contrast under user colors and link semantics require Plan 31 coordination and real
|
|
||||||
export checks. Plain SVG text readability does not establish document accessibility conformance.
|
|
||||||
@@ -1,768 +0,0 @@
|
|||||||
{
|
|
||||||
"onePage": {
|
|
||||||
"picture": {
|
|
||||||
"hidden": true,
|
|
||||||
"fit": "cover",
|
|
||||||
"url": "",
|
|
||||||
"size": 80,
|
|
||||||
"rotation": 0,
|
|
||||||
"aspectRatio": 1,
|
|
||||||
"borderRadius": 0,
|
|
||||||
"borderColor": "rgba(0, 0, 0, 0.5)",
|
|
||||||
"borderWidth": 0,
|
|
||||||
"shadowColor": "rgba(0, 0, 0, 0.5)",
|
|
||||||
"shadowWidth": 0
|
|
||||||
},
|
|
||||||
"basics": {
|
|
||||||
"name": "Alex Marin",
|
|
||||||
"headline": "Research coordinator",
|
|
||||||
"email": "alex.marin@example.invalid",
|
|
||||||
"phone": "",
|
|
||||||
"location": "Brussels, Belgium",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": ""
|
|
||||||
},
|
|
||||||
"customFields": []
|
|
||||||
},
|
|
||||||
"summary": {
|
|
||||||
"title": "About me",
|
|
||||||
"icon": "article",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"content": "<p>Research coordinator building clear public-service guidance.</p>"
|
|
||||||
},
|
|
||||||
"sections": {
|
|
||||||
"profiles": {
|
|
||||||
"title": "",
|
|
||||||
"icon": "messenger-logo",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": []
|
|
||||||
},
|
|
||||||
"experience": {
|
|
||||||
"title": "Work experience",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "exp-1",
|
|
||||||
"hidden": false,
|
|
||||||
"company": "Civic Atlas Lab",
|
|
||||||
"position": "Research coordinator",
|
|
||||||
"location": "Brussels, Belgium",
|
|
||||||
"period": "2023 – Present",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": "<ul><li>Coordinated multilingual guidance reviews.</li><li>Published accessible research summaries.</li></ul>",
|
|
||||||
"roles": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"education": {
|
|
||||||
"title": "Education and training",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "edu-1",
|
|
||||||
"hidden": false,
|
|
||||||
"school": "Northbridge Institute",
|
|
||||||
"degree": "BA Social Research",
|
|
||||||
"area": "Public policy",
|
|
||||||
"grade": "",
|
|
||||||
"location": "",
|
|
||||||
"period": "2019 – 2022",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"projects": {
|
|
||||||
"title": "Projects",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "project-1",
|
|
||||||
"hidden": false,
|
|
||||||
"name": "Open Guidance Map",
|
|
||||||
"period": "2025",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": "<p>Mapped public learning resources.</p>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"skills": {
|
|
||||||
"title": "Skills",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "skill-1",
|
|
||||||
"hidden": false,
|
|
||||||
"icon": "",
|
|
||||||
"iconColor": "",
|
|
||||||
"name": "Research",
|
|
||||||
"proficiency": "Advanced",
|
|
||||||
"level": 0,
|
|
||||||
"keywords": ["Interviews", "synthesis", "documentation"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"layout": "default"
|
|
||||||
},
|
|
||||||
"languages": {
|
|
||||||
"title": "Language skills",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "lang-1",
|
|
||||||
"hidden": false,
|
|
||||||
"language": "English",
|
|
||||||
"fluency": "Native",
|
|
||||||
"level": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "lang-2",
|
|
||||||
"hidden": false,
|
|
||||||
"language": "French",
|
|
||||||
"fluency": "B2 (self-assessed)",
|
|
||||||
"level": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"interests": {
|
|
||||||
"title": "",
|
|
||||||
"icon": "football",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": []
|
|
||||||
},
|
|
||||||
"awards": {
|
|
||||||
"title": "",
|
|
||||||
"icon": "trophy",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": []
|
|
||||||
},
|
|
||||||
"certifications": {
|
|
||||||
"title": "",
|
|
||||||
"icon": "certificate",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": []
|
|
||||||
},
|
|
||||||
"publications": {
|
|
||||||
"title": "",
|
|
||||||
"icon": "books",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": []
|
|
||||||
},
|
|
||||||
"volunteer": {
|
|
||||||
"title": "",
|
|
||||||
"icon": "hand-heart",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": []
|
|
||||||
},
|
|
||||||
"references": {
|
|
||||||
"title": "",
|
|
||||||
"icon": "phone",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"customSections": [
|
|
||||||
{
|
|
||||||
"title": "Other activities",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "custom-item-1",
|
|
||||||
"hidden": false,
|
|
||||||
"content": "<p>Community workshops and peer mentoring.</p>"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "other-activities",
|
|
||||||
"type": "summary"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"template": "onyx",
|
|
||||||
"layout": {
|
|
||||||
"sidebarWidth": 35,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"fullWidth": true,
|
|
||||||
"main": ["summary", "experience", "education", "languages", "skills", "projects", "other-activities"],
|
|
||||||
"sidebar": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"page": {
|
|
||||||
"gapX": 4,
|
|
||||||
"gapY": 18,
|
|
||||||
"marginX": 36,
|
|
||||||
"marginY": 36,
|
|
||||||
"format": "a4",
|
|
||||||
"locale": "en-US",
|
|
||||||
"hideLinkUnderline": false,
|
|
||||||
"hideIcons": false,
|
|
||||||
"hideSectionIcons": true
|
|
||||||
},
|
|
||||||
"design": {
|
|
||||||
"colors": {
|
|
||||||
"primary": "rgba(30, 85, 128, 1)",
|
|
||||||
"text": "rgba(32, 38, 43, 1)",
|
|
||||||
"background": "rgba(255, 255, 255, 1)"
|
|
||||||
},
|
|
||||||
"level": {
|
|
||||||
"icon": "star",
|
|
||||||
"type": "hidden"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"typography": {
|
|
||||||
"body": {
|
|
||||||
"fontFamily": "Arial",
|
|
||||||
"fontWeights": ["400"],
|
|
||||||
"fontSize": 10.5,
|
|
||||||
"lineHeight": 1.4
|
|
||||||
},
|
|
||||||
"heading": {
|
|
||||||
"fontFamily": "Arial",
|
|
||||||
"fontWeights": ["700"],
|
|
||||||
"fontSize": 10,
|
|
||||||
"lineHeight": 1.3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notes": "",
|
|
||||||
"styleRules": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overflow": {
|
|
||||||
"picture": {
|
|
||||||
"hidden": true,
|
|
||||||
"fit": "cover",
|
|
||||||
"url": "",
|
|
||||||
"size": 80,
|
|
||||||
"rotation": 0,
|
|
||||||
"aspectRatio": 1,
|
|
||||||
"borderRadius": 0,
|
|
||||||
"borderColor": "rgba(0, 0, 0, 0.5)",
|
|
||||||
"borderWidth": 0,
|
|
||||||
"shadowColor": "rgba(0, 0, 0, 0.5)",
|
|
||||||
"shadowWidth": 0
|
|
||||||
},
|
|
||||||
"basics": {
|
|
||||||
"name": "Alexandra Noor Marin-López",
|
|
||||||
"headline": "Research coordinator for multilingual public-service information",
|
|
||||||
"email": "alex.marin@example.invalid",
|
|
||||||
"phone": "",
|
|
||||||
"location": "Brussels, Belgium",
|
|
||||||
"website": {
|
|
||||||
"url": "https://example.invalid/alexandra/research",
|
|
||||||
"label": "Research portfolio"
|
|
||||||
},
|
|
||||||
"customFields": [
|
|
||||||
{
|
|
||||||
"id": "availability",
|
|
||||||
"icon": "",
|
|
||||||
"text": "Available for cross-border project work",
|
|
||||||
"link": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"summary": {
|
|
||||||
"title": "About me",
|
|
||||||
"icon": "article",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"content": "<p>Research coordinator building clear public-service guidance.</p>"
|
|
||||||
},
|
|
||||||
"sections": {
|
|
||||||
"profiles": {
|
|
||||||
"title": "Profiles",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "profile-1",
|
|
||||||
"hidden": false,
|
|
||||||
"network": "Portfolio",
|
|
||||||
"username": "alexandra",
|
|
||||||
"icon": "",
|
|
||||||
"iconColor": "",
|
|
||||||
"website": {
|
|
||||||
"url": "https://example.invalid/alexandra",
|
|
||||||
"label": "Selected writing",
|
|
||||||
"inlineLink": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"experience": {
|
|
||||||
"title": "Work experience",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "exp-1",
|
|
||||||
"hidden": false,
|
|
||||||
"company": "Civic Atlas Lab",
|
|
||||||
"position": "Research coordinator",
|
|
||||||
"location": "Brussels, Belgium",
|
|
||||||
"period": "2023 – Present",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": "<p>Supported a distributed team producing accessible guidance for community learning programmes.</p>",
|
|
||||||
"roles": [
|
|
||||||
{
|
|
||||||
"id": "role-1",
|
|
||||||
"position": "Programme lead",
|
|
||||||
"period": "September 2024 – Present (part-time)",
|
|
||||||
"description": "<p>Led editorial planning across four working languages.</p>"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "role-2",
|
|
||||||
"position": "Research associate",
|
|
||||||
"period": "January 2023 – August 2024",
|
|
||||||
"description": "<p>Built an evidence catalogue for public learning services.</p>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "exp-2",
|
|
||||||
"hidden": false,
|
|
||||||
"company": "Open Learning Observatory",
|
|
||||||
"position": "Research assistant",
|
|
||||||
"location": "Leuven, Belgium",
|
|
||||||
"period": "Autumn 2021 – Summer 2022",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"roles": [],
|
|
||||||
"description": "<p>Collected public documentation and organised findings for volunteer reviewers.</p>"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "exp-hidden",
|
|
||||||
"hidden": true,
|
|
||||||
"company": "HIDDEN_ITEM_SENTINEL",
|
|
||||||
"position": "",
|
|
||||||
"location": "",
|
|
||||||
"period": "",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"roles": [],
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"education": {
|
|
||||||
"title": "Education and training",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "edu-1",
|
|
||||||
"hidden": false,
|
|
||||||
"school": "Northbridge Institute",
|
|
||||||
"degree": "BA Social Research",
|
|
||||||
"area": "Public policy",
|
|
||||||
"grade": "Distinction",
|
|
||||||
"location": "Leuven, Belgium",
|
|
||||||
"period": "2019 – 2022",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": "<p>Studied research methods, plain-language writing and collaborative service design.</p>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"projects": {
|
|
||||||
"title": "Projects",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "project-1",
|
|
||||||
"hidden": false,
|
|
||||||
"name": "Open Guidance Map",
|
|
||||||
"period": "2025",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": "<p>Mapped public learning resources.</p><p>Reviewed regional catalogues with volunteers, comparing the clarity of eligibility, learning outcomes and application guidance. Each record includes an attributed source and an editorial review date.</p><p>Documented ambiguous wording without guessing the provider’s intent. Workshop participants tested revised navigation labels and contributed examples of questions that the directory should answer.</p><p>Prepared a maintenance guide so future editors can update links, preserve context and flag missing evidence. The guide separates direct source statements from editorial interpretations.</p>"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "project-2",
|
|
||||||
"hidden": false,
|
|
||||||
"name": "Undated community handbook",
|
|
||||||
"period": "",
|
|
||||||
"website": {
|
|
||||||
"url": "https://example.invalid/learning/resources/community-handbook/review-notes",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": "<p>Collected workshop notes without inventing a project date.</p>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"skills": {
|
|
||||||
"title": "Skills",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "skill-1",
|
|
||||||
"hidden": false,
|
|
||||||
"icon": "",
|
|
||||||
"iconColor": "",
|
|
||||||
"name": "Research",
|
|
||||||
"proficiency": "Advanced",
|
|
||||||
"level": 0,
|
|
||||||
"keywords": ["Interviews", "synthesis", "documentation"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"layout": "default"
|
|
||||||
},
|
|
||||||
"languages": {
|
|
||||||
"title": "Language skills",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "lang-1",
|
|
||||||
"hidden": false,
|
|
||||||
"language": "English",
|
|
||||||
"fluency": "Native",
|
|
||||||
"level": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "lang-2",
|
|
||||||
"hidden": false,
|
|
||||||
"language": "French",
|
|
||||||
"fluency": "B2 (self-assessed)",
|
|
||||||
"level": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"interests": {
|
|
||||||
"title": "Interests",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "int-1",
|
|
||||||
"hidden": false,
|
|
||||||
"name": "Walking",
|
|
||||||
"icon": "",
|
|
||||||
"iconColor": "",
|
|
||||||
"keywords": ["Urban history", "public spaces"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"awards": {
|
|
||||||
"title": "Awards",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "award-1",
|
|
||||||
"hidden": false,
|
|
||||||
"title": "Community research recognition",
|
|
||||||
"awarder": "Civic Learning Forum",
|
|
||||||
"date": "2025",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": "<p>Recognised collaborative documentation work.</p>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"certifications": {
|
|
||||||
"title": "Certifications",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "cert-1",
|
|
||||||
"hidden": false,
|
|
||||||
"title": "Accessible writing workshop",
|
|
||||||
"issuer": "Open Training Collective",
|
|
||||||
"date": "2024",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"publications": {
|
|
||||||
"title": "Publications",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "pub-1",
|
|
||||||
"hidden": false,
|
|
||||||
"title": "A practical guide to learning directories",
|
|
||||||
"publisher": "Community Methods Review",
|
|
||||||
"date": "2025",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"volunteer": {
|
|
||||||
"title": "Volunteering",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "vol-1",
|
|
||||||
"hidden": false,
|
|
||||||
"organization": "Neighbourhood Learning Circle",
|
|
||||||
"location": "Brussels, Belgium",
|
|
||||||
"period": "2022 – Present",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": "<p>Facilitated peer mentoring sessions.</p>"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"references": {
|
|
||||||
"title": "References",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "ref-1",
|
|
||||||
"hidden": false,
|
|
||||||
"name": "Available upon request",
|
|
||||||
"position": "",
|
|
||||||
"phone": "",
|
|
||||||
"website": {
|
|
||||||
"url": "",
|
|
||||||
"label": "",
|
|
||||||
"inlineLink": false
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"customSections": [
|
|
||||||
{
|
|
||||||
"title": "Other activities",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": false,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "custom-item-1",
|
|
||||||
"hidden": false,
|
|
||||||
"content": "<p>Community workshops and peer mentoring.</p>"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "other-activities",
|
|
||||||
"type": "summary"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "HIDDEN_SECTION_SENTINEL",
|
|
||||||
"icon": "none",
|
|
||||||
"columns": 1,
|
|
||||||
"hidden": true,
|
|
||||||
"keepTogether": false,
|
|
||||||
"startOnNewPage": false,
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"id": "hidden-custom",
|
|
||||||
"hidden": false,
|
|
||||||
"content": "<p>HIDDEN_CONTENT_SENTINEL</p>"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"id": "hidden-section",
|
|
||||||
"type": "summary"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"template": "onyx",
|
|
||||||
"layout": {
|
|
||||||
"sidebarWidth": 35,
|
|
||||||
"pages": [
|
|
||||||
{
|
|
||||||
"fullWidth": true,
|
|
||||||
"main": [
|
|
||||||
"profiles",
|
|
||||||
"summary",
|
|
||||||
"experience",
|
|
||||||
"education",
|
|
||||||
"projects",
|
|
||||||
"languages",
|
|
||||||
"skills",
|
|
||||||
"awards",
|
|
||||||
"certifications",
|
|
||||||
"publications",
|
|
||||||
"volunteer",
|
|
||||||
"interests",
|
|
||||||
"references",
|
|
||||||
"other-activities",
|
|
||||||
"hidden-section"
|
|
||||||
],
|
|
||||||
"sidebar": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"page": {
|
|
||||||
"gapX": 4,
|
|
||||||
"gapY": 18,
|
|
||||||
"marginX": 36,
|
|
||||||
"marginY": 36,
|
|
||||||
"format": "a4",
|
|
||||||
"locale": "en-US",
|
|
||||||
"hideLinkUnderline": false,
|
|
||||||
"hideIcons": false,
|
|
||||||
"hideSectionIcons": true
|
|
||||||
},
|
|
||||||
"design": {
|
|
||||||
"colors": {
|
|
||||||
"primary": "rgba(30, 85, 128, 1)",
|
|
||||||
"text": "rgba(32, 38, 43, 1)",
|
|
||||||
"background": "rgba(255, 255, 255, 1)"
|
|
||||||
},
|
|
||||||
"level": {
|
|
||||||
"icon": "star",
|
|
||||||
"type": "hidden"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"typography": {
|
|
||||||
"body": {
|
|
||||||
"fontFamily": "Arial",
|
|
||||||
"fontWeights": ["400"],
|
|
||||||
"fontSize": 10.5,
|
|
||||||
"lineHeight": 1.4
|
|
||||||
},
|
|
||||||
"heading": {
|
|
||||||
"fontFamily": "Arial",
|
|
||||||
"fontWeights": ["700"],
|
|
||||||
"fontSize": 10,
|
|
||||||
"lineHeight": 1.3
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notes": "PRIVATE_NOTES_SENTINEL",
|
|
||||||
"styleRules": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -478,7 +478,7 @@ If you're using S3-compatible storage, consider enabling versioning on your buck
|
|||||||
|
|
||||||
## Health checks
|
## Health checks
|
||||||
|
|
||||||
Reactive Resume exposes a health check endpoint at `/api/health` that verifies the application and its dependencies. It checks **database** and **storage**; if either is unhealthy, the endpoint returns HTTP `503`.
|
Reactive Resume exposes a health check endpoint at `/api/health` that verifies the application and its dependencies. It checks **database**, **storage**, and **Redis** when configured; if a configured dependency is unhealthy, the endpoint returns HTTP `503`.
|
||||||
|
|
||||||
### How it works
|
### How it works
|
||||||
|
|
||||||
@@ -590,3 +590,9 @@ Restart the application after setting or changing `ROOT_RESUME_ID`. With Docker
|
|||||||
Keep `APP_URL` set to the public origin and proxy the whole application normally, including API, uploads, fonts, and assets. Root mode uses that configured origin for its canonical URL; it does not infer a domain from request headers. A successful password challenge returns visitors to `/`.
|
Keep `APP_URL` set to the public origin and proxy the whole application normally, including API, uploads, fonts, and assets. Root mode uses that configured origin for its canonical URL; it does not infer a domain from request headers. A successful password challenge returns visitors to `/`.
|
||||||
|
|
||||||
This is a single-resume setting for one self-hosted instance. It does not register custom domains, manage DNS or TLS, or hide the rest of the application. Login and the dashboard remain available at their usual paths.
|
This is a single-resume setting for one self-hosted instance. It does not register custom domains, manage DNS or TLS, or hide the rest of the application. Login and the dashboard remain available at their usual paths.
|
||||||
|
|
||||||
|
## AI agent run duration
|
||||||
|
|
||||||
|
Each individual agent run has a four-minute execution timeout, reserving up to one minute for saving and cleanup within the shared five-minute budget. This is the same on Docker and Vercel Hobby. Normal answers return as soon as they finish; the limit never applies to an entire conversation. Follow-up questions get a fresh timer. On timeout, completed edits and saved history remain available, and you can ask the agent to continue.
|
||||||
|
|
||||||
|
Multiple application instances should share `REDIS_URL` and `DEPLOYMENT_NAMESPACE` for rate limits, resumable streams, cancellation, resume notifications, and view deduplication. Without Redis, all core resume features work on a single instance. Private agent attachments need S3-compatible storage or private Blob; local disk supports public uploads only.
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
---
|
||||||
|
title: "Self-hosting on Vercel"
|
||||||
|
description: "Deploy Reactive Resume on Vercel Hobby with Neon PostgreSQL, private Vercel Blob, and Upstash Redis."
|
||||||
|
---
|
||||||
|
|
||||||
|
This guide deploys Reactive Resume to a Vercel project with the Deploy with Vercel wizard. The wizard provisions a database, file storage, and Redis for you. You supply two secrets.
|
||||||
|
|
||||||
|
Vercel serves the static web assets from its CDN and runs the shared Hono server in one Node.js 24 Function. Docker is also supported and uses the same API, authentication, templates, and data format. See [Self-hosting with Docker](/self-hosting/docker) for that option.
|
||||||
|
|
||||||
|
## Before you start
|
||||||
|
|
||||||
|
You need:
|
||||||
|
|
||||||
|
- A Vercel account and a GitHub account.
|
||||||
|
- Two independent random secrets, `AUTH_SECRET` and `ENCRYPTION_SECRET`. Generate each one separately:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl rand -hex 32
|
||||||
|
```
|
||||||
|
|
||||||
|
Save both values in a password manager. You must keep the same values for the life of the installation. Losing `ENCRYPTION_SECRET` makes saved AI-provider API keys unreadable.
|
||||||
|
|
||||||
|
The wizard connects three services through Vercel Marketplace:
|
||||||
|
|
||||||
|
| Service | Used for |
|
||||||
|
| --- | --- |
|
||||||
|
| Neon PostgreSQL | Application data |
|
||||||
|
| Vercel Blob (**private**) | Uploaded pictures, files, and agent attachments |
|
||||||
|
| Upstash Redis | Agent streaming and cancellation, shared rate limits, live resume updates |
|
||||||
|
|
||||||
|
Quotas and permitted use depend on your Vercel, Neon, and Upstash plans. Review [Vercel limits](https://vercel.com/docs/functions/limitations), [Neon](https://vercel.com/marketplace/neon), and [Upstash](https://vercel.com/marketplace/upstash/upstash-kv) before you choose a plan. Turn off automatic paid upgrades if you want to stay inside a free allowance.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Each AI agent run stops active work after four minutes. This keeps the run, plus saving and cleanup, inside Vercel Hobby's five-minute Function limit. The limit applies to each question, not to the whole conversation. Docker uses the same limit.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
<Steps>
|
||||||
|
<Step title="Start the wizard">
|
||||||
|
Click the button:
|
||||||
|
|
||||||
|
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Freactive-resume%2Freactive-resume&project-name=reactive-resume&repository-name=reactive-resume&env=AUTH_SECRET%2CENCRYPTION_SECRET&envDescription=Generate+two+independent+secrets+with+openssl+rand+-hex+32.+Keep+these+values+across+deployments.&envLink=https%3A%2F%2Fdocs.rxresu.me%2Fself-hosting%2Fvercel&stores=%5B%7B%22type%22%3A%22integration%22%2C%22protocol%22%3A%22storage%22%2C%22integrationSlug%22%3A%22neon%22%2C%22productSlug%22%3A%22neon%22%7D%2C%7B%22type%22%3A%22integration%22%2C%22protocol%22%3A%22storage%22%2C%22integrationSlug%22%3A%22upstash%22%2C%22productSlug%22%3A%22upstash-kv%22%7D%2C%7B%22type%22%3A%22blob%22%2C%22access%22%3A%22private%22%7D%5D)
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Choose the Git scope">
|
||||||
|
On Hobby, select your **personal GitHub account**. Private repositories owned by a GitHub organization require Vercel Pro.
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Connect the services">
|
||||||
|
Approve Neon, Upstash, and Blob. Set Blob access to **private**. Pick nearby regions for all three, ideally close to the Function region (`iad1` by default).
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Enter the secrets">
|
||||||
|
Paste `AUTH_SECRET` and `ENCRYPTION_SECRET`.
|
||||||
|
</Step>
|
||||||
|
|
||||||
|
<Step title="Deploy">
|
||||||
|
Keep the project root at the repository root and keep the committed `vercel.json`. Do not select the `apps/web` subdirectory and do not add an SPA fallback rewrite.
|
||||||
|
|
||||||
|
The build compiles both apps and applies database migrations before the deployment goes live. You do not run migrations yourself.
|
||||||
|
</Step>
|
||||||
|
</Steps>
|
||||||
|
|
||||||
|
<Warning>
|
||||||
|
Do not paste Docker's `.env.example` into Vercel. Its local URLs and S3 settings select the wrong services.
|
||||||
|
</Warning>
|
||||||
|
|
||||||
|
## Check the deployment
|
||||||
|
|
||||||
|
1. Open `https://<your-project>.vercel.app/api/health`. `database`, `storage`, and `redis` should all report `healthy`. A sleeping Neon database can fail the first check; retry once.
|
||||||
|
2. Open the production domain and create an account.
|
||||||
|
3. Optional: add an AI provider under **Settings** to enable AI features.
|
||||||
|
4. Optional: configure SMTP for verification and password-reset emails. Without SMTP, emails are written to the Function logs.
|
||||||
|
5. Optional: add social or custom OAuth sign-in with the callback URLs in the [SSO guide](/self-hosting/sso).
|
||||||
|
|
||||||
|
## Use a custom domain
|
||||||
|
|
||||||
|
1. Add the domain to the Vercel project.
|
||||||
|
2. Set `APP_URL` to the full origin, for example `https://resume.example.com`.
|
||||||
|
3. Update the callback URLs of every OAuth provider you configured.
|
||||||
|
4. Redeploy.
|
||||||
|
|
||||||
|
Changing the domain does not move stored files. Files stay under the same `DEPLOYMENT_NAMESPACE`.
|
||||||
|
|
||||||
|
## Update the deployment
|
||||||
|
|
||||||
|
Redeploy the same project. Keep its connected services and secrets unchanged.
|
||||||
|
|
||||||
|
- Migrations run in the build step, never in runtime Functions. A database advisory lock serializes concurrent deployments.
|
||||||
|
- Rolling back to an older deployment does not roll back the database schema. Keep migrations backward-compatible, or restore a database backup.
|
||||||
|
|
||||||
|
## Preview deployments
|
||||||
|
|
||||||
|
Preview builds refuse to run migrations by default, so untrusted preview code cannot change your production database.
|
||||||
|
|
||||||
|
To enable previews:
|
||||||
|
|
||||||
|
1. Connect separate Neon, Upstash, and Blob resources to the **Preview** environment.
|
||||||
|
2. Set `ALLOW_PREVIEW_MIGRATIONS=true` for **Preview** only.
|
||||||
|
|
||||||
|
A storage namespace does not isolate SQL rows. Never connect the production database to the Preview environment.
|
||||||
|
|
||||||
|
## Back up your data
|
||||||
|
|
||||||
|
Back up the Neon database and the Blob store. Store `AUTH_SECRET` and `ENCRYPTION_SECRET` separately from those backups.
|
||||||
|
|
||||||
|
Moving between Docker and Vercel does not copy the database or files. Migrate them yourself.
|
||||||
|
|
||||||
|
## Build locally
|
||||||
|
|
||||||
|
A local Vercel build applies migrations, so run it only against an isolated database.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vercel pull --environment production
|
||||||
|
APP_URL=https://your-project.vercel.app vercel build --prod
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace sensitive pulled values with local-only ones first. The CLI has no deployment hostname before publishing, so `APP_URL` is required here. Cloud builds set it automatically.
|
||||||
|
|
||||||
|
## Environment variables
|
||||||
|
|
||||||
|
Explicit variables take precedence over the Marketplace aliases listed here.
|
||||||
|
|
||||||
|
| Variable | Required | Behavior |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `AUTH_SECRET` | Yes | Signs sessions and tokens. Keep it constant. |
|
||||||
|
| `ENCRYPTION_SECRET` | Yes | At least 32 characters. Encrypts saved AI-provider API keys. Keep it constant. |
|
||||||
|
| `APP_URL` | No | Public origin. Defaults to the production domain in Production and to the deployment domain in Preview. Set it for a custom domain. |
|
||||||
|
| `DATABASE_URL` | Injected | Pooled runtime connection. Falls back to `POSTGRES_URL`. |
|
||||||
|
| `DATABASE_MIGRATION_URL` | No | Direct connection for migrations. Falls back to `DATABASE_URL_UNPOOLED`, then `POSTGRES_URL_NON_POOLING`, then `DATABASE_URL`. |
|
||||||
|
| `DATABASE_POOL_MAX` | No | Maximum database connections per Function instance. Default `10`. |
|
||||||
|
| `REDIS_URL` | Injected | Redis TCP/TLS URL. Falls back to Upstash's `KV_URL`. REST credentials alone do not work. |
|
||||||
|
| `STORAGE_BACKEND` | No | Must resolve to `blob` on Vercel, which is the default. The build fails with any other value. |
|
||||||
|
| `BLOB_READ_WRITE_TOKEN` | Injected | Provided by the connected Blob store. `BLOB_STORE_ID` with Vercel OIDC also works. |
|
||||||
|
| `DEPLOYMENT_NAMESPACE` | No | Prefix for Blob objects and Redis keys. Defaults to `production`, or to a per-branch value in Preview. Keep it constant after you store files. Set different values if two installations share one Blob store or Redis database. |
|
||||||
|
| `ALLOW_PREVIEW_MIGRATIONS` | No | Set to `true` in Preview only after you connect isolated preview resources. |
|
||||||
|
|
||||||
|
For SMTP, OAuth providers, and feature flags, use the same variables as Docker. See [Self-hosting with Docker](/self-hosting/docker).
|
||||||
|
|
||||||
|
## Upload limits
|
||||||
|
|
||||||
|
Vercel limits Function request bodies to 4.5 MB. The web app sends larger requests through private Blob staging, so these application limits still apply:
|
||||||
|
|
||||||
|
- General uploads: 10 MB per file.
|
||||||
|
- Agent attachments: 25 MiB per file and 100 MiB per thread.
|
||||||
|
|
||||||
|
Blob objects are never public. The application serves public pictures and authorizes private files itself. API clients that send large RPC requests must follow the [large RPC requests](/guides/large-rpc-requests) protocol. REST (`/api/openapi`) and MCP request bodies stay subject to the 4.5 MB limit.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Symptom | Fix |
|
||||||
|
| --- | --- |
|
||||||
|
| First build fails on configuration | Check that all three services are connected to Production and both secrets are set. Remove any `S3_*` variables and any `STORAGE_BACKEND` value other than `blob`. |
|
||||||
|
| Preview build refuses to migrate | Connect isolated preview resources, then set `ALLOW_PREVIEW_MIGRATIONS=true` for Preview. |
|
||||||
|
| Large upload returns `413` | Use the web app or the [large RPC requests](/guides/large-rpc-requests) protocol. Vercel Pro does not raise the request body limit. |
|
||||||
|
| Agent reconnect or stop fails | Check the Upstash TLS URL, the remaining Upstash quota, and that every environment uses the expected `DEPLOYMENT_NAMESPACE`. |
|
||||||
|
| Sign-in redirects to another hostname | Set `APP_URL` to the domain you use and redeploy. Do not add wildcard trusted origins. |
|
||||||
|
| Files are missing after a configuration change | Restore the original `DEPLOYMENT_NAMESPACE` and Blob connection. |
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,697 +0,0 @@
|
|||||||
# SEO/AEO Performance Improvements Implementation Plan
|
|
||||||
|
|
||||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
||||||
|
|
||||||
**Goal:** Improve the homepage bootstrap, LCP media path, initial root metadata, media caching, and canonical documentation URLs without adding SSR or new dependencies.
|
|
||||||
|
|
||||||
**Architecture:** Keep each change in its current owner: the server web fallback injects root-only SEO and cache headers, the homepage hero uses native lazy video behavior, the static HTML protects the module bootstrap, and Mintlify config owns documentation redirects. Existing client-rendered SEO remains as the hydration and client-navigation fallback.
|
|
||||||
|
|
||||||
**Tech Stack:** TypeScript, React 19, TanStack Router, Hono, Vitest, Testing Library, Vite, Mintlify, ffmpeg, cwebp.
|
|
||||||
|
|
||||||
## Global Constraints
|
|
||||||
|
|
||||||
- Do not add a dependency, package, SSR layer, prerenderer, special AEO schema, or `llms.txt` work.
|
|
||||||
- Preserve existing `noindex, follow` behavior for application shells and public resume routes.
|
|
||||||
- Do not add unconfirmed LinkedIn, AI-provider, v4, or placeholder redirects.
|
|
||||||
- Keep the existing WebSite, SoftwareApplication/WebApplication, Project, and FAQPage JSON-LD facts.
|
|
||||||
- Keep `/videos/` limited to versioned immutable media filenames.
|
|
||||||
- Do not claim field LCP, Core Web Vitals, or GSC validation from local verification.
|
|
||||||
- Do not use the write-capable repository-wide `pnpm check`; run focused non-mutating checks.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 1: Initial root metadata and versioned-media caching
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
|
|
||||||
- Modify: `apps/server/src/static/web.test.ts`
|
|
||||||
- Modify: `apps/server/src/static/web.ts`
|
|
||||||
|
|
||||||
**Interfaces:**
|
|
||||||
|
|
||||||
- Consumes: `Request.url`, the built `apps/web/dist/index.html`, and Hono `serveStatic.onFound`.
|
|
||||||
- Produces: the unchanged `handleWebApp(request: Request): Promise<Response>` interface and the unchanged `serveWebDistStatic` middleware export.
|
|
||||||
|
|
||||||
- [ ] **Step 1: Make the static middleware options observable in the existing test**
|
|
||||||
|
|
||||||
Replace the current `serveStatic` mock with a hoisted mock and capture its options immediately after importing `web.ts`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const mocks = vi.hoisted(() => ({
|
|
||||||
serveStatic: vi.fn((_options?: unknown) => vi.fn()),
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock("@hono/node-server/serve-static", () => ({
|
|
||||||
serveStatic: mocks.serveStatic,
|
|
||||||
}));
|
|
||||||
|
|
||||||
type StaticOptions = {
|
|
||||||
onFound?: (
|
|
||||||
path: string,
|
|
||||||
context: {
|
|
||||||
req: { path: string };
|
|
||||||
header: (name: string, value: string) => void;
|
|
||||||
},
|
|
||||||
) => void | Promise<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const { handleWebApp } = await import("./web");
|
|
||||||
const staticOptions = mocks.serveStatic.mock.calls[0]?.[0] as StaticOptions | undefined;
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Write the failing root metadata test**
|
|
||||||
|
|
||||||
Add this test to `apps/server/src/static/web.test.ts`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
it("injects canonical metadata and structured data into tracking-parameter root requests only", async () => {
|
|
||||||
vi.mocked(fs.readFile).mockResolvedValue(`
|
|
||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Reactive Resume — A free and open-source resume builder</title>
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Reactive Resume is a free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume."
|
|
||||||
>
|
|
||||||
</head>
|
|
||||||
<body><div id="app"></div></body>
|
|
||||||
</html>
|
|
||||||
`);
|
|
||||||
|
|
||||||
const response = await handleWebApp(new Request("https://example.com/?utm_source=search"));
|
|
||||||
const html = await response.text();
|
|
||||||
|
|
||||||
expect(html).toContain('<link rel="canonical" href="https://example.com/">');
|
|
||||||
expect(html).toContain('<link rel="preload" href="/videos/timelapse-v1.webp" as="image" fetchpriority="high">');
|
|
||||||
expect(html).toContain('<meta property="og:url" content="https://example.com/">');
|
|
||||||
expect(html).toContain('<meta property="og:image" content="https://example.com/opengraph/banner.jpg">');
|
|
||||||
expect(html).toContain('id="reactive-resume-structured-data"');
|
|
||||||
expect(html).toContain('"@type":["SoftwareApplication","WebApplication"]');
|
|
||||||
expect(html).not.toContain("utm_source");
|
|
||||||
|
|
||||||
const dashboardResponse = await handleWebApp(new Request("https://example.com/dashboard"));
|
|
||||||
expect(await dashboardResponse.text()).not.toContain('rel="canonical"');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Write the failing immutable-cache test**
|
|
||||||
|
|
||||||
Add this test beside the root metadata test:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
it("caches versioned homepage media immutably", async () => {
|
|
||||||
const headers = new Headers();
|
|
||||||
|
|
||||||
await staticOptions?.onFound?.("", {
|
|
||||||
req: { path: "/videos/timelapse-v1.mp4" },
|
|
||||||
header: (name, value) => headers.set(name, value),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(headers.get("Cache-Control")).toBe("public, max-age=31536000, immutable");
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Run the focused server test and verify RED**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter server test -- src/static/web.test.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: two failures. The root response lacks canonical/JSON-LD markup and the static middleware has no `onFound` callback.
|
|
||||||
|
|
||||||
- [ ] **Step 5: Add root metadata constants and serializer**
|
|
||||||
|
|
||||||
Add the following block after `BASE_SECURITY_HEADERS` in `apps/server/src/static/web.ts`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const ROOT_TITLE = "Reactive Resume — A free and open-source resume builder";
|
|
||||||
const ROOT_DESCRIPTION =
|
|
||||||
"Reactive Resume is a free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.";
|
|
||||||
const ROOT_POSTER_PATH = "/videos/timelapse-v1.webp";
|
|
||||||
const ROOT_FAQ_ITEMS = [
|
|
||||||
{
|
|
||||||
question: "Is Reactive Resume really free?",
|
|
||||||
answer:
|
|
||||||
"Yes! Reactive Resume is completely free to use, with no hidden costs, premium tiers, or subscription fees. It's open-source and will always remain free.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "How is my data protected?",
|
|
||||||
answer:
|
|
||||||
"Your data is stored securely and is never shared with third parties. You can also self-host Reactive Resume on your own servers for complete control over your data.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "Can I export my resume to PDF?",
|
|
||||||
answer:
|
|
||||||
"Absolutely! You can export your resume to PDF with a single click. The exported PDF maintains all your formatting and styling perfectly.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "Is Reactive Resume available in multiple languages?",
|
|
||||||
answer:
|
|
||||||
"Yes, Reactive Resume is available in multiple languages. You can choose your preferred language in the settings page, or using the language switcher in the top right corner. If you don't see your language, or you would like to improve the existing translations, you can contribute to the translations on Crowdin.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "What makes Reactive Resume different from other resume builders?",
|
|
||||||
answer:
|
|
||||||
"Reactive Resume is open-source, privacy-focused, and completely free. Unlike other resume builders, it doesn't show ads, track your data, or limit your features behind a paywall.",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
question: "How do I share my resume?",
|
|
||||||
answer:
|
|
||||||
"You can share your resume via a unique public URL, protect it with a password, or download it as a PDF to share directly. The choice is yours!",
|
|
||||||
},
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
function createRootSeoMarkup(canonicalUrl: string) {
|
|
||||||
const origin = new URL(canonicalUrl).origin;
|
|
||||||
const imageUrl = `${origin}/opengraph/banner.jpg`;
|
|
||||||
const structuredData = {
|
|
||||||
"@context": "https://schema.org",
|
|
||||||
"@graph": [
|
|
||||||
{
|
|
||||||
"@type": "WebSite",
|
|
||||||
name: "Reactive Resume",
|
|
||||||
url: canonicalUrl,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": ["SoftwareApplication", "WebApplication"],
|
|
||||||
name: "Reactive Resume",
|
|
||||||
url: canonicalUrl,
|
|
||||||
description: ROOT_DESCRIPTION,
|
|
||||||
applicationCategory: "BusinessApplication",
|
|
||||||
operatingSystem: "Web",
|
|
||||||
isAccessibleForFree: true,
|
|
||||||
offers: {
|
|
||||||
"@type": "Offer",
|
|
||||||
price: "0",
|
|
||||||
priceCurrency: "USD",
|
|
||||||
},
|
|
||||||
codeRepository: "https://github.com/reactive-resume/reactive-resume",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "Project",
|
|
||||||
name: "Reactive Resume",
|
|
||||||
url: canonicalUrl,
|
|
||||||
sameAs: ["https://github.com/reactive-resume/reactive-resume"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"@type": "FAQPage",
|
|
||||||
mainEntity: ROOT_FAQ_ITEMS.map((item) => ({
|
|
||||||
"@type": "Question",
|
|
||||||
name: item.question,
|
|
||||||
acceptedAnswer: {
|
|
||||||
"@type": "Answer",
|
|
||||||
text: item.answer,
|
|
||||||
},
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return `
|
|
||||||
<link rel="canonical" href="${canonicalUrl}">
|
|
||||||
<link rel="preload" href="${ROOT_POSTER_PATH}" as="image" fetchpriority="high">
|
|
||||||
<meta property="og:type" content="website">
|
|
||||||
<meta property="og:site_name" content="Reactive Resume">
|
|
||||||
<meta property="og:title" content="${ROOT_TITLE}">
|
|
||||||
<meta property="og:description" content="${ROOT_DESCRIPTION}">
|
|
||||||
<meta property="og:url" content="${canonicalUrl}">
|
|
||||||
<meta property="og:image" content="${imageUrl}">
|
|
||||||
<meta name="twitter:card" content="summary_large_image">
|
|
||||||
<meta name="twitter:title" content="${ROOT_TITLE}">
|
|
||||||
<meta name="twitter:description" content="${ROOT_DESCRIPTION}">
|
|
||||||
<meta name="twitter:image" content="${imageUrl}">
|
|
||||||
<script id="reactive-resume-structured-data" type="application/ld+json">${JSON.stringify(structuredData)}</script>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 6: Add the immutable media header**
|
|
||||||
|
|
||||||
Replace the current `serveWebDistStatic` declaration with:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export const serveWebDistStatic = serveStatic({
|
|
||||||
root: staticRoot,
|
|
||||||
onFound: (_path, context) => {
|
|
||||||
if (context.req.path.startsWith("/videos/")) {
|
|
||||||
context.header("Cache-Control", "public, max-age=31536000, immutable");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 7: Inject metadata only for the root pathname**
|
|
||||||
|
|
||||||
Parse the request URL once and replace the closing head only for `/`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export async function handleWebApp(request: Request) {
|
|
||||||
const isHead = request.method === "HEAD";
|
|
||||||
const requestUrl = new URL(request.url);
|
|
||||||
const pathname = requestUrl.pathname;
|
|
||||||
|
|
||||||
if (!isNoindexShellPath(pathname) && isAssetPath(pathname)) {
|
|
||||||
return new Response(isHead ? null : "Not Found", { status: 404 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const headers = getFallbackResponseHeaders(pathname);
|
|
||||||
if (!headers) return notFoundResponse({ head: isHead, noindex: true });
|
|
||||||
|
|
||||||
if (isHead) return new Response(null, { status: 200, headers });
|
|
||||||
|
|
||||||
const html = await fs.readFile(indexHtmlPath, "utf-8");
|
|
||||||
const canonicalUrl = new URL("/", requestUrl.origin).toString();
|
|
||||||
const responseHtml = pathname === "/" ? html.replace("</head>", `${createRootSeoMarkup(canonicalUrl)}</head>`) : html;
|
|
||||||
|
|
||||||
return new Response(responseHtml, { headers });
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 8: Run the focused server test and verify GREEN**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter server test -- src/static/web.test.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: all tests in `src/static/web.test.ts` pass.
|
|
||||||
|
|
||||||
- [ ] **Step 9: Run the focused server typecheck**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter server typecheck
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: exit code 0.
|
|
||||||
|
|
||||||
- [ ] **Step 10: Commit Task 1**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add apps/server/src/static/web.test.ts apps/server/src/static/web.ts
|
|
||||||
git commit -m "fix(server): emit initial homepage SEO metadata"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 2: Homepage poster, interaction-loaded video, and Rocket Loader exclusion
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
|
|
||||||
- Create: `apps/web/src/routes/_home/-sections/hero.test.tsx`
|
|
||||||
- Modify: `apps/web/src/routes/_home/-sections/hero.tsx`
|
|
||||||
- Modify: `apps/web/src/routes/_home/index.tsx`
|
|
||||||
- Modify: `apps/web/index.html`
|
|
||||||
- Rename: `apps/web/public/videos/timelapse.mp4` to `apps/web/public/videos/timelapse-v1.mp4`
|
|
||||||
- Create: `apps/web/public/videos/timelapse-v1.webp`
|
|
||||||
|
|
||||||
**Interfaces:**
|
|
||||||
|
|
||||||
- Consumes: the existing `Hero` component and TanStack route head descriptor.
|
|
||||||
- Produces: the unchanged `Hero(): JSX.Element` interface and versioned `/videos/timelapse-v1.{webp,mp4}` public media URLs.
|
|
||||||
|
|
||||||
- [ ] **Step 1: Write the failing hero media test**
|
|
||||||
|
|
||||||
Create `apps/web/src/routes/_home/-sections/hero.test.tsx`:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
// @vitest-environment happy-dom
|
|
||||||
|
|
||||||
import type { PropsWithChildren } from "react";
|
|
||||||
import { i18n } from "@lingui/core";
|
|
||||||
import { I18nProvider } from "@lingui/react";
|
|
||||||
import { render } from "@testing-library/react";
|
|
||||||
import { describe, expect, it, vi } from "vitest";
|
|
||||||
|
|
||||||
type LinkProps = PropsWithChildren<{
|
|
||||||
to: string;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
vi.mock("@tanstack/react-router", () => ({
|
|
||||||
Link: ({ children, to, ...rest }: LinkProps) => (
|
|
||||||
<a href={to} {...rest}>
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock("@/components/animation/comet-card", () => ({
|
|
||||||
CometCard: ({ children }: PropsWithChildren) => <div>{children}</div>,
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock("@/components/animation/spotlight", () => ({
|
|
||||||
Spotlight: () => <div data-testid="spotlight" />,
|
|
||||||
}));
|
|
||||||
|
|
||||||
i18n.loadAndActivate({ locale: "en", messages: {} });
|
|
||||||
|
|
||||||
const { Hero } = await import("./hero");
|
|
||||||
|
|
||||||
describe("Hero", () => {
|
|
||||||
it("shows a poster and waits for user interaction before loading the video", () => {
|
|
||||||
const { container } = render(
|
|
||||||
<I18nProvider i18n={i18n}>
|
|
||||||
<Hero />
|
|
||||||
</I18nProvider>,
|
|
||||||
);
|
|
||||||
const video = container.querySelector("video");
|
|
||||||
|
|
||||||
expect(video).toHaveAttribute("poster", "/videos/timelapse-v1.webp");
|
|
||||||
expect(video).toHaveAttribute("preload", "none");
|
|
||||||
expect(video).toHaveAttribute("src", "/videos/timelapse-v1.mp4");
|
|
||||||
expect(video).toHaveAttribute("controls");
|
|
||||||
expect(video).not.toHaveAttribute("autoplay");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 2: Run the hero test and verify RED**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter web test -- src/routes/_home/-sections/hero.test.tsx
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: failure because the existing video autoplays and has no poster, controls, or `preload="none"`.
|
|
||||||
|
|
||||||
- [ ] **Step 3: Version the video and generate the poster**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git mv apps/web/public/videos/timelapse.mp4 apps/web/public/videos/timelapse-v1.mp4
|
|
||||||
poster_tmp_dir=$(mktemp -d /tmp/reactive-resume-poster.XXXXXX)
|
|
||||||
ffmpeg -loglevel error -ss 00:00:03 -i apps/web/public/videos/timelapse-v1.mp4 -frames:v 1 "$poster_tmp_dir/frame.png"
|
|
||||||
cwebp -quiet -q 82 "$poster_tmp_dir/frame.png" -o apps/web/public/videos/timelapse-v1.webp
|
|
||||||
magick identify -format '%wx%h %b\n' apps/web/public/videos/timelapse-v1.webp
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: `1146x720` and a WebP size substantially below the 4.2 MB video.
|
|
||||||
|
|
||||||
- [ ] **Step 4: Replace autoplay with native lazy video behavior**
|
|
||||||
|
|
||||||
Replace the video element in `apps/web/src/routes/_home/-sections/hero.tsx` with:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
<video
|
|
||||||
loop
|
|
||||||
muted
|
|
||||||
controls
|
|
||||||
playsInline
|
|
||||||
preload="none"
|
|
||||||
width={1146}
|
|
||||||
height={720}
|
|
||||||
poster="/videos/timelapse-v1.webp"
|
|
||||||
src="/videos/timelapse-v1.mp4"
|
|
||||||
aria-label={t`Timelapse demonstration of building a resume with Reactive Resume`}
|
|
||||||
className="aspect-[1146/720] w-full rounded-md border object-cover"
|
|
||||||
/>
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 5: Add the client-navigation poster preload**
|
|
||||||
|
|
||||||
Change the homepage route `links` array in `apps/web/src/routes/_home/index.tsx` to:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
links: [
|
|
||||||
{ rel: "canonical", href: canonicalUrl },
|
|
||||||
{ rel: "preload", href: "/videos/timelapse-v1.webp", as: "image", fetchPriority: "high" },
|
|
||||||
],
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 6: Protect the bootstrap and make the base title complete**
|
|
||||||
|
|
||||||
Change the title and module script in `apps/web/index.html` to:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<title>Reactive Resume — A free and open-source resume builder</title>
|
|
||||||
```
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script type="module" data-cfasync="false" src="/src/main.tsx"></script>
|
|
||||||
```
|
|
||||||
|
|
||||||
The `data-cfasync` attribute must remain before `src`.
|
|
||||||
|
|
||||||
- [ ] **Step 7: Run the hero test and verify GREEN**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter web test -- src/routes/_home/-sections/hero.test.tsx
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: the hero test passes.
|
|
||||||
|
|
||||||
- [ ] **Step 8: Run the focused web typecheck and production build**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter web typecheck
|
|
||||||
pnpm --filter web build
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: both commands exit 0.
|
|
||||||
|
|
||||||
- [ ] **Step 9: Verify the built bootstrap and media**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rg -n '<script[^>]*type="module"[^>]*data-cfasync="false"[^>]*src=' apps/web/dist/index.html
|
|
||||||
test -f apps/web/dist/videos/timelapse-v1.webp
|
|
||||||
test -f apps/web/dist/videos/timelapse-v1.mp4
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: one module script match and both versioned media files exist in `dist`.
|
|
||||||
|
|
||||||
- [ ] **Step 10: Commit Task 2**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add apps/web/index.html apps/web/public/videos/timelapse-v1.mp4 apps/web/public/videos/timelapse-v1.webp apps/web/src/routes/_home/-sections/hero.test.tsx apps/web/src/routes/_home/-sections/hero.tsx apps/web/src/routes/_home/index.tsx
|
|
||||||
git commit -m "perf(web): remove homepage video from the LCP path"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 3: Canonical documentation paths and redirects
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
|
|
||||||
- Rename: `docs/getting-started/index.mdx` to `docs/getting-started.mdx`
|
|
||||||
- Modify: `docs/docs.json`
|
|
||||||
- Modify: `docs/use-cases/free-resume-builder.mdx`
|
|
||||||
- Modify: `docs/use-cases/open-source-resume-builder.mdx`
|
|
||||||
|
|
||||||
**Interfaces:**
|
|
||||||
|
|
||||||
- Consumes: Mintlify page paths and `docs.json` redirects.
|
|
||||||
- Produces: `/getting-started` as the canonical introduction and permanent redirects from `/getting-started/index` and `/translation/README`.
|
|
||||||
|
|
||||||
- [ ] **Step 1: Run a desired-state check and verify RED**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
test -f docs/getting-started.mdx &&
|
|
||||||
test ! -e docs/getting-started/index.mdx &&
|
|
||||||
! rg -n '"getting-started/index"' docs/docs.json &&
|
|
||||||
! rg -n '\]\(/getting-started/index\)' docs --glob '*.mdx'
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: nonzero exit because the introduction still lives at `docs/getting-started/index.mdx`.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Move the introduction page**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git mv docs/getting-started/index.mdx docs/getting-started.mdx
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 3: Add redirects and update navigation**
|
|
||||||
|
|
||||||
Add this top-level field after `description` in `docs/docs.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"redirects": [
|
|
||||||
{
|
|
||||||
"source": "/getting-started/index",
|
|
||||||
"destination": "/getting-started"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "/translation/README",
|
|
||||||
"destination": "/contributing/translations"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
```
|
|
||||||
|
|
||||||
Change the first Getting Started navigation page from:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"getting-started/index"
|
|
||||||
```
|
|
||||||
|
|
||||||
to:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"getting-started"
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 4: Update internal links**
|
|
||||||
|
|
||||||
In `docs/use-cases/free-resume-builder.mdx`, change:
|
|
||||||
|
|
||||||
```md
|
|
||||||
Start with [Introduction](/getting-started) for the product overview or follow the [Quickstart](/getting-started/quickstart) to create your first resume on [rxresu.me](https://rxresu.me).
|
|
||||||
```
|
|
||||||
|
|
||||||
In `docs/use-cases/open-source-resume-builder.mdx`, change:
|
|
||||||
|
|
||||||
```md
|
|
||||||
- [Introduction](/getting-started)
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] **Step 5: Re-run the desired-state check and verify GREEN**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
test -f docs/getting-started.mdx &&
|
|
||||||
test ! -e docs/getting-started/index.mdx &&
|
|
||||||
! rg -n '"getting-started/index"' docs/docs.json &&
|
|
||||||
! rg -n '\]\(/getting-started/index\)' docs --glob '*.mdx'
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: exit code 0. The redirect source includes a leading slash, so it does not match the removed navigation value.
|
|
||||||
|
|
||||||
- [ ] **Step 6: Validate Mintlify redirects and links**
|
|
||||||
|
|
||||||
Run from the repository root:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd docs && pnpm dlx mint@4.2.748 broken-links --check-redirects
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: no broken internal links or invalid redirect destinations.
|
|
||||||
|
|
||||||
- [ ] **Step 7: Run Markdown and JSON checks**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm exec markdownlint-cli2 docs/getting-started.mdx docs/use-cases/free-resume-builder.mdx docs/use-cases/open-source-resume-builder.mdx
|
|
||||||
node -e 'JSON.parse(require("node:fs").readFileSync("docs/docs.json", "utf8"))'
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: both commands exit 0.
|
|
||||||
|
|
||||||
- [ ] **Step 8: Commit Task 3**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add docs/docs.json docs/getting-started.mdx docs/use-cases/free-resume-builder.mdx docs/use-cases/open-source-resume-builder.mdx
|
|
||||||
git commit -m "docs: repair canonical documentation paths"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 4: Fresh completion verification
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
|
|
||||||
- Verify only; do not create or modify files.
|
|
||||||
|
|
||||||
**Interfaces:**
|
|
||||||
|
|
||||||
- Consumes: all changes from Tasks 1–3.
|
|
||||||
- Produces: current test, typecheck, build, documentation, and diff evidence.
|
|
||||||
|
|
||||||
- [ ] **Step 1: Run focused formatting and lint checks**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm exec biome check --error-on-warnings apps/server/src/static/web.test.ts apps/server/src/static/web.ts apps/web/src/routes/_home/-sections/hero.test.tsx apps/web/src/routes/_home/-sections/hero.tsx apps/web/src/routes/_home/index.tsx
|
|
||||||
pnpm exec markdownlint-cli2 docs/superpowers/specs/2026-07-28-seo-aeo-performance-design.md docs/superpowers/plans/2026-07-28-seo-aeo-performance.md docs/getting-started.mdx docs/use-cases/free-resume-builder.mdx docs/use-cases/open-source-resume-builder.mdx
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: no errors or warnings.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Run focused tests**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter server test -- src/static/web.test.ts
|
|
||||||
pnpm --filter web test -- src/routes/_home/-sections/hero.test.tsx src/libs/seo.test.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: all focused tests pass.
|
|
||||||
|
|
||||||
- [ ] **Step 3: Run package typechecks**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter server typecheck
|
|
||||||
pnpm --filter web typecheck
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: both commands exit 0.
|
|
||||||
|
|
||||||
- [ ] **Step 4: Run production builds**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter web build
|
|
||||||
pnpm --filter server build
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: both commands exit 0.
|
|
||||||
|
|
||||||
- [ ] **Step 5: Re-run documentation validation**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd docs && pnpm dlx mint@4.2.748 broken-links --check-redirects
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: no broken internal links or invalid redirect destinations.
|
|
||||||
|
|
||||||
- [ ] **Step 6: Inspect the final built artifacts and diff**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rg -n '<script[^>]*type="module"[^>]*data-cfasync="false"[^>]*src=' apps/web/dist/index.html
|
|
||||||
magick identify -format '%wx%h %b\n' apps/web/public/videos/timelapse-v1.webp
|
|
||||||
git diff --check HEAD~3..HEAD
|
|
||||||
git status --short
|
|
||||||
git log -5 --oneline
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected:
|
|
||||||
|
|
||||||
- The built module script remains a normal module with `data-cfasync="false"` before `src`.
|
|
||||||
- The poster is `1146x720` and substantially smaller than the video.
|
|
||||||
- `git diff --check` reports no whitespace errors.
|
|
||||||
- The only untracked path not created by this plan may be the pre-existing `.playwright-mcp/` directory; do not stage or remove it.
|
|
||||||
- The log shows the design commit, plan commit, and three implementation commits.
|
|
||||||
|
|
||||||
- [ ] **Step 7: Report deployment-only follow-up**
|
|
||||||
|
|
||||||
Report these as unverified deployment checks, not completed local work:
|
|
||||||
|
|
||||||
- Disable Rocket Loader for `rxresu.me` with a Cloudflare Configuration Rule.
|
|
||||||
- Run ten cold production Chromium navigations and confirm headline and CTA visibility.
|
|
||||||
- Measure throttled mobile LCP.
|
|
||||||
- Monitor field LCP/INP and GSC canonical/5xx validation after deployment.
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,152 +0,0 @@
|
|||||||
# Implicit Social Signup Implementation Plan
|
|
||||||
|
|
||||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
||||||
|
|
||||||
**Goal:** Make every built-in social sign-in create an unknown user automatically while preserving the global signup restriction.
|
|
||||||
|
|
||||||
**Architecture:** Use Better Auth's native implicit-signup behavior by removing the provider-level opt-out. Delete the client-side `requestSignUp` distinction so every page uses the same social sign-in call, while `disableSignUp: env.FLAG_DISABLE_SIGNUPS` remains the server-enforced hard stop.
|
|
||||||
|
|
||||||
**Tech Stack:** TypeScript, Better Auth 1.6.26, React 19, Vitest, pnpm
|
|
||||||
|
|
||||||
## Global Constraints
|
|
||||||
|
|
||||||
- Do not add dependencies or new abstractions.
|
|
||||||
- Apply the behavior to Google, GitHub, and LinkedIn.
|
|
||||||
- Leave custom OAuth and passkey behavior unchanged.
|
|
||||||
- Keep `FLAG_DISABLE_SIGNUPS` authoritative on the server.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
- `packages/auth/src/config.test.ts`: Characterizes Reactive Resume's built-in social-provider signup policy.
|
|
||||||
- `packages/auth/src/config.ts`: Owns Better Auth provider configuration and the global signup restriction.
|
|
||||||
- `apps/web/src/features/auth/components/social-auth.tsx`: Starts social sign-in without page-specific signup intent.
|
|
||||||
- `apps/web/src/features/auth/pages/register.tsx`: Uses the shared social-auth component without signup-only props.
|
|
||||||
|
|
||||||
### Task 1: Use Better Auth's Native Implicit Social Signup
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Create: `packages/auth/src/config.test.ts`
|
|
||||||
- Modify: `packages/auth/src/config.ts:196-223`
|
|
||||||
- Modify: `apps/web/src/features/auth/components/social-auth.tsx:14-66,115-137`
|
|
||||||
- Modify: `apps/web/src/features/auth/pages/register.tsx:245`
|
|
||||||
|
|
||||||
**Interfaces:**
|
|
||||||
- Consumes: `auth.options.socialProviders`, `env.FLAG_DISABLE_SIGNUPS`, and `authClient.signIn.social({ provider, callbackURL })`.
|
|
||||||
- Produces: One social-auth flow in which existing identities sign in, unknown identities sign up implicitly, and globally disabled signups remain rejected by Better Auth.
|
|
||||||
|
|
||||||
- [ ] **Step 1: Write the failing configuration test**
|
|
||||||
|
|
||||||
Create `packages/auth/src/config.test.ts`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { describe, expect, it } from "vitest";
|
|
||||||
import { env } from "@reactive-resume/env/server";
|
|
||||||
import { auth } from "./config";
|
|
||||||
|
|
||||||
describe("social provider signup policy", () => {
|
|
||||||
it.each(["google", "github", "linkedin"] as const)(
|
|
||||||
"allows implicit signup through %s while honoring the global signup restriction",
|
|
||||||
(provider) => {
|
|
||||||
const config = auth.options.socialProviders?.[provider];
|
|
||||||
|
|
||||||
expect(config?.disableImplicitSignUp).toBeUndefined();
|
|
||||||
expect(config?.disableSignUp).toBe(env.FLAG_DISABLE_SIGNUPS);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
This test catches a provider being opted out of implicit signup or becoming detached from `FLAG_DISABLE_SIGNUPS`.
|
|
||||||
|
|
||||||
- [ ] **Step 2: Run the test and verify the expected failure**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter @reactive-resume/auth test -- src/config.test.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: FAIL for Google, GitHub, and LinkedIn because `disableImplicitSignUp` is currently `true`, not `undefined`.
|
|
||||||
|
|
||||||
- [ ] **Step 3: Enable native implicit signup and delete client-side signup intent**
|
|
||||||
|
|
||||||
In `packages/auth/src/config.ts`, remove only the three `disableImplicitSignUp: true` properties. Keep each provider's existing line:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
disableSignUp: env.FLAG_DISABLE_SIGNUPS,
|
|
||||||
```
|
|
||||||
|
|
||||||
In `apps/web/src/features/auth/components/social-auth.tsx`, delete `SocialAuthProps`, `SocialSignInOptions`, and `getSocialSignInOptions`. Change the component signature:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export function SocialAuth() {
|
|
||||||
```
|
|
||||||
|
|
||||||
Change the loading branch to:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
{isLoading ? <SocialAuthSkeleton /> : <SocialAuthButtons providers={providers} />}
|
|
||||||
```
|
|
||||||
|
|
||||||
Remove `requestSignUp` from the button props and function parameter:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type SocialAuthButtonsProps = {
|
|
||||||
providers: RouterOutput["auth"]["providers"]["list"];
|
|
||||||
};
|
|
||||||
|
|
||||||
function SocialAuthButtons({ providers }: SocialAuthButtonsProps) {
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace the Google, GitHub, and LinkedIn calls with the native Better Auth input shape:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
authClient.signIn.social({ provider: "google", callbackURL: "/dashboard" });
|
|
||||||
authClient.signIn.social({ provider: "github", callbackURL: "/dashboard" });
|
|
||||||
authClient.signIn.social({ provider: "linkedin", callbackURL: "/dashboard" });
|
|
||||||
```
|
|
||||||
|
|
||||||
In `apps/web/src/features/auth/pages/register.tsx`, change:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
<SocialAuth requestSignUp />
|
|
||||||
```
|
|
||||||
|
|
||||||
to:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
<SocialAuth />
|
|
||||||
```
|
|
||||||
|
|
||||||
Do not modify `LoginPage`; it already renders `<SocialAuth />`.
|
|
||||||
|
|
||||||
- [ ] **Step 4: Run the focused test and verify it passes**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter @reactive-resume/auth test -- src/config.test.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: PASS for all three providers.
|
|
||||||
|
|
||||||
- [ ] **Step 5: Run focused validation**
|
|
||||||
|
|
||||||
Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm --filter @reactive-resume/auth typecheck
|
|
||||||
pnpm --filter web typecheck
|
|
||||||
pnpm exec biome check packages/auth/src/config.test.ts packages/auth/src/config.ts apps/web/src/features/auth/components/social-auth.tsx apps/web/src/features/auth/pages/register.tsx
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected: all commands exit successfully with no diagnostics and no file changes.
|
|
||||||
|
|
||||||
- [ ] **Step 6: Commit the implementation**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git add packages/auth/src/config.test.ts packages/auth/src/config.ts apps/web/src/features/auth/components/social-auth.tsx apps/web/src/features/auth/pages/register.tsx
|
|
||||||
git commit -m "fix(auth): allow implicit social signup"
|
|
||||||
```
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
# Cover Letter Library Implementation Plan
|
|
||||||
|
|
||||||
> **For agentic workers:** Use executing-plans to implement this plan task by task. Backend ownership belongs to audit_builder; apps/web ownership belongs to root. Leave changes uncommitted for root review.
|
|
||||||
|
|
||||||
**Goal:** Persist cover letters independently, edit the same document from the library and builder, and export immutable application attachments.
|
|
||||||
|
|
||||||
**Architecture:** A user-owned cover_letter row stores rich text plus copied resume styling and sender information. Source resume/application links supply context, never synchronized content. Existing embedded letters remain independent and support explicit copying into the library.
|
|
||||||
|
|
||||||
**Tech Stack:** TypeScript, Zod, Drizzle/PostgreSQL, oRPC, React PDF, TanStack Query.
|
|
||||||
|
|
||||||
**Spec:** The contract and lifecycle below implement approved issue #3255 hybrid behavior.
|
|
||||||
|
|
||||||
## Global Constraints
|
|
||||||
|
|
||||||
- Base: origin/main e549d114ea020380b156197f6460faddbe3022fd; branch codex/issue-3255-cover-letter-library.
|
|
||||||
- No commits, pushes, merges or application attachment implementation in this worker.
|
|
||||||
- All shell commands use rtk. Source package boundaries follow AGENTS.md.
|
|
||||||
- Styling is copied at creation and on explicit refresh. Source deletion sets provenance IDs to null; saved appearance and content survive.
|
|
||||||
- Resume embedded cover-letter sections and their existing JSON imports remain untouched.
|
|
||||||
- Rich HTML must not introduce executable markup. Generated plain text is HTML-escaped before paragraph composition.
|
|
||||||
|
|
||||||
## Shared contract
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type CoverLetterStyle = {
|
|
||||||
basics: ResumeData["basics"];
|
|
||||||
picture: ResumeData["picture"];
|
|
||||||
metadata: Omit<ResumeData["metadata"], "notes" | "layout">;
|
|
||||||
sectionId: string;
|
|
||||||
itemId: string;
|
|
||||||
};
|
|
||||||
type CoverLetter = {
|
|
||||||
id: string; name: string; recipient: string; content: string;
|
|
||||||
style: CoverLetterStyle;
|
|
||||||
sourceResumeId: string | null; sourceApplicationId: string | null;
|
|
||||||
revision: number; createdAt: Date; updatedAt: Date;
|
|
||||||
};
|
|
||||||
type CoverLetterDocument = {
|
|
||||||
format: "reactive-resume-cover-letter"; version: 1;
|
|
||||||
name: string; recipient: string; content: string; style: CoverLetterStyle;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Exported types/schema: `@reactive-resume/schema/cover-letter/data`.
|
|
||||||
Pure helpers: `@reactive-resume/resume/cover-letter` exposes `createCoverLetterResumeData(letter: Pick<CoverLetter, "name" | "recipient" | "content" | "style">): ResumeData`, `copyCoverLetterStyle(data: ResumeData, sectionId?: string, itemId?: string): CoverLetterStyle`, and `coverLetterTextToHtml(text: string): string`.
|
|
||||||
|
|
||||||
oRPC namespace `coverLetters`:
|
|
||||||
|
|
||||||
| Procedure | Input | Output |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| list | `{ search?, resumeId?, applicationId?, limit?: number, offset?: number }` | `{ items: CoverLetter[], total: number }` |
|
|
||||||
| getById | `{ id }` | `CoverLetter` |
|
|
||||||
| create | `{ name, recipient?, content?, resumeId?, applicationId? }` | `CoverLetter` |
|
|
||||||
| update | `{ id, expectedRevision, name?, recipient?, content? }` | `CoverLetter` |
|
|
||||||
| refreshStyle | `{ id, expectedRevision, resumeId }` | `CoverLetter` |
|
|
||||||
| duplicate | `{ id, name? }` | `CoverLetter` |
|
|
||||||
| delete | `{ id, expectedRevision }` | `void` |
|
|
||||||
| copyEmbedded | `{ resumeId, sectionId, itemId, name? }` | `CoverLetter` |
|
|
||||||
| export | `{ id }` | `CoverLetterDocument` |
|
|
||||||
| import | `{ document: CoverLetterDocument }` | `CoverLetter` |
|
|
||||||
|
|
||||||
List defaults limit=20, offset=0; limit max100; literal case-insensitive name search. Stable ordering updatedAt descending, id descending. IDs and dates are server-owned. Every read/write enforces user ownership; inaccessible contexts return NOT_FOUND. Atomic revision predicates return CONFLICT for stale same-user mutations, NOT_FOUND for absent/foreign documents. Import always creates a new owned ID and clears provenance.
|
|
||||||
|
|
||||||
AI `applications.ai.draftMessage` retains `{ text }` and adds optional `coverLetterId`; cover-letter generation persists before returning. Follow-up generation stays transient. Client opens saved document by ID rather than making another copy.
|
|
||||||
|
|
||||||
## Lifecycle and UI integration
|
|
||||||
|
|
||||||
Library and builder select the same row. Editor uses RichInput and explicit Save with revision; CONFLICT offers reload without discarding current edits automatically. Name/content views render text or existing trusted rich-editor/PDF pathways, never raw HTML insertion. Empty document creation uses default resume styling; chosen resume copies basics/picture and metadata except private notes and resume layout. Composer includes only one full-width cover-letter section with empty standard sections and notes.
|
|
||||||
|
|
||||||
Refresh explicitly replaces copied styling/sender only, preserves letter text/name and identity. Embedded copy preserves original section/item IDs so existing targeted style rules continue to apply. Original embedded letter remains unchanged and independently editable. Account export includes coverLetters; standalone versioned JSON has content and styling needed to render without original context IDs. Images remain URL references, not embedded bytes. Resume deletion removes screenshots/PDFs only and keeps picture assets; deleting the entire account removes its owned assets and documents.
|
|
||||||
|
|
||||||
PDF uses `createResumePdfBlob(createCoverLetterResumeData(letter))`. Application action uploads the resulting File via existing `applications.attachDocument({id,kind:"cover-letter",file})`. Later edits/deletion of source letters never mutate uploaded attachment snapshots. PR #3395's escaping and composition move into pure domain helper; its attachment endpoint is reused.
|
|
||||||
|
|
||||||
## Task 1: Schema and pure composition
|
|
||||||
|
|
||||||
Files: packages/schema/src/cover-letter/data.ts; packages/resume/src/cover-letter.ts; their tests and package export maps.
|
|
||||||
|
|
||||||
- [ ] Add failing tests for snapshot isolation and preserved sender/target IDs; escaped `<script>` plain text; absence of resume sections/notes in composed data; round-trip document shape.
|
|
||||||
- [ ] Run `rtk proxy pnpm --filter @reactive-resume/resume exec vitest run src/cover-letter.test.ts` and capture RED.
|
|
||||||
- [ ] Implement exported contract and pure helpers; verify `expect(result.basics).toEqual(source.basics)` and `expect(result.metadata.notes).toBe("")`.
|
|
||||||
- [ ] Run schema/domain tests and typechecks to GREEN.
|
|
||||||
|
|
||||||
## Task 2: Owned persistence and contracts
|
|
||||||
|
|
||||||
Files: packages/db/src/schema/cover-letter.ts, schema/index.ts, generated migrations; packages/api/src/dto/cover-letter.ts; features/cover-letters/{service,router,html}.ts; routers/index.ts.
|
|
||||||
|
|
||||||
- [ ] Add failing service/procedure tests for account isolation, literal search and paging, stale update/delete/refresh conflicts, duplication, refreshed style preserving content, foreign context rejection and JSON import clearing context.
|
|
||||||
- [ ] Run `rtk proxy pnpm --filter @reactive-resume/api exec vitest run src/features/cover-letters` and capture RED.
|
|
||||||
- [ ] Implement explicit user predicates on all queries and atomic `{userId,id,revision}` update/delete conditions with revision increment.
|
|
||||||
- [ ] Add allowlisted rich-HTML sanitization at every persistence boundary, covering script/event/style/unsafe URL removal and rich-text formatting retention.
|
|
||||||
- [ ] Generate additive table migration with cascade owner and SET NULL source FKs; verify disposable DB preserves snapshots after source deletion.
|
|
||||||
- [ ] Run API tests and DB/schema/API typechecks; distinguish known email baseline errors.
|
|
||||||
|
|
||||||
## Task 3: Generation and backup integration
|
|
||||||
|
|
||||||
Files: packages/api/src/features/applications/ai.ts and tests; packages/api/src/features/auth/service.ts and tests.
|
|
||||||
|
|
||||||
- [ ] Test generation stores escaped content before response, persistence failure rejects operation, follow-up does not create a letter, and owner backup includes independent documents.
|
|
||||||
- [ ] Persist generated letter with linked application/resume context and return `coverLetterId` beside original text.
|
|
||||||
- [ ] Include owned cover-letter data in account backup without introducing restore assumptions for resume imports.
|
|
||||||
- [ ] Run focused and package tests; review root UI integration against fixed contract.
|
|
||||||
|
|
||||||
## Task 4: Verification and handoff
|
|
||||||
|
|
||||||
- [ ] Run scoped Biome, package typechecks, boundary check, generated migration no-changes check and meaningful regression suites.
|
|
||||||
- [ ] Send root exact changed files, RED/GREEN evidence, migration order and any unresolved limitations. Root performs final review and publishing.
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,667 +0,0 @@
|
|||||||
# Semantic CSS Stylesheet Design
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
Draft for user review. The product behavior in this document has been approved conversationally; the written
|
|
||||||
architecture still requires review before implementation planning.
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
Reactive Resume renders its templates with React PDF rather than browser HTML. React PDF accepts style objects on a
|
|
||||||
known component tree and supports a broad CSS-like property set, but it does not provide a browser DOM or a general
|
|
||||||
selector engine.
|
|
||||||
|
|
||||||
The current customization system stores constrained rules in `metadata.styleRules`. Each rule targets all sections, a
|
|
||||||
section type, or a section ID and applies an intent to one semantic slot. That design is safe and portable, but its form
|
|
||||||
UI is cumbersome to reproduce or share, and its target model cannot reach headers, individual items or fields, page
|
|
||||||
regions, or template-specific visual parts.
|
|
||||||
|
|
||||||
Semantic CSS replaces the form with a familiar text language. It retains typed compilation and semantic targets rather
|
|
||||||
than promising that arbitrary browser CSS can run inside React PDF.
|
|
||||||
|
|
||||||
## Goals
|
|
||||||
|
|
||||||
- Provide one copy-pastable text stylesheet for all PDF-specific visual customization.
|
|
||||||
- Keep Design, Typography, Layout, Page, and Picture controls as base settings.
|
|
||||||
- Let the stylesheet override those base visuals wherever an exposed semantic PDF node permits it.
|
|
||||||
- Target all sections, groups of section types, one section, one item, one field, structural regions, header content,
|
|
||||||
rich text, and documented template-specific parts.
|
|
||||||
- Support portable theme rules and optional resume-specific rules based on stable IDs.
|
|
||||||
- Support nearly all style properties that the pinned React PDF renderer can safely implement.
|
|
||||||
- Preserve invalid user text while rendering the last valid stylesheet.
|
|
||||||
- Produce identical behavior in browser preview, browser export, public PDF views, and server PDF export.
|
|
||||||
- Convert existing structured style rules without changing their rendered appearance.
|
|
||||||
|
|
||||||
## Non-goals
|
|
||||||
|
|
||||||
- The stylesheet does not edit resume content or mutate builder layout metadata.
|
|
||||||
- The stylesheet does not apply to DOCX or Markdown exports.
|
|
||||||
- It does not expose a browser DOM, JavaScript, arbitrary renderer objects, or executable expressions.
|
|
||||||
- It does not support animations, transitions, interactive pseudo-classes, CSS Grid, generated content, or browser-only
|
|
||||||
properties.
|
|
||||||
- It does not load fonts, images, imports, or any other remote or embedded asset.
|
|
||||||
- Font-family selection remains owned by the Typography section.
|
|
||||||
- Picture source, upload, crop, and visibility data remain owned by the Picture section. The rendered picture node can
|
|
||||||
still be sized, positioned, transformed, or hidden by the stylesheet.
|
|
||||||
|
|
||||||
## Product Model
|
|
||||||
|
|
||||||
The existing visual controls remain the base layer. Semantic CSS is the final author-controlled layer:
|
|
||||||
|
|
||||||
1. Builder visual settings and template defaults.
|
|
||||||
2. Template-specific computed styles.
|
|
||||||
3. Semantic CSS declarations.
|
|
||||||
4. Minimal crash-prevention invariants.
|
|
||||||
|
|
||||||
The stylesheet may visually hide, reorder, resize, or position existing output. These changes affect only PDF
|
|
||||||
presentation. They do not rewrite content, section ordering, page assignments, or other builder data.
|
|
||||||
|
|
||||||
## Persisted Data
|
|
||||||
|
|
||||||
Resume metadata gains a versioned stylesheet value:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type StylesheetSource = {
|
|
||||||
languageVersion: number;
|
|
||||||
text: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type SemanticStylesheet = {
|
|
||||||
mode: "legacy" | "semantic";
|
|
||||||
source: StylesheetSource;
|
|
||||||
applied: StylesheetSource;
|
|
||||||
};
|
|
||||||
|
|
||||||
type StylesheetMutationState = {
|
|
||||||
revision: number;
|
|
||||||
stylesheet: SemanticStylesheet;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
- `source.text` is the exact editable text and may be invalid.
|
|
||||||
- `applied.text` is the most recent valid text and is the only text used for rendering.
|
|
||||||
- Each value carries its own `languageVersion`, allowing an invalid source written for a future language version to
|
|
||||||
preserve and render an older valid program.
|
|
||||||
- `mode` is the persisted rendering discriminator. A missing stylesheet is interpreted as `legacy`.
|
|
||||||
- `revision` is server-owned concurrency metadata, not resume content. It is stored in a dedicated database column and
|
|
||||||
returned only in the stylesheet mutation envelope.
|
|
||||||
|
|
||||||
The compiled AST or intermediate representation is not persisted. Browser and server compilation is a pure operation
|
|
||||||
cached by language version, source hash, compiler build, semantic registry fingerprint, and PDF adapter fingerprint.
|
|
||||||
Caches are bounded and process-local; they are never treated as durable state.
|
|
||||||
|
|
||||||
Stylesheet state is owned by a dedicated authenticated mutation rather than the existing full-document autosave
|
|
||||||
mutation. It accepts an expected stylesheet revision and resume render-data version. The generic `resume.update` path
|
|
||||||
must preserve the database's stylesheet value instead of replacing it from submitted resume data. This preservation
|
|
||||||
behavior must deploy before clients can send Semantic CSS data.
|
|
||||||
|
|
||||||
Compilation and PDF preflight never run while holding a database lock. The mutation reads an immutable resume snapshot,
|
|
||||||
compiles and preflights against that snapshot, then performs a short transaction that compare-and-swaps both the
|
|
||||||
stylesheet revision and resume render-data version. If either changed, it returns a conflict without writing; the client
|
|
||||||
rebases its unsaved source onto the new snapshot and retries. This prevents promotion against content or base settings
|
|
||||||
that differ from those preflighted.
|
|
||||||
|
|
||||||
The server defines separate state transitions. A source can replace `applied` only after compilation and a bounded PDF
|
|
||||||
render preflight against the current resume succeed:
|
|
||||||
|
|
||||||
- **Edit source:** ignore client-applied text. Store the candidate in `source`. In semantic mode, also store it in
|
|
||||||
`applied` only when compilation and preflight succeed; otherwise preserve the row's current `applied`. In legacy mode,
|
|
||||||
edits remain an inactive draft.
|
|
||||||
- **Activate converted source:** require successful compilation, set `mode` to `semantic`, and store the candidate in
|
|
||||||
both source values after preflight. This requires an explicit **Activate Semantic CSS** action. Merely opening,
|
|
||||||
editing, or autosaving a legacy draft does not activate it.
|
|
||||||
- **Editor undo or redo:** independently compile the historical applied value carried by the local history entry, then
|
|
||||||
preflight it and atomically restore the historical source/applied pair. Reject the transition if the applied value is
|
|
||||||
invalid.
|
|
||||||
- **Import:** compile imported source. If it is invalid, independently validate the imported applied value and retain it
|
|
||||||
only after preflight; otherwise use an empty supported applied source.
|
|
||||||
- **Duplicate:** copy the server-owned stylesheet content while initializing a fresh concurrency revision for the new
|
|
||||||
resume.
|
|
||||||
- **Restore version:** restore the server-owned source/applied pair from the selected snapshot after validating the
|
|
||||||
applied value with its versioned compiler and preflight.
|
|
||||||
|
|
||||||
Every successful transition increments `revision` and returns the canonical state plus diagnostics. Worker jobs and
|
|
||||||
network requests carry the local edit generation and expected revision. The client serializes stylesheet mutations:
|
|
||||||
only one request is in flight, and later edits replace one queued candidate. Every acknowledgement advances the local
|
|
||||||
revision; its source/applied payload updates editor state only when its generation is still current. The queued candidate
|
|
||||||
then submits with the acknowledged revision. Warnings do not block application.
|
|
||||||
|
|
||||||
Concurrency revisions are excluded from JSON export and version snapshots. Import and duplicate initialize a fresh
|
|
||||||
revision; version restore increments the current resume's revision rather than restoring historical concurrency
|
|
||||||
metadata.
|
|
||||||
|
|
||||||
## Compiler Architecture
|
|
||||||
|
|
||||||
The compiler is a universal, environment-neutral package used by the web app, API, and PDF renderer:
|
|
||||||
|
|
||||||
```text
|
|
||||||
source
|
|
||||||
-> CSS tokenizer/parser
|
|
||||||
-> syntax AST
|
|
||||||
-> restricted-language validation
|
|
||||||
-> selector and value compilation
|
|
||||||
-> versioned StyleProgram + diagnostics
|
|
||||||
```
|
|
||||||
|
|
||||||
`StyleProgram` contains normalized selectors, declaration values, source locations, specificity, media conditions, and
|
|
||||||
structural directives. It contains no React or React PDF values. A PDF adapter translates resolved declarations into
|
|
||||||
React PDF styles and primitive props.
|
|
||||||
|
|
||||||
The parser should use a standards-compatible CSS parser rather than a hand-written partial tokenizer. Semantic CSS
|
|
||||||
validation sits on top of that parser and rejects unsupported CSS constructs explicitly.
|
|
||||||
|
|
||||||
Compilation and selector matching must remain deterministic. Diagnostics include severity, code, message, and exact
|
|
||||||
source range.
|
|
||||||
|
|
||||||
Source compilation reports syntax and language-contract diagnostics without needing a resume. A separate semantic
|
|
||||||
analysis pass evaluates a compiled program against the current resume's virtual tree and reports context-dependent
|
|
||||||
warnings such as valid selectors that match no node. Both passes use shared diagnostic types and codes.
|
|
||||||
|
|
||||||
Language versions are positive integers. A compiler implementation for a released version is immutable. Unsupported
|
|
||||||
source versions are preserved as opaque editable text but cannot replace `applied`; rendering continues with the
|
|
||||||
supported applied version or base styles when no supported applied value exists.
|
|
||||||
|
|
||||||
Every compiler version referenced by persisted `applied` data remains available. A compiler can be retired only after a
|
|
||||||
transactional migration recompiles and preflights every affected applied stylesheet with a newer version and no stored
|
|
||||||
resume references the old version.
|
|
||||||
|
|
||||||
## Virtual Semantic Tree
|
|
||||||
|
|
||||||
Selectors match a versioned, immutable virtual resume tree, not React component names:
|
|
||||||
|
|
||||||
```text
|
|
||||||
resume
|
|
||||||
page
|
|
||||||
region
|
|
||||||
header
|
|
||||||
picture
|
|
||||||
name
|
|
||||||
headline
|
|
||||||
contact-list
|
|
||||||
contact-item
|
|
||||||
section
|
|
||||||
section-heading
|
|
||||||
section-items
|
|
||||||
item
|
|
||||||
item-header
|
|
||||||
field
|
|
||||||
link
|
|
||||||
icon
|
|
||||||
level
|
|
||||||
rich-text
|
|
||||||
paragraph
|
|
||||||
list
|
|
||||||
list-item
|
|
||||||
list-marker
|
|
||||||
```
|
|
||||||
|
|
||||||
Template-owned chrome is exposed as `template-part` nodes. Every part name must be registered, documented, and stable.
|
|
||||||
Examples include `timeline-line`, `timeline-dot`, `featured-summary`, `sidebar-background`, and
|
|
||||||
`item-header-border`.
|
|
||||||
|
|
||||||
Each node carries only documented semantic attributes, including the applicable subset of:
|
|
||||||
|
|
||||||
- `id`: stable section or item ID.
|
|
||||||
- `type`: canonical section type.
|
|
||||||
- `name`: field, contact, or template-part name.
|
|
||||||
- `template`: selected template on the root.
|
|
||||||
- `placement`: `main` or `sidebar`.
|
|
||||||
- `region`: `header`, `main`, `sidebar`, `featured`, or another registered region.
|
|
||||||
- `page-number`: one-based layout page number.
|
|
||||||
- `role`: one or more stable roles such as `primary-text`, `secondary-text`, or `structured-link`.
|
|
||||||
|
|
||||||
Custom classes are not supported because resume data has no class-authoring surface. Groups are expressed through
|
|
||||||
selector lists, attributes, `:is()`, and `:where()`.
|
|
||||||
|
|
||||||
All shared primitives and all 15 templates must register their semantic nodes before Semantic CSS becomes the default.
|
|
||||||
Known semantic nodes that are absent from the current template are valid no-ops and produce warnings.
|
|
||||||
|
|
||||||
The normative node contract is:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type SemanticNode = {
|
|
||||||
key: string;
|
|
||||||
kind: SemanticNodeKind;
|
|
||||||
id?: string;
|
|
||||||
attributes: Readonly<Record<string, string>>;
|
|
||||||
roles: readonly string[];
|
|
||||||
children: readonly SemanticNode[];
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Each template builds one authoritative descriptor tree from `ResumeData`, template configuration, normalized rich-text
|
|
||||||
content, and the typed semantic registries. Selector matching, context-dependent diagnostics, inheritance, structural
|
|
||||||
resolution, and React rendering all consume that same tree. React components must not create unregistered semantic
|
|
||||||
children independently.
|
|
||||||
|
|
||||||
The registries normatively define allowed parentage, cardinality, field names, role names, stable keys, and
|
|
||||||
template-part placement. Experience roles, custom fields, rich-text nodes, featured summaries, and template-specific
|
|
||||||
header structures are explicitly represented rather than inferred from React children.
|
|
||||||
|
|
||||||
## Selector Language
|
|
||||||
|
|
||||||
Semantic CSS supports:
|
|
||||||
|
|
||||||
- Type selectors and the universal selector.
|
|
||||||
- ID and attribute selectors.
|
|
||||||
- Selector lists separated by commas.
|
|
||||||
- Descendant, child, adjacent-sibling, and general-sibling combinators.
|
|
||||||
- `:is()`, `:where()`, and `:not()`.
|
|
||||||
- Static structural pseudo-classes such as `:first-child`, `:last-child`, `:only-child`, `:nth-child()`, and
|
|
||||||
`:nth-of-type()`.
|
|
||||||
|
|
||||||
Interactive or browser-state pseudo-classes are errors.
|
|
||||||
|
|
||||||
`SemanticNode.id` is reflected to both `#id` and `[id="…"]`. `roles` is reflected as a space-separated `role`
|
|
||||||
attribute and matched with `[role~="token"]`. Other entries in `attributes` are exposed by their registered names.
|
|
||||||
Presence, `=`, `~=`, `|=`, `^=`, `$=`, and `*=` attribute operators are supported. Semantic element, attribute, role,
|
|
||||||
and registered keyword names are lowercase and ASCII case-sensitive. Values and IDs are case-sensitive. Selectors use
|
|
||||||
standard CSS escaping; quoted `[id="…"]` is the recommended syntax for UUIDs that would require identifier escapes.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```css
|
|
||||||
:root {
|
|
||||||
--accent: #2563eb;
|
|
||||||
--compact-gap: 4pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
section:is([type="experience"], [type="education"]) {
|
|
||||||
margin-bottom: 8pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
section#experience > section-heading {
|
|
||||||
color: var(--accent);
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
region[placement="sidebar"] section,
|
|
||||||
section#skills {
|
|
||||||
background-color: rgba(20, 30, 40, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
item[id="f27be2d2-13a9-4f16-8248-c8735a27dd1c"] field[name="period"] {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
resume[template="azurill"] template-part[name="timeline-dot"] {
|
|
||||||
background-color: var(--accent);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Portable styles should prefer section types, roles, placements, regions, and template attributes. Exact section and item
|
|
||||||
IDs are available when a rule intentionally belongs to one resume.
|
|
||||||
|
|
||||||
## Cascade and Inheritance
|
|
||||||
|
|
||||||
Semantic CSS follows familiar author-style cascade rules:
|
|
||||||
|
|
||||||
- `!important` declarations outrank normal declarations.
|
|
||||||
- Specificity compares IDs, then attributes and pseudo-classes, then element names.
|
|
||||||
- `:where()` contributes zero specificity.
|
|
||||||
- Equal specificity is resolved by source order.
|
|
||||||
- Custom properties cascade and inherit.
|
|
||||||
- Cyclic or unresolved variables are errors unless a valid fallback exists.
|
|
||||||
|
|
||||||
Only properties marked inheritable in the property registry inherit through the semantic tree. Box and layout
|
|
||||||
properties never inherit implicitly. The language supports `inherit`, `initial`, `unset`, and `revert`; `revert`
|
|
||||||
removes the winning Semantic CSS declaration at that node and exposes its builder/template base value. If the property
|
|
||||||
is inheritable and the semantic parent has a computed Semantic CSS value, normal inheritance can still supply that
|
|
||||||
parent value. `initial` uses the property registry's initial value, `inherit` uses the semantic parent's computed value,
|
|
||||||
and `unset` chooses `inherit` for inheritable properties and `initial` otherwise. `revert-layer` is unsupported.
|
|
||||||
|
|
||||||
Declarations are resolved after template styles. Existing cosmetic safety defaults such as text shrinking must move
|
|
||||||
below the stylesheet in precedence. Only constraints required to prevent renderer failure may remain above user
|
|
||||||
declarations, and each such constraint must be documented.
|
|
||||||
|
|
||||||
Resolution uses one immutable source-tree snapshot:
|
|
||||||
|
|
||||||
1. Match all selectors against original parentage and sibling order.
|
|
||||||
2. Calculate selector specificity according to CSS rules: `:is()` and `:not()` take their most specific argument,
|
|
||||||
while `:where()` has zero specificity.
|
|
||||||
3. Cascade declarations and custom properties, then calculate inherited values.
|
|
||||||
4. Resolve structural declarations once.
|
|
||||||
5. Omit `display: none` subtrees and stable-sort remaining siblings by `order`, using original sibling order for ties.
|
|
||||||
6. Render the resolved tree.
|
|
||||||
|
|
||||||
Hidden and reordered nodes never change which selectors match, positional pseudo-classes, sibling combinators, or
|
|
||||||
inheritance. Structural declarations cannot trigger a second selector pass.
|
|
||||||
|
|
||||||
## Properties, Values, and Units
|
|
||||||
|
|
||||||
The property registry exposes the applicable React PDF surface under familiar kebab-case names:
|
|
||||||
|
|
||||||
- Flexbox layout, including gaps and `order`.
|
|
||||||
- Width, height, minimum and maximum dimensions.
|
|
||||||
- Relative and absolute positioning, overflow, stacking, and display.
|
|
||||||
- Color, background color, and opacity.
|
|
||||||
- Text size, weight, style, line height, spacing, alignment, decoration, transform, indentation, overflow, and line
|
|
||||||
limits.
|
|
||||||
- Margins, padding, borders, radii, and supported transforms.
|
|
||||||
- Supported image sizing and object-fit behavior on the existing picture node.
|
|
||||||
|
|
||||||
`font-family` is rejected. Asset-bearing properties and functions such as `background-image`, `src`, and `url()` are
|
|
||||||
rejected.
|
|
||||||
|
|
||||||
Common shorthands such as `margin`, `padding`, `border`, `gap`, `flex`, and `transform` compile into normalized values.
|
|
||||||
Supported units are `pt`, `in`, `mm`, `cm`, `%`, `vw`, `vh`, `em`, and `rem`. Unitless PDF dimensions are interpreted
|
|
||||||
as points. `px` is accepted for familiarity and converted from 96 DPI to 72-DPI PDF points.
|
|
||||||
|
|
||||||
`rem` resolves against the root body font size from Typography. For `font-size`, `em` resolves against the semantic
|
|
||||||
parent's computed font size. For all other properties, it resolves against the target node's computed font size.
|
|
||||||
Relative-unit cycles are errors.
|
|
||||||
|
|
||||||
Media queries use standard syntax and support page width, page height, and orientation:
|
|
||||||
|
|
||||||
```css
|
|
||||||
@media (max-width: 500pt) {
|
|
||||||
region[placement="sidebar"] {
|
|
||||||
width: 30%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Page and pagination behavior uses standard properties where possible and namespaced extensions where React PDF exposes
|
|
||||||
primitive props rather than style properties:
|
|
||||||
|
|
||||||
```css
|
|
||||||
section[type="experience"] {
|
|
||||||
break-inside: avoid;
|
|
||||||
-resume-min-presence-ahead: 24pt;
|
|
||||||
}
|
|
||||||
|
|
||||||
page {
|
|
||||||
size: A4;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
-resume-fixed: true;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Supported structural declarations include:
|
|
||||||
|
|
||||||
- `display: none` to omit a semantic node.
|
|
||||||
- `order` to reorder siblings before React rendering.
|
|
||||||
- `break-before: page`.
|
|
||||||
- `break-inside: avoid`.
|
|
||||||
- `orphans` and `widows`.
|
|
||||||
- `-resume-fixed`.
|
|
||||||
- `-resume-min-presence-ahead`.
|
|
||||||
- `size` on page nodes.
|
|
||||||
|
|
||||||
Structural declarations are resolved while preparing semantic child descriptors, before the React component tree is
|
|
||||||
created. CSS cannot move a node to a different parent; absolute positioning can only change its visual placement.
|
|
||||||
|
|
||||||
`page-number` identifies the one-based authored `metadata.layout.pages` entry. React PDF may wrap one authored page into
|
|
||||||
multiple physical subpages; those physical subpages are not independently selectable. They inherit the authored page
|
|
||||||
context, and fixed nodes repeat on physical subpages created from that authored page.
|
|
||||||
|
|
||||||
Page sizing is evaluated in a non-circular phase. Non-media `size` declarations resolve first against builder defaults.
|
|
||||||
Media conditions then evaluate against that final authored page size. `size` inside `@media` is an error.
|
|
||||||
|
|
||||||
Values must be finite. Very large, negative, or overlap-prone values produce warnings rather than cosmetic clamping.
|
|
||||||
Hard technical limits exist only to prevent crashes, pathological allocations, or denial of service.
|
|
||||||
|
|
||||||
## Editor Experience
|
|
||||||
|
|
||||||
The Custom Styles right-sidebar section becomes a monospaced stylesheet editor. It also offers an expanded mode with
|
|
||||||
more editing space while retaining the live preview.
|
|
||||||
|
|
||||||
Editor capabilities include:
|
|
||||||
|
|
||||||
- CSS syntax highlighting.
|
|
||||||
- Line and column diagnostics with error and warning severity.
|
|
||||||
- Selector, attribute, property, keyword, and variable completion.
|
|
||||||
- Hover documentation generated from semantic and property registries.
|
|
||||||
- Color previews.
|
|
||||||
- Search and replace.
|
|
||||||
- Explicit formatting.
|
|
||||||
- Standard copy and paste.
|
|
||||||
- A clear applied state.
|
|
||||||
|
|
||||||
The editor preserves source text and formatting exactly unless the user explicitly formats it.
|
|
||||||
|
|
||||||
Compilation runs after a short debounce in a web worker. The status must distinguish:
|
|
||||||
|
|
||||||
- `Applied`.
|
|
||||||
- Applied with warnings.
|
|
||||||
- Errors, with an explicit message that preview and export use the last valid version.
|
|
||||||
|
|
||||||
The editor maintains source state separately from full-resume autosave. It runs a browser render preflight for a
|
|
||||||
compiled candidate and sends serialized, debounced, revisioned stylesheet mutations. It always consumes response
|
|
||||||
revisions, but replaces visible source/applied state only for the current edit generation. Existing coalesced undo and
|
|
||||||
redo behavior includes both stylesheet values and uses the explicit restore transition, so undo restores matching text
|
|
||||||
and rendered output.
|
|
||||||
|
|
||||||
## Diagnostics
|
|
||||||
|
|
||||||
Errors prevent a new source from becoming applied:
|
|
||||||
|
|
||||||
- Invalid CSS syntax.
|
|
||||||
- Unknown semantic element or attribute.
|
|
||||||
- Unknown or unsupported property.
|
|
||||||
- Invalid value, unit, selector, pseudo-class, at-rule, or variable cycle.
|
|
||||||
- Disallowed font or asset access.
|
|
||||||
- Exceeded source, rule, nesting, or selector-complexity limit.
|
|
||||||
|
|
||||||
Warnings do not prevent application:
|
|
||||||
|
|
||||||
- A known selector matches no node in the current resume or template.
|
|
||||||
- A property is valid but ineffective on the selected semantic node.
|
|
||||||
- An extreme value is likely to cause overlap, clipping, or unreadable output.
|
|
||||||
|
|
||||||
The server returns compiler diagnostics for save responses. Browser diagnostics remain immediate and use the same
|
|
||||||
compiler, semantic analyzer, and diagnostic codes.
|
|
||||||
|
|
||||||
Editable source, source locations, comments, and diagnostics are owner-only data. Public resume responses exclude both
|
|
||||||
stylesheet source values. They contain a fully resolved projection:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type PublicStyleProjection = {
|
|
||||||
formatVersion: 1;
|
|
||||||
languageVersion: number;
|
|
||||||
semanticTreeVersion: number;
|
|
||||||
registryFingerprint: string;
|
|
||||||
adapterFingerprint: string;
|
|
||||||
renderDataHash: string;
|
|
||||||
nodes: Readonly<Record<string, ResolvedPdfNodeStyle>>;
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
The server builds this projection from the applied program and authoritative semantic tree. It contains final
|
|
||||||
declarations and structural props keyed by stable node key, with variables already resolved and comments, variable
|
|
||||||
names, selectors, source spans, and diagnostics removed. The public browser accepts it only when all versions,
|
|
||||||
fingerprints, and render-data hash match.
|
|
||||||
|
|
||||||
`renderDataHash` is SHA-256 over a domain-separated, RFC 8785 JSON Canonicalization Scheme serialization of the complete
|
|
||||||
public render input and resolved node projection. The domain includes the projection format version. It excludes
|
|
||||||
owner-only metadata and both stylesheet source values. The browser recomputes the hash before accepting the projection.
|
|
||||||
On mismatch it requests a fresh projection or falls back to the server-rendered PDF. That fallback uses the existing
|
|
||||||
public-resume visibility/password policy and public rendering rate limits; it is not an authorization bypass. Server PDF
|
|
||||||
export compiles the database's applied value directly.
|
|
||||||
|
|
||||||
## Legacy Migration
|
|
||||||
|
|
||||||
`metadata.styleRules` remains readable during compatibility rollout.
|
|
||||||
|
|
||||||
If a resume has legacy rules but no active Semantic CSS value:
|
|
||||||
|
|
||||||
1. Existing PDF rendering continues to use legacy rules.
|
|
||||||
2. Opening Custom Styles deterministically converts the rules into Semantic CSS.
|
|
||||||
3. The generated source preserves target specificity and array order.
|
|
||||||
4. Camel-case intent properties become kebab-case CSS declarations.
|
|
||||||
5. Numeric dimensions become explicit point values.
|
|
||||||
6. Rule labels become comments.
|
|
||||||
7. Disabled rules become clearly labeled commented blocks.
|
|
||||||
8. Draft autosave keeps legacy rendering active.
|
|
||||||
9. The user compares the converted preview and explicitly selects **Activate Semantic CSS**; active stylesheet
|
|
||||||
rendering then takes precedence.
|
|
||||||
|
|
||||||
Legacy target and slot mappings compile to equivalent semantic selectors and roles. For example:
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* Experience heading */
|
|
||||||
section[type="experience"] > section-heading {
|
|
||||||
font-size: 20pt;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Conversion is behavioral rather than a blind property rename. It evaluates each rule through the legacy resolver,
|
|
||||||
including specificity, numeric clamps, link-decoration ordering, bold/template precedence, icon-size translation, and
|
|
||||||
known template exceptions. The serializer emits the effective stylesheet deltas needed to preserve the current
|
|
||||||
resume's rendered appearance. It retains portable original scopes where behavior is equivalent and emits
|
|
||||||
resume-specific role or ID exceptions where legacy composition requires them.
|
|
||||||
|
|
||||||
Labels, IDs, attribute values, comments, strings, and comment terminators are escaped through one CSS serializer. Legacy
|
|
||||||
declarations that had no rendered effect remain non-applying and are explained in generated comments rather than
|
|
||||||
silently gaining new behavior.
|
|
||||||
|
|
||||||
Visual parity is guaranteed at activation for the current resume data, template, and builder base settings. Subsequent
|
|
||||||
template or base-setting changes follow Semantic CSS behavior; they are not guaranteed to reproduce how the retired
|
|
||||||
legacy resolver would have reacted.
|
|
||||||
|
|
||||||
Legacy rules remain as read-only rollback data during the flagged compatibility phase. Old Reactive Resume JSON imports
|
|
||||||
continue to parse them. New exports include the complete versioned stylesheet value. Copying from the editor copies only
|
|
||||||
the editable `source`.
|
|
||||||
|
|
||||||
No bulk database migration is required.
|
|
||||||
|
|
||||||
The server-owned stylesheet revision requires a normal DDL migration that adds a revision column with a zero default.
|
|
||||||
The statement above means no bulk backfill or rewrite of existing resume JSONB rows is required.
|
|
||||||
|
|
||||||
## Security and Resource Limits
|
|
||||||
|
|
||||||
Semantic CSS is declarative and cannot execute code or fetch resources.
|
|
||||||
|
|
||||||
The compiler enforces bounded:
|
|
||||||
|
|
||||||
- Source length.
|
|
||||||
- Rule and declaration count.
|
|
||||||
- Selector length and combinator count.
|
|
||||||
- Functional pseudo-class nesting.
|
|
||||||
- Variable expansion depth.
|
|
||||||
- Media-query nesting.
|
|
||||||
|
|
||||||
Compiler caches are bounded by count and total memory. Browser compilation runs in a worker. Server compilation uses the
|
|
||||||
same limits before rendering or persistence. Unsupported language versions are rejected explicitly rather than silently
|
|
||||||
interpreted by a newer grammar.
|
|
||||||
|
|
||||||
The renderer-versioned property registry defines every property's value grammar, shorthand expansion, inheritance,
|
|
||||||
allowed primitive kinds, relative-unit behavior, and hard technical bounds. Validation runs again after variable and
|
|
||||||
shorthand expansion, so banned asset functions cannot be hidden inside either construct.
|
|
||||||
|
|
||||||
PDF generation additionally enforces maximum authored page dimensions, maximum output pages, render timeout, and memory
|
|
||||||
budgets. Candidate promotion performs this bounded render preflight before replacing `applied`. A preflight failure
|
|
||||||
saves the editable source, preserves the previous applied value, and returns a controlled diagnostic. Later renderer
|
|
||||||
failures caused by subsequent content changes return a controlled preview/export error but do not silently mutate
|
|
||||||
stylesheet history.
|
|
||||||
|
|
||||||
## Documentation Registry
|
|
||||||
|
|
||||||
Semantic element names, attributes, template-part names, properties, values, inheritance behavior, and supported node
|
|
||||||
types come from typed registries. The editor completion data, user documentation, compiler validation, and template
|
|
||||||
coverage tests are generated from these registries.
|
|
||||||
|
|
||||||
This makes undocumented template internals unreachable and prevents documentation from drifting away from runtime
|
|
||||||
behavior.
|
|
||||||
|
|
||||||
## Testing Strategy
|
|
||||||
|
|
||||||
### Compiler
|
|
||||||
|
|
||||||
- Golden lexer and parser fixtures for valid and invalid source.
|
|
||||||
- Selector matching, specificity, source order, `!important`, inheritance, variables, resets, shorthands, units, and
|
|
||||||
media queries.
|
|
||||||
- Structural directive resolution.
|
|
||||||
- Exact source-range diagnostics.
|
|
||||||
- Property-registry exhaustiveness against supported PDF adapter types.
|
|
||||||
- Fuzz and resource-limit tests proving malformed text cannot crash or hang compilation.
|
|
||||||
|
|
||||||
### Schema and persistence
|
|
||||||
|
|
||||||
- Revision compare-and-swap rejects stale concurrent saves.
|
|
||||||
- Preflight occurs outside database locks, followed by a short CAS on both stylesheet revision and resume render-data
|
|
||||||
version.
|
|
||||||
- Serialized mutations consume stale acknowledgements for revision advancement without replacing newer editor state.
|
|
||||||
- Out-of-order worker results cannot replace newer editor state.
|
|
||||||
- Valid source edits replace both stylesheet values.
|
|
||||||
- Invalid source edits are stored while the current applied value is preserved.
|
|
||||||
- Compile-valid but render-failing source is stored without replacing the current applied value.
|
|
||||||
- Editor undo/redo restores historical invalid source with its historical valid applied value.
|
|
||||||
- Generic full-resume updates preserve the server-owned stylesheet.
|
|
||||||
- Clients cannot forge `applied` through normal edit transitions.
|
|
||||||
- Imports with invalid source retain text and independently validate the imported applied value.
|
|
||||||
- Duplicate and version restore preserve valid source/applied pairs.
|
|
||||||
- Public DTOs redact source, comments, diagnostics, and source locations.
|
|
||||||
- Public projections reject registry, tree, adapter, or render-data-hash mismatches and use the defined fallback.
|
|
||||||
- Public render hashes use the canonical, domain-separated contract, and fallback rendering preserves public/password
|
|
||||||
authorization and rate limiting.
|
|
||||||
- Backend-first rolling deployment preserves stylesheet fields when old clients submit full resume data.
|
|
||||||
- Undo, redo, JSON import, JSON export, duplication, and version restore preserve stylesheet state.
|
|
||||||
- Legacy conversion preserves effective output across precedence quirks, clamps, template exceptions, and supported
|
|
||||||
intent properties.
|
|
||||||
|
|
||||||
### PDF rendering
|
|
||||||
|
|
||||||
- Shared semantic primitives receive correct ancestry and attributes.
|
|
||||||
- Header, picture, contacts, pages, regions, sections, items, fields, rich text, and template parts resolve styles.
|
|
||||||
- Structural hiding and ordering occur before rendering.
|
|
||||||
- Positional selectors and inheritance remain based on the immutable source tree after hiding and ordering.
|
|
||||||
- Authored-page selectors, wrapped physical subpages, fixed nodes, page size, and media queries follow the defined phase
|
|
||||||
model.
|
|
||||||
- Browser and server adapters resolve identical programs.
|
|
||||||
- Every template smoke-renders with a comprehensive stylesheet.
|
|
||||||
- Every registered node and template part has resolved-style coverage.
|
|
||||||
- All 15 templates have visual regression coverage; focused fixtures cover every unique template feature.
|
|
||||||
- Preview and exported PDF use the same applied stylesheet value.
|
|
||||||
|
|
||||||
### Web editor
|
|
||||||
|
|
||||||
- Diagnostics, completions, formatting, search, copy and paste, color previews, autosave, and expanded mode.
|
|
||||||
- Invalid edits preserve source and last-valid preview.
|
|
||||||
- Correcting invalid text applies it without losing formatting.
|
|
||||||
- Out-of-order compilation and save responses are discarded.
|
|
||||||
- Stale save acknowledgements still advance the mutation revision before the queued edit is sent.
|
|
||||||
- Revision conflicts rebase the editor without dropping unsaved source.
|
|
||||||
- Known-but-absent selectors produce warnings.
|
|
||||||
- Legacy conversion is deterministic and user-visible.
|
|
||||||
|
|
||||||
### End-to-end acceptance
|
|
||||||
|
|
||||||
One portable stylesheet is pasted into resumes using different templates. The test verifies group selectors, one
|
|
||||||
section-specific rule, one item-specific rule, a header rule, a rich-text rule, a template-part rule, a media query, and
|
|
||||||
a pagination directive. It then introduces an error, confirms that preview and export remain on the last valid version,
|
|
||||||
corrects the error, and confirms that preview and export update together.
|
|
||||||
|
|
||||||
## Rollout
|
|
||||||
|
|
||||||
1. Deploy the dormant compiler and registries, tolerant schema handling, public projection/redaction,
|
|
||||||
generic-update field preservation, and the dedicated revisioned stylesheet mutation to the entire backend fleet.
|
|
||||||
No client can activate Semantic CSS during this stage.
|
|
||||||
2. Introduce the legacy converter behind a disabled authoring feature flag.
|
|
||||||
3. Instrument shared PDF primitives and structural child preparation.
|
|
||||||
4. Instrument header and template-specific parts across all 15 templates.
|
|
||||||
5. Add the editor and revision/conflict behavior.
|
|
||||||
6. Run legacy and Semantic CSS rendering paths side by side in tests, without double-applying them.
|
|
||||||
7. Enable Semantic CSS for opted-in resumes while retaining legacy rollback data and monitoring compile failures,
|
|
||||||
revision conflicts, render latency, memory, output pages, and fallback usage.
|
|
||||||
8. Enable it by default after mixed-client compatibility, public-redaction, template coverage, visual regression,
|
|
||||||
resource-limit, and end-to-end gates pass.
|
|
||||||
|
|
||||||
The authoring flag controls editor availability and whether a rollout cohort creates new resumes in semantic mode.
|
|
||||||
Before default enablement, resumes outside that cohort start in legacy mode; after default enablement they start in
|
|
||||||
semantic mode with empty version-1 source values. Rendering always honors a persisted semantic mode even if authoring is
|
|
||||||
later disabled. A stylesheet is never applied on top of legacy rules; an active stylesheet takes sole precedence for
|
|
||||||
custom PDF styling.
|
|
||||||
|
|
||||||
## Success Criteria
|
|
||||||
|
|
||||||
- Users can copy one text block between resumes and reproduce portable PDF styling.
|
|
||||||
- Every documented semantic node and template part can be targeted consistently.
|
|
||||||
- One section or item can be targeted by stable ID without making portable selectors resume-specific.
|
|
||||||
- Invalid text is never lost and never breaks preview or export.
|
|
||||||
- Preview, public rendering, browser export, and server export agree.
|
|
||||||
- Existing custom styles retain visual parity after deterministic conversion.
|
|
||||||
- The system accepts no executable code, font choice, asset reference, or network-fetching construct.
|
|
||||||
- All 15 templates pass semantic coverage and PDF smoke tests.
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
# SEO/AEO Performance Improvements Design
|
|
||||||
|
|
||||||
## Goal
|
|
||||||
|
|
||||||
Improve the homepage's initial rendering, LCP path, canonical metadata, and documentation crawl hygiene using the existing web, server, and Mintlify seams.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This change will:
|
|
||||||
|
|
||||||
- Exclude the application bootstrap module from Cloudflare Rocket Loader rewriting.
|
|
||||||
- Replace the homepage's initial autoplay video load with a high-priority poster and interaction-triggered video load.
|
|
||||||
- Serve versioned homepage media with long-lived immutable caching.
|
|
||||||
- Put complete root-page canonical, social, and JSON-LD metadata in the initial HTML response.
|
|
||||||
- Move the documentation introduction to `/getting-started` and redirect confirmed historical paths.
|
|
||||||
|
|
||||||
This change will not:
|
|
||||||
|
|
||||||
- Add SSR, prerendering, a shared SEO package, special AEO schema, or `llms.txt` work.
|
|
||||||
- Change noindex behavior for auth, dashboard, builder, settings, templates, or public resume routes.
|
|
||||||
- Add redirects for unconfirmed LinkedIn, AI-provider, v4, or placeholder URLs.
|
|
||||||
- Change homepage animation or overlay code without an interaction-performance profile.
|
|
||||||
- Change the Cloudflare zone-wide Rocket Loader setting from repository code.
|
|
||||||
- Claim field LCP or GSC validation before post-deployment field data is available.
|
|
||||||
|
|
||||||
## Approach
|
|
||||||
|
|
||||||
Use the existing ownership seams:
|
|
||||||
|
|
||||||
- `apps/web/index.html` owns the bootstrap script attribute.
|
|
||||||
- `apps/web/src/routes/_home/-sections/hero.tsx` owns homepage media behavior.
|
|
||||||
- `apps/server/src/static/web.ts` owns root HTML transformation and static response headers.
|
|
||||||
- `docs/docs.json` and the documentation source files own canonical documentation paths.
|
|
||||||
|
|
||||||
The server will keep serving the same client application shell. Only requests whose pathname is `/` will receive root SEO markup, so tracking parameters such as `/?utm_source=...` consolidate to the root canonical without leaking root metadata into noindex application shells.
|
|
||||||
|
|
||||||
## Homepage boot and media
|
|
||||||
|
|
||||||
The module script in `apps/web/index.html` will place `data-cfasync="false"` before `src`, which is the Cloudflare-supported per-script Rocket Loader exclusion.
|
|
||||||
|
|
||||||
The current media will become:
|
|
||||||
|
|
||||||
- `/videos/timelapse-v1.webp`: a representative poster extracted from the existing video.
|
|
||||||
- `/videos/timelapse-v1.mp4`: the existing video under a versioned filename.
|
|
||||||
|
|
||||||
The homepage route will preload the poster as an image with `fetchpriority="high"`. The hero will render a native `<video>` with:
|
|
||||||
|
|
||||||
- `poster="/videos/timelapse-v1.webp"`
|
|
||||||
- `preload="none"`
|
|
||||||
- native controls
|
|
||||||
- no `autoPlay`
|
|
||||||
- the existing intrinsic width, height, label, and aspect ratio
|
|
||||||
|
|
||||||
This keeps the current layout while preventing the 4.2 MB video from entering the cold-load critical path. Playback and video data loading begin only when the user interacts with the native control.
|
|
||||||
|
|
||||||
The static file middleware will add `Cache-Control: public, max-age=31536000, immutable` to files under `/videos/`. The `/videos/` directory is therefore reserved for versioned media; changed media must use a new versioned filename.
|
|
||||||
|
|
||||||
## Initial root metadata
|
|
||||||
|
|
||||||
`handleWebApp` will transform the root shell before returning it. The injected head markup will contain:
|
|
||||||
|
|
||||||
- A canonical link whose URL is the request origin normalized to `/`.
|
|
||||||
- `Reactive Resume — A free and open-source resume builder` as the title.
|
|
||||||
- The existing complete product description.
|
|
||||||
- Open Graph and Twitter title, description, URL, and banner image metadata.
|
|
||||||
- The poster preload.
|
|
||||||
- The existing WebSite, SoftwareApplication/WebApplication, Project, and FAQPage JSON-LD graph.
|
|
||||||
|
|
||||||
The canonical derivation discards the request query and fragment. It yields `https://rxresu.me/` in production while preserving the request host expected by self-hosted instances.
|
|
||||||
|
|
||||||
The client-rendered metadata remains as a navigation and hydration fallback. A new package or cross-app source import is intentionally avoided; focused tests will lock the initial-response contract.
|
|
||||||
|
|
||||||
If a malformed build shell lacks `</head>`, the transform will return the original HTML rather than preventing the application from loading.
|
|
||||||
|
|
||||||
## Documentation canonical paths
|
|
||||||
|
|
||||||
The documentation introduction will move from:
|
|
||||||
|
|
||||||
- `docs/getting-started/index.mdx`
|
|
||||||
|
|
||||||
to:
|
|
||||||
|
|
||||||
- `docs/getting-started.mdx`
|
|
||||||
|
|
||||||
Navigation and internal links will use `/getting-started`. `docs/docs.json` will add permanent redirects:
|
|
||||||
|
|
||||||
- `/getting-started/index` to `/getting-started`
|
|
||||||
- `/translation/README` to `/contributing/translations`
|
|
||||||
|
|
||||||
The second source is grounded in the deleted historical `docs/translation/README.md` path. No guessed redirects will be added for URLs whose old source and accurate replacement are not present in repository history.
|
|
||||||
|
|
||||||
## Testing and verification
|
|
||||||
|
|
||||||
Implementation will follow focused red-green cycles:
|
|
||||||
|
|
||||||
1. Extend `apps/server/src/static/web.test.ts` with a failing test proving that a tracking-parameter root request receives the normalized canonical, complete metadata, poster preload, and JSON-LD while non-root shells do not.
|
|
||||||
2. Add a failing static-cache test proving that versioned `/videos/` responses receive the immutable cache header.
|
|
||||||
3. Add a failing homepage hero test proving the video has a poster, `preload="none"`, controls, and no autoplay.
|
|
||||||
4. Make the smallest production changes that pass each test.
|
|
||||||
|
|
||||||
Fresh verification will include:
|
|
||||||
|
|
||||||
- Focused server and web tests.
|
|
||||||
- Server and web typechecks.
|
|
||||||
- Server and web production builds.
|
|
||||||
- A built-HTML check that the module bootstrap retains `type="module"` and `data-cfasync="false"`.
|
|
||||||
- `mint broken-links --check-redirects` from the documentation directory.
|
|
||||||
- A final diff review confirming no unrelated files changed.
|
|
||||||
|
|
||||||
The ten cold production navigations, throttled LCP measurement, field Core Web Vitals, and GSC validation remain deployment checks because local tests cannot reproduce Cloudflare rewriting or the 28-day field-data window.
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
# SEO Comparison Content Cluster Design
|
|
||||||
|
|
||||||
## Goal
|
|
||||||
|
|
||||||
Create a neutral, source-backed comparison cluster in the Mintlify documentation site that helps people searching for
|
|
||||||
free resume builders decide whether Reactive Resume or another product better fits their workflow.
|
|
||||||
|
|
||||||
The cluster should earn non-branded, high-intent traffic for queries such as `Reactive Resume vs Canva` without
|
|
||||||
overstating Reactive Resume's capabilities or turning the documentation into generic affiliate-style content. Every
|
|
||||||
page should give the competing product credit where it is stronger, explain Reactive Resume's relevant shortcomings,
|
|
||||||
and end with a distinct reason to try Reactive Resume.
|
|
||||||
|
|
||||||
## Audience and document type
|
|
||||||
|
|
||||||
These pages are decision-oriented explanations for:
|
|
||||||
|
|
||||||
- Job seekers comparing free or freemium resume-building products.
|
|
||||||
- Users who care about exports, privacy, portability, AI assistance, or customization.
|
|
||||||
- Technical users evaluating open-source or self-hosted options.
|
|
||||||
|
|
||||||
They are not tutorials, product reviews, rankings, or declarations that one product is universally better.
|
|
||||||
|
|
||||||
## Search-result scope
|
|
||||||
|
|
||||||
The comparison set comes from a non-personalized US Google search for `free resume builder`, captured on July 28,
|
|
||||||
2026. Products from the first two result pages are included when the result represents an identifiable resume-building
|
|
||||||
product. Reddit threads, videos, generic list articles, Reactive Resume itself, and an ambiguous LinkedIn showcase
|
|
||||||
result are excluded.
|
|
||||||
|
|
||||||
Zety is included because it appeared as a sponsored result and was explicitly requested. Overleaf is included because
|
|
||||||
it was explicitly requested and represents a common LaTeX-based resume workflow, even though it is not a dedicated
|
|
||||||
resume builder.
|
|
||||||
|
|
||||||
The approved pages are:
|
|
||||||
|
|
||||||
1. `comparisons/reactive-resume-vs-canva.mdx`
|
|
||||||
2. `comparisons/reactive-resume-vs-resume-com.mdx`
|
|
||||||
3. `comparisons/reactive-resume-vs-myperfectresume.mdx`
|
|
||||||
4. `comparisons/reactive-resume-vs-resume-now.mdx`
|
|
||||||
5. `comparisons/reactive-resume-vs-adobe-express.mdx`
|
|
||||||
6. `comparisons/reactive-resume-vs-careercircle.mdx`
|
|
||||||
7. `comparisons/reactive-resume-vs-kickresume.mdx`
|
|
||||||
8. `comparisons/reactive-resume-vs-resume-io.mdx`
|
|
||||||
9. `comparisons/reactive-resume-vs-resumegemini.mdx`
|
|
||||||
10. `comparisons/reactive-resume-vs-jobscan.mdx`
|
|
||||||
11. `comparisons/reactive-resume-vs-resumod.mdx`
|
|
||||||
12. `comparisons/reactive-resume-vs-novoresume.mdx`
|
|
||||||
13. `comparisons/reactive-resume-vs-livecareer.mdx`
|
|
||||||
14. `comparisons/reactive-resume-vs-rezi.mdx`
|
|
||||||
15. `comparisons/reactive-resume-vs-freesumes.mdx`
|
|
||||||
16. `comparisons/reactive-resume-vs-zety.mdx`
|
|
||||||
17. `comparisons/reactive-resume-vs-overleaf.mdx`
|
|
||||||
|
|
||||||
No separate comparison hub is required. The visible Mintlify navigation group provides the crawlable index and avoids
|
|
||||||
adding another page that competes for the existing `Reactive Resume alternatives` intent.
|
|
||||||
|
|
||||||
## Navigation
|
|
||||||
|
|
||||||
Add a `Comparisons` group immediately after `Use Cases` in the Documentation tab of `docs/docs.json`. Include all 17
|
|
||||||
page paths so Mintlify adds them to navigation and its generated sitemap.
|
|
||||||
|
|
||||||
No new tab, custom component, schema type, or redirect is needed.
|
|
||||||
|
|
||||||
## Page structure
|
|
||||||
|
|
||||||
Every comparison page follows the same information order, but not shared prose:
|
|
||||||
|
|
||||||
1. Unique frontmatter title and description targeting the exact product-pair query.
|
|
||||||
2. An answer-first opening that states the main workflow difference in two or three sentences.
|
|
||||||
3. A `Quick comparison` table using only criteria relevant to that competitor.
|
|
||||||
4. A section explaining where the competing product is stronger.
|
|
||||||
5. A section explaining where Reactive Resume is stronger.
|
|
||||||
6. A `Which should you choose?` section with concrete user profiles for both options.
|
|
||||||
7. A short limitations note that names Reactive Resume's relevant shortcomings.
|
|
||||||
8. A `Sources` section with first-party links and a `Last checked: July 28, 2026` line.
|
|
||||||
9. A final Mintlify `Card` linking to `https://rxresu.me` with competitor-specific title and copy.
|
|
||||||
|
|
||||||
The page should usually be 600 to 1,000 words. Length is determined by meaningful differences, not a word-count target.
|
|
||||||
Small products with limited official documentation should receive shorter pages rather than padded text.
|
|
||||||
|
|
||||||
## Comparison criteria
|
|
||||||
|
|
||||||
Use a subset of these criteria when they materially distinguish the two products:
|
|
||||||
|
|
||||||
- What the free tier permits.
|
|
||||||
- Whether a designed PDF can be downloaded for free.
|
|
||||||
- Other export formats.
|
|
||||||
- Account requirements and where resume data is stored.
|
|
||||||
- Open-source license and public source availability.
|
|
||||||
- Self-hosting support.
|
|
||||||
- Template breadth and general design flexibility.
|
|
||||||
- Resume-specific editing and live preview.
|
|
||||||
- Built-in writing guidance, content libraries, scoring, or human review.
|
|
||||||
- AI model/provider model and whether AI is optional.
|
|
||||||
- Job-description matching or ATS-oriented analysis.
|
|
||||||
- Application tracking, API, or automation support.
|
|
||||||
- LaTeX or source-controlled authoring for Overleaf.
|
|
||||||
|
|
||||||
Do not force every criterion into every page. A focused comparison is more useful than a large identical matrix.
|
|
||||||
|
|
||||||
The word `free` must be qualified. Distinguish among free creation, free plain-text export, free designed PDF export,
|
|
||||||
limited download counts, trials, and paid features. Do not describe a product as free when the relevant final export
|
|
||||||
requires payment.
|
|
||||||
|
|
||||||
## Evidence policy
|
|
||||||
|
|
||||||
Every mutable or comparative claim must be checked against first-party sources during implementation:
|
|
||||||
|
|
||||||
- Official product and feature pages.
|
|
||||||
- Official pricing or plan-comparison pages.
|
|
||||||
- Official help centers or documentation.
|
|
||||||
- Official privacy policies when data handling is compared.
|
|
||||||
- Official source repositories and licenses for open-source claims.
|
|
||||||
|
|
||||||
Search snippets and third-party reviews can identify candidates but cannot support page claims. If first-party sources
|
|
||||||
conflict, use the narrower claim and describe the ambiguity. If a fact cannot be verified, omit it.
|
|
||||||
|
|
||||||
Avoid exact prices unless the price itself is necessary to explain the choice. Prefer durable descriptions such as
|
|
||||||
`paid plan` or `limited free tier`, link the current pricing page, and retain the checked date.
|
|
||||||
|
|
||||||
Vendor outcome statistics, review scores, user counts, and claims such as `ATS-approved` must not be repeated as
|
|
||||||
objective evidence. It is acceptable to say that a product offers an ATS checker or markets a template for ATS use when
|
|
||||||
the official source supports that narrower statement. No page may promise that a resume will pass an ATS or produce an
|
|
||||||
interview.
|
|
||||||
|
|
||||||
Reactive Resume claims should be verified against the current repository, documentation, hosted product, license, and
|
|
||||||
privacy policy. Existing use-case pages may be linked for details but should not be copied.
|
|
||||||
|
|
||||||
## Neutrality and shortcomings
|
|
||||||
|
|
||||||
Use plain, factual language. Avoid `best`, `winner`, `superior`, `revolutionary`, `powerful`, `seamless`, and similar
|
|
||||||
promotional terms unless they appear in a clearly attributed source title.
|
|
||||||
|
|
||||||
Each page must name at least one situation where the competitor is the better fit and one relevant Reactive Resume
|
|
||||||
limitation. Depending on the comparison, those limitations may include:
|
|
||||||
|
|
||||||
- Less general-purpose visual design freedom than Canva or Adobe Express.
|
|
||||||
- No LaTeX authoring workflow comparable to Overleaf.
|
|
||||||
- No built-in equivalent to a competitor's specialized ATS score, job-keyword workflow, content library, or human
|
|
||||||
review when current product evidence confirms that gap.
|
|
||||||
- Bring-your-own-provider setup and possible provider costs for AI-assisted features.
|
|
||||||
- Operational work required when choosing self-hosting.
|
|
||||||
|
|
||||||
Limitations must be tailored and verified rather than repeated mechanically across the cluster.
|
|
||||||
|
|
||||||
## Distinct search intent
|
|
||||||
|
|
||||||
Each page should answer the decision implied by its competitor:
|
|
||||||
|
|
||||||
| Competitor | Primary comparison angle |
|
|
||||||
| --- | --- |
|
|
||||||
| Canva | General visual-design tool versus a structured resume editor |
|
|
||||||
| Resume.com | Mainstream free builder versus open-source portability and self-hosting |
|
|
||||||
| MyPerfectResume | Guided writing and plan limits versus open-source, unrestricted core exports |
|
|
||||||
| Resume-Now | Guided AI/content workflow versus direct control and open-source operation |
|
|
||||||
| Adobe Express | General template editor versus structured resume data and workflow |
|
|
||||||
| CareerCircle | Career-services platform versus a standalone open-source resume system |
|
|
||||||
| Kickresume | Integrated AI/content tools versus bring-your-own AI and self-hosting |
|
|
||||||
| Resume.io | Commercial freemium workflow versus free core exports and open source |
|
|
||||||
| ResumeGemini | AI-guided builder versus an open-source, self-hostable workflow |
|
|
||||||
| Jobscan | ATS/job-description analysis versus broader resume ownership and automation |
|
|
||||||
| Resumod | AI and job-targeting assistance versus open-source control and self-hosting |
|
|
||||||
| Novorésumé | Guided templates and premium features versus unrestricted core resume management |
|
|
||||||
| LiveCareer | Guided content and career tools versus free, open-source editing and export |
|
|
||||||
| Rezi | Specialized ATS/keyword tooling versus provider choice, self-hosting, and automation |
|
|
||||||
| Freesumes | Templates and editorial resources versus structured ongoing resume management |
|
|
||||||
| Zety | Recruiter-style guidance and paid export workflow versus free core exports and open source |
|
|
||||||
| Overleaf | LaTeX collaboration and source control versus visual editing and structured resume data |
|
|
||||||
|
|
||||||
These angles are hypotheses to verify against current first-party evidence. If research disproves one, replace it with
|
|
||||||
the closest verified decision angle rather than forcing the planned contrast.
|
|
||||||
|
|
||||||
## Conversion calls to action
|
|
||||||
|
|
||||||
Every page ends with a link to the hosted Reactive Resume app, but its title and copy must reflect the comparison just
|
|
||||||
made. The planned CTA intents are:
|
|
||||||
|
|
||||||
| Competitor | CTA intent |
|
|
||||||
| --- | --- |
|
|
||||||
| Canva | Try a resume-specific editor |
|
|
||||||
| Resume.com | Keep resume data portable |
|
|
||||||
| MyPerfectResume | Build and export without a premium resume tier |
|
|
||||||
| Resume-Now | Edit without a subscription-based resume workflow |
|
|
||||||
| Adobe Express | Use structured resume fields instead of a general canvas |
|
|
||||||
| CareerCircle | Use a standalone resume builder |
|
|
||||||
| Kickresume | Choose and configure your own AI provider |
|
|
||||||
| Resume.io | Create, manage, and export without premium template gating |
|
|
||||||
| ResumeGemini | Try an open-source, self-hostable workflow |
|
|
||||||
| Jobscan | Build and own the resume before adding specialized analysis |
|
|
||||||
| Resumod | Keep deployment and resume data under your control |
|
|
||||||
| Novorésumé | Manage multiple resume versions without a premium document limit |
|
|
||||||
| LiveCareer | Use the core builder and exports without a paid plan |
|
|
||||||
| Rezi | Bring your own AI provider and keep AI optional |
|
|
||||||
| Freesumes | Move from a downloaded template to structured resume management |
|
|
||||||
| Zety | Export from a builder with no premium resume tier |
|
|
||||||
| Overleaf | Choose visual editing when LaTeX is unnecessary |
|
|
||||||
|
|
||||||
The implementation must adjust any CTA whose premise is not supported by current evidence. CTA text should invite an
|
|
||||||
appropriate reader to try Reactive Resume, not pressure every reader to switch.
|
|
||||||
|
|
||||||
## Internal linking
|
|
||||||
|
|
||||||
Link only to directly relevant existing pages, including:
|
|
||||||
|
|
||||||
- `/use-cases/free-resume-builder`
|
|
||||||
- `/use-cases/open-source-resume-builder`
|
|
||||||
- `/use-cases/privacy-focused-resume-builder`
|
|
||||||
- `/use-cases/self-hosted-resume-builder`
|
|
||||||
- `/use-cases/ai-resume-builder`
|
|
||||||
- `/guides/choosing-a-template`
|
|
||||||
- `/guides/importing-resumes`
|
|
||||||
- `/guides/exporting-your-resume`
|
|
||||||
- `/guides/using-ai`
|
|
||||||
|
|
||||||
Use two to four internal links per page. Do not create circular boilerplate link blocks or link every comparison page to
|
|
||||||
every other comparison page.
|
|
||||||
|
|
||||||
## Validation
|
|
||||||
|
|
||||||
Implementation is complete when:
|
|
||||||
|
|
||||||
- All 17 MDX files exist and appear in the `Comparisons` navigation group.
|
|
||||||
- Every file has a unique title, description, opening, comparison angle, limitations section, source list, and CTA.
|
|
||||||
- Every mutable competitor claim has a first-party source.
|
|
||||||
- Every page acknowledges a meaningful competitor advantage and a relevant Reactive Resume limitation.
|
|
||||||
- No page declares a universal winner, promises ATS passage, or repeats vendor outcome claims as facts.
|
|
||||||
- `docs/docs.json` parses successfully.
|
|
||||||
- Repository checks find no duplicate frontmatter titles or descriptions in the new files.
|
|
||||||
- All internal links in the new pages resolve.
|
|
||||||
- Mintlify's broken-link check passes for the affected documentation.
|
|
||||||
- A final diff review confirms that only the approved content pages, navigation, and implementation plan changed.
|
|
||||||
|
|
||||||
Search ranking and conversion are post-publication measurements, not implementation acceptance criteria. After
|
|
||||||
indexing, measure impressions, clicks, position, and visits from each comparison page before deciding whether to add
|
|
||||||
more competitors.
|
|
||||||
-298
@@ -1,298 +0,0 @@
|
|||||||
# Semantic CSS Author Reference and Unified Documentation Generation
|
|
||||||
|
|
||||||
**Date:** 2026-07-29
|
|
||||||
**Status:** Approved
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Reactive Resume will provide one canonical, author-facing Semantic CSS reference at:
|
|
||||||
|
|
||||||
`https://docs.rxresu.me/guides/semantic-css-reference`
|
|
||||||
|
|
||||||
The existing `docs/guides/semantic-css-reference.mdx` page will be expanded rather than duplicated. It will combine
|
|
||||||
hand-written explanations and copy-paste examples with generated tables sourced from the runtime registries and PDF
|
|
||||||
template manifests.
|
|
||||||
|
|
||||||
The Custom Styles editor will include a compact, accessible help hint linking directly to that page.
|
|
||||||
|
|
||||||
A new root command, `pnpm docs:gen`, will replace `pnpm docs:semantic-css` and regenerate:
|
|
||||||
|
|
||||||
1. Semantic CSS reference tables.
|
|
||||||
2. The resume-builder skill schema reference.
|
|
||||||
3. The complete JSON Schema embedded in the public schema guide.
|
|
||||||
4. The checked-in OpenAPI specification.
|
|
||||||
|
|
||||||
## Audience and goals
|
|
||||||
|
|
||||||
The reference is for resume authors who write Semantic CSS in the builder. It must let an author:
|
|
||||||
|
|
||||||
- Discover what selectors, properties, values, and directives exist.
|
|
||||||
- Understand which semantic nodes and template parts can be targeted.
|
|
||||||
- Copy working examples for common customizations.
|
|
||||||
- Diagnose invalid or ineffective styles.
|
|
||||||
- Understand portability, last-valid behavior, resource limits, and unsupported syntax.
|
|
||||||
|
|
||||||
The page is a language reference, not contributor documentation. Compiler architecture, AST implementation details,
|
|
||||||
internal adapter names, and package ownership stay out of the public page.
|
|
||||||
|
|
||||||
## Canonical page structure
|
|
||||||
|
|
||||||
The reference is organized for lookup rather than linear reading.
|
|
||||||
|
|
||||||
### 1. Semantic CSS in one minute
|
|
||||||
|
|
||||||
- The `@version 1;` directive.
|
|
||||||
- One complete, portable stylesheet.
|
|
||||||
- The relationship between editable source, applied source, preview, and export.
|
|
||||||
|
|
||||||
### 2. Selector grammar
|
|
||||||
|
|
||||||
- Universal, semantic type, ID, and attribute selectors.
|
|
||||||
- Supported attribute operators.
|
|
||||||
- Descendant, child, adjacent-sibling, and general-sibling combinators.
|
|
||||||
- Selector lists.
|
|
||||||
- Supported functional and structural pseudo-classes.
|
|
||||||
- Case-sensitivity behavior.
|
|
||||||
- Explicitly unsupported selector syntax.
|
|
||||||
- Paired valid and invalid examples.
|
|
||||||
|
|
||||||
### 3. Semantic element catalog
|
|
||||||
|
|
||||||
- Generated parent and child relationships.
|
|
||||||
- Generated attributes and roles.
|
|
||||||
- Known attribute value domains.
|
|
||||||
- Portable section-type selectors versus resume-specific IDs.
|
|
||||||
- Rich-text structure, including distinct list-item row and list-item content semantics.
|
|
||||||
|
|
||||||
### 4. Cascade and values
|
|
||||||
|
|
||||||
- Specificity, source order, selector-list specificity, inheritance, and `!important`.
|
|
||||||
- Semantic CSS behavior for `initial`, `inherit`, `unset`, and `revert`.
|
|
||||||
- Author custom properties, nested `var()` fallbacks, unresolved variables, and cycles.
|
|
||||||
- Reserved read-only `--resume-*` system variables.
|
|
||||||
- Numbers, lengths, units, colors, functions, and shorthands.
|
|
||||||
|
|
||||||
### 5. Property reference
|
|
||||||
|
|
||||||
- Generated property table grouped by category.
|
|
||||||
- Applicability by semantic node.
|
|
||||||
- Inheritance.
|
|
||||||
- Accepted units and constrained keywords where authoritative metadata exists.
|
|
||||||
- Examples for text, spacing, borders, flex layout, images, transforms, and structural properties.
|
|
||||||
|
|
||||||
The generated table must not present a loose registry hint as an exhaustive value grammar. Value syntax that is
|
|
||||||
implemented by parser or cascade logic remains hand-written unless it has authoritative shared metadata.
|
|
||||||
|
|
||||||
### 6. PDF behavior
|
|
||||||
|
|
||||||
- Page sizing.
|
|
||||||
- Hiding and stable sibling ordering.
|
|
||||||
- Pagination, fixed content, minimum presence ahead, orphans, and widows.
|
|
||||||
- Media-query grammar, evaluation order, and page-dimension behavior.
|
|
||||||
- React PDF-specific layout limitations that affect authors.
|
|
||||||
|
|
||||||
### 7. Template-specific selectors
|
|
||||||
|
|
||||||
- A generated matrix for all 15 templates.
|
|
||||||
- Exact template-part names.
|
|
||||||
- Selector forms.
|
|
||||||
- Owner or placement conditions.
|
|
||||||
- Allowed semantic children.
|
|
||||||
- Portability warnings and guarded selector examples.
|
|
||||||
|
|
||||||
The matrix is generated from actual template manifests, not an independently maintained list.
|
|
||||||
|
|
||||||
### 8. Diagnostics and limits
|
|
||||||
|
|
||||||
- Stable compiler and preflight diagnostic codes.
|
|
||||||
- Severity.
|
|
||||||
- Meaning and likely corrective action.
|
|
||||||
- Source, selector, declaration, node, page, size, timeout, and memory limits.
|
|
||||||
- Last-valid preview and export behavior after an invalid edit.
|
|
||||||
|
|
||||||
### 9. Copy-paste recipes
|
|
||||||
|
|
||||||
- Restyle section headings.
|
|
||||||
- Target a section type.
|
|
||||||
- Target one section, item, or field.
|
|
||||||
- Style sidebar content by placement.
|
|
||||||
- Customize rich-text lists.
|
|
||||||
- Change authored page dimensions.
|
|
||||||
- Prevent awkward page breaks.
|
|
||||||
- Customize optional template decoration.
|
|
||||||
- Apply dimension-dependent PDF styles with `@media`.
|
|
||||||
|
|
||||||
### 10. Unsupported capabilities and portability checklist
|
|
||||||
|
|
||||||
- Unsupported selector, at-rule, layout, asset, font, script, interaction, and network capabilities.
|
|
||||||
- Guidance for keeping a stylesheet portable across templates.
|
|
||||||
|
|
||||||
## Generated documentation architecture
|
|
||||||
|
|
||||||
### Command
|
|
||||||
|
|
||||||
The root package exposes:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pnpm docs:gen
|
|
||||||
```
|
|
||||||
|
|
||||||
The existing `docs:semantic-css` command is replaced by `docs:gen`, leaving one canonical documentation-generation
|
|
||||||
entrypoint.
|
|
||||||
|
|
||||||
### Semantic CSS reference data
|
|
||||||
|
|
||||||
Generated Semantic CSS sections consume existing authoritative sources:
|
|
||||||
|
|
||||||
- Supported versions and compile limits.
|
|
||||||
- Semantic element registry.
|
|
||||||
- Property registry.
|
|
||||||
- Read-only system-variable registry.
|
|
||||||
- PDF template manifests.
|
|
||||||
- Shared compiler and preflight diagnostic catalogs.
|
|
||||||
|
|
||||||
The generator emits deterministic, marker-delimited sections into
|
|
||||||
`docs/guides/semantic-css-reference.mdx`.
|
|
||||||
|
|
||||||
Generated factual sections include:
|
|
||||||
|
|
||||||
- Semantic elements, parents, attributes, roles, and known value domains.
|
|
||||||
- Property category, applicability, inheritance, units, and constrained keywords.
|
|
||||||
- System variables.
|
|
||||||
- Per-template template parts.
|
|
||||||
- Diagnostics.
|
|
||||||
- Compile and preflight limits.
|
|
||||||
|
|
||||||
Manual prose remains outside generated markers.
|
|
||||||
|
|
||||||
### Resume JSON Schema
|
|
||||||
|
|
||||||
The generator computes the canonical Resume JSON Schema once from `resumeDataSchema` using Zod's JSON Schema
|
|
||||||
conversion.
|
|
||||||
|
|
||||||
That canonical schema drives two outputs:
|
|
||||||
|
|
||||||
1. `skills/resume-builder/references/schema.md`
|
|
||||||
- A compact, AI-friendly Markdown reference.
|
|
||||||
- Field hierarchy, types, required fields, constraints, and representative shapes.
|
|
||||||
- Derived from the canonical JSON Schema rather than maintained separately.
|
|
||||||
|
|
||||||
2. `docs/guides/json-resume-schema.mdx`
|
|
||||||
- The complete canonical JSON Schema inside a generated, marker-delimited JSON block.
|
|
||||||
- Human-written explanation remains outside the generated block.
|
|
||||||
|
|
||||||
### OpenAPI specification
|
|
||||||
|
|
||||||
OpenAPI generation is exposed through one reusable, pure generator owned by `apps/server/src/openapi`.
|
|
||||||
|
|
||||||
- The runtime `/api/openapi/spec.json` handler calls it with `env.APP_URL`.
|
|
||||||
- A sibling server documentation-generation script calls it with `https://rxresu.me` and writes `docs/spec.json`.
|
|
||||||
- The root `docs:gen` command orchestrates the tooling generator and this server-owned OpenAPI generator.
|
|
||||||
- The checked-in output is `docs/spec.json`.
|
|
||||||
- The API version comes from the current application version.
|
|
||||||
|
|
||||||
This removes drift between runtime OpenAPI output and the checked-in documentation artifact, including stale versions
|
|
||||||
and localhost server URLs.
|
|
||||||
|
|
||||||
### Determinism and failure behavior
|
|
||||||
|
|
||||||
Generation must:
|
|
||||||
|
|
||||||
- Produce stable ordering and formatting.
|
|
||||||
- Require every expected marker.
|
|
||||||
- Fail on duplicate or missing markers.
|
|
||||||
- Fail on inconsistent template-manifest coverage.
|
|
||||||
- Avoid silently leaving a partially updated reference that appears authoritative.
|
|
||||||
|
|
||||||
The generator computes all output text before writing any target. It does not add a general transaction framework.
|
|
||||||
|
|
||||||
## Custom Styles help hint
|
|
||||||
|
|
||||||
The Semantic CSS editor's shared chrome displays this hint directly above the code editor:
|
|
||||||
|
|
||||||
> **Not sure what to write?** Browse the Semantic CSS language reference.
|
|
||||||
|
|
||||||
The link:
|
|
||||||
|
|
||||||
- Targets `https://docs.rxresu.me/guides/semantic-css-reference`.
|
|
||||||
- Opens in a new tab.
|
|
||||||
- Uses `rel="noopener noreferrer"`.
|
|
||||||
- Uses the existing `BookOpenIcon`, marked as decorative.
|
|
||||||
- Has translated visible text.
|
|
||||||
- Includes translated screen-reader text indicating that it opens in a new tab.
|
|
||||||
- Appears in both the standard desktop editor and the mobile focus sheet because both use the same editor chrome.
|
|
||||||
|
|
||||||
The implementation stays local to the stylesheet editor. It does not introduce a shared component or central URL
|
|
||||||
registry for one link.
|
|
||||||
|
|
||||||
## Documentation navigation
|
|
||||||
|
|
||||||
`docs/docs.json` lists `guides/semantic-css-reference` immediately after `guides/using-custom-styles`.
|
|
||||||
|
|
||||||
The public route is:
|
|
||||||
|
|
||||||
`https://docs.rxresu.me/guides/semantic-css-reference`
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
### Generator verification
|
|
||||||
|
|
||||||
- `pnpm docs:gen` regenerates all four artifact groups.
|
|
||||||
- A non-mutating test generates into temporary files and compares them byte-for-byte with committed outputs.
|
|
||||||
- Generated output is deterministic across repeated runs.
|
|
||||||
- Every runtime template part appears in the generated template matrix.
|
|
||||||
- Cross-registry checks reject inconsistent template-part parent or child coverage.
|
|
||||||
- The generated OpenAPI document matches the shared runtime generator for the documentation URL and current version.
|
|
||||||
- Both schema Markdown targets are derived from the same canonical Resume JSON Schema.
|
|
||||||
|
|
||||||
### Example verification
|
|
||||||
|
|
||||||
- Complete copy-paste examples marked as valid compile successfully.
|
|
||||||
- Selected intentionally invalid examples produce their documented diagnostic.
|
|
||||||
- Small illustrative fragments that are not complete stylesheets are not forced through a full compiler test.
|
|
||||||
|
|
||||||
### UI verification
|
|
||||||
|
|
||||||
The stylesheet editor test verifies:
|
|
||||||
|
|
||||||
- Accessible link name.
|
|
||||||
- Exact public URL.
|
|
||||||
- New-tab target.
|
|
||||||
- `noopener noreferrer`.
|
|
||||||
- Presence in the standard editor.
|
|
||||||
- Presence in the mobile focus sheet.
|
|
||||||
|
|
||||||
### Focused gates
|
|
||||||
|
|
||||||
- Tooling tests and typecheck.
|
|
||||||
- Resume/schema tests and typechecks affected by exported metadata.
|
|
||||||
- PDF manifest/reference consistency tests and typecheck.
|
|
||||||
- API/server OpenAPI tests and typechecks.
|
|
||||||
- Web editor tests and typecheck.
|
|
||||||
- Workspace boundary check.
|
|
||||||
- Focused formatting and Markdown validation.
|
|
||||||
|
|
||||||
Chrome verification is not required.
|
|
||||||
|
|
||||||
## Out of scope
|
|
||||||
|
|
||||||
- Contributor/compiler architecture documentation.
|
|
||||||
- A second Semantic CSS reference route.
|
|
||||||
- Splitting the reference across multiple pages.
|
|
||||||
- Interactive documentation playgrounds.
|
|
||||||
- New editor completion or hover features.
|
|
||||||
- New Semantic CSS syntax or rendering behavior, except for correcting factual registry inconsistencies required to generate an
|
|
||||||
accurate reference.
|
|
||||||
- General documentation URL centralization.
|
|
||||||
|
|
||||||
## Acceptance criteria
|
|
||||||
|
|
||||||
- The canonical reference documents every author-facing Semantic CSS selector, semantic element, property, variable, directive,
|
|
||||||
value family, template part, diagnostic family, limit, and unsupported syntax category.
|
|
||||||
- The reference contains copy-paste examples for common author goals.
|
|
||||||
- Generated facts come from authoritative runtime metadata and have staleness coverage.
|
|
||||||
- `pnpm docs:gen` refreshes the Semantic CSS tables, both Resume JSON Schema references, and `docs/spec.json`.
|
|
||||||
- Runtime and checked-in OpenAPI output share one generator.
|
|
||||||
- The reference is visible in documentation navigation.
|
|
||||||
- The Custom Styles editor links to the exact public reference route on desktop and mobile.
|
|
||||||
- No unrelated product behavior or documentation architecture is introduced.
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user