# F4.I1.T3 — Sidebar do manifesto + Ctrl+K; no-JS funcional

| Campo | Valor |
|---|---|
| **Fase** | F4 — Produto & CMS |
| **Issue** | F4.I1 — CMS próprio e editor (plano 12) |
| **kd task** | `task_013robpb` (`closed`) |
| **Âncora** | `wiki/implementation/04-produto-cms.md` |
| **Status da revisão** | 🔧 com correções |

## O que foi implementado

- `Core\Internal\Navigation\PanelMenu`: agrupamento por pasta, ordem determinística, negação por padrão, sem link morto/fora da área.
- Sidebar do shell; navegação rápida (Ctrl+K) em popover nativo — abre e navega sem JS (JS só filtra/setas/Esc).

## Arquivos para revisar

- `src/core/Internal/Navigation/PanelMenu.php`
- `src/core/Internal/Navigation/Test/PanelMenuTest.php`
- `src/layout/Cms/partials/{sidebar,nav-items,palette,orbital}.blade.php`
- `src/layout/Cms/Assets/js/app.js`
- `src/layout/Lang/*/shell.php` (títulos de grupo)
- `src/tests-e2e/tests/07-cms-shell.spec.ts`

## Pontos de revisão

### PHP 8.4
- [ ] `declare(strict_types=1)`; tipos nativos (union/enum/`readonly`); sem `mixed` desnecessário.
- [ ] Dependências **injetadas** (nenhum `new`/`app()`/`resolve()` no corpo); sem traits/abstract/generics.
- [ ] `match` em vez de cadeias de `if`; `?->`/`??`; sem código morto; PHPStan `max` limpo.

### Laravel 12
- [ ] Nativo primeiro (Eloquent casts/scopes, `FormRequest`, `Gate`, scheduler, events) — sem reimplementar.
- [ ] Sem closure em rota; resources/rotas nomeadas; `config()` só lê, `env()` só em `config/`.
- [ ] Migração **idempotente**; `down()` → `backup_*` (nunca `DROP`); sem `orderBy` cru quando `latest/oldest` serve.

### HTML semântico + a11y (eMAG/WCAG 2.2 AA)
- [ ] Um `<main>`, headings em nível correto, landmarks; só ARIA quando necessário.
- [ ] Alvos ≥ 24 px (≥ 44 px em ponteiro grosso); foco visível; rótulos em todos os campos; contraste (axe).
- [ ] Funciona **sem JS**; `sniffCSS` sem `fail`/`warn` fora do ledger.

### CSS moderno
- [ ] `oklch`, `clamp`, `rem`/`fr`/`dvh`/`%`; `@layer` (a11y por último); `@property`; subgrid com fallback.
- [ ] Sem `!important`, sem `style="…"` inline, sem hex/rgb (exceto justificado); sem CDN.

### Decisões do projeto (ADRs)
- [ ] Respeita a ADR do escopo (ver cabeçalho) e não escapa das demais (001, 002, 004, 013, 015…).
- [ ] Determinismo: relógio/id injetados, ordem explícita, dados idempotentes (ADR-001).

### i18n (ADR-012)
- [ ] Nenhuma string visível fixa; 4 locales com paridade de chaves; `route()`/`localized_url()` (nunca path literal).

### Observabilidade/segurança/testes
- [ ] Span + `log_event('{module}.{action}')` com atributos `scalar|null`, **sem PII** (ADR-024).
- [ ] Teste que falha sem a mudança (vermelho→verde) + evidência no `kd`; `make quality` 2×.

### Específicos desta task

- [ ] Item cuja rota não existe ou é de outra área é **omitido**.
- [ ] Módulo inativo e grupo `Suporte` filtrados (ADR-033/034).

## Comandos úteis

```bash
make quality            # lint + PHPStan max + gates + testes
make e2e                # Playwright + axe (se a task toca UI)
make check-boundaries   # fronteiras core/services/layout/modules
```

---

> Revisar contra `AGENTS.md`, `wiki/adr/` e o plano citado. Registrar achados no `kd` (`kd write --anchor`) e, se houver correção, teste de regressão.

## Notas da revisão

| Data | Revisor | Resultado | Observações |
|---|---|---|---|
| 2026-10-18 | agente | 🔧 | **A** (alto) o índice da navegação rápida saía **com acento** e o filtro do cliente comparava cru → digitar `noticias` não achava *Notícias* (o teclado brasileiro digita sem acento); corrigido nos **dois lados** (`Str::ascii`+`mb_strtolower` no servidor, NFD+diacríticos no cliente), com PHPUnit varrendo os `data-search` e e2e nos **dois sentidos** (provado vermelho revertendo o cliente: `Expected: 1, Received: 0`) — `fact_00pxqhk9`; **B** (alto) a regra de alvo em **ponteiro grosso** (2.75rem) vivia em `layout.css`, que **só o portal importa** → o painel inteiro ficava sem ela e **nenhum gate percebia** (o CMS não tem projeto mobile no e2e nem medição no sniff) → movida para a folha **compartilhada** (`a11y.css`) + bloco próprio em `shell.css` + **gate novo** em `DesignTokensTest` que compara as folhas comuns aos dois shells (vermelho antes) — `fact_001j5p7x`, `risk_01utjqyo`; **C** (médio) `context_key` era calculado no shell e **nunca renderizado** (dado morto) → a navegação rápida passou a mostrar de que grupo vem o destino (`cms-palette__context`) + teste — `fact_00owvn4i`; **D** (baixo) `url('/')` na marca do painel em vez de `route('cms.dashboard')` → risco latente (hoje as duas geram a mesma string, nenhum teste distingue) — `risk_00frr34f`; **E** (obs) só o dashboard recebe `aria-current` — `risk_00urwtfp`; **F** (obs) a lista plana da navegação rápida é montada **na view** do shell (o `PanelMenu` testado só expõe a árvore) — `risk_01qasi11`; **G** (obs) `Ctrl+K` é global e dispara também dentro do editor — `risk_006noxdx`. Verificados e OK: permissão **negação por padrão**, rota inexistente/ de outra área omitida (nunca link morto/fora do painel), grupo `Suporte` filtrado em profundidade, ordem determinística (`position` → nome), `popover`/`popovertarget` (abre e navega **sem JS**), `Esc` explícito com devolução de foco, `aria-keyshortcuts`, 4 locales dos títulos de grupo (todos os grupos existem), zero closure em rota, zero CDN, axe sem violações nas duas telas do CMS. |

> Resultado: `✅ revisado` · `🔧 com correções` · `⏭️ delegado` (com justificativa).
