- Wrap root layout with SafeAreaProvider, use useSafeAreaInsets in all
screens so FAB/action bar/scroll content clear Android nav bar
- Add deleteIncidentFromGit() (Gitea/GitHub/GitLab) — detail screen now
offers Cancel / Local only / Local + Repo when incident was pushed
- Add Mistral as AI provider (OpenAI-compat, api.mistral.ai/v1,
mistral-small-latest default)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The action consistently fails trying to re-download cmdline-tools
from Google CDN (corrupt zip). The ubuntu-latest runner already ships
a working Android SDK at $ANDROID_HOME — call sdkmanager directly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DB migration v2: git_pushed_at column on incidents
- markIncidentPushed(id): stamps the push timestamp
- loadGitConfig(): shared helper in lib/git.ts, removes duplication
- Home screen: colored dot per incident (red=never pushed,
orange=dirty/modified after push, green=up to date);
dot visible only when git is configured
- Home screen: long-press enters selection mode, tap toggles;
action bar with Select unpushed / Select all shortcuts +
bulk push with sequential progress counter
- [id].tsx: marks incident pushed after successful push,
updates local state without reload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After each incident save, query existing titles with the same prefix
to find the real max number, then use max(dbMax, tplNum) + 1.
Prevents duplicate counters when incidents were created before the
template was configured.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- lib/ai.ts: unified AI client supporting Anthropic (native format) and
OpenAI-compatible providers (OpenAI, Perplexity, Ollama, OpenRouter, custom)
- settings: 6-provider chip selector, base URL auto-filled on preset select,
Anthropic URL hidden (fixed), model/key placeholders per provider
- settings: git repo config replaced by single Repo URL field (.git paste)
auto-parsed to instance URL + owner + repo at save time
- new.tsx: Suggest button between Symptom and Root Cause, visible when AI
enabled and title+symptom filled; pre-fills rootCause and fix inline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- lib/git.ts: add branch:"main" to Gitea POST payload (required when repo has no
branch yet or is freshly initialized); treat 409 on GET same as 404 (empty repo
has no tree, Gitea returns 409 Conflict — proceed to create first file)
- lib/git.ts: improve 404 error hint with actionable checklist (URL format,
owner/repo names, token scope, repo existence)
- settings.tsx: add Gitea info card clarifying URL format (no /api/v1), token
format (raw value only, no "Bearer" prefix), repo initialization requirement
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- IncidentDetailScreen: add Delete button (confirm dialog) wired to deleteIncident()
- git.ts: fix Gitea POST vs PUT (use PUT when file exists/sha present); add
pre-flight validation for missing URL/token/owner/repo; add 15s fetch timeout
with AbortError handling; improve error messages with actionable hints
- [id].tsx: guard URL for Gitea/GitLab before calling pushIncidentToGit
- settings.tsx: move ToggleRow/SegmentRow/Field outside SettingsScreen to fix
TextInput focus loss on each keystroke (component remount on rerender)
- settings.tsx: add Title Template setting with auto-increment info card
- settings.tsx: add AI base URL + model fields; expand AI section description
- new.tsx: pre-fill title from pref_title_template; increment counter after save
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
new.tsx: service field was missing its mic button — VoiceField type
excluded "service" and the label used a plain Text instead of labelRow().
index.tsx: incident list only loaded on mount; router.back() does not
remount the screen so new incidents never appeared. useFocusEffect
replaces the one-shot useEffect so the list reloads on every focus.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CrashBoundary and ErrorUtils debug handler were temporary scaffolding
to identify the launch crash (react version mismatch). Removing now
that the root cause is fixed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
react-dom@19.2.7 has a hard peer dep on react@^19.2.7, breaking
npm ci when react is pinned to 19.2.3. npm overrides force both
packages to 19.2.3 so the lock file resolves cleanly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
react-native@0.85.3 bundles react-native-renderer@19.2.3. Having
react@19.2.7 in package.json caused a version mismatch error at
runtime ("Incompatible React versions") that killed the process
before any JS could render, appearing as a native crash.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
react-native.config.js excludes react-native-reanimated and
react-native-worklets from Android native linking — neither is used in
the app and react-native-gesture-handler has a dedicated noreanimated
codepath for exactly this case.
build-apk.sh step 4.6 forces newArchEnabled=false in gradle.properties
after expo prebuild, as a fallback for New Architecture init issues
on Android 15.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
react-native-worklets and react-native-reanimated compile .so files
without -Wl,-z,max-page-size=16384, causing a native crash at startup
on Android 15 devices that use 16KB memory pages (Pixel 9+, Galaxy S25+).
Patch applied in build-apk.sh and CI workflow after npm install, before
the Gradle build. Guard prevents double-patching on repeated runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Catches React render errors (ErrorBoundary) and fatal JS errors
(ErrorUtils.setGlobalHandler) and displays the stacktrace on-screen
instead of crashing to desktop. Temporary — remove after crash identified.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SplashScreen.hideAsync() and the splash section in app.json were both
introduced after v0.1.5 and are common to every crashing build.
Reverting to the exact v0.1.5 baseline to isolate the crash source.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Release body now includes conventional-commit bullet points, a compare
link (prev_tag...current_tag) and a link to CHANGELOG.md for both the
GitHub release and a new Gitea release created via API.
GITEA_TOKEN secret must be added to GitHub repo secrets to enable
the Gitea release step.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
expo-router 56 already calls _internal_preventAutoHideAsync() internally.
Calling preventAutoHideAsync() at module level created a second user lock
that blocked the native splash indefinitely on Android, causing a crash.
SafeAreaProvider removed as expo-router's ExpoRoot already wraps with it.
Keep SplashScreen.hideAsync() to release the splash after SecureStore reads.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add SplashScreen.preventAutoHideAsync() at module level and hideAsync()
once SecureStore reads complete. Add splash backgroundColor to app.json
to match app theme. Wrap layout in SafeAreaProvider.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Debug APK required Metro bundler at runtime. Switch to a signed release
build using a local keystore (same pattern as Postiz-Android): keystore
decoded from KEYSTORE_B64 secret, build-apk.sh patches build.gradle and
assembles a standalone APK.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cancel-in-progress for same ref prevents parallel duplicate builds
- post-build step creates a GitHub Release with APK as named asset
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>