offline-first
Skill0 starsChanged 32 days agoMITView on GitHub
Offline-first Android data discipline: the local source as the single source of truth, repositories as the only data-layer entry point, Room without main-thread access, migrations instead of destructive fallback, and one DataStore instance per file. Use when writing or reviewing repositories, Room databases, DataStore delegates, or sync workers. Core rules have machine checks in checks/.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- Cure-Consulting-Group/ProductEngineeringSkillstheir own version, first seen Nov 4, 2026
- ffsshhttiikk/opencode-agents-skillstheir own version, first seen Feb 28, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- Cure-Consulting-Group/ProductEngineeringSkills★ 3same name
- ffsshhttiikk/opencode-agents-skills★ 2same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository11 other files it gives its agents.
- android-testingSkill
- compose-architectureSkill
- compose-migrationSkill
- compose-multiplatformSkill
- compose-performanceSkill
- compose-testingSkill
- dependency-injectionSkill
- gradle-build-logicSkill
- kmp-fundamentalsSkill
- material3-themingSkill
- release-engineeringSkill
The file
We split this skill into the rules it gives an agent
8 rules. About 0 are common, 5 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 16
After ANY data-layer edit, run the Verification step (bottom of this file).
- Distinctive: line 20
MUST structure offline-first reads around a local source of truth (a database): repositories expose data as observable types (Flow) read …
- Distinctive: line 26
MUST NOT let state holders touch data sources directly: other layers "should not access data sources directly.
- Very specific: line 56
MUST NOT call allowMainThreadQueries(): "To prevent queries from blocking the UI, Room does not support database access on the main thread" …
- Very specific: line 78
MUST NOT ship fallbackToDestructiveMigration(): when a migration is needed and no path is defined, Room "permanently deletes all data from …
- Distinctive: line 108
MUST NOT create more than one DataStore instance for a given file in the same process: "Doing so can break all DataStore functionality...
- Distinctive: line 147
Run after every data-layer edit (from the project root):
- Very specific: line 156
All four must pass (exit 0) before declaring the task done.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Offline-First
Operational rules for a coding agent. RIGHT/WRONG pairs are canonical. After ANY data-layer edit, run the Verification step (bottom of this file).
1. The local source is the single source of truth
MUST structure offline-first reads around a local source of truth (a database): repositories expose data as observable types (Flow) read from the local source, and writes go to the local source first, then sync to the network. "An offline-first app must be able to perform read operations without network access."
MUST NOT let state holders touch data sources directly: other layers "should not access data sources directly. The entry points to the data layer are always repository classes" — state holders (ViewModels) "should never have a direct dependency on a data source". The domain and UI layers must never communicate directly with the network layer.
WRONG:
@HiltViewModel
class FeedViewModel @Inject constructor() : ViewModel() {
private val api = Retrofit.Builder()
.baseUrl("https://example.com/")
.build() // network source in a state holder
}RIGHT:
@HiltViewModel
class FeedViewModel @Inject constructor(
private val feedRepository: FeedRepository, // Flow from the local DB
) : ViewModel()The check flags data-source imports (Retrofit, OkHttp, Ktor, Room, DataStore) inside ViewModel files. Repositories are exempt — they ARE the data layer.
2. Room never runs on the main thread
MUST NOT call allowMainThreadQueries(): "To prevent queries from
blocking the UI, Room does not support database access on the main
thread" — the builder escape hatch reintroduces exactly the jank and ANR
Room exists to prevent. DAOs expose suspend functions for one-shot
operations and Flow for observable queries, executed from coroutines.
WRONG:
Room.databaseBuilder<AppDb>(context, "app.db")
.allowMainThreadQueries()
.build()RIGHT:
Room.databaseBuilder<AppDb>(context, "app.db").build()
// @Dao: suspend fun loadUserById(id: Int): User
// fun loadUsers(): Flow<List<User>>3. Migrations, not destruction
MUST NOT ship fallbackToDestructiveMigration(): when a migration is
needed and no path is defined, Room "permanently deletes all data from
the app's database tables" — silent user-data loss on every schema bump.
Provide Migration(startVersion, endVersion) objects via
addMigrations(...). The targeted variants
(fallbackToDestructiveMigrationFrom / ...OnDowngrade) are the
documented tools for deliberately scoped rebuilds — the check flags only
the blanket option.
WRONG:
Room.databaseBuilder<AppDb>(context, "app.db")
.fallbackToDestructiveMigration()
.build()RIGHT:
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Book ADD COLUMN pub_year INTEGER")
}
}
Room.databaseBuilder<AppDb>(context, "app.db")
.addMigrations(MIGRATION_1_2)
.build()4. One DataStore instance per file
MUST NOT create more than one DataStore instance for a given file in the
same process: "Doing so can break all DataStore functionality... DataStore
will throw IllegalStateException when reading or updating data." Each
preferencesDataStore(name = ...) delegate maps to a file — names must be
unique across the app.
WRONG:
// SettingsStore.kt
val Context.settingsStore by preferencesDataStore(name = "settings")
// ProfileStore.kt
val Context.profileStore by preferencesDataStore(name = "settings") // same file!RIGHT:
// SettingsStore.kt
val Context.settingsStore by preferencesDataStore(name = "settings")
// ThemeStore.kt
val Context.themeStore by preferencesDataStore(name = "theme")DataStore is for small datasets: "If you need to support large or complex datasets, partial updates, or referential integrity, consider using Room instead."
5. What the checks cannot see (review-verified)
- Sync strategy: queued/lazy writes with WorkManager ("persistent work"
is what it is for) — unique work with
ExistingWorkPolicy.KEEP, a network constraint, andResult.retry()for automatic exponential backoff. - Conflict resolution (last-write-wins timestamps) and LCE UI states (Loading / Content / Error) around offline reads.
- SharedPreferences are legacy: "If you're using SharedPreferences to store data, consider migrating to DataStore instead."
Verification
Run after every data-layer edit (from the project root):
bash <skill-dir>/checks/check_no_data_source_in_viewmodel.sh <kotlin-src-dir>
bash <skill-dir>/checks/check_room_allow_main_thread.sh <kotlin-src-dir>
bash <skill-dir>/checks/check_room_fallback_destructive.sh <kotlin-src-dir>
bash <skill-dir>/checks/check_datastore_dup_names.sh <kotlin-src-dir>All four must pass (exit 0) before declaring the task done. Checks are deterministic heuristics — they catch the documented anti-patterns above, not every offline-first bug. §5 is verified by review against references/offline-first.md.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

