AppBuildInfo gains releaseServerUrl, which :app fills from RELEASE_SERVER_URL (https://<kvitt.publicHost>:8443,
D-121). AboutRepositoryImpl shows it while no server is paired, in place of DataModule.DEFAULT_SERVER_URL, which
is deleted: :data no longer names a host. The fakes' AboutState uses the fixture https://kvitt.example.org:8443;
30 S-21/S-22 goldens re-recorded (only the host changed; oxipng -o 4 --strip safe). New test: unpaired About shows
the release server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
The release server's host name comes from -Pkvitt.publicHost, else kvitt.publicHost in
android/local.properties, else the placeholder kvitt.example.invalid. :app exposes it as
BuildConfig.PUBLIC_HOST, the manifest placeholder publicHost (the https invite-link intent
filter, port 8443) and RELEASE_SERVER_URL, which :feature:onboarding receives through the
@ReleaseServerUrl Hilt qualifier for S-1's prefill. Tests and goldens use fixed hosts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
AndroidApplicationPlugin and AndroidLibraryPlugin repeated the SDK levels, Java 17, unit-test resources, lint, Kotlin,
Robolectric JVM and JUnit set-up; both call configureAndroidModule(CommonExtension) now (the gate's task inputs did
not change: every test task stayed up to date). :app imports nothing from coil-compose; it still arrives through
:core:design, so the runtime classpaths are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
* Milestone and ownership notes that no longer hold ("owned by M5/M6/M8", "M8 replaces it", "(M8b)", "M7x add",
"proposed decision in the M5 report", which is D-027 and D-028) in build scripts, :core:design, :core:fakes, :data,
:app, :core:model and the settings string files.
* SyncEngine: the clock_skew KDoc sat orphaned above restampAfterClockSkew's; it now documents renewMutationId.
* libs.versions.toml header: no milestone, the drift date.
* README: the module table as built (no owners, no DebugFakesModule, which M8 removed), the test fixtures in the
architecture check and the shared screenshot harness.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Eight modules each had a copy of the same configuration-context harness (ScreenSnapshots/Snapshots); M12f had to fix
the same capture bug in two of them. :core:design's test fixtures (src/testFixtures, package
se.hagstedt.kvitt.design.testing) now hold Variant, TestClock, TestEnvironment, ScreenSnapshots with its three
capture modes (Root; Screen, with the hand-advanced frame clock; ScreenWhenIdle), MainDispatcherRule and withMain.
Test fixtures rather than a :core:testing module: :core:design's own tests can use them without a dependency on
itself, no module is added to the §6.2 graph, and lint treats them as test sources. The architecture check rejects
test fixtures outside test configurations. Modules keep only their own frame (analytics household, settle size and
inline overlays, settings avatar pictures). Every golden is unchanged.
kvitt.android.screenshots applies Roborazzi with outputDir src/test/screenshots and adds the screenshot test
libraries; the feature plugin applies it and adds the test fixtures, so the seven feature build scripts lose their
copied blocks. Removed: roborazzi-junit-rule and roborazzi-compose (never imported), the explicit
lifecycle-viewmodel-compose (feature plugin, :app) and hilt-lifecycle-viewmodel-compose in :app (both still arrive
through the features; release and debug runtime classpaths are identical), and the unused catalog entries
androidx-hilt-navigation-compose and androidx-lifecycle-viewmodel-compose. @Before/@After setMain pairs in nine test
classes use MainDispatcherRule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- AppViewModel.onLink drops an invite link while paired (AC-2.6, D-084), so it never prefills S-1 later.
- PushTokens: a failed E-17 PUT (network, 429, 5xx) is kept by PushTokenRetry and sent again after each successful
sync until accepted; a newer FID replaces it (AC-13.7).
- Notifications are VISIBILITY_PRIVATE with a generic public version (notif_generic_title, notif_generic_body):
no description, amount, account or name on a secure lock screen (security review L-7).
- androidResources.localeFilters en, sv (SPEC G-8, AC-19.1).
- Tests: AppViewModelTest (links while paired and unpaired, AC-2.9 permission after the first finished successful
sync), PushTokenRetryTest, NotificationPrivacyTest.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
NavHost enter, exit, pop and size transitions are None, so pushed screens,
tab switches, editors and the session gate change at once instead of the
default 700 ms cross-fade. NavTransitionsTest asserts the values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
isMinifyEnabled and isShrinkResources on the release build type with
proguard-android-optimize.txt and an app proguard-rules.pro that adds no rules:
the libraries' consumer rules cover kotlinx.serialization, Room 3, Hilt, Ktor,
WorkManager (worker class names kept) and Firebase. Debug is unchanged.
Signed release APK 43 356 272 -> 11 019 453 bytes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
AboutScreen loses onOpenLicences, AboutActions its onOpenLicences, the
:app call site its no-op lambda. settings_about_licences was unused in
every module afterwards and is dropped from both strings.xml files and
docs/ux/strings.csv. The 24 S-22 goldens are re-recorded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Drop the en_dark variant from every screenshot helper and render every
golden at mdpi (Robolectric qualifier w411dp-h<height>dp-mdpi instead of
xhdpi/xxhdpi), so a 411 x 891 dp screen is 411 x 891 px. All goldens
deleted and re-recorded; 1143 -> 865 PNGs. README describes the layout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Server: E-3 invite_kind; E-12 link ?from=<admin name>, E-4 passes it into kvitt://invite;
X-Kvitt-App-Version-Name stored in devices.app_version_name (migration 0003, schema 3);
OpenAPI and examples follow. Phone: :core:model, :core:fakes, :data, features and :app
updated together; goldens re-recorded for S-20 (version name) and new S-1/S-22 states.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
:app binds every :core:model interface to :data in debug and release (AppDataModule, D-085); the
debug fakes module is gone. NavHost over all feature screens with the session gate (S-1, S-2, S-18),
bottom destinations from each screen's own bar, result snackbars, deep links (kvitt://invite and the
/i/<CODE> landing link) and notification taps. :adapter:fcm maps payload v1 to PushEvent and registers
by Firebase Installation ID (firebase-messaging 26, D-087). Push work runs in WorkManager: expedited
sync waited up to 20 s, then the notification from local data or the generic text. WorkManager uses
KvittWorkerFactory, Coil uses :data's ImageLoader, SyncTriggers on start and foreground, update check
in release only. Network security configs (debug cleartext, D-086; pinned-root variant), release
signing from local.properties, notification permission after the first sync.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
AppBuildInfo from BuildConfig for both variants (buildConfig enabled); the
release source set includes DataBindingsModule (D-041), so features'
@HiltViewModels resolve in release as they do against the debug fakes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Features' @HiltViewModel classes need bindings in :app's graph; until M8 binds :data, debug builds bind
FakeRepositories(FakeScenarios.empty). Debug source set only; M8 deletes the file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Gradle project in android/ with every module of SPEC §6.2 (build-logic convention plugins, version catalog,
architecture check in check), :core:model types and repository interfaces of §14.2.4 and §14.5, :domain
implementing §14.5.2 and §14.5.4 (125 vector cases), scenario-driven fakes with the empty scenario, Room 3
entities and KvittDatabase v1 with exported schema, :app shell with Hilt, en/sv strings with MissingTranslation
as a lint error, Roborazzi goldens of the empty screen in light and dark.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>