Work / Mobile
Warrant Portal
Kotlin Android app that turns an estate engine’s blocked decisions into plain-language questions for the executor.
- Kotlin
- Android WebView and WebViewAssetLoader
- Gradle Kotlin DSL
- Android SpeechRecognizer
- React
- TypeScript
- Vite
- Service worker (offline PWA)
TL;DR
- In the last hours of the hackathon I turned Warrant outward, toward the family settling the estate, and directed Claude Code through the build.
- Every gap in the fact ledger becomes a plain-language task for the executor, and a blocked rule shows the statute behind the question.
- Built as a native Kotlin shell that serves the portal offline; the APK went into the team’s repo about 15 minutes before the 3:30 PM presentations.
The problem
An estate engine that refuses to guess is only useful if someone answers its questions. The person who can answer is usually a grieving family member acting as executor, not a lawyer, and not someone who will read a rules engine’s output.
What I built
The portal’s task engine turns every gap in the ledger into one of five task types: provide a fact, answer a form question, review a form, resolve a quarantined claim, or approve an action. Each question says why it is being asked, and a blocked rule shows the statute behind it. Answers flow back into the ledger as attestations.
An executor can type, speak, or answer with a document. Documents are hashed with SHA-256 on the device, and the file stays on the phone. Partial answers always submit, and a test keeps internal fact keys off the screen.
The Android app is a native Kotlin shell that serves the Vite-built portal offline through WebViewAssetLoader and a service worker, with two product flavors: demo and portal.
Key decisions
- Decision: a WebView shell around the web portal. Why: one portal codebase, packaged as an installable app within the event. Trade-off: Android’s WebView has gaps a browser doesn’t, and each one needs a native patch.
- Decision: hash documents on the device. Why: the ledger can attest to a document without the file leaving the phone. Trade-off: the engine sees a fingerprint, not the contents.
- Decision: “skip” and “decline” leave a fact unknown, never false. Why: the rules engine runs on three-valued logic, and a skipped question must not be read as a “no”. Trade-off: plans can stay blocked until someone answers.
The hard part
Two Android-only bugs surfaced in the shell. WebView’s MIME-type guesser returned nothing for .js files, so the portal’s ES modules were silently refused. And WebView has no SpeechRecognition API, so voice answers had nowhere to go. The shell now serves JavaScript with an explicit type, and a native SpeechRecognizer bridge implements the standard web API inside the WebView, so the portal’s speech code runs unchanged.
Results
- A 1.65 MB APK (package
app.warrant.portal, Android 8.0 and up) pushed to the team’s repo at 15:14 on presentation day. - A task engine covering five task types, wired back into the fact ledger as attestations.
What I’d do next
- Test on physical devices and publish screenshots.
- Sign a release build and publish the portal source with the rest of Warrant.
Links
- The engine behind it: Warrant + Forge
Verified numbers
| Metric | Value | Source |
|---|---|---|
| Android APK (minSdk 26, targetSdk 35) | 1.65 MB | local: WebstormProjects/accountward/warrant-portal.apk |
| Task types the portal turns ledger gaps into | 5 | local: warrant-backup/warrant-plus-final.bundle |
| Minutes before the presentations that the APK was pushed | about 15 | local: warrant-backup/warrant-plus-final.bundle |