From a7a3883bc235215465846d72a01be23547c2f10a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Sat, 1 Aug 2026 01:00:39 -0300 Subject: [PATCH] chore: remove cutover residue (stage scripts, checklist, PR template) Delete dead db:refresh:stage / agent:promote; mark CUTOVER done; drop STAGE_DATABASE_URL checklist item that no longer applies. --- .github/pull_request_template.md | 3 +- docs/AGENT-OPS.md | 2 +- docs/CHANGELOG-AGENTS.md | 2 + docs/CUTOVER-MAIN-ONLY.md | 21 +++--- package.json | 2 - scripts/agent-promote.mjs | 20 ------ scripts/refresh-stage.mjs | 112 ------------------------------- 7 files changed, 12 insertions(+), 150 deletions(-) delete mode 100644 scripts/agent-promote.mjs delete mode 100644 scripts/refresh-stage.mjs diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2a4d59e2..820f2140 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -10,8 +10,7 @@ Closes # - [ ] `pnpm format:check` e `pnpm check:cycles` limpos - [ ] Órfãos da mudança deletados na mesma entrega (knip em CI confirma) - [ ] **Se tocou schema** (migration, collection, campo, Consent key, dado de boot): `db:seed:minimal` atualizado **neste PR** (`scripts/lib/seed-minimal-manifest.mjs` + pin unit) — senão a label `needs:migration` está errada -- [ ] Nenhum `DATABASE_URL`/`ALLOW_REMOTE_DB` de stage/prod em script ou workflow -- [ ] `pnpm build` NÃO aparece em workflow que carregue `STAGE_DATABASE_URL` +- [ ] Nenhum `DATABASE_URL`/`ALLOW_REMOTE_DB` de prod em script ou workflow - [ ] Se muda o que o agente precisa saber sempre: `AGENTS.md`/kernels atualizados; entregas registram UMA entrada curta em `docs/CHANGELOG-AGENTS.md` (nunca no AGENTS.md) ## Notas para review diff --git a/docs/AGENT-OPS.md b/docs/AGENT-OPS.md index 1e2c8f65..d7ff20f9 100644 --- a/docs/AGENT-OPS.md +++ b/docs/AGENT-OPS.md @@ -46,7 +46,7 @@ main → ci.yml full suite → vercel deploy --prod (se verde) → requeue se HE | `pnpm configure:branch-protection [-- --dry-run]` | Proteção de `main`: `checks` + `migration-lock`, `strict=false`, 0 reviews | | `pnpm db:seed:minimal` | DB mínimo sintético | -`pnpm agent:promote` e `pnpm db:refresh:stage` são **legado** (recusam / não fazem parte do fluxo). Labels: `ready|in-progress|blocked|done|in-prod`, `prio:*`, `kind:*`, `needs:*`. +Labels: `ready|in-progress|blocked|done|in-prod`, `prio:*`, `kind:*`, `needs:*`. ## Contrato de PR diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index 3b9d79cf..de7a8619 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente --- +**Recently resolved (2026-08-01):** **Cutover residue cleanup** — removed dead `pnpm db:refresh:stage` / `scripts/refresh-stage.mjs` and `pnpm agent:promote` / `scripts/agent-promote.mjs`; closed `docs/CUTOVER-MAIN-ONLY.md`; scrubbed PR template `STAGE_DATABASE_URL` checklist item. Branch + Environment `stage` already deleted. + **Recently resolved (2026-08-01):** **PRs de agente = Ready + auto-merge (audit incluso)** — removida a exceção do `engineering-audit` (artefatos também armam `gh pr merge --auto --merge`). Safety net GHA `agent-pr-ready-automerge.yml`: PRs `cursor/*` → `main` Draft→Ready + auto-merge. Rule always-on `agent-pr-workflow.mdc`. **Recently resolved (2026-07-31):** **Cloud `ensure-postgres` falso negativo no start** — o script tratava “role/DBs `teqo` prontos” como “Postgres aceitando conexões”. Em snapshot warm o cluster já está `online` sem role/DBs; `start_postgres` falhava com `postgres is installed but not accepting connections` **antes** de `bootstrap_databases`, o `start` do `environment.json` saía ≠0 e a UI marcava `INSTALL_FAILED` mesmo com o agente utilizável. Fix: separar `server_ready` (`pg_isready` / peer) de `teqo_ready`, bootstrap após o server up, retry no wait; `cloud-setup.sh` pina `DATABASE_URL` e asserta `payload_migrations` pós-migrate (no-op silencioso visto no JIT). Ruído cosmético no mesmo log (dirs de recording, `.installed` de plugins ENOENT) é race do exec-daemon — não é do Teqo. diff --git a/docs/CUTOVER-MAIN-ONLY.md b/docs/CUTOVER-MAIN-ONLY.md index fad0b429..f04a4d51 100644 --- a/docs/CUTOVER-MAIN-ONLY.md +++ b/docs/CUTOVER-MAIN-ONLY.md @@ -1,20 +1,15 @@ -# Cutover checklist (main-only + gated deploy) — human, once +# Cutover checklist (main-only + gated deploy) — done -Status after agent delivery (2026-07-31): +Completed 2026-08-01. Branch `stage` and GitHub Environment `stage` are deleted. Production deploys only from `ci.yml` after the full verifier is green. -- [x] Code PR opened (main-only cutover) — see PR targeting `main` +- [x] Code PR opened (main-only cutover) — merged to `main` - [x] `pnpm configure:branch-protection` applied on `main` (required: `checks` + `migration-lock`, `strict=false`, 0 reviews) - [x] No open PRs targeting `stage` at cutover time - [x] Repo secrets present (`VERCEL_*`, `POOL_GITHUB_TOKEN`) - [x] `VERCEL_ORG_ID` / `VERCEL_PROJECT_ID` synced from local `.vercel/project.json` (`vercel link`) -- [ ] Confirm `ci.yml` deploy job green after token+IDs are correct - - Misleading CLI error _"Could not retrieve Project Settings"_ usually means **403** (bad/expired `VERCEL_TOKEN` or wrong IDs) — regenerate token at vercel.com/account/tokens if deploy still fails +- [x] `ci.yml` deploy job green (`vercel@55`, Node `24.x`, alias https://pt.jorgesolla.com.br) +- [x] Branch `stage` deleted +- [x] GitHub Environment `stage` (and `STAGE_*` secrets) deleted +- [x] Dead stage tooling removed (`db:refresh:stage`, `agent:promote`) -After the cutover PR merges to `main`: - -1. Validate Vercel Git skip: push to `main` must **not** start a Vercel Git build (`ignoreCommand` exit 0). Production comes only from `ci.yml` → deploy. -2. Confirm `ci.yml` runs full suite; deploy job runs or fails closed with a clear missing-secrets message until `VERCEL_*` are set. -3. `POOL_GITHUB_TOKEN=… pnpm agent:pool -- doctor` must read repo variables. -4. Optionally delete branch `stage` and its Environment/secrets when idle. - -See [AGENT-OPS.md](AGENT-OPS.md). +Operational reference: [AGENT-OPS.md](AGENT-OPS.md). diff --git a/package.json b/package.json index a3071ddd..b7e13fe6 100644 --- a/package.json +++ b/package.json @@ -31,12 +31,10 @@ "db:seed:consent": "cross-env NODE_OPTIONS=\"--no-deprecation --import=tsx/esm --import=./scripts/seed-loader.mjs\" node scripts/seed-consent.mjs", "db:seed:projecao": "cross-env NODE_OPTIONS=\"--no-deprecation --import=tsx/esm --import=./scripts/seed-loader.mjs\" node scripts/import-projecao.mjs", "db:seed:minimal": "cross-env NODE_OPTIONS=\"--no-deprecation --import=tsx/esm --import=./scripts/seed-loader.mjs\" node scripts/seed-minimal.mjs", - "db:refresh:stage": "node scripts/refresh-stage.mjs", "agent:claim": "node scripts/agent-claim.mjs", "agent:register": "node scripts/agent-register.mjs", "agent:prioritize": "node scripts/agent-prioritize.mjs", "agent:file-miss": "node scripts/agent-file-miss.mjs", - "agent:promote": "node scripts/agent-promote.mjs", "agent:pool": "node scripts/agent-pool.mjs", "configure:branch-protection": "node scripts/configure-branch-protection.mjs", "build:demographics": "cross-env NODE_OPTIONS=\"--no-deprecation --import=tsx/esm\" node scripts/build-bahia-demographics.mjs", diff --git a/scripts/agent-promote.mjs b/scripts/agent-promote.mjs deleted file mode 100644 index ef6eb2fc..00000000 --- a/scripts/agent-promote.mjs +++ /dev/null @@ -1,20 +0,0 @@ -/** - * `pnpm agent:promote` — REMOVED (main-only cutover). - * - * Feature PRs merge directly to `main`. Production deploy is gated by `ci.yml` - * (`vercel deploy --prod` after the full suite). There is no stage→main promote. - * - * See docs/AGENT-OPS.md. - */ - -import { dieAgent, parseArgs } from './lib/agent-github.mjs' - -const die = dieAgent('promote') -parseArgs(process.argv.slice(2), new Set()) - -die( - 'Removed: stage→main promote no longer exists.\n' + - ' Open PRs with `gh pr create --base main`.\n' + - ' Production deploy runs from `.github/workflows/ci.yml` after the full verifier is green.\n' + - ' Details: docs/AGENT-OPS.md', -) diff --git a/scripts/refresh-stage.mjs b/scripts/refresh-stage.mjs deleted file mode 100644 index 547e21c3..00000000 --- a/scripts/refresh-stage.mjs +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Refresh the stage database: create a NEW Neon branch from the current prod - * snapshot and swap the `STAGE_DATABASE_URL` secret in the GitHub `stage` - * Environment. Human / `workflow_dispatch` only — agents must never run this - * (it touches the prod Neon project). - * - * Refresh is "new branch + swap secret", never DELETE/INSERT of real data: - * Neon branches are copy-on-write snapshots of prod at creation time. - * - * Requirements: - * NEON_API_KEY — Neon API key with access to the prod project (get one at - * https://console.neon.tech/app/settings/api-keys). Never - * committed; export it in your shell or pass via env. - * gh — authenticated GitHub CLI (updates the secret). - * - * Usage: - * NEON_API_KEY=… pnpm db:refresh:stage # refresh + swap secret - * NEON_API_KEY=… pnpm db:refresh:stage -- --delete-old # also delete the previous stage branch - * NEON_API_KEY=… node scripts/refresh-stage.mjs --dry-run - */ - -import { dieWithLabel } from './lib/cli.mjs' - -const LABEL = 'refresh:stage' -const die = dieWithLabel(LABEL) - -const PROJECT_ID = process.env.NEON_PROJECT_ID -if (!PROJECT_ID) { - die('Set NEON_PROJECT_ID (Neon console → project id).') -} -const DATABASE_NAME = 'neondb' -const ROLE_NAME = 'neondb_owner' -const API = 'https://console.neon.tech/api/v2' - -const args = new Set(process.argv.slice(2)) -const DRY_RUN = args.has('--dry-run') -const DELETE_OLD = args.has('--delete-old') - -const apiKey = process.env.NEON_API_KEY -if (!apiKey) { - die( - 'NEON_API_KEY is not set. This script is human-only: it creates a branch on the PROD Neon ' + - 'project and swaps the stage secret. Export a Neon API key and re-run.', - ) -} - -const request = async (method, path, body) => { - const response = await fetch(`${API}${path}`, { - method, - headers: { authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' }, - ...(body ? { body: JSON.stringify(body) } : {}), - }) - if (!response.ok) { - die(`Neon API ${method} ${path} → ${response.status}: ${await response.text()}`) - } - return response.json() -} - -const log = (message) => console.log(`[${LABEL}] ${message}`) - -// 1. Find the prod default branch (the snapshot source) and the current stage branch. -const { branches } = await request('GET', `/projects/${PROJECT_ID}/branches`) -const prodBranch = branches.find((branch) => branch.default) -if (!prodBranch) die(`No default branch found on project ${PROJECT_ID}.`) -const oldStage = branches.find((branch) => branch.name === 'stage') -log(`prod branch: ${prodBranch.id} (${prodBranch.name})`) -if (oldStage) log(`current stage branch: ${oldStage.id}`) - -if (DRY_RUN) { - log('dry-run — would create a new `stage` branch from prod and swap STAGE_DATABASE_URL.') - process.exit(0) -} - -// 2. Replace the old stage branch with a fresh one from prod (delete first so -// the name is free; the old branch is a stale snapshot, not data we keep). -if (oldStage) { - await request('DELETE', `/projects/${PROJECT_ID}/branches/${oldStage.id}`) - log(`deleted old stage branch ${oldStage.id}`) -} - -const timestamp = new Date().toISOString().slice(0, 10) -const created = await request('POST', `/projects/${PROJECT_ID}/branches`, { - branch: { name: 'stage', parent_id: prodBranch.id }, - endpoints: [{ type: 'read_write' }], -}) -const stageBranch = created.branch -log(`created stage branch ${stageBranch.id} from prod snapshot (${timestamp})`) - -// 3. Resolve the new connection string. UNPOOLED on purpose: the int suite -// (and Payload's own advisory locks) need real session state, which the -// transaction-mode pooler does not guarantee — the suite hung and GitHub -// cancelled the run at the 1-hour mark on 2026-07-30 with the pooled URL. -const { uri } = await request( - 'GET', - `/projects/${PROJECT_ID}/connection_uri?branch_id=${stageBranch.id}` + - `&database_name=${DATABASE_NAME}&role_name=${ROLE_NAME}&pooled=false`, -) -log(`new stage connection string: ${uri.replace(/:\/\/[^@]*@/, '://***@')}`) - -// 4. Swap the GitHub secret in the `stage` Environment. -const { execFileSync } = await import('node:child_process') -execFileSync('gh', ['secret', 'set', 'STAGE_DATABASE_URL', '--env', 'stage', '--body', uri], { - stdio: 'inherit', -}) -log('STAGE_DATABASE_URL updated in the GitHub `stage` environment.') - -if (DELETE_OLD && oldStage) { - log('note: --delete-old is a no-op now — the old branch was already replaced in step 2.') -} - -log('OK — stage database refreshed. Next ci-stage run uses the new snapshot.') -process.exit(0)