Commit graph

257 commits

Author SHA1 Message Date
Cursor Agent
dfa080f74d
B89 — Ações rápidas em Contatos (liderança)
Wire the B79 drawer on /campanha/contatos for leader role with the B45
catalog (register-supporter, my-contacts). Actions use in-page hash anchors
instead of staff home-search; no drawer on Início (existing B79 gate).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 05:27:45 +00:00
Cursor Agent
a7ecdbc844
chore: format B88 quick-action files 2026-08-01 05:12:11 +00:00
Cursor Agent
9b76f19b85
B88 — Ações rápidas em Organizações (lista + detalhe)
Registra /campanha/organizacoes no registry B79 com launcher
new-organization na lista; detalhe vazio até existir filtro de
lideranças por org ou município óbvio único.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 05:11:31 +00:00
Cursor Agent
bdbab71112
fix(test): retry municipality pair when word-start queries collide 2026-08-01 04:52:17 +00:00
Cursor Agent
2cd1e7cfd7
fix(test): retry municipality allocation until word-start queries are distinct 2026-08-01 04:49:36 +00:00
Cursor Agent
b7ad9ad326
merge: sync with main (B80 municipalities quick actions) 2026-08-01 04:44:03 +00:00
Cursor Agent
39b9065ad2
style: Prettier after rebase merge 2026-08-01 04:35:38 +00:00
Cursor Agent
66e84ab85c
fix: resolve rebase conflict markers in quick-action unit spec 2026-08-01 04:35:38 +00:00
Cursor Agent
eea2d9afa1
B84 — Ações rápidas na vertical Atividades (lista + detalhe)
Registra catálogo B84 no registry B79: Nova atividade e Planejar giro na
lista; wizards A1–A5 com ?municipio= no detalhe quando há município, mais
atalhos Editar/Tarefas/Atualizações. Context bridge no detalhe; drawer
omitido em /atividades/giros (compositor E13 já tem CTAs).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 04:35:38 +00:00
Cursor Agent
3463389942
style: prettier registry 2026-08-01 04:22:25 +00:00
Cursor Agent
6f4402dab6
style: prettier 2026-08-01 04:21:25 +00:00
Cursor Agent
6030d42e2f
B80 — Ações rápidas em Municípios (lista + detalhe A1–A5)
Registra catálogo staff no drawer B79 para /campanha/municipios e
/campanha/municipios/[slug]: lista espelha Início (sem prefill); detalhe
prefill ?municipio= nos wizards A1–A5 e omite Ver esquecidos.

Closes #16

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 04:21:15 +00:00
Cursor Agent
09e76ec8cb
B81 — format 2026-08-01 04:10:16 +00:00
Cursor Agent
7f2ac29368
B81 — simplify: path constant, role cache, href parity test
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 04:09:35 +00:00
Cursor Agent
8c9eeaf273
B81 — register staff Início quick actions on /campanha/territorios
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-01 04:06:22 +00:00
Cursor Agent
18fe4f8e9b
fix(agent-pool): ban -fast model slugs
Never map or request fast=true. Slugs ending in -fast are stripped to
the base (with a warn); model-selection and the pool plan document the ban.
2026-08-01 03:34:35 +00:00
Cursor Agent
629364f3e4
fix(agent-pool): map Grok effort + Kimi reasoning slugs to Cloud API
cursor-grok-4.5-{low,medium,high}[-fast] and kimi-k3-low were falling
back to composer-2.5 against live /v1/models (grok-4.5 + effort,
kimi-k3 + reasoning). Teach resolvePoolModel the mapping, document
when to pick each Grok effort in model-selection, and align plan/work
skills.
2026-08-01 03:32:41 +00:00
Francisco Ignácio Fontoura Solla
6c90d76f10 test: pin exported skip predicates so knip stays clean 2026-07-31 21:09:20 -03:00
Francisco Ignácio Fontoura Solla
f021992de0 ci: main-only cutover with gated Vercel deploy
Remove stage/promote/Neon smoke. PR CI targets main; ci.yml full-suite
verifier deploys via vercel CLI and requeues when HEAD moves. Pool uses
POOL_GITHUB_TOKEN and POOL_BASE_REF=main. Docs/skills/rules retargeted.
2026-07-31 20:57:18 -03:00
Francisco Ignácio Fontoura Solla
42d1a7be93 ci(pr): prettier + ignore local tool artifacts in format check 2026-07-31 20:57:18 -03:00
Francisco Ignácio Fontoura Solla
9d810d1564 ci(pr): cascade phase1→phase2 with blast-radius skips
Add scope classifiers for code/build jobs, always-on test-location
guardrails, and gate:ci parity. Default affected base is origin/main.
2026-07-31 20:57:18 -03:00
Cursor Agent
7a876ec334 refactor(notifications): bell loaders read through owner access, no bypass
countUnreadNotifications/loadNotificationList took a caller-supplied
recipientID with module-level admin bypass — one misuse away from reading
someone else's inbox. They now take the actor (CampaignUser) and read
with overrideAccess: false under canReadOwnNotifications, keeping the
explicit recipient filter as a second fence. Pin: owner isolation at the
loader level in campaignNotifications.int.spec.ts.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-31 20:57:18 -03:00
Cursor Agent
6705db80eb security: lock public globals update to payloadAdminOnly (Pass 4 P0)
The four public globals (site-settings, home, metadata, privacy-policy)
spelled update access as "any authenticated user" — and campaign JWTs
authenticate against /api/*, so any campaign session (leader included)
could PATCH the public site. payloadAdminOnly matches the collection
lockdown precedent; editors lose config-global writes deliberately
(content editing stays on canManagePublishedContent collections).

Pins: identity + deny (coordinator/editor) + allow (admin fn) + anonymous
read in collectionAccessLockdown.int.spec.ts. Recurrence guard:
globalAccessConventions.unit.spec.ts bans the Boolean(user) shape in
src/globals.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-31 20:57:18 -03:00
Cursor Agent
84fed02ee5 test(miss-54): deterministic guard for unsettled goto→goto + nav convention
The SavedFilters incident shape — a page.goto fired while the previous
heavy RSC navigation was still in flight (net::ERR_ABORTED) — gets a
build-level guard: two gotos with no settle between them (await expect /
waitForURL / click / reload / login / catch) fail the suite. Measured
baseline on the current tree: 58 gotos across 17 specs, 0 unsettled
pairs — the six SOFT pairs from the miss audit all settle on awaited
assertions, so the guard starts clean without churning them.

TESTING.md records the convention the miss asked for: goto for cold
loads and URL contracts; in-shell navigation for continuity scenarios;
interactive-state waits before dynamic clicks; a dev-mode caveat for the
campaignLeaderships chip spec (deterministic dev failure at the
suggestion/commit step, prod green — ledgered).
2026-07-31 20:57:18 -03:00
Cursor Agent
5c59793e4c test(miss-53): gate WebAuthn ceremonies on authenticator readiness
The app's biometrics probe is one-shot per island mount: when
isUserVerifyingPlatformAuthenticatorAvailable() resolves before the CDP
virtual authenticator answers, the enrollment form / biometric button
never mount and no wait flips them (prod-build CI flake). New
expectCampaignBiometricsReady(page) polls the platform answer BEFORE the
surface whose island probes it; adopted at both ceremony entries in
campaignWebAuthn (verified green locally). Conventions pin:
e2eNavigationConventions fails the build when a virtual-authenticator
spec skips the readiness gate.
2026-07-31 20:57:18 -03:00
Francisco Ignácio Fontoura Solla
b1a34a8c3a fix(tests): allocate homeSearch municipalities; scope B42 trend drawer 2026-07-31 20:57:17 -03:00
Cursor Agent
e51a1dc9b2 style: prettier campaignHomeActions after URL anchor edits 2026-07-31 20:57:17 -03:00
Cursor Agent
03314e620a test(miss-73): deterministic allocator guardrails in conventions spec
Recurrence prevention for the deadlock class, three rules:
1. a spec file calling getMunicipality(<arg>) while mutating
   municipalities (assign/touch/create*/payload.update municipality)
   fails the build — the class miss #73 named;
2. hardcoded municipio=<slug> deep links banned in tests/e2e —
   claimMunicipality().slug is the only source;
3. e2e name: /.../ regex literals containing a catalog municipality name
   must be ^-anchored — the literal-regex dodge of the P3-C template
   guard, keyed on the catalog so copy regexes stay untouched.
2026-07-31 20:57:17 -03:00
Cursor Agent
6b894a04c5 test(miss-73): allocate municipalities, never pin seeded slugs (deadlock class)
Int specs pinning seeded rows (cairu/feira-de-santana/catalog[0..2] or
class-derived fixed slugs) and mutating them deadlocked municipality_rels
under the parallel suite (40P01); e2e specs deep-linking ?municipio=cairu
raced the same row from two workers.

- homeSearchStateDeputies: 2 fixed slugs -> 2 allocations.
- municipalityPageData classe test: rewritten class-AGNOSTIC — allocates
  rows until two distinct territorial classes (same pinned behavior:
  filter narrows, totals honest, classe sort = descending weight), no
  fixed reduto/marginal slugs. nivel test + facet test: plain allocations.
- campaignHomeActions/campaignWizardChrome: 11 hardcoded cairu deep links
  -> claimMunicipality(); hit clicks disambiguate by href (catalog names
  are not prefix-unique); caption/URL assertions use ^-anchored full-URL
  regexes (P3-C template guard). /Cairu/i literals gone.

Verified: int 17/17, e2e 18/18 (dev mode), unit 1106/1106.
2026-07-31 20:57:17 -03:00
Francisco Solla
7a66fe4f25
Merge pull request #5 from fsolla/cursor/p4-shared-sheet-host-78a7
Miss #52 guardrail: shared sheet host on the chip-relation lists + fail-loud guard
2026-07-31 13:21:48 -03:00
Cursor Agent
c83cf28497 test(miss-52): sheet cells without the shared host fail loud
- CampaignCellEditOverlay: dev-mode console.error when a variant=sheet
  cell mounts without CampaignListSheetProvider (prod keeps the
  lazy-mount fallback for resilience; unit tests exercise it, so the
  invariant is development-only).
- sharedSheetHostConventions.unit.spec.ts: any tsx rendering
  <MunicipalityPortfolioCell / <LeadershipStateDeputyRelationCell must
  reference CampaignListSheetProvider — the next chip table cannot
  reintroduce per-cell Drawers by omission.
2026-07-31 13:17:19 -03:00
Cursor Agent
9ae062c77c refactor(lib): close the lib→utilities type-import hole + Local API omission guard
- suggestionCatalog type-imports MunicipalityTerritorialClass from
  lib/territorialClassAnchors (the single source) instead of the
  utilities re-export — direction inversion introduced in Pass 3.
- ESLint src/lib zone: @/utilities|components|app restriction now rejects
  type imports too (allowTypeImports: false); shared types live in lib
  contract modules by policy.
- engagementLevel imports DAY_MS from lib/text (fold missed in P3-K).
- searchHomeAdvisors: delete stale 'Intentional admin bypass' comment —
  the call is overrideAccess: false under the staff gate.
- New guard localApiOverrideAccessConventions.unit.spec.ts: payload calls
  with user must declare overrideAccess (baseline measured: 59 calls, 0
  missing), and runtime dynamic import/require of payload/next server
  modules is banned in utilities (dodges the server-only sweep).
2026-07-31 13:17:10 -03:00
Cursor Agent
0991c04cd7 fix(seed-posts): import dieWithLabel before calling it (Pass 4 P1)
The P3-H CLI consolidation left seed-posts calling dieWithLabel with no
import — a ReferenceError on the empty-fetch abort path, invisible to
lint/typecheck/knip. Recurrence guard in the same delivery:
scriptCliConventions.unit.spec.ts fails the build when a script calls
dieWithLabel/dieAgent without importing (or defining) it, and bans
re-spelled 'const DAY_MS =' outside src/lib/text.ts (P3-K's fold leaked
one private copy in engagementLevel.ts).
2026-07-31 13:17:10 -03:00
Francisco Solla
8dd60cf962 A6 — Dobradinha 2026 automática quando o TSE publicar candidaturas (pós-15/08) (#82)
* docs(A6): freshness audit da #27 no plano de dobradinhas 2026

electionInsights.ts foi deletado no Pass 2 W4a (modulo puro novo),
electionAlliances.ts nao nasce (campoParties + electionPartySpectrum ja
carregam a taxonomia), questoes em aberto resolvidas (tiers, score,
proporcionais, lockdown de lideranca), card na aba Eleicoes, overview da
lista fora de escopo, cache election-tse com bust na conciliacao Fase 5.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* feat(A6): insight de dobradinhas 2026 na aba Eleicoes do municipio

Lista ranqueada de candidatos proporcionais que concorrem de novo em 2026
(runningAgain2026=sim) por geografia do municipio: score 0,6 alinhamento
(campo 2026 + espectro Bolognesi) + 0,4 forca local normalizada pelo maximo
do conjunto. Estado pendente ate a conciliacao pos-15/08; Solla excluido
(referencia, nao parceiro); leitura derivada sem escrita; loader cacheado
sob a tag election-tse como os irmaos; link para /campanha/dobradinhas
como caminho de registro operacional.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* test(A6): cobertura unit/int do insight + lease das colecoes eleitorais

Unit: tiers (campo/espectro/falha-para-o-meio), score, ordenacao, empates,
limite, zero votos. Int: pending2026 sem conciliacao (mesmo com votos
locais), so adversarios, misto com exclusao de Solla/nao e geografia
alheia. Lease advisory election-collections serializa os dois specs que
tratam as colecoes eleitorais como test-owned (o import wipe-all apagaria
as fixtures do novo spec no meio do assert, 48-way paralelo).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* refactor(A6): fixes do /simplify (3 reviewers paralelos)

P1: campo coalition morto removido do input/select/mapping (sem consumidor).
P1: hooks do lease com timeout explicito de 120s nos dois specs eleitorais
(o wait advisory passa dos 10s default do Vitest sob contencao — flake em
ambas as direcoes). P2: sonda de conciliacao via payload.count (idioma da
casa), desempate final por candidateNumber (ordem deterministica), geography
como prop na secao (sem lookup duplicado), casing Dep. Federal/Estadual
alinhado, marcador 1313 via BASELINE_TICKET_2022, entrada no CHANGELOG.

Declinados com justificativa: zones como tupla nao-vazia (churn em tipo
compartilhado por assert provada no catalogo) e early-start da promise
antes do Promise.all (loader-dentro-do-Suspense e o idioma da casa; ganho
so em cache frio).

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-31 00:14:27 -03:00
Cursor Agent
83f0b1cf48 fix: remove leftover merge conflict markers in int test 2026-07-31 02:34:15 +00:00
Cursor Agent
c7aac4da1b test: pin home search municipality scope fixtures to distinct slugs 2026-07-31 02:34:15 +00:00
Cursor Agent
5d45909780 fix: drop unused quick-action setter hook until B80 2026-07-31 02:34:14 +00:00
Cursor Agent
224cbf84f5 test: cover quick action context setter hook for knip 2026-07-31 02:34:14 +00:00
Cursor Agent
a2eabaccba B79 — chassis do bottom drawer de ações rápidas + busca global (mobile)
Monta CampaignQuickActionsDrawer não-modal no layout (app) para staff fora
do Início e de /acoes, com snap collapsed/expanded, padding de peek no scroll,
registry vazio para catálogos B80+, e busca global reutilizada via
CampaignStaffGlobalSearch.

Closes #15

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-31 02:34:08 +00:00
Francisco Solla
b2b11c3dfe Merge pull request #81 from fsolla/cursor/d2-push-sino-in-app-af30
D2 — Push + sino in-app
2026-07-30 23:25:11 -03:00
Cursor Agent
b9a81f7ad0 D2 — skip notification hooks in Vitest; fixture cleanup for push rows 2026-07-31 02:09:52 +00:00
Cursor Agent
de67acf41b D2 — Push + sino in-app para /campanha
- Collections Notification e PushSubscription com migration
- Sino no header (mobile + desktop) com drawer de avisos lidos/não lidos
- Hooks em municipalityUpdate, supporter, activity e convite aceito
- Push Web via web-push + handlers no service worker /campanha/sw.js
- Opt-in LGPD em /campanha/perfil (chave campanha-notificacoes-push)
- Testes unit/int de contrato e access

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-31 01:51:41 +00:00
Francisco Ignácio Fontoura Solla
773e4426eb fix(test): queries distintas em specs de portfolio de município
Nova Soure vs Nova Canaã compartilham o primeiro token; distinctWordStartQuery
escolhe palavra que casa só com o alvo e evita falso positivo no assessor.
2026-07-30 22:29:20 -03:00
Francisco Ignácio Fontoura Solla
0fece8cef5 fix(test): isola municípios nos homeSearch specs para evitar deadlock
Specs de portfolio deixam de mutar cairu/feira compartilhados e passam a
usar getMunicipality() alocado; testDatabaseLease ignora updatedAt volátil.
2026-07-30 22:07:40 -03:00
Francisco Ignácio Fontoura Solla
b533139eb2 feat(ops): promote último commit ci-stage green sem exigir head verde
agent:promote passa a escolher o SHA mais novo à frente de main com
ci-stage success e usa promote/last-green quando o head de stage está
vermelho, destravando o bootstrap manual para main.
2026-07-30 21:56:50 -03:00
Cursor Agent
90a97d3c76 style: prettier 2026-07-31 00:27:50 +00:00
Cursor Agent
6c8d7d418c B78 — Indicar carregamento na navegação do wizard
Reusa CampaignListPendingBoundary no layout autenticado para compartilhar
useTransition entre o header mobile e o conteúdo do passo. Voltar, X, Pular,
seleção de município e tiles de passo passam por CampaignWizardNavLink;
o corpo dima com aria-busy e o header mostra skeleton nos slots de título.

CampaignTransitionAnchor agora retorna a promise do router para manter o
pending honesto até a navegação terminar.

Closes #14
2026-07-31 00:24:39 +00:00
Francisco Solla
e04db48742 feat(ops): auto-promote stage→main e merge em stage sem exigir head (#68)
* feat(ops): auto-promote stage→main e merge em stage sem exigir head

Substitui o promote manual por workflow disparado no ci-stage green,
relaxa strict na proteção de stage (script configure:branch-protection)
e documenta o novo paradigma em AGENT-OPS e work-issue.

* style: format AGENT-OPS and configure-branch-protection for ci-pr

* feat(ops): pnpm push com ensure-repo-deps determinístico

Bootstrap install + Husky antes de todo push (pnpm push e pre-push).
Documenta fluxo canônico em work-issue, agent-pr-workflow e AGENT-OPS.

* fix(ops): pnpm push roda gate no script, sem depender do Husky

Cloud environment.json chama prepare explicitamente após install.

* test(int): isolate advisor portfolio search from shared cairu slug

Parallel int specs were stomping municipality.advisors on the fixed
cairu row; allocate per-test municipalities instead.
2026-07-30 21:12:52 -03:00
Francisco Solla
5ee9e840a2 Merge pull request #67 from fsolla/cursor/wizard-mudar-tendencia-a971
B64 — Wizard mudar tendência (grid colorido + justificativa + Pular)
2026-07-30 20:10:37 -03:00
Francisco Solla
680ff56754 feat(agent-pool): orquestrador de pool de workers Cursor Cloud (fases A–D) (#65)
* feat(agent-pool): fundacao read-only do orquestrador (fase A)

- docs/plans/agent-pool-orchestrator.md: plano completo (14 secoes)
- buildClaimQueue extraido de agent-claim para agent-github (comportamento identico, pinado)
- agent-pool-eligibility: isAutonomousClaimable puro e fail-closed + buildPoolQueue
- agent-pool-state: parsePoolConfig, marcadores pool-worker, classifyPoolClaim, spawn plan, auto-stop
- agent-pool-github: leitura de repo variables (degrada nao-estrito), claims/PRs/migration-lock
- pnpm agent:pool -- status|tick --dry-run (inspecao local; live chega na fase B)
- 34 specs unit novos (992/992 verde); lint/tsc/knip/cycles/format verdes

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* feat(agent-pool): spawn remoto de workers via Cursor API (fase B)

- agent-pool-cursor: client fino da Cloud Agents API v1 (fetch injetavel; CursorApiError com status/body)
- agent-pool-models: resolvePoolModel contra /v1/models (id/alias, sufixo -fast -> param, fallback composer-2.5 com warn)
- agent-pool-prompt: contrato do worker (claim pre-feito, pula passo 1; PR base stage + Closes #N; promote proibido)
- claim coordenado no tick: lock otimista + flip + marcador; agentId idempotente bc-<uuid>; 409 recupera; rollback em falha de spawn
- agent:pool workflow --action start|stop|pause|resume|tick|status (variables POOL_*); doctor; wrappers gh workflow run
- .github/workflows/agent-pool.yml (dispatch-only; schedule/triggers na fase C)
- 19 specs novos (1011/1011); lint/tsc/knip/cycles/format verdes

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* feat(agent-pool): ciclo de vida do supervisor (fase C)

- derivePoolClaims com status real dos runs (getCursorRun), heal de spawn via agentId deterministico, cancel de duplicatas
- reconcilePoolClaims: falha terminal -> comentario + blocked + archive; merge concluido -> slot livre + archive
- auto-stop real: fila drenada + zero ativos -> POOL_ENABLED=false (exclusoes migration-busy nao desligam)
- job summary markdown (GITHUB_STEP_SUMMARY) — superficie remota de status
- workflow: schedule */10min + pull_request closed em stage (ativam na default branch apos promote)
- 2 specs novos (1013/1013); lint/tsc/knip/cycles/format verdes

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* docs(agent-pool): runbook operacional e adendo do spike (fase D)

- skill agent-pool: runbook humano (pre-requisitos, start/stop/pause remoto, ciclo de vida, triage, smoke de aceite)
- AGENT-OPS: linha na tabela de comandos + secao Agent pool (canal, secrets, ativacao pos-promote)
- CHANGELOG-AGENTS: entrada curta da entrega
- engineering-audit: pause/resume do pool no protocolo do modo solitario
- plano secao 14.1: evidencias do spike 2026-07-30 (environment repo-file confirmado, id bc-<uuid>, degradacao 403, pendencias do primeiro run com CURSOR_API_KEY)

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

* fix(test): teardown flake no homeSearchUi.unit.spec (dono do CI)

O scheduler do react (prefetch do next/link) podia disparar apos o teardown
do jsdom e derrubar o job unit com 'window is not defined' (run 30587862940).
afterEach(cleanup) — padrao dos 14 specs irmaos.

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-07-30 20:08:34 -03:00