refactor: retire the brand before mantra, and keep the three records of it

Phase 0 of docs/curated-to-mantra.md, item 2. The app has been Mantra since
before this repository had a docs/ directory, and four things still said Torch,
the brand before it: the theme composable, `TorchTheme`, at 116 sites; the
user agent, `UserAgent.APP_NAME` and `CLIENT_NAME`; two error strings a user
could actually be shown ("tell X to use Torch", "finished setting up Torch");
and iOS, where `Config.xcconfig` built `PRODUCT_NAME=Torch` under the bundle id
`ac.aux.compose.Aux` and the project's product reference was still `Aux.app` --
two brands ago. All four now say Mantra.

**This is done natively, before anything is pulled from the fork, so that the
fork's theme maps onto a name that means something.** The Curated fork retired
Torch itself in d26cf6c7 (`TorchTheme` -> `CuratedTheme`, later `CurareTheme`).
The pull rewrites every fork commit into this repository's names, and until
now the only honest target for `CurareTheme` was `TorchTheme` -- the brand
before last -- which would have had every pulled screen wrapping itself in a
name the app stopped using long ago. `MantraTheme` is what the rewrite maps to
from here; docs/scripts/curated-unbrand.py's theme, `UserAgent` and iOS rules
are changed in this commit for that reason, and the comment that said "change
here if Phase 0 renames it" goes with them. Retiring Torch as part of the pull
instead -- by taking the rewritten d26cf6c7 -- was rejected because that commit
would then be a fork commit renaming something the fork never had, and because
the iOS half of the debt was never the fork's to pay.

**The user agent went last, and the reason it could go at all is written where
the next reader will look.** `CLIENT_NAME` is the `client` tag on every relay
list this app publishes, which is why it was left alone when the top bar was
fixed: it goes on the wire, so it read as a network identity question. It is
attribution and nothing derives from it, which is the difference between it
and the two `mantra/` hash tags that must never move; the comment on the
`HomeScreen` bar and the paragraph in material-design-conformance.md that both
said "still says Torch" now say that, in order, rather than describing a state
this commit ends.

**Three records keep the old name on purpose.** material-design-conformance.md:278
records a finding -- the top bar once read "Torch" while the window title read
"Mantra" -- and changing it would falsify the record. The paragraph at :654 is
rewritten rather than deleted because it was a statement of current state, and
the current state changed. `NavigationRoutingTest`'s "Introducing... Torch"
fixture is the string a stranded user actually saw, and the test is about the
stranding. Nothing else in the tree says Torch.

**iOS is unverified.** The build gates the apple targets behind `isMacOsX` and
this was built on linux; the xcconfig and pbxproj edits are the same shape the
fork's went through, and this is the first time this repository has named
itself there. Build it on a Mac before believing it.

:composeApp:compileDebugKotlinAndroid and :composeApp:compileKotlinJvm clean;
:composeApp:jvmTest 736 tests, 0 failures; :composeApp:testDebugUnitTest 403
tests, 0 failures; :composeApp:m3Audit all budgets met.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kgothatso Ngako
2026-09-13 11:42:36 +02:00
parent b796a32be3
commit 4681ac1a03
74 changed files with 161 additions and 155 deletions

View File

@@ -121,7 +121,7 @@ Three gaps:
today; the trap springs the first time an expressive component does.
2. **The medium and high contrast schemes are dead code.** All four are declared
`private val`; `TorchTheme` only ever selects `darkScheme` or `lightScheme`.
`private val`; `MantraTheme` only ever selects `darkScheme` or `lightScheme`.
There is no plumbing to the platform's contrast setting, so the two schemes
that would honour it are unreachable. The accessibility foundation's first
principle is *honour individuals* — *"supporting varying preferences and
@@ -138,7 +138,7 @@ Two entry points render outside the theme:
under the **default** `MaterialTheme`. Its `MaterialTheme.colorScheme.error`
and `typography.headlineSmall` are baseline M3, not this app's. It is the
first screen a desktop user sees.
- `Profile.kt:183` and 50 other `@Preview` bodies wrap in `TorchTheme` by hand,
- `Profile.kt:183` and 50 other `@Preview` bodies wrap in `MantraTheme` by hand,
which is correct, but there is no preview that exercises dark, high-contrast
or a wide window — so nothing catches the two problems below.
@@ -418,7 +418,7 @@ find-and-replace.
Keep the existing hex values for the roles already defined so nothing shifts
visually; this phase adds, it does not restyle.
2. **Reach the contrast schemes.** *Built.* `TorchTheme` takes a `ThemeContrast`
2. **Reach the contrast schemes.** *Built.* `MantraTheme` takes a `ThemeContrast`
defaulted from a new `platformThemeContrast()` expect/actual, and the six-way
selection table lives in common code as `appColorScheme`. The old
`themeColorScheme` expect took four arguments and did both jobs; it splits into
@@ -441,7 +441,7 @@ find-and-replace.
3. **Give the theme its other three slots.** *Built.* The expressive-vs-standard
question was put to the product owner on 2026-09-08 and answered
**expressive**, so `TorchTheme` calls `MaterialExpressiveTheme` with all four
**expressive**, so `MantraTheme` calls `MaterialExpressiveTheme` with all four
slots passed explicitly — colour, `MotionScheme.expressive()`, `MantraShapes`,
`AuxTypography`. Explicitly, because `MaterialExpressiveTheme` would otherwise
default them to `expressiveLightColorScheme()` and friends, which is Material's
@@ -472,10 +472,10 @@ find-and-replace.
`container`/`on`/`onContainer`. `CreateProfileScreen.kt:311` and `:329` then
stop pairing them with `Color.White`/`Color.DarkGray` by eye.
6. **Wrap the desktop gate.** *Built.* `Main.kt` moves `TorchTheme` outside the
6. **Wrap the desktop gate.** *Built.* `Main.kt` moves `MantraTheme` outside the
`unlocked` branch, so `PassphraseGate` — the first screen a desktop user sees —
stops composing under the default `MaterialTheme`. `MantraApp` keeps its own
`TorchTheme`, so the unlocked branch is wrapped twice: android and ios enter
`MantraTheme`, so the unlocked branch is wrapped twice: android and ios enter
through `MantraApp` and would lose the theme entirely if it moved out, and a
second application of the same values costs one `CompositionLocalProvider`.
@@ -651,8 +651,12 @@ the whole sentence is, and several of them are pluralisations —
`(if (n == 2) "event" else "events")` — which want a real plural resource rather than a
format argument. `m3-extract-formatted.py --remaining` lists them.
`UserAgent.APP_NAME` still says "Torch". It goes on the wire to relay operators, so it is
a network identity question rather than a content one.
`UserAgent.APP_NAME` and `CLIENT_NAME` said "Torch" until the pull described in
[curated-to-mantra.md](./curated-to-mantra.md) retired the brand for good, in its Phase 0.
They were left for last on purpose: `CLIENT_NAME` goes on the wire as the `client` tag on
every relay list this app publishes, so it was a network identity question rather than a
content one. It could move because it is attribution and nothing derives from it -- unlike
the two `mantra/` hash tags, which are inputs to key derivation and must never move.
---
@@ -729,13 +733,13 @@ panes last because both were product decisions.
see below.
2. **`Breakpoint`**, the five-value enum with `ofWidth` as a pure function so the
thresholds are assertable without a Compose runtime. `TorchTheme` classifies
thresholds are assertable without a Compose runtime. `MantraTheme` classifies
once and provides `LocalBreakpoint`, so no two screens can disagree about the
window they are both in.
It reads `currentWindowDpSize()` rather than `currentWindowAdaptiveInfo()`,
because the latter also computes a `Posture` from the platform's fold state —
on android, `WindowInfoTracker` and an activity. This call sits in `TorchTheme`,
on android, `WindowInfoTracker` and an activity. This call sits in `MantraTheme`,
which wraps all 51 `@Preview` bodies in the tree, and a preview context is not
an activity.
@@ -950,9 +954,9 @@ reduced. And a crossfade is told from a cut by the one frame in which *both*
states are composed.
**A test found a design flaw rather than a bug.** The reduced case read 54dp of
slide at first, because the test provided `LocalReducedMotion` around `TorchTheme`
slide at first, because the test provided `LocalReducedMotion` around `MantraTheme`
and the theme overwrote it. The fix is not in the test: `reducedMotion` is now a
`TorchTheme` parameter defaulted to the platform, exactly as `contrast` is,
`MantraTheme` parameter defaulted to the platform, exactly as `contrast` is,
because a value nothing can override is a value nothing can test — and because the
desktop actual is a hardcoded `false` that a settings screen will need to override
anyway.
@@ -1017,7 +1021,7 @@ finding that out is most of what it was worth.
High contrast is deliberately not in the annotation. Contrast is a property of
the scheme rather than of a screen, `ColorSchemeContrastTest` measures every
pair in all six, and there is no `@Preview` parameter for it — it needs
`TorchTheme(contrast = …)` in the body. `ThemeGallery` covers the six once,
`MantraTheme(contrast = …)` in the body. `ThemeGallery` covers the six once,
over components rather than screens, with `dynamicColor = false` because an
android 12+ preview would otherwise paint all six columns from the wallpaper.
It is the only place the medium and high contrast schemes can be seen at all.
@@ -1104,7 +1108,7 @@ Phases 1–5 can be worked in parallel by different people if 1 lands first;
`ExperimentalMaterial3ExpressiveApi` opt-ins and the pinned alpha both point
expressive, but it is a product decision with a visible outcome, and it should
be made deliberately rather than inherited from an import.
- **Whether dynamic colour should keep overriding the brand.** `TorchTheme` defaults
- **Whether dynamic colour should keep overriding the brand.** `MantraTheme` defaults
`dynamicColor = true`, and on Android 12+ that wins unconditionally — so on
essentially every current Android device, none of the six schemes below is used
and the app renders in whatever the user's wallpaper produced. Verified on an API