B62+: flatten adversary signal to boolean and harden leader redirect e2e.

The simplify pass dropped the vestigial object wrapper on E11 input and aligned the leader home test with the retrying toHaveURL idiom used elsewhere.
This commit is contained in:
Francisco Ignácio Fontoura Solla 2026-07-29 23:33:46 -03:00
parent 120ed8d9b5
commit a8cb771303
4 changed files with 13 additions and 15 deletions

View file

@ -95,8 +95,8 @@ export type MunicipalityTriggerInput = {
coverageRatio: number | null
coverageDeficit: number
/** Adversary signal within the window, if any (P1 variant) — presence reads as confirmed. */
adversarySignal: { present: true } | null
/** Adversary signal within the window (P1 variant) — presence reads as confirmed. */
hasAdversarySignal: boolean
/** Any non-draft activity recently held or upcoming (the "agenda" leg of P1). */
hasRecentOrUpcomingActivity: boolean
/** Activities `realizado` within the effort window (K-A's "esforço no ciclo"). */
@ -314,7 +314,7 @@ const patternP1: SuggestionPattern = {
evaluate: (input) => {
if (input.territorialClass !== 'reduto' || !input.inCoreBlock) return null
const threatened = input.adversarySignal !== null
const threatened = input.hasAdversarySignal
const own2018 = input.ownVotesByYear[YEAR_2018] ?? 0
const own2022 = input.ownVotesByYear[YEAR_2022] ?? 0
const dropped = own2018 > 0 && own2022 <= own2018 * SUGGESTION_ANCHORS.voteDropRatio
@ -339,7 +339,7 @@ const patternP1: SuggestionPattern = {
}
if (dormant) factors.push(`${signalAgeFactor(input.lastSignalAgeDays)}, sem agenda no período`)
const triageLevel: SuggestionTriageLevel = input.adversarySignal ? 1 : 3
const triageLevel: SuggestionTriageLevel = input.hasAdversarySignal ? 1 : 3
return { patternId: 'P1', triageLevel, factors }
},
}

View file

@ -284,11 +284,11 @@ export const loadMunicipalitySuggestions = async (
])
// Any adversary signal in the window reads as confirmed (B62: sinal = tipo + texto).
const adversarySignalByMunicipality = new Map<number, { present: true }>()
const adversarySignalMunicipalityIDs = new Set<number>()
for (const signal of signals.docs) {
const id = relationshipId(signal.municipality)
if (id === null) continue
adversarySignalByMunicipality.set(id, { present: true })
adversarySignalMunicipalityIDs.add(id)
}
const agendaByMunicipality = new Map<number, { hasAgenda: boolean; completedCount: number }>()
@ -369,7 +369,7 @@ export const loadMunicipalitySuggestions = async (
lastPledgeAgeDays: municipalitySignalAgeInDays(pledgeAggregate.lastPledgeAt, now),
coverageRatio: coverage.coverageRatio,
coverageDeficit: coverage.deficit,
adversarySignal: adversarySignalByMunicipality.get(municipality.id) ?? null,
hasAdversarySignal: adversarySignalMunicipalityIDs.has(municipality.id),
hasRecentOrUpcomingActivity: agenda?.hasAgenda ?? false,
completedActivityCount: agenda?.completedCount ?? 0,
}

View file

@ -301,11 +301,9 @@ test.describe('Municípios — jornadas por papel', () => {
await expect(page.getByText('Cadastre apoiadores pelo celular.')).toBeVisible()
// The leader redirect aborts `goto`'s load event (ERR_ABORTED); the
// redirect itself is the assertion, so navigate and wait for the target.
await Promise.all([
page.waitForURL(`${campaign.baseURL}/campanha/contatos`),
page.goto(`${campaign.baseURL}/campanha/municipios`).catch(() => {}),
])
// redirect itself is the assertion, and `toHaveURL` retries onto it.
await page.goto(`${campaign.baseURL}/campanha/municipios`).catch(() => {})
await expect(page).toHaveURL(`${campaign.baseURL}/campanha/contatos`)
const supporterName = fixtures.value('Apoiador Liderança')
const supporterPhone = fixtures.phone()

View file

@ -42,7 +42,7 @@ const input = (overrides: Partial<MunicipalityTriggerInput> = {}): MunicipalityT
lastPledgeAgeDays: 3,
coverageRatio: 0.6,
coverageDeficit: 400,
adversarySignal: null,
hasAdversarySignal: false,
hasRecentOrUpcomingActivity: false,
completedActivityCount: 0,
...overrides,
@ -93,7 +93,7 @@ describe('P1 — reduto dormente ou ameaçado', () => {
})
it('escalates to level 1 on any adversary signal — presence reads as confirmed', () => {
expect(evaluate('P1', reduto({ adversarySignal: { present: true } }))?.triageLevel).toBe(1)
expect(evaluate('P1', reduto({ hasAdversarySignal: true }))?.triageLevel).toBe(1)
})
it('fires on a 2018→2022 vote drop even with a fresh signal', () => {
@ -315,7 +315,7 @@ describe('evaluateMunicipalityTriggers — ordering and silence', () => {
territorialClass: 'reduto',
inCoreBlock: true,
lqByYear: { 2014: 2.5, 2018: 2.6, 2022: 2.4 },
adversarySignal: { present: true },
hasAdversarySignal: true,
coverageRatio: 1.2,
}),
)