You are Worker C on the Bookshelf project (~/bookshelf). Implement the DATA LAYER. FIRST, READ THESE — they are the contract, follow them exactly, do not invent alternative names or restate them back to me: ~/bookshelf/docs/SPEC.md (authoritative product/technical spec) ~/bookshelf/docs/HANDOFF.md (operational state and gotchas already paid for) ## Your scope — these packages ONLY, under app/app/src/main/java/org/modg/bookshelf/ data.local Room: BookEntity/BookcaseEntity/ShelfEntity, SyncState, Converters, BookDao/BookcaseDao/ShelfDao, BookshelfDatabase data.remote PocketBaseApi (Retrofit), request/response DTOs, PbAuthInterceptor data.repo BookRepository, LocationRepository, AuthRepository, SyncEngine data.prefs SettingsStore (DataStore: server URL, auth token, per-collection sync cursors, last-sync time) Plus `AppContainer` (manual DI, per SPEC "NO Hilt/kapt") and its wiring into the existing BookshelfApplication.kt. ## HARD BOUNDARIES — you share this repo with Worker D, running right now - DO NOT create or edit anything under `data.metadata`, `ui.scan`, or any `ui.*` package. Those are Worker D's / wave 3's. Touching them WILL cause a conflict. - DO NOT edit `app/build.gradle.kts` or `gradle/libs.versions.toml`. Every dependency you need (room+ksp, retrofit, kotlinx-serialization, okhttp, datastore, work-runtime, robolectric, coroutines-test) is ALREADY declared and wired. If you genuinely believe something is missing, DO NOT add it — say so in your final report and work around it. - Worker D will need metadata lookup reachable from AppContainer. Do NOT try to wire it. Just leave AppContainer easy to extend; D exposes a plain class that gets wired later. ## Build/verify — CRITICAL Never run `./gradlew` directly; a second worker builds concurrently and you will corrupt each other's build. ALWAYS build with the serialized wrapper: ~/bookshelf/tasks/gw assembleDebug ~/bookshelf/tasks/gw testDebugUnitTest It takes the lock and may block until the other worker's build finishes. That is expected — wait for it, do not bypass it. ## Definition of done — all must actually pass, verified by you, not assumed 1. `~/bookshelf/tasks/gw assembleDebug` exits 0. 2. `~/bookshelf/tasks/gw testDebugUnitTest` exits 0. 3. Real unit tests with real assertions (SPEC: "Do not write assertion-free tests"): - SyncEngine conflict resolution / last-write-wins on `updated` - push ordering + syncState transitions, incl. 404-on-update -> drop local, 409/duplicate-id -> switch to PATCH - client-side 15-char lowercase-alnum id generation - DAO queries via Robolectric, proving `deleted = 0` filtering works 4. Soft delete everywhere. All reads come from Room. Nothing blocks on network. ## Report back (keep it short — it is read by a token-constrained orchestrator) - exact pass/fail of the two gradle commands above - files created, one line each - anything in SPEC.md you could NOT satisfy, and why. Do not paper over gaps: a truthfully reported gap is worth more than a false green.