S-7's account, date, payer and split chips (a wrapping FlowRow whose chips
moved when a value changed) become four fixed full-width rows in a stable
order: Konto, Datum, Betalt av, Delning. `EditorOptionRows` in :core:design
(segmented-group shape, the chips' tonal surface-container-high,
secondary-container while the split section is open; leading 24 dp element,
label column as wide as the widest label, value one line with an ellipsis,
trailing chevron or swap). At >= 1.5x font scale the label sits over the value,
which takes up to two lines and whose two-line height every row keeps.
A Robolectric test lays out S-7 with short and long values at 100 % and 200 %
and asserts identical row bounds. The chip composables stay (S-9, S-19).
S-11: connected buttons get 8 dp side padding so "Household" fits half the
filter bar at 1x.
Goldens re-recorded: every s7_* variant, the S-11 en_light goldens, the
analytics_filter_and_total en_light sheet, and the new editor_option_rows
sheet; oxipng -o 4 --strip safe.
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
- D-097: chart bars top 4 dp, bottom 2 dp (M8b f0575d3); design-spec §2.9, §3.10
- D-100: no SSH from the Mac to the Pis; M2c-pi delivers infra/pi/ for the owner
to run; OQ-3 answered by .env placeholders; revises D-023, keeps D-001
- D-101: kvitt-release.jks at the repository root, git-ignored; revises D-024
- M8 end-to-end test result and M8b fixes in §11.2/§11.3; OQ-5 answered;
128 px picture size not needed (§14.2.4)
- HANDOFF and prompts/README rewritten for the current state and numbering
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- Invite code field (S-1): the value stays the raw normalised code and the
grouping hyphens are a VisualTransformation with an exact OffsetMapping, so
the cursor stays after the last typed character when a hyphen appears and a
backspace after a hyphen removes the character before it. Unit tests.
- Category quick row and grid: the selected item's check badge is drawn in a
layer above the clipped item, so it is no longer cut at the tile corner.
- Edge to edge: KvittTopAppBar pads for the top safe-drawing inset (status
bar) with its fill behind it; an ancestor's inset is consumed, not doubled.
Catalogue sample with a 24 dp inset.
- Stacked bar chart corners 4 / 2 dp (owner override of 10 / 4, proposed D-097).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
The stored fcm_token is the Firebase Installation ID; HTTP v1 deprecates
message.token (accepted only during the transition) in favour of message.fid.
Push examples and push_fcm tests follow.
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
Resource-backed snackbars (showKvittSnackbar) in account, onboarding and
settings; SettingsScaffold replaced by KvittScaffold + SettingsMessageEffect.
KvittButton(loading) for S-6 saving and S-16 Synka nu. S-10 missing state
disables Synka nu offline (connectivity in the ViewModel, new
detail_missing_offline goldens). S-6 system back asks to discard unsaved
changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Records the readings of M7a to M7f (b9b061a, 6ac711d, 082b8dc, 31760f9,
1e42f76, a54eb7c) and the M6b design-system fix pass (3a1bd14): S-11/S-12
captions, titles and states; navigation wiring and result snackbars (§13.2);
new template day default (AC-9.8); manual update check (AC-16.6); FieldError
field names and settlement editing (AC-11.6); quickRowCategories; two-line
category labels; long day ranges (§9.8); amounts open with two decimals
(§9.6); string keys (strings.csv 578 rows); "Synka nu" online only (§9.9).
§11 marks M7a-M7f and M6b landed, M8 and M7g started; §11.3 lists contract
requests for a later contract pass (M12e). Docs only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Bugs: viewerPercentOf reads basis points (Samples in bp); FullSplitPicker and the
split labels use editor_split_full_on_you ("Allt på dig"); AmountInput.of restores
money with both decimals (AmountInput.percent for ratio fields), replacing M7c's
moneyInput; quickRowCategories does the D-060 fill itself; KvittScaffold resolves
resource-backed snackbars (showKvittSnackbar) while shown.
Gaps: ConfirmDialog optional and styled body; KvittButton loading form (M7a uses it,
ProgressBottomButton removed); InviteCodeField wraps after a group, placeholder,
imeAction; EmptyState icon tile and actionEnabled; DateFormatter.monthName and
dayRangeLong; AnalyticsFilterBar periodOwnRow; segmentedItem builder; screen tokens
in KvittDimens (OnboardingDimens removed); common_ok, common_undo; module-proposed
keys recorded in strings.csv; quick-row labels end in an ellipsis on line two.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Hub, profile (photo picker), categories (drag reorder, editor with D-059 delete reasons), recurring
expenses (list and S-7-style editor with schedule, AC-9.8 default start), devices, invites and about,
against the :core:model interfaces. Scenarios and scripted online failures in fakes/settings.
84 states screenshot-tested in sv/en x light/dark (336 goldens); state-holder unit tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Expense editor (S-7) and expense detail (S-8) in :feature:expense against the
:core:model interfaces and :core:design components: defaults (AC-8.6), keypad
input (§9.6), the four split modes with live preview (AC-8.5, AC-8.7, AC-8.8),
category suggestion with the picked-by-hand flag (CS-2, CS-3, CS-7), quick row
from frequentCategoryIds (D-059, D-060), inline errors, delete and discard
confirmations; detail with hero, facts, timeline, recurring, deleted and missing
states. Fake scenarios in fakes/expense, view-model tests and 127 goldens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
:feature:home: Home with balance hero, sync pill, accounts, archived section,
empty and first-sync states, update banner and "Tillåt installation" dialog;
sync status with pending rows, rejections of every §14.4.6 reason and the
clock-skew banner. :feature:account: account detail with header, ledger by
month, overflow menu, archive/unarchive/delete and their errors; account
editor with name, percent fields (two decimals), slider, presets, preview,
name_taken/validation errors and offline state. Fake scenarios in
fakes/home and fakes/account; full-screen Roborazzi goldens for every state
in sv/en × light/dark (200 % for long states); ViewModel unit tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
SettleScreen (global, per account, editing) and SettlementDetailScreen with
Hilt ViewModels over the :core:model interfaces; proposals, remaining,
warnings and enablement come from SettlementRepository. Fake scenarios in
fakes/settle/, 19 states x sv/en x light/dark goldens (+ 200 % for long),
unit tests for amount input and both state holders.
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
KvittTheme with every Material and Kvitt colour role light and dark, the 14
category colours with containers, chart and avatar colours, type scale with
tabular amount styles, spacing, shapes (stable fallbacks: MaterialShapes is not
in Material 3 1.4.0), elevation and motion springs; 191 Material Symbols
Rounded vector drawables (33 category tokens filled and outline, UI icons);
253 shared strings from docs/ux/strings.csv in values and values-sv with
plurals; pure-Kotlin MoneyFormatter, DateFormatter (ISO weeks) and AmountInput
with unit tests; every component of design-spec §3 with TalkBack semantics,
including the ledger rows and editor fields (D-019) and Canvas charts; a
preview catalogue in the debug source set; 212 Roborazzi goldens (49 sheets x
light/dark x sv/en plus 16 at 200 %). The two :app empty-screen goldens are
re-recorded because they render the real theme now (PNG only).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
ExpenseEditorOptions.frequentCategoryIds per active account (up to five active
categories by non-deleted expense count in the account, ties by sort_order) and
CategoryRepository.deletable(id) (AC-7.6), from Room queries in :data and the
same rule in :core:fakes. Room schema unchanged (version 1).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- D-055 build-time copy of the keyword table, D-056 parseCategoryKeywords,
D-057 enum declaration orders, D-058 E-7 example shows server_version
15 and 16 and its test compares them again.
- D-051 fulfilled: §9.7 hex values from docs/ux/design-spec.md §2.3;
design-spec.md binding, strings.csv the source of string resources.
- D-059: ExpenseEditorOptions.frequentCategoryIds and
CategoryRepository.deletable; new milestone M12d with PLAN-M12d and
prompts/M12d.md; M7a-f depend on M6 and M12d.
- suggestCategory readings (null when other is archived), §11.2 landed
marks and the 2026-10-18 dry run, dev stack on schema 2, PLAN-M12b
SQLite 3.51.x, design-spec §6 notes in PLAN-M6/M7c/M7f; HANDOFF rewritten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Kalas (muted) design spec with final tokens, components and every screen S-1 to
S-22 in all states; 80-frame screen catalogue with 155 PNGs in docs/ux/screens/;
strings.csv with D-017 module prefixes (558 rows, en and sv); colour tokens
renamed to the landed contract names slate and amber; fixed category ids;
proposed-decisions.md with the final §9.7 hex values and UX-9. Supersedes
tokens.md, components.md, screens.md and the U1 Kalas PNGs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2TeboHB8AtnzTrt3bWFzY
Owner picked direction 3 Kalas with calmer colours. The palette, category and chart
colours are muted (closest category pair CIEDE2000 13.7 light, 12.3 dark), the page
uses the Material 3 default typeface (D-020), S-7 shows the category chosen from the
description, and a before/after section compares bright and muted. Owner answers,
findings and proposed decision and spec text are in docs/ux/proposed-decisions.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three visual directions (Skärgård, Kvitto, Kalas) as self-contained HTML pages with
every SPEC §13.3 screen in light and dark, Swedish, plus PNG exports and the exporter
script. Direction-independent docs: ia.md, screens.md, flows.md, components.md,
tokens.md (values for all three directions), strings.csv (key,en,sv,notes), and the
comparison with a recommendation in directions/README.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- migrations/0002_categories_v2.sql, byte-identical to contracts/schema/migrations/0002_categories_v2.sql
(SPEC §14.1.1), with an identity test; SCHEMA_VERSION = 2 (E-1 reports it, the importer's check follows).
- Seed test on a fresh database (the fourteen rows of AC-7.2, server_version 11 to 14) and an upgrade test of a
v1 database in use (0001 applied through the migrator, then users, a custom, a renamed and an archived default,
an account, a recurring template, expenses): every earlier row unchanged, the four rows above the old
sequence, foreign_key_check empty, schema_version 2, a pull from the old cursor returns exactly the four rows.
- DefaultCategoryKey with the fourteen keys (wire enum and contracts/openapi.yaml); icons 33 and colours 14
(§9.7) in validation.
- The two category examples M12a held back (E-26 response-default, sync/pull-page-categories-v2) and its
contracts-check patch for them and the enum.
- Importer: splitwise_categories.csv per the §10.8 v0.8 table (D-053), DEFAULT_KEYS of fourteen, synthetic
fixtures for the finer mapping (Insurance/Försäkringar to insurance among them), a schema-1 refusal test.
- Test harnesses seed users at server_version 15/16 (after the fourteen seeds); pull counts 10 -> 14.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- :core:model: DefaultCategoryKey in AC-7.2 order (14 keys: housing, insurance, children, fees added),
ColorToken + SLATE, AMBER, CategoryIcons.ALL + key, shield, account_balance (33),
ExpenseRepository.suggestCategory(accountId, description); ContractTest updated.
- :domain: parseCategoryKeywords (pure, shared by :data and :core:fakes) with a parser test; the
suggestCategory records and rule unchanged, suggest_category vectors pass.
- :data: PlatformUnicodeText (java.text.Normalizer, Character), CategoryKeywords parsed once from the
Java resource that copyCategoryKeywords copies from contracts/category-keywords.csv at build time
(missing file fails the build), ExpenseRepositoryImpl.suggestCategory over the Room snapshot and the
DataStore last-used category; tests: byte identity and the 14.8 rules of the bundled table, the
UnicodeText binding, and Room/Robolectric cases for history, first token, ties, keywords, fallback,
archived categories, deleted expenses, both users and accounts, null account.
- :core:fakes: the 14 defaults with the schema v2 ids, fake suggestCategory through :domain with its own
build-time copy of the table; tests.
- Room schema unchanged (version 1).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- contracts/schema/migrations/0002_categories_v2.sql: server migration 0002 rebuilds categories with the
14-key default_key CHECK, inserts housing, insurance, children and fees with server_versions taken from
the global sequence, sets schema_version 2. server-v1.sql unchanged.
- contracts/category-keywords.csv: the keyword table of the suggestion (148 entries, from docs/ux).
- contracts/test-vectors/suggest_category.json: 65 Kotlin-only cases of CS-4 to CS-6.
- contracts/examples/E-1-health: schema_version 2.
- tools/contracts-check.py: reference suggestCategory, keyword-table checks, 0002 load checks on a fresh
and an in-use database, plain icon/color check, new icon and colour tokens.
- android/domain: suggestCategory (CS-4 to CS-6, code points, platform NFC and categories through
UnicodeText), vector runner for suggest_category, eight vector files.
- server/crates/kvitt-domain/tests/vectors.rs: suggest_category is Kotlin-only.
The OpenAPI DefaultCategoryKey extension and the two new category examples are held back until the
server's wire types know the new keys (M12b).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
D-044 adds nine Swedish Splitwise category names to the SPEC §10.8 table and
splitwise_categories.csv. D-045 records the reconciliation against the totals
rows; D-046 the importer CLI semantics and exit codes. M9 marked landed at
ab298ab, the M11 rehearsal recorded, server/README.md and prompts/README.md
updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
CLI per §10.8 with --help, --dry-run and a summary; English and Swedish
headers with member columns matched by name; CSV quoting; payments as
settlements with one allocation; trailing totals row used to reconcile
the account balance (mismatch rolls back, exit 4); non-SEK rows abort
unless --fx; committed category table data/splitwise_categories.csv;
idempotent re-runs through import_key; rows validated with the server's
push checks, audited with source import, no pushes; one BEGIN IMMEDIATE
transaction. Tests over synthetic fixtures only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- D-035 unusable mutation_id/entity_type/entity.id fails the whole push with 400; other shape errors per mutation.
- D-036 0 mutations is batch_too_large ("outside 1 to 100").
- D-037 never-synced delete runs full step-8 validation; current on every rejection; created_at kept; canonical ids.
- D-038 FCM retries: one send plus three retries (1/5/25 s), 401 drops the token, other 4xx not retried.
- D-039 pruning strictly older than 90 days; tasks::start returns AppState with the push queue, SqlAuditWriter default.
- Clarifications: step 8 order, negative since, pull read transaction, FCM JWT kid and cache, token clearing only if
unchanged, unusable credentials, scheduler clock jumps, rotate-epoch exit codes, /i/ empty code, image build needs
a C toolchain and CA roots (dev image rebuilt 2026-10-18).
- PLAN-M4 pruning wording, prompts/README status, HANDOFF at bd6f10a.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
- src/sync/: pull with UNION ALL paging and cursor_ahead; push processing of SPEC §14.4.4 steps 1 to 12 in one
BEGIN IMMEDIATE with a savepoint per mutation, LWW with delete-wins, tombstones, clock_skew, replay from
sync_mutations, sync_batch for more than 3 notifiable changes; 90-day pruning.
- src/audit/: SqlAuditWriter and the §14.4.7 diff (now the default in AppState::new).
- src/push/: bounded PushHub (100, drop oldest), FCM HTTP v1 client with service-account JWT (RS256, jsonwebtoken
on aws-lc-rs) and token cache, sender with 1/5/25 s retries and token clearing; disabled without credentials.
- src/tasks/: start installs push and spawns the 06:00 generation (and at start) and 04:00 pruning loops on the
injectable clock.
- cli: rotate-epoch; serve uses into_make_service_with_connect_info; stale doc comments updated; /i/ empty code
gets the HTML 404.
- tests: sync_api, push_fcm (local stub of Google's endpoints), scheduler_daily; skeleton routes test now asserts
no route is 501.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
Records the decisions proposed by M3 (503cee5) and M5 (c28c52b). M5's proposals
D-030 to D-033 are renumbered D-040 to D-043; D-035 to D-039 are reserved for M4.
Adds the D-027 clock-skew re-stamp rule, M5's contract requests for M8 (PLAN-M8,
SPEC 11.3), the connect-info note for M4 (PLAN-M4), the picture size note (PLAN-M6),
and updates prompts/README status.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
DAOs on M2b's Room 3 entities and KvittDatabase v1 (schema 1.json unchanged), DTOs for every wire
object and endpoint body with a round-trip test over all 80 contract examples, the Ktor client with the
section 14.3.1 headers and 14.3.2 error mapping, the Keystore-encrypted token store in auth.preferences_pb,
the sync engine of section 14.4 (push batches, pull pages, epoch recovery, rejections, clock skew, 401 and
426 handling) tested with MockEngine including AC-15.6, WorkManager triggers, every :core:model repository,
the APK updater behind an installer interface, Coil pictures, Hilt modules, Room migration test
infrastructure. Fakes: the five settled TODO(spec) comments resolved per SPEC v0.4 (settlement row in an
account's ledger carries that account's allocation).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Records the decisions proposed in the M2a (fdf2093) and M2b (fad6fc9)
reports: D-005 OpenAPI equality normalisations and CommonRules, D-006
bp_sum_not_10000 token, D-007 per-crate no-unwrap deny, D-010 Roborazzi,
D-011 IncompleteCustomRangeException, D-012 Room 3 with M2b's entities and
1.json as a contract file, D-013 JDK 25 Robolectric flags, D-014
failOnNoDiscoveredTests (D-008, D-009 unused). Clarifications in §6, §8.2,
§8.6, §9.6, §10.3, §10.4, §11, §14.2, §14.3, §14.5, §14.7, §14.8 and
AC-11.7. PLAN-M5 builds on the existing entities; v0.4 readings added to
PLAN-M3, M5, M7b to M7f. HANDOFF rewritten: next free D-040, spec v0.5,
server schema 2, Room schema 2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>