Title-bar gesture arena hijack
Desktopspecs/desktop-apps/title-bar-double-click.kmd
❌ Don't Wraps the whole bar with onDoubleTap + onPanStart.// ❌ Wraps the whole title bar — hijacks the gesture arena.
GestureDetector(
onDoubleTap: () => windowManager.maximize(),
onPanStart: (_) => windowManager.startDragging(),
child: TitleBarRow(...),
)
✓ Do Drag is implicit; double-tap scoped to free area.// ✅ Drag is implicit; double-tap is scoped to the free area.
KoderTitleBar(
title: 'My App',
trailing: [KoderTitleBarFreeArea()],
)
Why: GestureDetector at the top of the bar wins the gesture arena even over child buttons. Users tap close, the window maximizes instead. KoderTitleBar handles drag and double-tap with the right scopes.
specs/i18n/contract.kmd
❌ Don't Plain string literal — never reaches the locale pipeline.// ❌ One language; never reaches the ICU pipeline.
Text('Sign in to continue')
✓ Do ICU key lookup with en-US baseline + pt-BR mandatory.// ✅ Backed by ICU keys with en-US baseline + pt-BR mandatory.
Text(KoderL10n.of(context).t('auth.gate.title'))
Why: Hardcoded strings are invisible to translators, to the locale switcher, and to /k-housekeep. They fossilize as English forever.
Hand-rolled error telemetry
Errorsspecs/errors/reporting.kmd
❌ Don't Local box / file / API; no 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()});
✓ Do KoderErrorReporter with explicit consent.// ✅ 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);
Why: Local stores leak with the device, miss the consent toggle, and never feed services/foundation/reporter. The reporter handles batching, anonymization, and offline queues.
Scaffold without window insets
Layoutspecs/app-layout/safe-area.kmd
❌ Don't Plain Scaffold; status bar overlaps content.// ❌ Status bar / gesture bar overlap user content.
Scaffold(
body: ListView(children: items),
)
✓ Do KoderSafeScaffold respects every platform inset.// ✅ Window insets respected on every surface.
KoderSafeScaffold(
body: ListView(children: items),
)
Why: Notches, gesture bars, desktop chrome, and CSS env(safe-area-inset-*) all need to be honored. KoderSafeScaffold abstracts the platform differences.
Local login form against Flow
Authspecs/koder-app/behaviors.kmd
❌ Don't TextField for password; bypasses 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), ...),
]),
)
✓ Do KoderSignInButton delegates to OIDC PKCE.// ✅ Delegates to Koder ID; PKCE + state validation handled by the SDK.
KoderSignInButton(
redirectUri: 'kall://auth/callback',
onSuccess: (user) => Navigator.of(context).pushReplacement(...),
)
Why: Per RFC-006 every Koder app authenticates through Koder ID. Local forms cannot do PKCE, cannot honor 2FA / passkeys, and re-implement timing-safe lookup poorly.
ThemeMode hardcoded before preference load
Themespecs/themes/light-dark.kmd
❌ Don't Forces dark on every cold start.// ❌ Forces dark on every cold start; flash of wrong theme on devices
// where the user picked light.
return MaterialApp(
themeMode: ThemeMode.dark,
...
);
✓ Do Resolve from saved preference; fall back to 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;
}
Why: Hard-setting a mode causes the flash of wrong theme and ignores the user's earlier choice. Read SharedPreferences in main() before runApp.
Back behavior reinvented per screen
Navigationspecs/navigation/back-behavior.kmd
❌ Don't Each screen ships its own 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(),
)
✓ Do KoderBackScope at the root.// ✅ Single source of truth — the SDK handles back/Esc consistently.
KoderBackScope(
enableSystemExitAtRoot: true,
child: HomeScreen(),
)
Why: Per-screen logic drifts: two pops, tab switch on back, exit only sometimes. Users can't predict what back does. KoderBackScope centralizes the rule per RFC.
Pre-wake buffer streamed to server
Voicespecs/voice/wake-word.kmd
❌ Don't Audio uploaded continuously.// ❌ 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);
});
✓ Do Pre-wake stays local; only post-wake ships.// ✅ 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);
Why: specs/voice/wake-word.kmd makes the pre-wake ring buffer strictly local. Streaming it violates the privacy contract and the user's consent.
Language switch via full reload
i18nspecs/i18n/contract.kmd
❌ Don't window.location.reload() drops scroll + form state.// ❌ Full page reload kills scroll position and form state.
function setLocale(loc) {
document.cookie = 'locale=' + loc;
window.location.reload();
}
✓ Do ICU subscription; consumers rebuild in place.// ✅ ICU subscription rebuilds string consumers in place.
import { koderL10n } from 'koder-web-kit';
function setLocale(loc) {
koderL10n.set(loc); // persists + notifies subscribers
}
Why: Reload feels like a bug to the user. The locale change should be observable; consumers update without losing context.
Non-canonical app subdomain
URLspecs/landing-pages/products.kmd
❌ Don't app.<produto>.koder.dev — breaks OAuth callbacks.<!-- ❌ Non-canonical host. OAuth callbacks registered for
<produto>.koder.dev now break. -->
<a href="https://app.kall.koder.dev/">Open the app</a>
✓ Do <produto>.koder.dev — canonical.<!-- ✅ 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>
Why: Every OAuth callback registered for the canonical host fails on the app.* variant. specs/landing-pages/products.kmd §URL pins the canonical host.
Access token in localStorage
Storagespecs/koder-app/behaviors.kmd
❌ Don't Any same-origin script can read it.// ❌ Access token in localStorage. Any same-origin script can read it;
// shared devices leak it across users.
localStorage.setItem('token', accessToken);
✓ Do Platform secure store (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);
Why: Shared devices and XSS get cross-user token reads. The secure store is the only place that survives every threat model.
Hardcoded viewport / inset values
Layoutspecs/app-layout/safe-area.kmd
❌ Don't Pixel widths and pixel paddings./* ❌ 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;
}
✓ Do max-width 100vw + env(safe-area-inset-*) padding./* ✅ 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);
}
Why: Multi-monitor with mixed DPI breaks pixel math. The CSS environment variables expose the actual safe area without guessing.