Harden Superuser architecture boundaries
1. Summary
Stacks on #8 (codex/superuser-mode-transitions-and-ui). Merge after #8.
The seven-file scope adds source guardrails, regression fixtures, debug validation, and developer documentation without changing production runtime code:
Makefile
.github/workflows/common-build.yaml
app/src/test/java/com/nutomic/syncthingandroid/runtime/RuntimeSeamTest.java
app/src/test/java/com/nutomic/syncthingandroid/service/SyncthingBackupArchiveTest.java
scripts/check_superuser_architecture.py
scripts/test_superuser_architecture.py
docs/superuser-architecture-guardrails.md
2. Evidence
- Before: Red-first fixtures reproduced unauthorized preference writes, privileged interface expansion, backend bypasses, conditional import catch fallthrough, and Kotlin key-alias misses.
- After: 39 architecture fixtures and the production scan pass. Focused archive, import-preference, and runtime JVM regressions pass.
make test,make all,IS_COPILOT=true make all, andgit diff --cached --checkpass. The repository's allowlistedallvalidation passed on the staged revision (validation-2026-10-11T08-26-03-617Z-302cbc7c). Final local Cubic review found no issues; the formal delegation review passed all technical checks, includingchange_attribution. - Physical qualification: Magisk, KernelSU, APatch, and SELinux device testing NOT RUN.
3. Merge danger
Door: Two-way
Blast radius: Validation
The new checks run in make test and normal debug PR validation. Release and signing behavior are unchanged. This PR depends on #8 and should be merged only after its base branch is integrated.
Summary by cubic
Adds source-level architecture guardrails that prevent regressions of the Superuser Mode boundaries without changing production runtime code. A new Python checker scans production Java, Kotlin, AIDL, and Gradle files for forbidden patterns, backed by 39 positive and negative fixtures.
- Debug CI and
make testnow run the checker, fixtures, and JVM unit tests; release and signing behavior is unchanged. The check rejectslibsuimports outside the two root transport files, non-corelibsuartifacts, generic privileged APIs, RootService/AIDL, process-name signals in execution arguments, mode-preference writes outside the approved owners, and import paths that mutate live state before validation aborts. - New JVM regressions verify a root backend launch failure never falls back to the App UID backend, malformed backup extraction leaves live config and identity files byte-for-byte intact, and a superseded device refresh never reaches the folder editor. The folder editor test releases its latches in a
finallyblock so a failure cannot hang the suite. - Docs capture the enforced boundaries and the checker's deliberately bounded lexical scope.
This stacks on the superuser-mode-transitions-and-ui branch and should merge after that base is integrated.