Espelho da coluna de Assessores: chips com nome do contato, popover/sheet
com Command + delta otimista e criação inline (Nome + Celular). Sem
migration — leitura reversa de leadership.municipalities no bundle e
escrita reusando as actions B34/wizard; rota POST
/campanha/municipios/leaderships via campaignJsonMutationRoute.
WebAuthn platform passkeys: opt-in enrollment after a password login, a
password-less sign-in that mints the same 14-day campaign-token, and
revocation from the profile. The session is minted through Payload's public
pieces (checkLoginPermission, addSessionToUser, getFieldsToSign, jwtSign)
inside a transaction with an advisory lock, so a passkey login neither
bypasses a lockout nor evicts the account's other sessions. The rpID is
derived from NEXT_PUBLIC_SITE_URL and resolves to null when the request host
disagrees, so previews never offer a ceremony the browser would refuse.
`municipality.engagementLevel` (staff-only, no default) records what the
campaign decided to invest in a município, next to the E8/E10 metrics that
describe what it knows. Moving it is a reallocation decision, so only
unrestricted staff may, every move carries a rationale plus the signals that
would reverse it, and the write files an `allocationDecision` in the same
transaction under an advisory lock taken before the current level is read —
otherwise the snapshot's `from` is a guess. The enum gains `movimento`: a level
move is neither `aceita` nor `descarta`.
No backfill. Null is "Sem nível", and filtering by it is the triage queue.
That is also why the list sorts `nivel` in memory rather than in the query:
Postgres puts NULLs first on DESC, so a native sort would open "N4 primeiro"
on a page of municípios nobody has decided about yet. The filter stays native.
The hysteresis rules from research §6.8 are pure and client-safe, so the
Popover explains a block before submitting and the server re-runs them anyway;
a blocked move answers 409 with typed violations and the override is recorded
with exactly the rules it contradicted.
- Popover + NativeSelect sempre montado na coluna Status de
/campanha/liderancas, sem toggle de modo de edição
- Auto-save (~150ms) via POST /campanha/liderancas/support-status,
reusando isSameOriginRequest/campaignJsonMutationRoute (B24/B27),
sem revalidatePath
- Extrai leadershipStaffEditSafeMessages (adotado também em
liderancas/formActions.ts e nova/formActions.ts) e
utilities/leadershipLabels.ts (3º call site de supportStatusLabels,
agora fonte única também para SupportStatusBadge)
- Novo e2e tests/e2e/campaignLeaderships.e2e.spec.ts; setup.e2e.spec.ts
prewarma as rotas novas
- Fecha o Adiado com gatilho do B29; docs/plans, notebook e roadmap
atualizados
Gate: tsc/lint/format/knip(P3 pré-existente)/check:cycles/unit+int/e2e
verdes; two /simplify passes (labels/estado duplicado/isSupportStatus;
flush-on-close ao fechar o popover, comentário órfão de summary/
campos summary mortos removidos de SupportStatusBadge).
Replaces the checkbox-list + "Salvar assessores" button in the
Assessors popover (/campanha/municipios) with removable chips + a
Command combobox, delta-based auto-save (one advisor add/remove per
request, never the whole array), and optimistic UI with request
sequencing to resolve out-of-order responses correctly.
- New POST /campanha/municipios/advisors JSON route (mirrors
expected-votes/), backed by setMunicipalityAdvisorMembership(Record)
in actions/municipality.ts (text advisory lock + transaction).
- nextAdvisorIdsAfterMembership moved to
src/lib/municipalityAdvisorMembership.ts with unit tests.
- Shared isSameOriginRequest (src/utilities/sameOriginRequest.ts) and
uniqueRelationshipIds (src/utilities/relationship.ts) helpers,
de-duplicating the same-origin CSRF check and the
Set+map(relationshipId)+filter pattern across advisor call sites.
- MunicipalityListAdvisorsControl.tsx rewritten: chips, Command
combobox, per-item pending affordance, rollback on error, and a
request-sequence guard so only the most recently sent delta's
response is adopted when concurrent optimistic updates settle.
- e2e coverage for the combobox auto-save flow; setup prewarms the new
POST-only route to avoid Playwright dev-server HMR aborting the
first request.
- Plan and roadmap marked as delivered (B27).
Nova rota staff-only /campanha/conceitos (glossário curado em
src/lib/campaignIntelligenceConcepts.ts, revisado junto das fórmulas
que descreve em municipalityPotential.ts/goalCoverage.ts) com
descoberta por "Saiba mais" nos tooltips do card "Conta da cadeira" e
entrada fixa no rodapé da sidebar (staffSecondaryNav). Corrige defeito
E8 de formatElectionNumber (meta fracionária lida como cem mil em
pt-BR). Ajustes pós-/simplify: MetricExplanation.conceptID obrigatório,
href da sidebar desacoplado do módulo de conteúdo (client bundle).
Rename the operational unit "Praça"/"Núcleo" to "Município" (code identifier
`municipality`) and land the 2026-07-23 course changes:
- New `candidate` role with unrestricted visibility (`isCampaignStaff` now
includes it) alongside coordinator/advisor; leader is locked down to the
supporter contact tool only.
- Demandas become staff-only; dobradinhas modeled via a `stateDeputy` entity.
- Camaçari consolidated to a single município (ZE 170/171 aggregated);
Salvador keeps its 19 zone municípios.
- Map moves to the staff dashboard; `/campanha/municipios` is list/overview.
- Vote pledges keep the declared vs estimated (scenario) asymmetry; leaders
never see estimates.
Destructive migration for the campaign vertical (drops nucleus/plaza-era
tables, wipes campaign rows, seeds 435 municípios). Public-site data untouched.
Includes the post-delivery /simplify cleanups: shared reloadStaffActor /
reloadCoordinatorActor helpers, req.context cache for getFreshCampaignUser,
parallelized dashboard loads, candidate dashboard-copy fix, and dead
leader-dashboard / roleKind code removal.
436 seeded Praças (municipality-based; per-TSE-zone in Salvador/Camaçari) become
the operational unit; roles renamed to coordinator/advisor/leader with advisors
scoped to administered Praças. Leadership is one record per person across many
Praças/organizations; vote pledges split leader-declared vs staff-estimated
(estimates never serialized to leaders). Adds organizations, demand workflow
with staff-only cost/receipts, action-plan results with deputy presence, manual
political trend, multi-candidate comparison and the diverging red/white/blue
map mode. Migration 20260721_020109_remodel_plazas deliberately resets the
campaign vertical in production (public site untouched); test suite ported to
the new model (unit 187 / int 306 / e2e 9 green).
Adds electoral nuclei, leaderships, updates, and WhatsApp invite flows behind campaign auth, plus the consolidated schema migration that Vercel applies on deploy.