The android/ directory contains 7 Kotlin files + 3 *.gradle.kts files,
all banned by the Hyperpolymath language policy:
android/settings.gradle.kts android/build.gradle.kts android/app/build.gradle.kts android/app/src/main/java/ai/neurophone/MainActivity.kt android/app/src/main/java/ai/neurophone/NeurophoneService.kt android/app/src/main/java/ai/neurophone/BootReceiver.kt android/app/src/main/java/ai/neurophone/NativeLib.kt android/app/src/main/java/ai/neurophone/widget/NeurophoneAppWidget.kt android/app/src/main/java/ai/neurophone/widget/NeurophoneWidgetActions.kt android/app/src/main/java/ai/neurophone/widget/NeurophoneWidgetConfigureActivity.kt
This keeps two estate CI gates permanently red:
-
Check for Banned Languages(.github/workflows/language-policy.yml:87-94) -
governance / Language / package anti-pattern policy(estate-wide reusable)
Both hard-fail on any .kt / .kts / *.java match.
-
Migrate the Android UI + service to Gossamer (Hyperpolymath’s estate-canonical mobile framework: Zig FFI + Ephapax linear types
webview shell, replaces Tauri across the estate). -
Both CI checks turn green.
-
Behaviour preserved: sensor capture, foreground presence, widgets, boot-start.
-
Sub-PRs are small and reviewable; nothing bulk-deleted until owner approves this RFC.
-
Gossamer lives at
hyperpolymath/gossamer. Webview shell on Android viaGossamerActivity(android/src/main/java/io/gossamer/GossamerActivity.java, MPL-2.0) which loadslibgossamer.soand bridges JS↔native viaGossamerBridge. -
Precedent:
panllmigratedsrc-tauri/→src-gossamer/; webview UI unchanged, swappedtauricrate forgossamer-rs,tauri.conf.jsonforgossamer.conf.json,cargo tauri buildfordeno task buildinvoking thegossamerCLI. Desktop only — panll did not migrate a foreground service or home-screen widget; this neurophone migration is the first estate consumer to exercise that surface. -
Gossamer Android maturity: ROADMAP Phase 3 ("Mobile") lists Android as Partially Implemented. Only
GossamerActivity+GossamerBridgeexist upstream today. Foreground service,AppWidgetProvider,BroadcastReceiver,SensorManagerintegration — none exist upstream. No open Gossamer GH issues file these gaps.
| Kotlin component | LoC | Current role | Replacement |
|---|---|---|---|
|
377 |
UI shell (status / input / response), sensor capture in-activity,
permissions, intent handling (MAIN, SEND, VIEW |
|
|
162 |
|
|
|
25 |
|
|
|
93 |
JNI surface declarations + sensor-type-string→int mapping. |
DELETE entirely. All JNI exports already stubbed in
|
|
152 |
|
|
|
47 |
Lightweight |
|
|
70 |
Widget config UI (one checkbox: local-only mode), saved to prefs. |
|
|
~100 |
Android Gradle Plugin build config. |
Converted to |
Android instantiates the following by class name at platform boundaries. They cannot be implemented in pure Rust because Android’s class loader demands a class implementing the listed Android framework superclass:
-
Activity(main + widget-configure) -
Service -
BroadcastReceiver(boot + widget actions) -
AppWidgetProvider(extendsBroadcastReceiver)
Three resolution paths exist; this RFC proposes (A) and explicitly rejects (B) and ©. Owner picks before sub-PR work begins.
-
All shims are Java (MPL-2.0), one file per Android system surface (Service, BootReceiver, AppWidget, WidgetActions, WidgetConfigure, MainActivity).
-
Each shim is a single class, single delegating method body — typically 3-10 lines — that immediately JNIs into Rust. No business logic in Java.
-
The estate-wide language-policy CI currently blocks all
.javaand.kt. The Java check has to either (i) gain an exemption forandroid/*/.javaacross the estate via the reusable governance workflow inhyperpolymath/standards, OR (ii) gain a neurophone-local exemption only. Pre-PR coordination on which scope is acceptable. -
Outcome: zero Kotlin in the tree; small audited Java shim surface (estimated < 250 LoC total across all shims combined, vs. current 926 LoC of Kotlin); CI passes once exemption lands.
-
Replace home-screen widget with a quick-settings tile + persistent notification.
-
Pro: smaller JVM shim surface (no AppWidgetProvider + no WidgetActions + no WidgetConfigureActivity = 3 fewer shims).
-
Con: regresses the user-visible home-screen widget feature. The
docs/OS_INTEGRATION.adocdoc lists the home-screen widget as the primary Android integration surface.
Per Gossamer ROADMAP Phase 3 (Mobile, partially implemented) — the seven missing platform-integration capabilities (service, widget, receivers, sensors, intent dispatch, prefs, deep-links) are not in Gossamer’s current scope. Gossamer is today a webview shell.
Owner decision 2026-06-02 (Q6): contribute the missing surface
upstream to hyperpolymath/gossamer as a gossamer-android-services
companion module. The four shim base classes (Service, BroadcastReceiver,
AppWidgetProvider, Activity-with-bridge) land in Gossamer upstream so
future estate Android consumers inherit them.
Implications:
-
Sub-PR #3 (neurophone Gossamer scaffolding) blocks on the Gossamer upstream PR landing.
-
The Gossamer companion API shape needs owner review — generic enough for future consumers (idaptik-ums Android port, etc.), not just neurophone-shaped.
-
neurophone-side shims (Service / BootReceiver / Widget triple) become thin subclasses extending the Gossamer base classes rather than Android framework classes directly.
Each is a small, reviewable PR. Numbers are PR ordering, not commit count.
-
#1 (THIS PR — DRAFT, no code changes) — RFC for owner review. No Kotlin deleted, no Gossamer scaffolding generated.
-
#2 — CI exemption for hand-written Java mobile shims. Update
.github/workflows/language-policy.ymlstepCheck for Java/Kotlin filesto exemptandroid//.java. Coordinated withhyperpolymath/standardsfor the reusable governance check. Lands before any Gossamer scaffolding so CI doesn’t go redder en route.* -
#3 — Gossamer scaffolding (no behaviour). Add
gossamer-rsto the Rust workspace; creategossamer.conf.json; add anandroid/Java shim forNeurophoneMainActivity extends GossamerActivityloading a placeholder HTML page. Keep the existing Kotlinandroid/directory side-by-side so we can A/B sanity-check (rename toandroid-legacy/if the build system can’t tolerate both). -
#4 — Port
NativeLibto Rust. Fill outcrates/neurophone-android/src/lib.rswith the 10 JNI exports the current KotlinNativeLibdeclares. Wire to existing crates (neurophone-core,sensors,lsm,esn,bridge,llm,claude-client). Tests: JNI roundtrip via a Rust test that simulatesJNIEnv. -
#5 — Port
NeurophoneService. Java shim (NeurophoneRuntimeService extends Service) → JNI into Rust. Salience window, wake-lock plumbing, widget tick — all Rust. -
#6 — Port
BootReceiver. Java shim → single JNI fn. -
#7 — Port widget triple (provider / actions / configure). Java shims; widget render decisions in Rust pushed back via one data-bearing JNI fn.
-
#8 — AffineScript UI (replaces
activity_main.xmlview-binding layout). AffineScript compiles to JS, bundles toandroid/src/main/assets/(or wherever Gossamer loads from), loaded inGossamerActivity.gossamer_rs::App::command()registrations wirestart/stop/query/get_contextto the Rust core. -
#9 — Delete legacy Kotlin
android/tree. Both banned-language checks now green. Behaviour parity verified on emulator AND Oppo Reno 13 hardware (per Q5 — emulator for development, hardware for the final close-out) + Termux CLI smoke test.
PRs #2 and #3 are sequenced and blocking. PRs #4 through #7 can land in parallel after #3. PR #8 needs #4 done. PR #9 is gated on all prior.
Note: the section above is the original sequence. The
revised sequence per Q6 lives in Sequencing implications of Q6
below — read that for the post-2026-06-02 plan.
| # | Question | Resolution |
|---|---|---|
Q1 |
CI exemption scope — estate-wide via |
Estate-wide. Filed as |
/)android/./src/.\.java$ |
Q2 |
Standards reusable owner — who reviews the cross-repo PR? |
Self-answered. Owner approves directly; no third-party gatekeeper. |
Q3 |
Widget-configure activity — keep or drop? |
DROP. The single setting (local-only mode) moves into the main Gossamer UI; widget gets sensible default at install. Saves one Java shim ( |
Q4 |
Gradle DSL — Groovy |
Groovy |
Q5 |
Hardware test plan — Oppo Reno 13 or emulator + Termux? |
Both. Emulator + Termux smoke test during sub-PR development (faster iteration); Oppo Reno 13 hardware verification before sub-PR #9 deletes the legacy Kotlin tree. |
Q6 |
Shim location — neurophone-local or factor |
Q6’s "contribute upstream now" choice changes the sub-PR sequence:
-
The original sub-PR #3 (Gossamer scaffolding in neurophone) now depends on a NEW prerequisite: Gossamer companion PR in
hyperpolymath/gossameraddinggossamer-android-serviceswith the four shim base classes. -
The Gossamer companion design needs owner review separately — the API shape for
GossamerForegroundService,GossamerAppWidgetProvider,GossamerBootReceiver, etc. has to be generic enough for future estate Android consumers (not just neurophone). -
neurophone’s sub-PRs #5/#6/#7 (the Service/BootReceiver/Widget ports) become consumers of the Gossamer companion — they
extendthe upstream base classes rather thanextend android.app.Servicedirectly.
Revised sequence:
-
#1 (THIS PR) — RFC, no code changes.
-
#2a — CI exemption (standards#341, neurophone#107). IN FLIGHT.
-
#2b — NEW — Gossamer companion PR:
gossamer-android-servicesmodule with four shim base classes + tests. Owner reviews API shape. -
#3 — Gossamer scaffolding in neurophone (depends on #2b).
-
#4 — Port NativeLib to Rust (parallel-safe).
-
#5/#6/#7 — Port Service / BootReceiver / Widget triple (depends on #2b + #3).
-
#8 — AffineScript UI.
-
#9 — Delete legacy Kotlin
android/.
-
Check for Banned Languagesjob — green onmain. -
governance / Language / package anti-pattern policy— green onmain. -
android/directory — contains only Java shims + Groovy Gradle + XML resources; no.kt/.ktsfiles. -
APK builds via the Gossamer Android build command (per Gossamer Phase 3 conventions).
-
Foreground service + boot-restart + widget + sensor capture all work on hardware.
-
Termux CLI smoke test passes (
neurophonecommand — unchanged by this migration, but verified not regressed).
-
No changes to the seven core Rust crates (
lsm,esn,bridge,sensors,llm,claude-client,neurophone-core) outside what JNI wiring strictly requires. -
No changes to the Termux CLI path.
-
No upstream Gossamer changes in this neurophone migration. Gossamer mobile Phase 3 advances on its own track.