fix(cloud): ensure-postgres bootstrap before teqo readiness check

Warm Cloud snapshots often have PostgreSQL online without the teqo
role/DBs. start_postgres previously gated on teqo_ready, exited before
bootstrap_databases, and marked the environment INSTALL_FAILED even
though agents still ran. Also pin DATABASE_URL and assert migrations
in cloud-setup so a silent no-op migrate cannot slip through.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-07-31 19:27:40 +00:00 committed by Francisco Ignácio Fontoura Solla
parent 7a876ec334
commit 503a775b9f
3 changed files with 66 additions and 15 deletions

View file

@ -20,16 +20,35 @@ if [ ! -f .env ]; then
echo "[cloud-setup] wrote .env from .env.example (local-only values)"
fi
TEQO_URL='postgresql://teqo:teqo@localhost:5432/teqo'
TEQO_TEST_URL='postgresql://teqo:teqo@localhost:5432/teqo_test'
assert_migrated() {
local db_url="$1"
local label="$2"
local count
count="$(PGPASSWORD=teqo psql "${db_url}" -Atc 'SELECT COUNT(*) FROM payload_migrations' 2>/dev/null || echo 0)"
if [ "${count}" -lt 1 ]; then
echo "[cloud-setup] ERROR: ${label} has no payload_migrations after migrate (got '${count}')" >&2
exit 1
fi
echo "[cloud-setup] ${label}: ${count} migrations applied"
}
# Dev database (teqo) and test database (teqo_test).
# Pin DATABASE_URL on both — do not rely on dotenv alone (JIT snapshots have
# seen a silent no-op migrate when the URL was only in a freshly written .env).
echo "[cloud-setup] migrating teqo..."
pnpm migrate
DATABASE_URL="${TEQO_URL}" pnpm migrate
assert_migrated "${TEQO_URL}" teqo
echo "[cloud-setup] migrating teqo_test..."
DATABASE_URL=postgresql://teqo:teqo@localhost:5432/teqo_test pnpm migrate
DATABASE_URL="${TEQO_TEST_URL}" pnpm migrate
assert_migrated "${TEQO_TEST_URL}" teqo_test
echo "[cloud-setup] seeding teqo..."
pnpm db:seed:minimal
DATABASE_URL="${TEQO_URL}" pnpm db:seed:minimal
echo "[cloud-setup] seeding teqo_test..."
DATABASE_URL=postgresql://teqo:teqo@localhost:5432/teqo_test pnpm db:seed:minimal
DATABASE_URL="${TEQO_TEST_URL}" pnpm db:seed:minimal
echo "[cloud-setup] OK — minimal database ready (dev + test)."
echo "[cloud-setup] gate:fast/gate:push need no DB; use pnpm push (not raw git push --no-verify)."

View file

@ -1,15 +1,27 @@
#!/usr/bin/env bash
# Cursor Cloud VMs have no Docker and cannot run docker compose (overlayfs fails inside
# the agent container). Native PostgreSQL matches the local DATABASE_URL contract.
#
# Important: "server accepting connections" ≠ "teqo role/DBs exist". A warm snapshot
# often has the cluster online with no app databases yet. Never gate cluster start on
# teqo readiness — bootstrap runs after the server is up.
set -euo pipefail
PG_VERSION="${PG_VERSION:-16}"
PG_CLUSTER="${PG_CLUSTER:-main}"
# pg_ctlcluster / systemctl can report "started" before the postmaster accepts TCP.
READY_ATTEMPTS="${PG_READY_ATTEMPTS:-15}"
READY_SLEEP_SECS="${PG_READY_SLEEP_SECS:-1}"
pg_ready() {
teqo_ready() {
PGPASSWORD=teqo psql -h localhost -U teqo -d teqo -c 'SELECT 1' >/dev/null 2>&1
}
server_ready() {
pg_isready -h localhost -p 5432 >/dev/null 2>&1 \
|| sudo -u postgres psql -c 'SELECT 1' >/dev/null 2>&1
}
detect_pg_version() {
if [ -d "/etc/postgresql" ]; then
ls /etc/postgresql/ 2>/dev/null | sort -nr | head -1
@ -18,8 +30,19 @@ detect_pg_version() {
echo "${PG_VERSION}"
}
wait_for_server() {
local attempt
for attempt in $(seq 1 "${READY_ATTEMPTS}"); do
if server_ready; then
return 0
fi
sleep "${READY_SLEEP_SECS}"
done
return 1
}
start_postgres() {
if pg_ready; then
if server_ready; then
return 0
fi
@ -28,15 +51,17 @@ start_postgres() {
if command -v pg_ctlcluster >/dev/null 2>&1 && [ -n "${version}" ]; then
sudo pg_ctlcluster "${version}" "${PG_CLUSTER}" start 2>/dev/null || true
sleep 2
pg_ready && return 0
if wait_for_server; then
return 0
fi
fi
if command -v systemctl >/dev/null 2>&1; then
sudo systemctl start "postgresql@${version}-${PG_CLUSTER}" 2>/dev/null || \
sudo systemctl start postgresql 2>/dev/null || true
sleep 2
pg_ready && return 0
if wait_for_server; then
return 0
fi
fi
echo "[ensure-postgres] ERROR: postgres is installed but not accepting connections" >&2
@ -59,10 +84,17 @@ WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'teqo_test')\gexec
SQL
}
if command -v psql >/dev/null 2>&1 && [ -d "/etc/postgresql" ]; then
ensure_ready() {
start_postgres
bootstrap_databases
pg_ready
if ! teqo_ready; then
echo "[ensure-postgres] ERROR: teqo databases exist but connection as teqo/teqo failed" >&2
exit 1
fi
}
if command -v psql >/dev/null 2>&1 && [ -d "/etc/postgresql" ]; then
ensure_ready
echo "[ensure-postgres] OK (existing cluster)"
exit 0
fi
@ -70,7 +102,5 @@ fi
echo "[ensure-postgres] installing postgresql-${PG_VERSION} (Ubuntu package; local dev uses PG 17 via Docker)..."
sudo apt-get update -qq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}"
start_postgres
bootstrap_databases
pg_ready
ensure_ready
echo "[ensure-postgres] OK"

View file

@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente
---
**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.
**Recently resolved (2026-07-31):** **Pass 4 — auditoria de engenharia autônoma + guardrails das misses** — primeira execução em modo autônomo (Cursor Cloud) da skill engineering-audit, com precheck solitário (pool desligado) e primeiro harvest de `kind:agent-miss` (escopo OPS2: nasce `docs/GUARDRAILS.md`). **P0:** os 4 globals públicos (`site-settings`/`home`/`metadata`/`privacy-policy`) aceitavam qualquer autenticado no `update` — JWT de campanha (liderança inclusa) editava o site público via `/api/globals/*`; lockdown `payloadAdminOnly` + pins + guarda ([PR #91](https://github.com/fsolla/teqo/pull/91); editores perdem escrita nos globals de config deliberadamente). **P1:** `seed-posts.mjs` chamava `dieWithLabel` sem importar ([PR #92](https://github.com/fsolla/teqo/pull/92), com guarda de presença de import + `DAY_MS` single-source + ESLint lib→utilities sem type-import + guarda de `overrideAccess` omitido). **Misses:** #73 (deadlock `municipality_rels` — classe reproduzida: 7 violações int + 11 deep links e2e; migração para o alocador com teste de classe reescrito classe-agnóstico + guarda de 3 regras, [PR #93](https://github.com/fsolla/teqo/pull/93)); #52 (Drawer por célula em 3 listas de chips → `CampaignListSheetProvider` + invariante dev + guarda, [PR #94](https://github.com/fsolla/teqo/pull/94)); #53 (portão de readiness do autenticador virtual) + #54 (guarda de goto não assentado + convenção TESTING.md) em [PR #95](https://github.com/fsolla/teqo/pull/95); #83 fechada (guarda `agent:ship` já viva). **Registrado, não retry-fixed:** spec de chips de `campaignLeaderships` falha deterministicamente em dev na árvore limpa (nenhum POST dispara; prod 48/48 verde) — causa raiz via debug workflow (P4-L). Artefatos: seção Pass 4 no plano de qualidade, 15 linhas novas no ledger (2 stale fechadas: P3-A/P3-B), plano [`entrega-engenharia-p4.md`](docs/plans/entrega-engenharia-p4.md) com ondas P4-A→P4-L + mapa de guardas + defer+gatilho.
**Recently resolved (2026-07-31):** **CI build skip para diffs fora da árvore buildável** — `scripts/build-affected.mjs` + `classifyBuildScope` (núcleo em `test-affected-core.mjs`) decidem `run|none`; job `build-scope` barato no `ci-pr.yml` e o job `build` (Postgres + `pnpm build`) só roda em `mode=run`. Docs/tests/tooling/workflows pulam o build; `src/`, `public/` e configs do Next (`next.config`/`tsconfig`/`tailwind`/`postcss`/`components.json`/lockfile) forçam. `gate:ci` espelha. Push em `main` (`ci.yml`) continua sempre full.