C86+ — simplify: JSDoc do contrato home, testes de borda e nota no impl plan

Fixes do /simplify (3 revisores read-only): JSDoc de normalizePathname/isListPath
documentando o invariante (homes nunca são '/' nem terminam em slash), teste de
multi-slash e inputs degenerados, comentário de isCampaignHomePath encurtado.
Sem mudança de comportamento.
This commit is contained in:
Francisco Ignácio Fontoura Solla 2026-08-04 01:13:44 -03:00
parent 4a90fb19ec
commit b74268714c
4 changed files with 30 additions and 4 deletions

View file

@ -107,3 +107,21 @@ N/A — refactor estrutural; nenhuma métrica, série, ranking ou mapa novo.
- [ ] Invariantes AGENTS/engineering-standards: client-safe, sem migration, sem access/Consent, identificadores em inglês
- [ ] Testes de domínio: specs existentes dos 9 wrappers + describe novo de `isListPath`/`normalizePathname`
- [ ] Self-score decision-quality: 5/5 (decisões caras com rejeitadas; appetite respeitado; rabbit holes nomeados; depth check reusa o home existente; outcome preservado)
## Simplify (2026-08-04) — aplicado e deferido
3 revisores paralelos (read-only) no diff da sessão; veredito do revisor de corretude: **bordas fechadas, comportamento preservado** (tabela de equivalência borda a borda dos matchers antigo × `isListPath`). Revisor de reuse: **sem duplicação real remanescente** (zero matchers exatos fora do diff; prefix-matches de descendentes deixados de fora por semântica diferente).
**Aplicados (fixes pontuais que preservam comportamento):**
- JSDoc de `normalizePathname` documenta o invariante "homes nunca são `/` nem terminam com slash" (S1 quality).
- Wording do JSDoc de `isListPath` corrigido ("equals `home`, optionally with a single trailing slash") (S3 quality).
- Testes novos pinando multi-slash (`/campanha//`) e inputs degenerados (`''`, `'/'` como home, double trailing slash) (S2 quality + S1 correctness).
- Comentário de `isCampaignHomePath` encurtado para o why ("same exact-match rule as the sidebar nav") (S4 quality).
**Descartados:**
- `isLeadershipListPath` como wrapper privado com 1 call site — manter (consistência de padrão com os 7 irmãos; inline seria micro-otimização de estilo) (S1 reuse, score 1).
- Contrato `home` sem tipo restrito — JSDoc documenta; gatilho: chamador futuro com home terminando em `/` (S2 correctness, score 1).
**Defer com gatilho (já registrados na intenção, não reabrir):** derivação de regex numérica de detalhe (3º vertical com `[id]` numérico — hoje 2: advisor + supporter); cache `Map<CampaignRole>` (4º site com shape compatível); id `register-supporter` sobrecarregado (agregação de catálogos).

View file

@ -8,7 +8,7 @@ import {
type CampaignRole,
} from '@/lib/campaignRoles'
/** Início matches only exactly — same rule as sidebar nav. */
/** Same exact-match rule as the sidebar nav. */
export const isCampaignHomePath = (pathname: string): boolean => isListPath(pathname, CAMPAIGN_HOME)
export const isCampaignActionsPath = (pathname: string): boolean =>

View file

@ -17,13 +17,14 @@ export type OrganizationQuickActionSurface =
| { kind: 'detail'; organizationSlug: string }
/**
* Strips a single trailing slash for client-side path comparisons, preserving `/`.
* Homes in this codebase are never `/`, so `/campanha/` → `/campanha` while `/` stays `/`.
* Strips a single trailing slash for client-side path comparisons, preserving `/`
* (`/campanha/` → `/campanha`, while `/` stays `/`). Homes in this codebase never
* are `/` nor end with a slash — `isListPath` relies on that contract.
*/
export const normalizePathname = (pathname: string): string =>
pathname.length > 1 && pathname.endsWith('/') ? pathname.slice(0, -1) : pathname
/** Whether `pathname` is exactly `home`, with an optional trailing slash (list pages). */
/** Whether `pathname` equals `home`, optionally with a single trailing slash. */
export const isListPath = (pathname: string, home: string): boolean =>
normalizePathname(pathname) === home

View file

@ -133,6 +133,13 @@ describe('campaignQuickActionPaths (shared list helpers, C86+)', () => {
expect(normalizePathname('/campanha')).toBe('/campanha')
expect(normalizePathname('/')).toBe('/')
expect(normalizePathname('/campanha/apoiadores/')).toBe('/campanha/apoiadores')
expect(normalizePathname('/campanha//')).toBe('/campanha/')
})
it('pins the degenerate inputs the inline matchers used to handle', () => {
expect(isListPath('/campanha/demandas//', '/campanha/demandas')).toBe(false)
expect(isListPath('', '/campanha/demandas')).toBe(false)
expect(isListPath('/', '/')).toBe(true)
})
})