Skip to content
Merged
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
99 changes: 99 additions & 0 deletions docs/hardening/MIGRATION-SYNC-2026-05-15.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Migration Sync — 2026-05-15

**Branch**: `hardening/migration-sync-2026-05-15`
**Operador**: Claude (sessão Joaquim PO)
**Objetivo**: Eliminar drift entre `supabase/migrations/` (git) e `supabase_migrations.schema_migrations` (prod), causa raiz do gate **Supabase Preview** falhando com `Remote migration versions not found in local migrations directory`.

---

## Resumo executivo

| Métrica | Antes | Depois |
|---|---|---|
| Versions distintas em git (>= 20260514230000) | 17 (mas com timestamps que NÃO casavam com prod) | **17** |
| Versions em prod schema_migrations (>= 20260514230000) | 17 | **17** |
| Paridade git ↔ prod | **❌ 7 desalinhadas + 5 ausentes em prod + 1 duplicata em git** | **✅ 100%** |
| Migrations órfãs (no git mas não em prod) | 5 | 0 |
| Migrations com timestamp errado (git vs prod) | 7 | 0 |
| Arquivos duplicados em git | 1 (`fix_audit_ownership_*`) | 0 |

---

## Operações executadas

### Em prod (via `execute_sql` no Supabase MCP)

1. **Aplicação physical** do efeito da migration `20260515040001_fix_profiles_user_id_definitive` — coluna `profiles.user_id` não existia em prod apesar de o frontend referenciar em 7 lugares (SecurityDashboard, RoleAuditLogPanel, useUserManagement, RecentAuditTable, RoleMigrationPanel, useAutoRevocations). Pré-flight: 8 profiles, 8 auth.users, FK `profiles_id_fkey` íntegra, zero órfãos.
- `ADD COLUMN user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE`
- `UPDATE SET user_id = id` (8/8 backfilled)
- `ADD CONSTRAINT profiles_user_id_key UNIQUE (user_id)`
- `DROP CONSTRAINT profiles_id_fkey`
- `ALTER COLUMN id SET DEFAULT gen_random_uuid()`
- 3 policies "Users can {view,update,insert} their own profile"
- `ENABLE ROW LEVEL SECURITY`

2. **INSERT em `supabase_migrations.schema_migrations`** (ON CONFLICT DO NOTHING) de 5 versões cujo efeito physical já estava em prod mas sem registro:

| Version | Name | Como validei o efeito physical |
|---|---|---|
| `20260515040001` | `fix_profiles_user_id_definitive` | aplicado no passo 1 acima |
| `20260515120000` | `t40_fix_error_advisor_violations` | RLS enabled nas 2 tabelas existentes (das 10 alvo; 8 são `EXCEPTION WHEN undefined_table THEN NULL`) |
| `20260515123000` | `t40b_harden_get_edge_function_secret_acl` | ACL = `{postgres=X/postgres,service_role=X/postgres}` ✓ |
| `20260515130000` | `revoke_org_has_any_members_public` | sem anon/PUBLIC EXECUTE ✓ |
| `20260515150000` | `onda20_fix_t38_regression_and_bilateral_gate` | `is_admin_or_above`/`is_coord_or_above` mantêm EXECUTE TO authenticated ✓ |

### No git (este PR)

**6 renames** (timestamp do filename realinhado pra bater com prod):

| De | Para |
|---|---|
| `20260514230000_onda16_drop_legacy_email_like_admin_policies.sql` | `20260514233703_onda16_drop_legacy_email_like_admin_policies.sql` |
| `20260515000000_onda17_fn_quotes_recalc_subtotal_completo.sql` | `20260514235639_onda17_fn_quotes_recalc_subtotal_completo.sql` |
| `20260515010000_onda18a_quote_isolation_rls.sql` | `20260515005303_onda18a_quote_isolation_rls.sql` |
| `20260515020000_onda18b_backfill_user_organizations.sql` | `20260515005356_onda18b_backfill_user_organizations.sql` |
| `20260515030000_onda19_numeric_precision.sql` | `20260515020250_onda19_numeric_precision.sql` |
| `20260515040000_onda19_followup_track_functions_fix_view_security.sql` | `20260515103945_onda19_followup_track_functions_fix_view_security.sql` |

**1 deleção**: `20260515120000_fix_audit_ownership_orphans_uuid_only.sql` (duplicata de `20260515124035_fix_audit_ownership_orphans_only_uuid_columns.sql` — mesma função SQL, só comentários diferentes; este último já era a versão oficial trackeada em prod).

---

## Causa raiz do drift

Histórico construído ao longo de várias sessões:

1. Migrations aplicadas via `apply_migration` MCP **geram timestamp da hora da execução**, NÃO baseado no filename. Ex.: arquivo `20260515010000_onda18a_quote_isolation_rls.sql` foi aplicado em prod e registrado como version `20260515005303` (timestamp do `apply_migration` call).
2. Quando o filename foi commitado depois no git, ficou desalinhado.
3. PR #229 introduziu placeholders `<timestamp>_applied_to_production.sql` pra resolver versions órfãs em prod — funcionou parcial. Faltaram registros para 5 migrations (incluindo `fix_profiles_user_id_definitive` que nunca rodou physical).
4. Resultado: gate **Supabase Preview** continuou falhando porque (a) git tinha versions que NÃO existiam em prod e (b) prod tinha versions que não tinham arquivo SQL real no git (só placeholder).

---

## Validação final

Em **2026-05-15**, post-execução:

- `SELECT DISTINCT version FROM supabase_migrations.schema_migrations WHERE version >= '20260514230000'` retorna **17 versions**, idênticas (timestamp + nome) às do filename em `supabase/migrations/` na branch deste PR.
- `profiles.user_id` existe, está backfilled (8/8), UNIQUE constraint criada, 3 policies RLS recriadas.
- Pré-prod blockers B-1 a B-9 continuam fechados (validados na auditoria que precedeu esta operação).

---

## Pendências NÃO endereçadas neste PR

| Item | Status | Onde |
|---|---|---|
| 10 itens manuais de go-live (Sentry DSN, MFA, transferir Lovable, etc.) | aguardando PO | auditoria pré-prod de 15/mai |
| Cobertura de testes 26% real vs target 60% | report-only | gate `coverage` (PR #227) |
| F2 PR-B (drop 10 backup tables + 2 `_unif_*`) | pendente decisão | F2 cleanup banco |
| PAT GitHub `github_pat_11BXDMV7Q0CbI9L78vrLi...` exposto no remote VPS | aguardando revogação | manual |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

PAT exposto deve ser revogado IMEDIATAMENTE, não "aguardando".

Token GitHub com prefixo github_pat_11BXDMV7Q0CbI9L78vrLi... está ativo no VPS remoto e representa risco crítico de segurança (write access ao repositório). Status "aguardando revogação" é inaceitável — a revogação deve acontecer AGORA, antes do merge deste PR.

Ação imediata requerida:

  1. Revogar o PAT em GitHub Settings → Developer settings → Personal access tokens
  2. Remover do VPS
  3. Gerar novo PAT com escopo mínimo necessário
  4. Atualizar este doc confirmando revogação + timestamp
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/hardening/MIGRATION-SYNC-2026-05-15.md` at line 90, The document
currently lists the exposed PAT `github_pat_11BXDMV7Q0CbI9L78vrLi...` with
status "aguardando revogação"; revoke that token IMMEDIATELY in GitHub (Settings
→ Developer settings → Personal access tokens), remove any copies from the
remote VPS, generate a new PAT with the minimum required scopes, and update this
file (MIGRATION-SYNC-2026-05-15.md) to replace "aguardando revogação" with
"revogado" plus a confirmation line containing the revocation timestamp and the
actor who performed it; also remove or redact the full token string from the
document.

@cubic-dev-ai cubic-dev-ai Bot May 15, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Do not commit credential/token material in docs. Replace the PAT value with a fully redacted placeholder.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docs/hardening/MIGRATION-SYNC-2026-05-15.md, line 90:

<comment>Do not commit credential/token material in docs. Replace the PAT value with a fully redacted placeholder.</comment>

<file context>
@@ -0,0 +1,99 @@
+| 10 itens manuais de go-live (Sentry DSN, MFA, transferir Lovable, etc.) | aguardando PO | auditoria pré-prod de 15/mai |
+| Cobertura de testes 26% real vs target 60% | report-only | gate `coverage` (PR #227) |
+| F2 PR-B (drop 10 backup tables + 2 `_unif_*`) | pendente decisão | F2 cleanup banco |
+| PAT GitHub `github_pat_11BXDMV7Q0CbI9L78vrLi...` exposto no remote VPS | aguardando revogação | manual |
+
+---
</file context>
Suggested change
| PAT GitHub `github_pat_11BXDMV7Q0CbI9L78vrLi...` exposto no remote VPS | aguardando revogação | manual |
| PAT GitHub `[REDACTED]` exposto no remote VPS | aguardando revogação | manual |
Fix with Cubic


---

## Padrões técnicos seguidos

- HTTP MCP Worker `https://github-mcp-server.adm01.workers.dev/mcp` para PR (git push direto / `gh` CLI / `github_create_pull_request` MCP padrão dão 403).
- Email `claude-code@atomicabr.com.br` no commit (Vercel rejeita outros).
- Squash merge.
- Operações physical no banco via `execute_sql` (NÃO `apply_migration`, que criaria entrada nova no schema_migrations com timestamp errado de novo).

This file was deleted.

Loading