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.
This commit is contained in:
Francisco Ignácio Fontoura Solla 2026-08-01 01:00:39 -03:00
parent e61e80b6dc
commit a7a3883bc2
7 changed files with 12 additions and 150 deletions

View file

@ -10,8 +10,7 @@ Closes #<!-- número da Issue (claim) -->
- [ ] `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

View file

@ -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

View file

@ -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.

View file

@ -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).

View file

@ -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",

View file

@ -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',
)

View file

@ -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)