Phase 4 wired KrillApp.Project.TaskList into the local-first onboarding path. Locally-hosted task lists needed overdue UI that updates automatically without a server-side scheduler. The chosen approach (Decision 7 in design.md) is a 60-second Compose LaunchedEffect tick that drives recomposition of overdue badges and NodeState escalation entirely client-side. LocalSwarmHost.createNode() had a when block that handled Project and Journal but fell through to type.meta() for TaskList, producing an unnamed default rather than propagating the user-supplied name.
shared/.../localhost/LocalSwarmHost.kt: added KrillApp.Project.TaskList -> TaskListMetaData(name = name, createdAt = now, updatedAt = now) branch so the flow-engine’s CreateNode step produces a correctly named task list.composeApp/.../tasklist/EditTaskList.kt: added a var currentTime state updated every 60 s via LaunchedEffect; threaded currentTime into TaskRow as nowMillis replacing inline Clock.System.now() calls; added a LaunchedEffect(currentTime, meta) that computes NodeState via computeTaskListState and calls nodeManager.update() for locally-hosted nodes, keeping the swarm graph badge current without a server tick.composeApp/.../project/ProjectScreen.kt: converted val now = Clock.System.now() in TaskListBody to a var backed by the same 60-second tick pattern so the project summary view also refreshes overdue labels automatically.shared/.../resources/flows/task-list.json: replaced comingSoon: true stub with a real six-step flow (intro → input → CreateNode Project → CreateNode TaskList → openEditor → chainTo what-next).shared/.../resources/KrillApp.Project.TaskList.json: appended the local-hosting caveat to description.when blocks when adding a new local type: whenever a new node type is marked requiresServer: false, search for all when (type) blocks in LocalSwarmHost and check that the new type has an explicit branch; falling through to type.meta() discards the user-supplied name and timestamps.var currentTime by remember + LaunchedEffect tick rather than inline Clock.System.now() calls; inline calls produce a snapshot at composition time and never update while the composable stays on screen.nowMillis rather than re-reading the clock: pass the tick’s currentTime down to child composables as a parameter (here: TaskRow) so all overdue calculations within a single composition frame agree on the same timestamp.