Sequestro do gesture arena na barra de título
Desktopspecs/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.
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
Errosspecs/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
Layoutspecs/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
Authspecs/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
Temaspecs/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çãospecs/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
Vozspecs/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
i18nspecs/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
URLspecs/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
Storagespecs/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
Layoutspecs/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.