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:
parent
e61e80b6dc
commit
a7a3883bc2
7 changed files with 12 additions and 150 deletions
3
.github/pull_request_template.md
vendored
3
.github/pull_request_template.md
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
)
|
||||
|
|
@ -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)
|
||||
Loading…
Reference in a new issue