diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 0000000..2df1072 --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,57 @@ +[mcp_servers.playwright] +command = "npx" +args = ["-y", "@playwright/mcp@latest"] + +[mcp_servers.vercel_vrdex] +url = "https://mcp.vercel.com" + +[mcp_servers.convex] +command = "npx" +args = ["convex", "mcp", "start", "--project-dir", "."] + +[mcp_servers.aws_docs] +command = "C:/Users/steve/.local/bin/uvx.exe" +args = ["awslabs.aws-documentation-mcp-server@latest"] + +[mcp_servers.aws_docs.env] +FASTMCP_LOG_LEVEL = "ERROR" +AWS_DOCUMENTATION_PARTITION = "aws" + +[mcp_servers.aws_iac] +command = "C:/Users/steve/.local/bin/uvx.exe" +args = ["awslabs.aws-iac-mcp-server@latest"] + +[mcp_servers.aws_iac.env] +FASTMCP_LOG_LEVEL = "ERROR" + +[mcp_servers.aws_terraform] +command = "uvx" +args = ["awslabs.terraform-mcp-server@latest"] + +[mcp_servers.aws_terraform.env] +FASTMCP_LOG_LEVEL = "ERROR" +AWS_PROFILE = "default" +AWS_REGION = "us-east-1" + +[mcp_servers.aws_mcp] +command = "uvx" +args = [ + "--from", + "mcp-proxy-for-aws@latest", + "mcp-proxy-for-aws.exe", + "https://aws-mcp.us-east-1.api.aws/mcp", + "--service", + "aws-mcp", + "--profile", + "default", + "--region", + "us-east-1", +] + +[mcp_servers.daytona] +command = "C:/Users/steve/AppData/Roaming/bin/daytona/daytona.exe" +args = ["mcp", "start"] + +[mcp_servers.daytona.env] +APPDATA = "C:\\Users\\steve\\AppData\\Roaming" +HOME = "C:\\Users\\steve" diff --git a/.codex/skills/vrcdn/SKILL.md b/.codex/skills/vrcdn/SKILL.md new file mode 100644 index 0000000..252230b --- /dev/null +++ b/.codex/skills/vrcdn/SKILL.md @@ -0,0 +1,22 @@ +--- +name: vrcdn +description: Work with VRCDN provider setup, URL formats, smoke tests, and VRDex restream POC flows without leaking stream credentials. +--- + +# VRCDN + +This is the Codex wrapper for the repo source skill: + +- `.opencode/skills/vrcdn/SKILL.md` + +Read that source skill before working on VRCDN provider setup, smoke tests, URL +handling, or restream POC diagnosis. Treat it as the source of truth for safety +boundaries and validation commands. + +Codex translation notes: + +- Use Codex MCP tools when available; otherwise use the documented CLI fallback. +- Never print, commit, or paste stream keys, provider passwords, combined ingest + URLs, signed URLs, or copied provider secrets. +- Report only public preview/playback URLs when they contain no credential + material, plus secret names, task IDs, sanitized event names, and status. diff --git a/.codex/skills/vrdex-onboarding/SKILL.md b/.codex/skills/vrdex-onboarding/SKILL.md new file mode 100644 index 0000000..1b807bd --- /dev/null +++ b/.codex/skills/vrdex-onboarding/SKILL.md @@ -0,0 +1,21 @@ +--- +name: vrdex-onboarding +description: Onboard a new Codex agent or maintainer to VRDex's repo structure, software-factory posture, and local-vs-global context model. +--- + +# VRDex Onboarding + +This is the Codex wrapper for the repo source skill: + +- `.opencode/skills/vrdex-onboarding/SKILL.md` + +Read that source skill before onboarding a new agent or maintainer. Treat it as +the source of truth for repo orientation. + +Codex translation notes: + +- Read `AGENTS.md` first, then `AGENTS.local.md` when present. +- Use `docs/agentic/codex.md` for Codex-specific MCP, skill, and worktree notes. +- Keep `.opencode/skills` as the detailed source of truth and keep this wrapper thin. +- Do not work directly in the protected `main` mirror except for explicitly + requested mirror maintenance. diff --git a/.gitignore b/.gitignore index bce539f..16b4dba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ AGENTS.local.md .opencode/state/ +.codex/state/ node_modules/ .husky/_/ .convex-home/ diff --git a/AGENTS.local.md.example b/AGENTS.local.md.example index b6b8729..8e54d9f 100644 --- a/AGENTS.local.md.example +++ b/AGENTS.local.md.example @@ -18,6 +18,11 @@ Copy this file to `AGENTS.local.md` and edit it freely. Keep repo-wide rules in - Fill in local model preferences, formatting preferences, test expectations, or communication habits here. +## Worktree Role + +- Set this checkout's role explicitly, such as `protected main mirror` or `active feature worktree`. +- Protected main mirrors should stay on `main` and clean. Routine work should happen under `D:/bench/VRDex-wt/`. + ## Scratchpad - Put personal drafts, one-off notes, and junk-drawer artifacts outside the repo unless I explicitly ask to commit them. diff --git a/apps/discord-gateway/package.json b/apps/discord-gateway/package.json index 22eb89d..28ee2ca 100644 --- a/apps/discord-gateway/package.json +++ b/apps/discord-gateway/package.json @@ -7,8 +7,11 @@ "node": ">=24 <25" }, "scripts": { + "prestart": "node ../../scripts/guard-main-worktree.mjs", "start": "tsx src/index.ts", + "pretypecheck": "node ../../scripts/guard-main-worktree.mjs", "typecheck": "tsc --noEmit --project tsconfig.json", + "pretest": "node ../../scripts/guard-main-worktree.mjs", "test": "node --import tsx --test src/**/*.test.ts" }, "dependencies": { diff --git a/apps/docs/package.json b/apps/docs/package.json index eda3d13..1edb090 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -6,9 +6,13 @@ "node": ">=24 <25" }, "scripts": { + "prebuild": "node ../../scripts/guard-main-worktree.mjs", "build": "docusaurus build", + "preclear": "node ../../scripts/guard-main-worktree.mjs", "clear": "docusaurus clear", + "preserve": "node ../../scripts/guard-main-worktree.mjs", "serve": "docusaurus serve", + "prestart": "node ../../scripts/guard-main-worktree.mjs", "start": "docusaurus start" }, "dependencies": { diff --git a/apps/web/package.json b/apps/web/package.json index 3fcbe13..e1cf4d0 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -6,23 +6,41 @@ "node": ">=24 <25" }, "scripts": { + "predev": "node ../../scripts/guard-main-worktree.mjs", "dev": "next dev", + "prebuild": "node ../../scripts/guard-main-worktree.mjs", "build": "next build", + "prebuild:storybook": "node ../../scripts/guard-main-worktree.mjs", "build:storybook": "storybook build", + "prebuild:vercel": "node ../../scripts/guard-main-worktree.mjs", "build:vercel": "pnpm check:vercel-env && next build", + "precheck:vercel-env": "node ../../scripts/guard-main-worktree.mjs", "check:vercel-env": "node ./scripts/check-vercel-env.mjs", + "prestart": "node ../../scripts/guard-main-worktree.mjs", "start": "next start", + "prestorybook": "node ../../scripts/guard-main-worktree.mjs", "storybook": "storybook dev -p 6006 --host 127.0.0.1", + "prelint": "node ../../scripts/guard-main-worktree.mjs", "lint": "eslint", + "pretest:e2e": "node ../../scripts/guard-main-worktree.mjs", "test:e2e": "playwright test --grep-invert \"@visual|@snapshot|@storybook\"", + "pretest:e2e:hosted": "node ../../scripts/guard-main-worktree.mjs", "test:e2e:hosted": "playwright test --grep @flow --project=desktop-chromium", + "pretest:e2e:hosted:smoke": "node ../../scripts/guard-main-worktree.mjs", "test:e2e:hosted:smoke": "playwright test --grep-invert \"@visual|@flow|@snapshot|@storybook\"", + "pretest:e2e:snapshots": "node ../../scripts/guard-main-worktree.mjs", "test:e2e:snapshots": "playwright test --grep @snapshot --grep-invert @storybook", + "pretest:e2e:snapshots:update": "node ../../scripts/guard-main-worktree.mjs", "test:e2e:snapshots:update": "playwright test --grep @snapshot --grep-invert @storybook --update-snapshots", + "pretest:e2e:visual": "node ../../scripts/guard-main-worktree.mjs", "test:e2e:visual": "playwright test --grep @visual", + "pretest:storybook:snapshots": "node ../../scripts/guard-main-worktree.mjs", "test:storybook:snapshots": "playwright test --config playwright.storybook.config.mjs --grep @storybook-snapshot", + "pretest:storybook:snapshots:update": "node ../../scripts/guard-main-worktree.mjs", "test:storybook:snapshots:update": "playwright test --config playwright.storybook.config.mjs --grep @storybook-snapshot --update-snapshots", + "pretest:storybook:visual": "node ../../scripts/guard-main-worktree.mjs", "test:storybook:visual": "playwright test --config playwright.storybook.config.mjs --grep @storybook-visual", + "pretypecheck": "node ../../scripts/guard-main-worktree.mjs", "typecheck": "next typegen && node ./scripts/ensure-next-type-stubs.mjs && tsc --noEmit --incremental false" }, "dependencies": { diff --git a/docs/agentic/README.md b/docs/agentic/README.md index 76afe20..9bbff02 100644 --- a/docs/agentic/README.md +++ b/docs/agentic/README.md @@ -3,6 +3,7 @@ ## Core docs - `docs/agentic/contributor-workflow.md` - canonical contributor-friendly, agent-compatible workflow contract +- `docs/agentic/codex.md` - Codex-specific MCP, skill, and protected-worktree notes - `docs/agentic/definition-of-done.md` - canonical closeout checklist for non-trivial feature work - `docs/agentic/definition-of-ready.md` - canonical readiness checklist for non-trivial feature work - `docs/agentic/product-analytics-and-feature-flags.md` - canonical first-pass rollout, flagging, and product instrumentation policy diff --git a/docs/agentic/codex.md b/docs/agentic/codex.md new file mode 100644 index 0000000..6dfdde3 --- /dev/null +++ b/docs/agentic/codex.md @@ -0,0 +1,90 @@ +# Codex Notes + +VRDex has historically used OpenCode for long-running local agent work. Codex +should reuse the same durable playbooks while mapping OpenCode-specific tools to +Codex capabilities. + +## Startup Context + +- Read `AGENTS.md` first, and `AGENTS.local.md` when present. +- Use `docs/agentic/README.md` for the core software-factory docs. +- Use `.codex/skills/*/SKILL.md` as thin Codex entry points when repo-local + skills are available. +- Do not work directly in the protected `main` mirror except for explicitly + requested mirror maintenance. + +## Worktree Policy + +Current local convention: + +- Protected main mirror: `D:/bench/VRDex` +- Active worktree root: `D:/bench/VRDex-wt` +- Feature worktrees: `D:/bench/VRDex-wt/` + +The protected mirror should stay on `main` and clean. Routine implementation, +review recycle work, and Codex parity changes should happen in named worktrees +under `D:/bench/VRDex-wt`. + +The root package scripts run `scripts/guard-main-worktree.mjs` before local +development, build, lint, test, and verification commands. The guard blocks +those commands on `main` unless one of these is true: + +- CI is running. +- `GITHUB_ACTIONS=true` is set. +- `VRDEX_ALLOW_PROTECTED_WORKTREE=1` is set for intentional mirror maintenance. + +## Skills + +The OpenCode source skills live under `.opencode/skills//SKILL.md`. + +Codex wrappers live under `.codex/skills//SKILL.md`. Each wrapper keeps +Codex-valid frontmatter and points back to the OpenCode source skill. If a +Codex session does not auto-discover repo-local skills, open the wrapper or +source skill by path. + +Current wrappers: + +- `vrcdn` +- `vrdex-onboarding` + +Keep `.opencode/skills` as the detailed source of truth. Keep `.codex/skills` +as thin compatibility shims, not duplicated long playbooks. + +## Tool Mapping + +- OpenCode plugins under `.opencode/plugins` are not Codex tools unless the + active Codex tool list explicitly exposes equivalent functions. +- The OpenCode `/supervisor` command and `.opencode/plugins/supervisor-loop.*` + remain OpenCode experiments. In Codex, use thread coordination tools only when + they are explicitly available in the active tool list. +- For GitHub and PR review loops, prefer a GitHub connector when available; + otherwise use `gh` and manual polling. +- For frontend verification, use the Codex Browser plugin for local targets when + available, plus screenshot evidence for meaningful UI changes. +- For library, SDK, CLI, and cloud-service docs, use Codex documentation tools + such as Context7 when available, or primary-source web docs when required. +- For reminders, monitors, or later follow-ups, use Codex automations only when + the user asks for that behavior. + +## MCP Config + +Codex project MCP config lives at `.codex/config.toml` and mirrors the OpenCode +`.opencode/opencode.json` servers: + +- `playwright` +- `vercel_vrdex` +- `convex` +- `aws_docs` +- `aws_iac` +- `aws_terraform` +- `aws_mcp` +- `daytona` + +These are project-scoped VRDex servers. Keep them in repo-local config rather +than relying on global Codex config inheritance. Global Codex MCP entries should +be reserved for genuinely cross-repo tools. + +If an MCP server is not active in the current Codex session, use the documented +CLI fallback (`vercel`, `aws`, `npx convex`, `gh`) or report the missing +capability. Production data, infrastructure, billing, or secret mutations still +require the normal approval and escalation workflow. diff --git a/package.json b/package.json index d9a3451..6e82a0d 100644 --- a/package.json +++ b/package.json @@ -6,47 +6,90 @@ "node": ">=24 <25" }, "scripts": { + "guard:main-worktree": "node scripts/guard-main-worktree.mjs", + "prebootstrap:backend:local": "node scripts/guard-main-worktree.mjs", "bootstrap:backend:local": "pnpm verify:backend:local", + "predev:backend:local": "node scripts/guard-main-worktree.mjs", "dev:backend:local": "node scripts/run-convex-local.mjs dev --local", + "predev:web": "node scripts/guard-main-worktree.mjs", "dev:web": "pnpm --filter web dev", + "predev:discord-gateway": "node scripts/guard-main-worktree.mjs", "dev:discord-gateway": "pnpm --filter @vrdex/discord-gateway start", + "predev:docs": "node scripts/guard-main-worktree.mjs", "dev:docs": "pnpm --filter docs start", + "prebuild:web": "node scripts/guard-main-worktree.mjs", "build:web": "pnpm --filter web build", + "prebuild:docs": "node scripts/guard-main-worktree.mjs", "build:docs": "pnpm --filter docs build", + "prebuild:storybook": "node scripts/guard-main-worktree.mjs", "build:storybook": "pnpm --filter web build:storybook", + "prebuild:web:vercel": "node scripts/guard-main-worktree.mjs", "build:web:vercel": "pnpm --filter web build:vercel", + "precheck:web:vercel-env": "node scripts/guard-main-worktree.mjs", "check:web:vercel-env": "pnpm --filter web check:vercel-env", + "prelint:markdown": "node scripts/guard-main-worktree.mjs", "lint:markdown": "markdownlint-cli2", + "preproof:restream:ffmpeg": "node scripts/guard-main-worktree.mjs", "proof:restream:ffmpeg": "node scripts/restream-ffmpeg-proof.mjs run", + "precheck:restream:ffmpeg": "node scripts/guard-main-worktree.mjs", "check:restream:ffmpeg": "node scripts/restream-ffmpeg-proof.mjs check", + "preproof:restream:local": "node scripts/guard-main-worktree.mjs", "proof:restream:local": "node --import tsx scripts/restream-local-validation.ts run", + "precheck:restream:local": "node scripts/guard-main-worktree.mjs", "check:restream:local": "node --import tsx scripts/restream-local-validation.ts check", + "preproof:restream:control": "node scripts/guard-main-worktree.mjs", "proof:restream:control": "node --import tsx scripts/restream-control-demo.ts", + "preproof:restream:live-control": "node scripts/guard-main-worktree.mjs", "proof:restream:live-control": "node scripts/restream-live-control-proof.mjs", + "preproof:restream:worker": "node scripts/guard-main-worktree.mjs", "proof:restream:worker": "node scripts/run-restream-worker-benchmark.mjs", + "preproof:restream:worker:live-control": "node scripts/guard-main-worktree.mjs", "proof:restream:worker:live-control": "node scripts/run-restream-worker-benchmark.mjs live-control", + "preproof:restream:worker:matrix": "node scripts/guard-main-worktree.mjs", "proof:restream:worker:matrix": "node scripts/run-restream-worker-benchmark-matrix.mjs", + "preops:event-media:ecs-bridge": "node scripts/guard-main-worktree.mjs", "ops:event-media:ecs-bridge": "node scripts/event-media-ecs-bridge.mjs", + "prelint:web": "node scripts/guard-main-worktree.mjs", "lint:web": "pnpm --filter web lint", + "pretest:e2e": "node scripts/guard-main-worktree.mjs", "test:e2e": "pnpm --filter web test:e2e", + "pretest:e2e:hosted": "node scripts/guard-main-worktree.mjs", "test:e2e:hosted": "pnpm --filter web test:e2e:hosted", + "pretest:e2e:hosted:smoke": "node scripts/guard-main-worktree.mjs", "test:e2e:hosted:smoke": "pnpm --filter web test:e2e:hosted:smoke", + "pretest:e2e:snapshots": "node scripts/guard-main-worktree.mjs", "test:e2e:snapshots": "pnpm --filter web test:e2e:snapshots", + "pretest:e2e:snapshots:update": "node scripts/guard-main-worktree.mjs", "test:e2e:snapshots:update": "pnpm --filter web test:e2e:snapshots:update", + "pretest:e2e:visual": "node scripts/guard-main-worktree.mjs", "test:e2e:visual": "pnpm --filter web test:e2e:visual", + "pretest:storybook:snapshots": "node scripts/guard-main-worktree.mjs", "test:storybook:snapshots": "pnpm --filter web test:storybook:snapshots", + "pretest:storybook:snapshots:update": "node scripts/guard-main-worktree.mjs", "test:storybook:snapshots:update": "pnpm --filter web test:storybook:snapshots:update", + "pretest:storybook:visual": "node scripts/guard-main-worktree.mjs", "test:storybook:visual": "pnpm --filter web test:storybook:visual", + "pretest:backend": "node scripts/guard-main-worktree.mjs", "test:backend": "node --import tsx --test tests/backend/**/*.test.ts", + "pretest:discord-gateway": "node scripts/guard-main-worktree.mjs", "test:discord-gateway": "pnpm --filter @vrdex/discord-gateway test", + "pretypecheck:backend": "node scripts/guard-main-worktree.mjs", "typecheck:backend": "tsc --noEmit --project convex/tsconfig.json", + "pretypecheck:discord-gateway": "node scripts/guard-main-worktree.mjs", "typecheck:discord-gateway": "pnpm --filter @vrdex/discord-gateway typecheck", + "prerun:backend:health:local": "node scripts/guard-main-worktree.mjs", "run:backend:health:local": "pnpm verify:backend:local", + "pretypecheck:web": "node scripts/guard-main-worktree.mjs", "typecheck:web": "pnpm --filter web typecheck", + "preverify:backend:local": "node scripts/guard-main-worktree.mjs", "verify:backend:local": "node scripts/run-convex-local.mjs dev --local --once --run health:status --tail-logs disable", + "precheck:backend:generated": "node scripts/guard-main-worktree.mjs", "check:backend:generated": "pnpm verify:backend:local && git diff --exit-code -- convex/_generated", + "preverify:web": "node scripts/guard-main-worktree.mjs", "verify:web": "pnpm lint:web && pnpm typecheck:web && pnpm build:web", + "preverify:docs": "node scripts/guard-main-worktree.mjs", "verify:docs": "pnpm lint:markdown && pnpm build:docs", + "preverify": "node scripts/guard-main-worktree.mjs", "verify": "pnpm verify:docs && pnpm verify:web && pnpm check:backend:generated && pnpm typecheck:backend && pnpm test:backend", "prepare": "husky" }, diff --git a/scripts/guard-main-worktree.mjs b/scripts/guard-main-worktree.mjs new file mode 100644 index 0000000..483fa70 --- /dev/null +++ b/scripts/guard-main-worktree.mjs @@ -0,0 +1,55 @@ +import { execFileSync } from "node:child_process"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const scriptDir = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(scriptDir, ".."); + +if (process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true") { + process.exit(0); +} + +if (process.env.VRDEX_ALLOW_PROTECTED_WORKTREE === "1") { + console.warn( + "[guard] Protected-main guard bypassed via VRDEX_ALLOW_PROTECTED_WORKTREE=1.", + ); + process.exit(0); +} + +const runGit = (args) => + execFileSync("git", args, { + cwd: repoRoot, + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + }).trim(); + +let branchName = ""; + +try { + branchName = runGit(["branch", "--show-current"]); +} catch { + process.exit(0); +} + +if (branchName !== "main") { + process.exit(0); +} + +const lifecycleEvent = process.env.npm_lifecycle_event ?? "local workflow"; +const currentDir = process.cwd(); + +console.error(`Protected main worktree guard blocked \`${lifecycleEvent}\`.`); +console.error(`Current branch: ${branchName}`); +console.error(`Working directory: ${currentDir}`); +console.error(""); +console.error("VRDex keeps the local main checkout as a clean mirror."); +console.error("Create a feature worktree under D:/bench/VRDex-wt instead:"); +console.error( + " git worktree add -b codex/ D:/bench/VRDex-wt/ origin/main", +); +console.error(""); +console.error( + "If this is intentional mirror maintenance or an emergency on main, rerun with VRDEX_ALLOW_PROTECTED_WORKTREE=1.", +); + +process.exit(1);