Fold duplicated campanha access, loaders, list/filter/form shells, and scripts into shared modules; reorganize utilities by domain; stabilize e2e; and document prevention-first audit workflow so the same smells cannot creep back in.
`postJson` caught a non-JSON response and threw a plain `Error` carrying
"Resposta inválida do servidor.", but all three islands read `message` only off
a `CampaignWebAuthnError` — so the sentence was written for the person and then
thrown down the channel that discards messages. Deleted rather than re-typed:
the islands' own fallback is better copy for a field worker, which is the same
reason the five list-cell controls substitute their own. Nothing user-visible
changes, because the string was already unreachable; the comment now records
why the branch is absent, so it does not come back.
The login failure sentence was also spelled twice for one slot on one screen —
the route's `genericMessage` and the button's fallback — so it became
`CAMPAIGN_BIOMETRIC_LOGIN_ERROR_MESSAGE`, twin of the enroll constant the
register half already shares between its route and its card.
Also widened two doc comments this branch made stale: `campaignJsonMutationRoute`
and `campaignJsonRequest` both claimed to serve only the list cells' quick edits,
and four of nine callers are now WebAuthn ceremonies.
`campaignJsonMutationRoute` now builds every JSON POST route under /campanha —
`isSameOriginRequest` had been copied into five of them, so the CSRF policy was
failing open by omission; a convention guard sweeps all of `src/app` and names
its three exceptions. `postCampaignJson` is the only place that spells
`credentials: 'same-origin'`, and `useCampaignCellAutosave` is the debounce /
abort / revert / flush-on-close machine the quick-edit cells shipped three
times.
The shell keeps the `aria-live` region outside the Popover/Drawer, since closing
is what commits and it was unmounting the node that would announce the result;
`variant="sheet"` took a `footer` slot, which let the signal control drop the
overlay it only kept for one button.
Two bugs the extraction made visible: a revert issued while a different payload
was in flight was dropped silently, and `change()` never cleared a past failure,
which outranks everything the live region would otherwise say.
- MunicipalityListAdvisorsControl: stop advancing lastPropsIDsRef from a
successful delta response — it exists only to detect a genuine external
prop change, and writing it there made a later stale re-render of the
pre-edit content look external and clobber optimistic state back to it.
Also reconcile with the latest confirmed server set on the error path
once every in-flight delta settles (previously only the success path did),
and consolidate chip/tooltip name resolution onto the shared
advisorEntriesFromIds helper instead of a bespoke lookup with an
'#id' placeholder.
- Extract campaignJsonMutationErrorResponse/parseCampaignJsonRequestBody
(src/utilities/campaignJsonMutationRoute.ts) and adopt them in both
municipios/advisors/route.ts and municipios/expected-votes/route.ts,
removing the duplicated body-parse + error-mapping ladder.
- Municipality.ts's validateMunicipalityAdvisors now uses
uniqueRelationshipIds instead of re-inlining the Set+map+filter dance
the helper was extracted for.
Verified: tsc --noEmit, lint, format:check, check:cycles all clean;
full unit suite (450 tests) and the municipality/advisor int suites pass.
knip's pre-existing payload.config.ts load error is unrelated (P3).
Playwright's spawned webServer failed to boot in this sandbox (unrelated
module-resolution environment issue, reproduced even before this diff);
manual pnpm dev boots fine and the change is covered by unit/int tests.