diff --git a/.agents/PAPERCUTS.md b/.agents/PAPERCUTS.md index f9a941f7d..08894019f 100644 --- a/.agents/PAPERCUTS.md +++ b/.agents/PAPERCUTS.md @@ -10,6 +10,7 @@ data, or sensitive paths. ## Open +- [ ] `2026-09-08T23:33:10Z` — `cto` — `web/` has 252 pre-existing prettier violations on `main`. The first iteration of the BUS-27 web CI job included `pnpm --dir web run format:check`, which would fail every PR until reconciled. Reverted the format gate (commit `b9014de` on branch `ctof/t1-foundations-ci-gates`) with an in-file TODO comment. Tracked as BUS-42: run `prettier --write web/`, commit the churn on a separate PR, then re-add the gate. - [ ] `2026-08-20T20:36:32Z` — `codex` — The PR preview Access check treats an immediate workers.dev 404 as proof the preview is public, even though the same URL can begin returning the expected Access redirect seconds later; retry 404 responses as propagation-era errors before failing and recommending stage destruction. - [ ] `2026-08-18T03:06:44Z` — `claude` — Changing an MCP tool's `outputSchema` while the dev server hot-reloads makes in-flight MCP sessions reject the tool's own (already billed) results — clients validate against the schema cached at connect time, surfacing as "must NOT have additional properties". Note in the MCP dev docs/skill: reconnect the MCP session after any output-schema change before re-testing live. - [ ] `2026-08-05T20:59:09Z` — `codex` — The documented `pnpm seed:rank-tracking` command fails before opening local D1 because `scripts/seed-rank-tracking.ts` imports the provider-aware `src/db/schema` barrel and plain `tsx` cannot load the resulting `cloudflare:workers` URL. Keep the seed script on dialect-local schema imports or run it through a Workers-compatible execution path. (Workaround: seed via raw SQL with `wrangler d1 execute DB --local`.) @@ -20,6 +21,7 @@ data, or sensitive paths. - [ ] `2026-07-14T01:28:30Z` — `claude` — Regenerating the lockfile (adding or moving a dep) makes `pnpm install` re-run the `minimumReleaseAge` gate on transitive peers already pinned at that exact version (`mysql2`, `sql-escaper`, `@aws-sdk/credential-providers`), failing the install even though nothing about them changed. `pnpm install --config.minimumReleaseAge=0` — then confirm the lockfile diff stays version-neutral — unblocks it; worth documenting that regen step so the gate doesn't re-block already-pinned versions. - [ ] `2026-07-10T21:28:46Z` — `codex` — `pnpm --dir badseo run typecheck` works through the root toolchain but `pnpm --dir badseo run build` can't find Vite because `badseo/node_modules` is absent. Document or enforce the package-local install before validating the `badseo/` subpackage. - [ ] `2026-07-10T21:32:10Z` — `codex` — Formatting the `badseo/` workspace with `pnpm exec prettier` fails because Prettier is only available from the repository root. Document the root-only formatter command or expose a workspace-local formatting script. +- [ ] `2026-09-08T23:53:04Z` — `cto` — From a heartbeat-timer wake (`invocationSource: "timer"`, no specific issue), `POST /api/issues/{id}/comments` and `PATCH /api/issues/{id}` both return `403 cross_issue_influence_run_context_required`, even with `X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID` set. Reads work fine. Net effect: CTO cannot post a status-refresh comment on any of its in_progress issues from a pure heartbeat tick — only from a wake that was assigned to that issue. Two writes failed before giving up this run, so it's worth either (a) auto-attaching a writable run context to in-progress issues the agent owns, or (b) only firing timer heartbeats against issues that have pending work. ## Resolved diff --git a/.agents/PROPOSED-AGENTS-MD.md b/.agents/PROPOSED-AGENTS-MD.md new file mode 100644 index 000000000..2202bcbbd --- /dev/null +++ b/.agents/PROPOSED-AGENTS-MD.md @@ -0,0 +1,55 @@ +# Proposed AGENTS.md additions — BUS-19 / Roadmap T1 + +This file is the proposal a CTO engineer drafted for inclusion in +`AGENTS.md` once a maintainer approves. The runtime control-plane gate +currently blocks direct edits to `AGENTS.md` without explicit approval, +so the proposal lives here for review. See BUS-19 on the Paperclip +instance for the `request_confirmation` interaction. + +--- + +## Workflow + +### Branch naming + +Use `/` where `scope` is the agent or change category and +`short-desc` is a kebab-cased summary. Examples from recent history: + +- `ctof/t1-foundations-ci-gates` — this branch +- `bensenescu/codex/fix-invalid-offlineaccess-scope` +- `every-app/263-release-notes-public-defaults` + +The scope can be nested (e.g. `bensenescu/codex/...`) when the agent role is a +two-word phrase; keep the leaf segment a verb phrase that fits in a PR title. + +### Pull request convention + +- Title: `[] ` (matches the branch scope). +- Body: link the issue or ticket, state the change in 2–4 bullets, call out any + papercuts discovered (`.agents/PAPERCUTS.md` §Open). +- Keep PRs scoped to one concern. If a branch touches multiple unrelated + concerns, split it before requesting review. +- CI must be green before merge. The required checks are defined in branch + protection; see `.github/workflows/ci.yml` for the job list. +- Squash-merge to `main`. The squash commit message becomes the permanent + record, so phrase it the way you would a commit message, not a PR title. + +### Static-HTML file-touch rules + +Open‑seo's `web/` workspace is a Vite + Fumadocs static site. Its visible +output is generated from sources, not authored directly. Treat the following +directories as read‑only for normal changes: + +- `web/dist/**` — build output. Never edit by hand; regenerate via + `pnpm --dir web run build`. If a file there is "wrong", the source is in + `web/content/` (MDX), `web/src/` (TS/TSX), or `web/public/` (static assets). +- `src/routeTree.gen.ts` — generated by TanStack Router. Never edit by hand. +- `worker-configuration.d.ts` — generated by `pnpm run cf-typegen`. Never + edit by hand. + +For assets that need to live alongside the built site (favicons, OG images, +downloadable PDFs), put them in `web/public/` and reference them by absolute +path — Vite copies them into `dist/` unchanged. If you find yourself wanting +to edit a file under `web/dist/`, stop and ask whether the change belongs in +`web/content/` or `web/public/` instead; log the friction in +`.agents/PAPERCUTS.md` if the rule cost you time. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f100b982..7b7e47276 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,8 +10,16 @@ concurrency: group: ci-${{ github.ref }} cancel-in-progress: true +# Branch protection: the "Required status checks" for `main` must include: +# - "Lint / Typecheck / Test / Build (root)" (job: ci) +# - "Website typecheck + build" (job: web) +# - "Self-host Docker image build" (job: docker-build) +# GitHub branch-protection settings are configured in the repo settings +# UI; this comment is the canonical reminder for whoever sets it up. + jobs: ci: + name: "Lint / Typecheck / Test / Build (root)" runs-on: ubuntu-latest steps: @@ -32,28 +40,76 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Run CI checks - run: pnpm run ci:check + # Named gates per BUS-19 (Roadmap T1 Foundations). + # `lint` — oxlint with the type-aware plugin set (see .oxlintrc.json). + # `typecheck` — tsc --noEmit on the root tsconfig. + # `test` — vitest run (the CI variant uses the dot reporter). + # `build` — vite build + tsc --noEmit (the worker bundle, not the static site). + # The job is marked required on `main` so a green run is the merge gate. + - name: Lint + run: pnpm run lint + + - name: Typecheck + run: pnpm run typecheck - - name: Run tests + - name: Test run: pnpm run test:ci - # Runs the leanWorkerBundle generateBundle assertion: fails if a - # denylisted package (dataforseo-client, autumn-js, ...) re-enters the - # worker's eager startup graph. See vite-plugin-lean-worker-bundle.ts. - name: Build worker (eager-bundle guard) - run: pnpm vite build + run: pnpm run build + + # Aggregated gate: prettier + knip + tsc (badseo) + oxlint + + # sync-plugin-skills. Catches drift the four named scripts above + # don't (formatting, dead-code, generated plugin skills, badseo + # typecheck). Runs after the named gates so a name-gate failure + # surfaces first. + - name: Aggregated checks + run: pnpm run ci:check + + web: + name: "Website typecheck + build" + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install root dependencies + run: pnpm install --frozen-lockfile + + # The `web/` workspace is the static marketing + docs site + # (Vite + Fumadocs). It has its own lockfile and toolchain, so we + # install it explicitly. The root `lint` script excludes `web/` + # (see .oxlintrc.json ignorePatterns), which is why these steps + # live in a separate job. - name: Install website dependencies run: pnpm --dir web install --frozen-lockfile - - name: Run website type checks + # Web prettier (`pnpm --dir web run format:check`) is intentionally + # NOT a required check yet — `web/` has 252 pre-existing formatting + # violations on `main`, so adding the gate now would make the web + # job red from day one. Tracked as a separate follow-up; once web/ + # is reconciled, drop this comment and add the gate. + + - name: Typecheck website run: pnpm --dir web run types:check - name: Build website run: pnpm --dir web run build docker-build: + name: "Self-host Docker image build" runs-on: ubuntu-latest steps: diff --git a/package.json b/package.json index 485064a8a..5d7c26407 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,8 @@ "build": "vite build && tsc --noEmit", "lint": "oxlint . --type-aware", "lint:fix": "oxlint . --type-aware --fix", + "typecheck": "tsc --noEmit", + "ci:pr": "pnpm run lint && pnpm run typecheck && pnpm run test:ci && pnpm run build", "preview": "npm run build && vite preview --port 3001", "deploy": "npm run db:migrate:prod && npm run build && wrangler deploy -c dist/open_seo_audit/wrangler.json && wrangler deploy", "deploy:selfhost": "node scripts/selfhost-deploy-preflight.mjs && vite build --mode selfhost && tsc --noEmit && pnpm alchemy deploy --env-file .env.selfhost --stage selfhost",