Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .agents/PAPERCUTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.)
Expand All @@ -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

Expand Down
55 changes: 55 additions & 0 deletions .agents/PROPOSED-AGENTS-MD.md
Original file line number Diff line number Diff line change
@@ -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 `<scope>/<short-desc>` 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: `[<scope>] <imperative summary>` (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.
72 changes: 64 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down