Anti-padrões

Pares concretos ❌ vs ✅ pros modos de falha que continuamos vendo em code review. Cada card linka a spec que governa a resposta certa; a explicação diz por que a errada morde.

Anti-padrões

Sequestro do gesture arena na barra de título

Desktop

specs/desktop-apps/title-bar-double-click.kmd

Não Embrulha a barra inteira com onDoubleTap + onPanStart.
// ❌ Wraps the whole title bar — hijacks the gesture arena.
GestureDetector(
  onDoubleTap: () => windowManager.maximize(),
  onPanStart: (_) => windowManager.startDragging(),
  child: TitleBarRow(...),
)
Faça Drag implícito; double-tap escopado na área livre.
// ✅ Drag is implicit; double-tap is scoped to the free area.
KoderTitleBar(
  title: 'My App',
  trailing: [KoderTitleBarFreeArea()],
)

Por quê: GestureDetector no topo da barra ganha o gesture arena até sobre botões filhos. Usuário toca em fechar, janela maximiza. KoderTitleBar trata drag e double-tap com escopos certos.

Strings UI hardcoded

i18n

specs/i18n/contract.kmd

Não Literal de string crua — nunca chega no pipeline de locale.
// ❌ One language; never reaches the ICU pipeline.
Text('Sign in to continue')
Faça Lookup de chave ICU com baseline en-US + pt-BR obrigatório.
// ✅ Backed by ICU keys with en-US baseline + pt-BR mandatory.
Text(KoderL10n.of(context).t('auth.gate.title'))

Por quê: Strings hardcoded são invisíveis pra tradutores, pro switcher de locale e pro /k-housekeep. Fossilizam em inglês pra sempre.

Telemetria de erro hand-rolled

Erros

specs/errors/reporting.kmd

Não Box / file / API local; sem consent gate.
// ❌ Custom storage; no consent gate; never reaches the reporter.
final box = await Hive.openBox('errors');
await box.add({'msg': error.toString(), 'time': DateTime.now()});
Faça KoderErrorReporter com consentimento explícito.
// ✅ Consent-gated, anonymized, queues offline, ships to foundation/reporter.
KoderErrorReporter.configure(
  endpoint: Uri.parse('https://reporter.koder.dev/v1/events'),
  consentDefault: false,
);
await KoderErrorReporter.report(error: caughtError);

Por quê: Stores locais vazam com o device, perdem o toggle de consent e nunca alimentam services/foundation/reporter. O reporter trata batching, anonimização e queue offline.

Scaffold sem window insets

Layout

specs/app-layout/safe-area.kmd

Não Scaffold raso; barra de status sobrepõe conteúdo.
// ❌ Status bar / gesture bar overlap user content.
Scaffold(
  body: ListView(children: items),
)
Faça KoderSafeScaffold respeita todo inset da plataforma.
// ✅ Window insets respected on every surface.
KoderSafeScaffold(
  body: ListView(children: items),
)

Por quê: Notches, barras de gesto, chrome de desktop e CSS env(safe-area-inset-*) precisam todos ser honrados. KoderSafeScaffold abstrai as diferenças.

Form de login local contra o Flow

Auth

specs/koder-app/behaviors.kmd

Não TextField pra senha; bypass do Koder ID.
// ❌ Password handled locally; bypasses Koder ID + OIDC PKCE.
Form(
  child: Column(children: [
    TextField(controller: usernameCtrl),
    TextField(controller: passwordCtrl, obscureText: true),
    ElevatedButton(onPressed: () => api.login(usernameCtrl.text, passwordCtrl.text), ...),
  ]),
)
Faça KoderSignInButton delega pra OIDC PKCE.
// ✅ Delegates to Koder ID; PKCE + state validation handled by the SDK.
KoderSignInButton(
  redirectUri: 'kall://auth/callback',
  onSuccess: (user) => Navigator.of(context).pushReplacement(...),
)

Por quê: Pela RFC-006 todo app Koder autentica via Koder ID. Forms locais não fazem PKCE, não honram 2FA / passkeys e re-implementam lookup timing-safe mal.

ThemeMode hardcoded antes do load da preferência

Tema

specs/themes/light-dark.kmd

Não Força dark em todo cold start.
// ❌ Forces dark on every cold start; flash of wrong theme on devices
// where the user picked light.
return MaterialApp(
  themeMode: ThemeMode.dark,
  ...
);
Faça Resolve da preferência salva; cai pra ThemeMode.system.
// ✅ Reads SharedPreferences before runApp; falls back to ThemeMode.system.
ThemeMode _resolve(String? saved) {
  if (saved == 'dark') return ThemeMode.dark;
  if (saved == 'light') return ThemeMode.light;
  return ThemeMode.system;
}

Por quê: Setar mode hardcoded causa flash de tema errado e ignora a escolha anterior do usuário. Leia SharedPreferences no main() antes do runApp.

Back reinventado por tela

Navegação

specs/navigation/back-behavior.kmd

Não Cada tela tem seu próprio WillPopScope.
// ❌ Each screen invents its own back rule. Two pops, tab switch on
// back, "are you sure?" on every back — pick your favourite bug.
WillPopScope(
  onWillPop: () async {
    if (somethingDirty) return await _showConfirm();
    Navigator.of(context).pop();
    return false;
  },
  child: ChildScreen(),
)
Faça KoderBackScope na raiz.
// ✅ Single source of truth — the SDK handles back/Esc consistently.
KoderBackScope(
  enableSystemExitAtRoot: true,
  child: HomeScreen(),
)

Por quê: Lógica per screen deriva: dois pops, tab switch no back, sair só às vezes. Usuário não consegue prever o que back faz. KoderBackScope centraliza a regra pela RFC.

Buffer pré-wake streamado pro servidor

Voz

specs/voice/wake-word.kmd

Não Áudio enviado continuamente.
// ❌ Streams the pre-wake ring buffer to the server. Privacy contract
// violated; the buffer must never leave the device.
audioStream.listen((chunk) {
  api.uploadAudio(chunk);
});
Faça Pré-wake fica local; só pós-wake é enviado.
// ✅ Pre-wake stays local; only post-wake intent is shipped — and only
// if the user has explicitly enabled voice in Settings.
final wake = await KoderWakeWord.standby(); // local ring buffer
final intent = await KoderTalkMode.capture(); // shipped only after wake
api.handleIntent(intent);

Por quê: specs/voice/wake-word.kmd faz o ring buffer pré-wake estritamente local. Streamar viola o contrato de privacidade e o consent do usuário.

Troca de idioma com reload total

i18n

specs/i18n/contract.kmd

Não window.location.reload() perde scroll + form state.
// ❌ Full page reload kills scroll position and form state.
function setLocale(loc) {
  document.cookie = 'locale=' + loc;
  window.location.reload();
}
Faça Subscription ICU; consumers rebuildam in place.
// ✅ ICU subscription rebuilds string consumers in place.
import { koderL10n } from 'koder-web-kit';
function setLocale(loc) {
  koderL10n.set(loc); // persists + notifies subscribers
}

Por quê: Reload parece bug pro usuário. A mudança de locale deve ser observável; consumers atualizam sem perder contexto.

Subdomínio app não-canônico

URL

specs/landing-pages/products.kmd

Não app.<produto>.koder.dev — quebra callbacks OAuth.
<!-- ❌ Non-canonical host. OAuth callbacks registered for
     <produto>.koder.dev now break. -->
<a href="https://app.kall.koder.dev/">Open the app</a>
Faça <produto>.koder.dev — canônico.
<!-- ✅ Canonical product URL. /about hosts the marketing landing
     page; /auth/callback is the OIDC redirect target. -->
<a href="https://kall.koder.dev/">Open the app</a>

Por quê: Todo callback OAuth registrado pro host canônico falha na variante app.*. specs/landing-pages/products.kmd §URL fixa o host canônico.

Access token em localStorage

Storage

specs/koder-app/behaviors.kmd

Não Qualquer script same-origin lê.
// ❌ Access token in localStorage. Any same-origin script can read it;
// shared devices leak it across users.
localStorage.setItem('token', accessToken);
Faça Secure store da plataforma (KeyStore / Keychain / libsecret).
// ✅ Platform secure store (KeyStore on Android, Keychain on iOS,
// libsecret on Linux, IndexedDB+WebCrypto wrapper on web).
import { koderSecureStore } from 'koder-web-kit';
await koderSecureStore.set('token', accessToken);

Por quê: Devices compartilhados e XSS conseguem ler tokens cross-user. O secure store é o único lugar que sobrevive em todo threat model.

Viewport / insets hardcoded

Layout

specs/app-layout/safe-area.kmd

Não Larguras e paddings em pixel.
/* ❌ Fixed pixel sizes break multi-monitor with mixed DPI and ignore
   the safe-area insets. */
.app {
  width: 1280px;
  padding-top: 24px;   /* status bar height ??? */
  padding-bottom: 24px;
}
Faça max-width 100vw + padding env(safe-area-inset-*).
/* ✅ Responsive + insets. Same code works on phone, desktop, and PWA. */
.app {
  max-width: 100vw;
  padding-top:    max(env(safe-area-inset-top),    1rem);
  padding-bottom: max(env(safe-area-inset-bottom), 1rem);
}

Por quê: Multi-monitor com DPI misto quebra matemática de pixel. As variáveis CSS env(*) expõem a safe area real sem chute.