Skip to content

RFC: Android Kotlin → Rust/Gossamer migration#97

Open
hyperpolymath wants to merge 5 commits into
mainfrom
chore/gossamer-migration-rfc
Open

RFC: Android Kotlin → Rust/Gossamer migration#97
hyperpolymath wants to merge 5 commits into
mainfrom
chore/gossamer-migration-rfc

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Status

DRAFT — RFC only. Awaiting owner review BEFORE any bulk conversion.
No Kotlin deleted, no Gossamer scaffolding generated, no behaviour change.

Supersedes #96 (was titled "Tauri 2" — owner correction: Gossamer is the
estate target).

Why

android/ ships 7 Kotlin files + 3 *.gradle.kts files, all banned by the
Hyperpolymath language policy. Two CI gates permanently red:

  • Check for Banned Languages (.github/workflows/language-policy.yml:87-94)
  • governance / Language / package anti-pattern policy (estate-wide)

What this PR contains

A single document — docs/migrations/RFC-ANDROID-KOTLIN-TO-RUST.adoc
mapping every Kotlin component to its Gossamer replacement and proposing
a 9-PR landing sequence.

Component mapping (one-line summary)

Kotlin file Replacement
MainActivity.kt NeurophoneMainActivity extends GossamerActivity
NeurophoneService.kt Hand-written Java Service shim → JNI into Rust
BootReceiver.kt Hand-written Java BroadcastReceiver shim → JNI
NativeLib.kt Delete; lives in crates/neurophone-android/src/lib.rs
NeurophoneAppWidget.kt Hand-written Java AppWidgetProvider shim
NeurophoneWidgetActions.kt Hand-written Java BroadcastReceiver shim
NeurophoneWidgetConfigureActivity.kt Java Activity shim (or drop)
*.gradle.kts Groovy *.gradle (matches Gossamer upstream)

Key structural finding

Gossamer Android is webview-only today. ROADMAP Phase 3 (Mobile)
lists Android as Partially Implemented — only GossamerActivity +
GossamerBridge exist upstream. The 7 platform-integration surfaces
neurophone needs (service, widget, receivers, sensors, intent dispatch,
prefs, deep-links) have no upstream Gossamer support and no open
Gossamer issues
.

Recommendation: build all 7 downstream in neurophone's own android/
tree as small Java shims that JNI immediately into Rust. Don't block
neurophone on Gossamer upstream PRs. Estimated total shim surface: < 250
LoC of Java vs. current 926 LoC of Kotlin.

Estate precedent

  • panll migrated src-tauri/src-gossamer/ (commit 598d537,
    ADR-0001 in b935727). Webview UI unchanged; swapped tauri crate
    for gossamer-rs, tauri.conf.json for gossamer.conf.json,
    cargo tauri build for deno task build invoking the gossamer CLI.
  • panll is desktop only — its migration did not exercise foreground
    service or home-screen widget surfaces; neurophone will be the first.

Sub-PR sequence (proposed)

  1. This PR — RFC only.
  2. CI exemption for android/**/*.java (coordinated with hyperpolymath/standards).
  3. Gossamer scaffolding: gossamer-rs workspace dep, gossamer.conf.json,
    NeurophoneMainActivity Java shim loading placeholder HTML.
  4. Port NativeLib Kotlin → crates/neurophone-android/src/lib.rs.
  5. Port NeurophoneService → Java shim + Rust impl.
  6. Port BootReceiver.
  7. Port widget triple (provider / actions / configure).
  8. AffineScript UI compiled to JS, loaded by Gossamer webview.
  9. Delete legacy Kotlin android/ tree — both CI gates green.

PRs 2 & 3 sequenced; 4–7 parallelizable after 3; 8 needs 4; 9 gated on all.

Open questions for owner

  1. CI exemption scope — estate-wide via hyperpolymath/standards
    or neurophone-local? Linchpin question.
  2. Estate governance — who owns the cross-repo anti-pattern policy
    callable in standards?
  3. Widget config sacrifice — acceptable to drop the configure activity?
  4. Gradle DSL — Groovy *.gradle (matches Gossamer upstream) or
    keep Kotlin *.gradle.kts under a separate exemption?
  5. Hardware test plan — Oppo Reno 13 vs emulator + Termux?
  6. Upstream contribution — build shims neurophone-local, or factor
    a gossamer-android-services companion as we go?

Constraints honoured

  • All new files MPL-2.0 SPDX.
  • No new GitHub Actions added.
  • No Deno/npm/TypeScript changes.

Test plan

  • RFC document renders as AsciiDoc.
  • Owner reviews component mapping for completeness.
  • Owner picks CI-exemption scope (open Q1).
  • Owner resolves Gradle DSL choice (open Q4).
  • Sub-PR Add STATE.scm project checkpoint file #2 (CI exemption) opens once approved.

🤖 Generated with Claude Code

hyperpolymath and others added 3 commits June 2, 2026 10:24
Draft RFC for owner review before any bulk conversion. Maps each of the
7 Kotlin files + 3 *.gradle.kts to a Rust/Tauri replacement, calls out
the unavoidable JVM-bytecode surface (Service / BroadcastReceiver /
AppWidgetProvider), and proposes a 9-PR landing sequence gated on
CI-exemption coordination for Tauri-generated Java shims under
src-tauri/gen/android/.

No Kotlin deleted, no Tauri scaffolding generated — pure planning
artefact. Both banned-language CI gates remain red until the sequenced
sub-PRs land in order.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Owner correction: estate target is Gossamer (Hyperpolymath's Zig+Ephapax+
webview framework), not Tauri. Rewrites the RFC accordingly:

- Component mapping now references GossamerActivity / GossamerBridge from
  hyperpolymath/gossamer (MPL-2.0), not Tauri 2.
- Sub-PR sequence rewritten: gossamer-rs crate + gossamer.conf.json
  scaffolding (panll precedent at src-gossamer/) rather than `cargo tauri
  android init`.
- New "Upstream-vs-downstream scope" section. Gossamer Phase 3 (Mobile)
  is webview-only upstream; foreground service, AppWidgetProvider,
  BroadcastReceiver, SensorManager integration have no Gossamer support
  today and no open Gossamer issues. RFC recommends building all seven
  downstream in neurophone's android/ tree as application-specific Java
  shims, not blocking on Gossamer upstream PRs.
- SPDX clash section removed; everything is MPL-2.0 per owner.
- Gradle DSL question added (Groovy *.gradle vs Kotlin *.gradle.kts).

Still draft, still no Kotlin deleted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Maps every external fun in NativeLib.kt to (a) the Rust JNI export
we'll write in crates/neurophone-android/src/lib.rs and (b) the
underlying neurophone-core API it composes. Surfaces 5 gaps:

1. No start/stop/is_running lifecycle on NeuroSymbolicSystem
   → keep as JNI-layer concept (bool in static state holder).
2. Sensor-type int→string mapping currently in Kotlin
   → move into Rust JNI layer as const lookup.
3. query() uses heuristic prefer_local; Kotlin queryLocal/queryClaude
   expect hard routing → introduce QueryRoute enum on core.
4. No get_neural_context() on core → add method composing from get_state().
5. No reset() on core → JNI layer replaces held instance.

Estimated sub-PR #4 size: ~250 LoC new Rust + ~10 LoC core change +
JNI roundtrip tests. Shovel-ready when sub-PRs #2 (CI exemption) +
#3 (Gossamer scaffolding) land.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath
Copy link
Copy Markdown
Owner Author

Added docs/migrations/JNI-SURFACE-AUDIT.adoc (commit 944f94f) as head-start for sub-PR #4.

Maps all 11 external fun in NativeLib.kt to Rust JNI exports and underlying neurophone-core APIs. Surfaces 5 gaps:

  1. Lifecycle (start/stop/isRunning) — no equivalent in core; keep as JNI-layer concept.
  2. Sensor-type mapping — move from Kotlin to Rust const lookup.
  3. Hard query routingqueryLocal/queryClaude need QueryRoute enum on core (small refactor).
  4. getNeuralContext — needs new method on core composing from get_state().
  5. reset() — JNI layer replaces held instance, no core change needed.

Estimated sub-PR #4 size: ~250 LoC new Rust + ~10 LoC core change. Shovel-ready when sub-PRs #2 + #3 land.

hyperpolymath added a commit that referenced this pull request Jun 2, 2026
…igration in flight) (#105)

## Summary

The `android/` subtree is owner-authored Kotlin/Gradle work-in-flight
currently being migrated to Gossamer (see RFC PR #97). It is not
vendored upstream — it is intentional code. Two complementary governance
checks currently treat it as a banned-language violation:

- `Check for Banned Languages` (this repo's
`.github/workflows/language-policy.yml` — self-contained `find -name
'*.kt' ...`)
- `governance / Language / package anti-pattern policy` (the standards
reusable's `Check banned-language files` step — honours
`.hypatia-ignore` + `.hypatia-baseline.json`)

This PR adds a coordinated carve-out for both. After it lands on `main`,
the recently-merged PR #101 (ESN spectral-radius scaling — obligation
1.1) inherits the carve-out for any follow-up CI runs, and future
Android-touching PRs go green by default until the Gossamer migration
lands and `android/` is removed.

## Changes

**Commit 1 — `chore(cicd): add android/** carve-out to
.hypatia-ignore`**

Adds two complementary exemption files at the repo root:

- `.hypatia-baseline.json` — canonical glob form (`file_pattern:
"android/**"`) with `tracking_issue: "#97"`.
Consumed by the standards governance-reusable workflow's `in_baseline()`
helper (glob → regex translation: `**` → `.*`).
- `.hypatia-ignore` — legacy per-file enumeration (10 lines, one per
currently-tracked `.kt`/`.kts` file). Belt-and-braces for any tool that
only reads the flat-file form (the governance reusable's `is_exempt()`
checks both; `grep -qxF` requires literal lines, not globs).

**Commit 2 — `ci(language-policy): exclude android/ from Java/Kotlin
banned-language scan (Gossamer migration in flight — RFC #97)`**

Edits the `Check for Java/Kotlin files` step in
`.github/workflows/language-policy.yml`. The inline workflow does not
read `.hypatia-ignore`, so it needs its own path exclusion. Replaces the
single-line `find -name '*.kt' ...` with a `-prune` + `-o` form that
excludes `./android/` and `./.git/`:

```bash
VIOLATIONS=$(find . \
  \( -path './android' -o -path './android/*' -o -path './.git/*' \) -prune -o \
  \( -name "*.java" -o -name "*.kt" -o -name "*.kts" \) -type f -print)
```

## Verification

Local verification before push:

- New `find` pattern returns empty on the current tree.
- Old `find` would have caught 10 files under `android/`.
- `git ls-files '*.kt' '*.kts' '*.java' | grep -v '^android/'` returns
empty — no non-Android Kotlin/Java anywhere, so the carve-out is
precise.

## Rollback condition

Once the Gossamer migration (RFC PR #97) lands and `android/` is removed
from the repo, all three additions (`.hypatia-baseline.json`,
`.hypatia-ignore`, and the `find -prune` clause in
`language-policy.yml`) should be removed together.

## Related

- RFC PR #97 — Android Kotlin → Rust/Gossamer migration (the work that
makes this carve-out time-bounded).
- PR #101 — ESN spectral-radius scaling (already MERGED; the two
governance checks were the last red on it).
@hyperpolymath hyperpolymath marked this pull request as ready for review June 2, 2026 11:17
@hyperpolymath
Copy link
Copy Markdown
Owner Author

Sub-PR #2 opened as #107 (draft).

Path 2B per the RFC: deletes the local Java/Kotlin check, relies on the estate-wide reusable. Companion to hyperpolymath/standards#341 which adds the android/**/src/**/*.java carve-out.

Merge order: standards#341 first → neurophone auto-consumes via @main pin → neurophone#107 second.

After both merge, sub-PR #3 (Gossamer scaffolding) becomes unblocked.

Q1: estate-wide CI exemption (standards#341).
Q2: self-answered.
Q3: drop widget-configure activity.
Q4: Groovy *.gradle.
Q5: emulator+Termux for dev, Oppo Reno 13 for final close-out.
Q6: contribute upstream — `gossamer-android-services` companion in
    hyperpolymath/gossamer with four shim base classes (Service,
    BroadcastReceiver, AppWidgetProvider, Activity-with-bridge).

Q6 changes the sub-PR sequence: a new #2b — Gossamer companion PR —
blocks the original #3 (neurophone Gossamer scaffolding). Revised
9-step sequence captured in the new "Sequencing implications of Q6"
section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath
Copy link
Copy Markdown
Owner Author

Q6 unblocker filed upstream: hyperpolymath/gossamer#71 — design + planning issue for the gossamer-android-services companion (four shim base classes: ForegroundService, BootReceiver, AppWidgetProvider, + GossamerActivity intent-hook extension).

The issue contains the full API sketch (Java + Zig), motivating neurophone use case, native-side surface, sequencing, and 4 owner sign-off asks.

Per the revised RFC sequence, neurophone sub-PR #3 (Gossamer scaffolding) is gated on the Gossamer companion landing. No neurophone work can proceed past sub-PR #2 (already in #117) until gossamer#71 lands.

This completes the in-session planning ladder. Three-system propagation infrastructure also in flight (.git-private-farm#66 + hypatia#418 + gitbot-fleet#248 + .git-private-farm#67) but those are orthogonal.

hyperpolymath added a commit that referenced this pull request Jun 2, 2026
…117)

## Why

PR #105 added the android/** carve-out via **two** mechanisms:

- (a) Prune `android/` from a local `find` step in
`.github/workflows/language-policy.yml`
- (b) Add `.hypatia-baseline.json` with `file_pattern: "android/**"`

The estate-wide reusable at
`hyperpolymath/standards/.github/workflows/governance-reusable.yml`
(called from this repo's `governance.yml`) **already** reads
`.hypatia-baseline.json` and exempts matched paths in its own
`language-policy` job (see `governance-reusable.yml:91-285`).

**(b) alone is sufficient.** (a) is a duplicate that uses the brittle
`find` mechanism the reusable explicitly warns against — `git ls-files`
respects `.gitignore` correctly.

## What this PR does

Removes the local `Check for Java/Kotlin files` step. The neurophone
`.hypatia-baseline.json` carve-out continues to handle the android/**
exemption via the estate-wide reusable.

## Net behaviour change

**Zero.** The local check duplicated the estate check. Both gate on
Java/Kotlin files; both exempt android/** via different mechanisms.
Removing the local check means there's one mechanism instead of two, and
the remaining mechanism is the auditable baseline file.

## Properties preserved

- Kotlin (`.kt`, `.kts`) is still fully banned — by the estate-wide
reusable's `enforce "Java/Kotlin files"` block.
- Non-Android Java (e.g. a stray `Foo.java` at repo root) is still
blocked — same path.
- The `android/**` carve-out is still respected via
`.hypatia-baseline.json`.

## Stale-entry cleanup at end of migration

When the Gossamer migration completes and `android/` is removed, the
baseline entry will become stale. The reusable's `Detect stale baseline
entries` job (`governance-reusable.yml:127+`) will warn. The entry can
be removed in the same PR that deletes `android/` (i.e. sub-PR #9 of the
migration sequence in RFC PR #97).

## Supersedes

- `hyperpolymath/standards#341` (closed) — estate-wide grep change was
redundant given the baseline mechanism already exists.
- `#107` (closed) — same intent as this PR but
pre-#105; rebase would have been a different change.

## Test plan

- [x] Workflow YAML is well-formed (visually verified).
- [ ] CI passes — local language-policy.yml runs without the Java/Kotlin
step; estate-wide reusable runs and exempts android/** via baseline.
- [ ] Negative test: a hypothetical `Foo.java` at repo root would still
trigger the estate-wide `Language / package anti-pattern policy` job
(verified by inspection of `governance-reusable.yml:345` + `enforce()`
at line 310 + `is_exempt()` at line 292).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant