Technical Design: Mission-Based Checklists
Technical design for reusable blueprints, household members, and active mission checklists.
1. Overview
Mission-Based Checklists help users manage complex, recurring household events by combining pre-defined modular checklists (Blueprints) into a single active mission. This offloads the mental effort of remembering what goes where for specific life scenarios.
1.1 Core Concepts
| Concept | Description |
|---|---|
Blueprint |
A reusable, static template of items/tasks (e.g., "Son’s Weekend Bag", "Hospital Visit") |
Blueprint Item |
An individual item in a blueprint with assignee and optional inventory link |
Mission |
An active event combining one or more blueprints into a unified checklist |
Mission Item |
A concrete instance of a blueprint item within an active mission |
Inventory Bridge |
Auto-checks inventory stock for blueprint items linked to inventory |
1.2 Key User Flows
┌─────────────────────────────────────────────────────────────────┐
│ 1. CREATE BLUEPRINTS │
│ "Son's Weekend Bag" → [Diapers, Milk, Clothes, Toys] │
│ "Hospital Visit" → [Reports, ID, Water, Charger] │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 2. LAUNCH MISSION │
│ "Trip to Hyderabad" = Son's Bag + Mounika's Bag + Car Check │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 3. TRACK PROGRESS │
│ [████████░░] Son's Bag (80%) │ [██░░░░░░░░] Car (20%) │
│ ✓ Diapers ✓ Milk ○ Clothes ○ Toys │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 4. COMPLETE & ARCHIVE │
│ Mission archived. Blueprints remain for next use. │
└─────────────────────────────────────────────────────────────────┘
2. Feature Breakdown
| Phase | Feature | Description | Priority |
|---|---|---|---|
1 |
Blueprint CRUD |
Create/edit/delete blueprint templates |
HIGH |
2 |
Blueprint Items |
Add items with assignees and inventory links |
HIGH |
3 |
Mission Launch |
Select blueprints and create active mission |
HIGH |
4 |
Mission Tracking |
Check off items, view progress per blueprint |
HIGH |
5 |
Inventory Bridge |
Auto-check stock for linked items |
MEDIUM |
6 |
Mission Archive |
Complete and archive missions |
MEDIUM |
7 |
Person Management |
Manage household members for assignment |
LOW |
3. Data Model
3.1 Core Entities
HouseholdMember
Represents a person in the household for task assignment.
@Entity(tableName = "household_members")
data class HouseholdMemberEntity(
@PrimaryKey
val id: String, // UUID
val name: String, // "Mounika", "Son"
val nickname: String?, // Short display name
val avatarColor: String, // Hex color for avatar circle
val avatarEmoji: String?, // Optional emoji avatar
val isOwner: Boolean = false, // The primary app user
val createdAt: Long
)
Blueprint
A reusable checklist template.
@Entity(tableName = "blueprints")
data class BlueprintEntity(
@PrimaryKey
val id: String, // UUID
val title: String, // "Son's Weekend Bag"
val description: String?, // Optional description
val category: String, // BlueprintCategory enum
val tags: String?, // JSON array of tags
val iconEmoji: String?, // Visual icon
val color: String?, // Theme color (hex)
val createdBy: String?, // FK to household_members (nullable)
val useCount: Int = 0, // Times used in missions
val lastUsedAt: Long?,
val createdAt: Long,
val updatedAt: Long
)
enum class BlueprintCategory {
TRAVEL,
HEALTH,
SOCIAL,
WORK,
KIDS,
HOME,
CUSTOM
}
BlueprintItem
An item within a blueprint template.
@Entity(
tableName = "blueprint_items",
foreignKeys = [
ForeignKey(
entity = BlueprintEntity::class,
parentColumns = ["id"],
childColumns = ["blueprintId"],
onDelete = ForeignKey.CASCADE
)
],
indices = [Index("blueprintId")]
)
data class BlueprintItemEntity(
@PrimaryKey
val id: String, // UUID
val blueprintId: String, // FK to blueprints
val name: String, // "Diapers", "Water Bottle"
val quantity: Int = 1, // Default quantity needed
val notes: String?, // "Pack extra for long trips"
val assignedToId: String?, // FK to household_members (nullable)
// Inventory Link
val isInventoryLinked: Boolean = false,
val inventoryItemKey: String?, // Ingredient key for matching
val sortOrder: Int = 0,
val createdAt: Long
)
Mission
An active event combining blueprints.
@Entity(tableName = "missions")
data class MissionEntity(
@PrimaryKey
val id: String, // UUID
val name: String, // "Trip to Hyderabad"
val description: String?,
val status: String, // MissionStatus enum
val scheduledDate: Long?, // Optional target date
val startedAt: Long,
val completedAt: Long?,
val archivedAt: Long?
)
enum class MissionStatus {
ACTIVE, // In progress
COMPLETED, // All items checked
ARCHIVED, // Moved to history
CANCELLED // Abandoned
}
MissionBlueprint
Join table linking missions to blueprints.
@Entity(
tableName = "mission_blueprints",
primaryKeys = ["missionId", "blueprintId"],
foreignKeys = [
ForeignKey(
entity = MissionEntity::class,
parentColumns = ["id"],
childColumns = ["missionId"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = BlueprintEntity::class,
parentColumns = ["id"],
childColumns = ["blueprintId"],
onDelete = ForeignKey.CASCADE
)
],
indices = [Index("missionId"), Index("blueprintId")]
)
data class MissionBlueprintEntity(
val missionId: String,
val blueprintId: String,
val addedAt: Long
)
MissionItem
A concrete instance of a blueprint item in an active mission.
@Entity(
tableName = "mission_items",
foreignKeys = [
ForeignKey(
entity = MissionEntity::class,
parentColumns = ["id"],
childColumns = ["missionId"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = BlueprintEntity::class,
parentColumns = ["id"],
childColumns = ["sourceBlueprintId"],
onDelete = ForeignKey.SET_NULL
)
],
indices = [Index("missionId"), Index("sourceBlueprintId")]
)
data class MissionItemEntity(
@PrimaryKey
val id: String, // UUID
val missionId: String, // FK to missions
// Source tracking
val sourceBlueprintId: String?, // Which blueprint this came from
val sourceBlueprintItemId: String?, // Original blueprint item
// Item details (copied from blueprint for independence)
val name: String,
val quantity: Int = 1,
val notes: String?,
val assignedToId: String?,
// Status
val isChecked: Boolean = false,
val checkedAt: Long?,
val checkedByMemberId: String?,
// Inventory link status
val isInventoryLinked: Boolean = false,
val inventoryItemKey: String?,
val inventoryStatus: String?, // InventoryCheckStatus enum
val sortOrder: Int = 0
)
enum class InventoryCheckStatus {
NOT_LINKED, // No inventory link
IN_STOCK, // Sufficient quantity
LOW_STOCK, // Below required quantity
OUT_OF_STOCK, // Not in inventory
UNKNOWN // Couldn't check
}
3.2 Relationship Diagram
+-------------------+ +---------------------+ +-------------------+
| HouseholdMember |<------| BlueprintItem |------>| Blueprint |
+-------------------+ N:1 | (assignedTo) | N:1 +-------------------+
+---------------------+ │
│ 1:N
▼
+-------------------+ +---------------------+ +-------------------+
| Mission |<----->| MissionBlueprint |<----->| Blueprint |
+-------------------+ 1:N | (join table) | N:1 +-------------------+
│ +---------------------+
│
│ 1:N
▼
+-------------------+
| MissionItem |-----> Copied from BlueprintItem
+-------------------+ (with status tracking)
3.3 Database Migration
// Migration v6 -> v7
val MIGRATION_6_7 = object : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) {
// Household members
database.execSQL("""
CREATE TABLE IF NOT EXISTS household_members (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
nickname TEXT,
avatarColor TEXT NOT NULL,
avatarEmoji TEXT,
isOwner INTEGER NOT NULL DEFAULT 0,
createdAt INTEGER NOT NULL
)
""")
// Blueprints
database.execSQL("""
CREATE TABLE IF NOT EXISTS blueprints (
id TEXT PRIMARY KEY NOT NULL,
title TEXT NOT NULL,
description TEXT,
category TEXT NOT NULL,
tags TEXT,
iconEmoji TEXT,
color TEXT,
createdBy TEXT,
useCount INTEGER NOT NULL DEFAULT 0,
lastUsedAt INTEGER,
createdAt INTEGER NOT NULL,
updatedAt INTEGER NOT NULL
)
""")
// Blueprint items
database.execSQL("""
CREATE TABLE IF NOT EXISTS blueprint_items (
id TEXT PRIMARY KEY NOT NULL,
blueprintId TEXT NOT NULL,
name TEXT NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
notes TEXT,
assignedToId TEXT,
isInventoryLinked INTEGER NOT NULL DEFAULT 0,
inventoryItemKey TEXT,
sortOrder INTEGER NOT NULL DEFAULT 0,
createdAt INTEGER NOT NULL,
FOREIGN KEY(blueprintId) REFERENCES blueprints(id) ON DELETE CASCADE
)
""")
database.execSQL("CREATE INDEX IF NOT EXISTS idx_blueprint_items_blueprint ON blueprint_items(blueprintId)")
// Missions
database.execSQL("""
CREATE TABLE IF NOT EXISTS missions (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
description TEXT,
status TEXT NOT NULL,
scheduledDate INTEGER,
startedAt INTEGER NOT NULL,
completedAt INTEGER,
archivedAt INTEGER
)
""")
// Mission-Blueprint join table
database.execSQL("""
CREATE TABLE IF NOT EXISTS mission_blueprints (
missionId TEXT NOT NULL,
blueprintId TEXT NOT NULL,
addedAt INTEGER NOT NULL,
PRIMARY KEY(missionId, blueprintId),
FOREIGN KEY(missionId) REFERENCES missions(id) ON DELETE CASCADE,
FOREIGN KEY(blueprintId) REFERENCES blueprints(id) ON DELETE CASCADE
)
""")
database.execSQL("CREATE INDEX IF NOT EXISTS idx_mission_blueprints_mission ON mission_blueprints(missionId)")
database.execSQL("CREATE INDEX IF NOT EXISTS idx_mission_blueprints_blueprint ON mission_blueprints(blueprintId)")
// Mission items
database.execSQL("""
CREATE TABLE IF NOT EXISTS mission_items (
id TEXT PRIMARY KEY NOT NULL,
missionId TEXT NOT NULL,
sourceBlueprintId TEXT,
sourceBlueprintItemId TEXT,
name TEXT NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
notes TEXT,
assignedToId TEXT,
isChecked INTEGER NOT NULL DEFAULT 0,
checkedAt INTEGER,
checkedByMemberId TEXT,
isInventoryLinked INTEGER NOT NULL DEFAULT 0,
inventoryItemKey TEXT,
inventoryStatus TEXT,
sortOrder INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY(missionId) REFERENCES missions(id) ON DELETE CASCADE,
FOREIGN KEY(sourceBlueprintId) REFERENCES blueprints(id) ON DELETE SET NULL
)
""")
database.execSQL("CREATE INDEX IF NOT EXISTS idx_mission_items_mission ON mission_items(missionId)")
database.execSQL("CREATE INDEX IF NOT EXISTS idx_mission_items_blueprint ON mission_items(sourceBlueprintId)")
}
}
4. Inventory Bridge
4.1 Purpose
When a blueprint item is linked to inventory (e.g., "Diapers" links to diapers in the pantry), the system automatically checks stock levels when launching a mission.
4.2 Stock Check Flow
class InventoryBridge @Inject constructor(
private val itemRepository: ItemRepository,
private val ingredientMatcher: IngredientMatcher
) {
suspend fun checkBlueprintItemStock(
item: BlueprintItem
): InventoryCheckResult {
if (!item.isInventoryLinked || item.inventoryItemKey == null) {
return InventoryCheckResult.NotLinked
}
val inventory = itemRepository.getAllGroceryItems().first()
val match = ingredientMatcher.findMatches(item.inventoryItemKey, inventory)
return when (match) {
is MatchResult.Found -> {
when {
match.totalAvailable >= item.quantity ->
InventoryCheckResult.InStock(match.totalAvailable, match.unit)
match.totalAvailable > 0 ->
InventoryCheckResult.LowStock(match.totalAvailable, item.quantity, match.unit)
else ->
InventoryCheckResult.OutOfStock
}
}
is MatchResult.Fuzzy ->
InventoryCheckResult.Unknown("Possible match found, please verify")
is MatchResult.NotFound ->
InventoryCheckResult.OutOfStock
}
}
suspend fun checkMissionItems(
missionItems: List<MissionItem>
): Map<String, InventoryCheckResult> {
return missionItems
.filter { it.isInventoryLinked }
.associate { item ->
item.id to checkBlueprintItemStock(
BlueprintItem(
id = item.id,
name = item.name,
quantity = item.quantity,
isInventoryLinked = true,
inventoryItemKey = item.inventoryItemKey
)
)
}
}
}
sealed class InventoryCheckResult {
object NotLinked : InventoryCheckResult()
data class InStock(val available: Double, val unit: String) : InventoryCheckResult()
data class LowStock(val available: Double, val required: Int, val unit: String) : InventoryCheckResult()
object OutOfStock : InventoryCheckResult()
data class Unknown(val message: String) : InventoryCheckResult()
}
5. UI Design
5.1 Blueprint Library Screen
+------------------------------------------+
| <- Blueprints [+] |
+------------------------------------------+
| |
| [Travel] [Health] [Kids] [Work] [All] |
| |
| --- Travel --- |
| |
| +------------------------------------+ |
| | [Bag] Son's Weekend Bag [>] | |
| | 5 items • Used 3 times | |
| +------------------------------------+ |
| |
| +------------------------------------+ |
| | [Car] Car Maintenance Check [>] | |
| | 8 items • Never used | |
| +------------------------------------+ |
| |
| --- Health --- |
| |
| +------------------------------------+ |
| | [Hospital] Hospital Visit [>] | |
| | 6 items • Used 1 time | |
| +------------------------------------+ |
| |
+------------------------------------------+
5.2 Blueprint Detail/Edit Screen
+------------------------------------------+
| <- Son's Weekend Bag [*] [...] |
+------------------------------------------+
| |
| Category: Travel |
| Tags: #kids #weekend |
| |
| --- Items (5) --- [+] |
| |
| +------------------------------------+ |
| | [ ] Diapers (x10) | |
| | [M] Mounika [Inv] In stock | |
| +------------------------------------+ |
| |
| +------------------------------------+ |
| | [ ] Milk Bottles (x2) | |
| | [M] Mounika [Inv] Low stock! | |
| +------------------------------------+ |
| |
| +------------------------------------+ |
| | [ ] Extra Clothes (x2) | |
| | [O] Owner | |
| +------------------------------------+ |
| |
| +------------------------------------+ |
| | [ ] Favorite Toy | |
| | Note: The blue elephant | |
| +------------------------------------+ |
| |
| [Start Mission with this Blueprint] |
| |
+------------------------------------------+
Legend:
[M] = Assigned to Mounika
[O] = Assigned to Owner
[Inv] = Linked to inventory
5.3 Mission Launch Screen
+------------------------------------------+
| <- New Mission |
+------------------------------------------+
| |
| Mission Name: |
| [Trip to Hyderabad________________] |
| |
| Scheduled Date: [Jan 30, 2026_____] |
| |
| --- Select Blueprints --- |
| |
| +------------------------------------+ |
| | [x] Son's Weekend Bag | |
| | 5 items | |
| +------------------------------------+ |
| |
| +------------------------------------+ |
| | [x] Mounika's Travel Essentials | |
| | 8 items | |
| +------------------------------------+ |
| |
| +------------------------------------+ |
| | [x] Car Maintenance Check | |
| | 8 items | |
| +------------------------------------+ |
| |
| +------------------------------------+ |
| | [ ] Hospital Visit | |
| | 6 items | |
| +------------------------------------+ |
| |
| --- Summary --- |
| 3 blueprints selected • 21 total items |
| 2 items need restocking |
| |
| [Cancel] [Launch Mission] |
+------------------------------------------+
5.4 Active Mission Screen
+------------------------------------------+
| <- Trip to Hyderabad [...] | |
+------------------------------------------+
| Jan 30, 2026 • 15 of 21 complete |
| [████████████████████░░░░░░░░] 71% |
+------------------------------------------+
| |
| --- Son's Weekend Bag (4/5) --- |
| [████████████████░░░░] 80% |
| |
| +------------------------------------+ |
| | [x] Diapers (x10) [M] | |
| +------------------------------------+ |
| | [x] Milk Bottles (x2) [M] | |
| +------------------------------------+ |
| | [x] Extra Clothes (x2) [O] | |
| +------------------------------------+ |
| | [x] Favorite Toy [M] | |
| +------------------------------------+ |
| | [ ] Snacks [O] | |
| | [!] Out of stock | |
| +------------------------------------+ |
| |
| --- Car Check (2/8) --- |
| [████░░░░░░░░░░░░░░░░] 25% |
| |
| +------------------------------------+ |
| | [x] Tire pressure [O] | |
| +------------------------------------+ |
| | [x] Fuel level [O] | |
| +------------------------------------+ |
| | [ ] First aid kit [O] | |
| +------------------------------------+ |
| ... (more items) |
| |
+------------------------------------------+
| [Complete Mission] |
+------------------------------------------+
6. Business Logic
6.1 Duplicate Handling
When the same item appears in multiple blueprints:
enum class DuplicateHandling {
SHOW_SEPARATELY, // Show each instance (default)
MERGE_QUANTITIES // Combine into single item with summed quantity
}
class MissionComposer @Inject constructor(...) {
fun composeMission(
blueprintIds: List<String>,
duplicateHandling: DuplicateHandling = DuplicateHandling.SHOW_SEPARATELY
): List<MissionItem> {
val allItems = blueprintIds.flatMap { getBlueprintItems(it) }
return when (duplicateHandling) {
DuplicateHandling.SHOW_SEPARATELY -> allItems.map { it.toMissionItem() }
DuplicateHandling.MERGE_QUANTITIES -> mergeDuplicates(allItems)
}
}
private fun mergeDuplicates(items: List<BlueprintItem>): List<MissionItem> {
return items
.groupBy { it.name.lowercase() }
.map { (_, groupedItems) ->
val first = groupedItems.first()
MissionItem(
name = first.name,
quantity = groupedItems.sumOf { it.quantity },
notes = groupedItems.mapNotNull { it.notes }.joinToString("; "),
assignedToId = first.assignedToId,
isInventoryLinked = groupedItems.any { it.isInventoryLinked },
inventoryItemKey = groupedItems.firstNotNullOfOrNull { it.inventoryItemKey }
)
}
}
}
6.2 Mission Completion
class MissionManager @Inject constructor(...) {
fun canCompleteMission(mission: Mission, items: List<MissionItem>): Boolean {
return items.all { it.isChecked }
}
suspend fun completeMission(missionId: String) {
val mission = missionRepository.getMission(missionId) ?: return
val items = missionRepository.getMissionItems(missionId)
if (!canCompleteMission(mission, items)) {
throw IllegalStateException("Cannot complete mission: not all items checked")
}
// Update mission status
missionRepository.updateMissionStatus(
missionId = missionId,
status = MissionStatus.COMPLETED,
completedAt = System.currentTimeMillis()
)
// Increment use count for all blueprints
mission.blueprintIds.forEach { blueprintId ->
blueprintRepository.incrementUseCount(blueprintId)
}
}
suspend fun archiveMission(missionId: String) {
missionRepository.updateMissionStatus(
missionId = missionId,
status = MissionStatus.ARCHIVED,
archivedAt = System.currentTimeMillis()
)
}
}
7. File Structure
app/src/main/kotlin/dev/sysout/homey/app/features/
└── mission/
├── data/
│ ├── HouseholdMemberEntity.kt
│ ├── BlueprintEntity.kt
│ ├── BlueprintItemEntity.kt
│ ├── MissionEntity.kt
│ ├── MissionBlueprintEntity.kt
│ ├── MissionItemEntity.kt
│ ├── BlueprintDao.kt
│ ├── MissionDao.kt
│ ├── HouseholdMemberDao.kt
│ ├── BlueprintRepository.kt
│ ├── MissionRepository.kt
│ └── HouseholdMemberRepository.kt
├── domain/
│ ├── Blueprint.kt
│ ├── BlueprintItem.kt
│ ├── Mission.kt
│ ├── MissionItem.kt
│ └── HouseholdMember.kt
├── bridge/
│ └── InventoryBridge.kt
├── compose/
│ └── MissionComposer.kt
└── ui/
├── blueprints/
│ ├── BlueprintListScreen.kt
│ ├── BlueprintDetailScreen.kt
│ ├── BlueprintEditScreen.kt
│ └── BlueprintViewModel.kt
├── missions/
│ ├── MissionLaunchScreen.kt
│ ├── ActiveMissionScreen.kt
│ ├── MissionHistoryScreen.kt
│ └── MissionViewModel.kt
└── components/
├── BlueprintCard.kt
├── MissionItemRow.kt
├── ProgressBar.kt
└── MemberAvatar.kt
8. Navigation
// Add to Screen.kt
object Blueprints : Screen("mission/blueprints", "Blueprints", Icons.Default.Checklist)
object BlueprintDetail : Screen("mission/blueprints/{blueprintId}", "Blueprint", Icons.Default.Checklist) {
fun createRoute(blueprintId: String) = "mission/blueprints/$blueprintId"
}
object BlueprintEdit : Screen("mission/blueprints/{blueprintId}/edit", "Edit Blueprint", Icons.Default.Edit) {
fun createRoute(blueprintId: String) = "mission/blueprints/$blueprintId/edit"
}
object BlueprintCreate : Screen("mission/blueprints/new", "New Blueprint", Icons.Default.Add)
object MissionLaunch : Screen("mission/launch", "New Mission", Icons.Default.RocketLaunch)
object ActiveMission : Screen("mission/active/{missionId}", "Mission", Icons.Default.PlayArrow) {
fun createRoute(missionId: String) = "mission/active/$missionId"
}
object MissionHistory : Screen("mission/history", "History", Icons.Default.History)
9. Implementation Order
| Phase | Deliverable | Dependencies |
|---|---|---|
1 |
Entities + DAOs + Migration |
Database setup |
2 |
Blueprint CRUD UI |
Phase 1 |
3 |
Blueprint Items with assignees |
Phase 2, HouseholdMember |
4 |
Mission Launch flow |
Phases 2-3 |
5 |
Active Mission tracking |
Phase 4 |
6 |
Inventory Bridge integration |
Phase 5, existing IngredientMatcher |
7 |
Mission archive & history |
Phase 5 |
10. Open Questions
-
Recurring Missions: Should missions be schedulable to auto-launch weekly/monthly?
-
Sharing: Share blueprints between households?
-
Notifications: Remind user of upcoming scheduled missions?
-
Quick Actions: "One-tap" launch for frequently used single-blueprint missions?
-
Templates Gallery: Pre-built blueprints (e.g., "Baby Travel Kit", "Camping Trip")?
Document Version: 1.0 Created: 2026-01-25 Status: Ready for Review