v4.7 — Kindling Widgets (iOS)
The iOS home-screen widget stops being a glance-only surface — refined visuals, one earned Coach line, and a tap to lay today's stone without opening the app.
Summary
Before this milestone, the iOS home-screen widget (shipped in v4.1, made reactive in v4.2) showed a flat stone/flame streak metaphor and could only be looked at, never acted on. Its visual language didn't match design direction 1d, "Kindling: feel it and do it," and every check-in still required opening the app.
This milestone refined the widget's visuals into a stone-tower streak with a gold capstone, added one earned Coach line rendered only when it had something real to say, and — for the first time — made the widget directly actionable: an App Intents check-off that lays today's stone without leaving the home screen. The refinement landed across Small, Medium, and a new Large family, plus three Lock Screen accessory families (circular, rectangular, inline). All six phases shipped as separate, flag-gated PRs direct to master over two days, deliberately avoiding a long-lived integration branch (an explicit lesson carried over from v4.2). The whole feature sat behind kindling_widget_enabled, fail-closed and byte-identical to the pre-kindling render when off, at 0% rollout throughout.
This milestone's ROADMAP file was never updated past "Phase 6 dispatched," even though Phase 6 (the coverage sweep and this page's antecedent doc work) shipped the same day. That gap was caught and corrected by Orion's milestone-ledger reconciliation (OBJ-2150, 2026-08-04) and closed by this page and its companion audit, authored retroactively two weeks after the code itself shipped.
Goal
Refine the iOS home-screen widget to design direction 1d — "Kindling: feel it and do it": a refined stone-tower streak, one earned Coach line, and a tappable App Intents check-off, delivered across Small / Medium / Large / Lock Screen. The widget becomes a glance-and-act surface — see the tower, feel the streak, lay today's stone without opening the app — in Objectuve's anti-dark-pattern brand voice.
Scope — What Shipped
- Stable per-habit
idonWidgetHabit(from goalpublic_id) and an optionalcoachLine: String?on the widget snapshot — bothvar, notlet, so an absent key decodes asnilinstead of crashing on an older cached snapshot (WidgetSnapshot.swift). - A deterministic, state-keyed Coach-line template set (
useWidgetSnapshot.ts), rendered on Medium and Large, fail-closed when unavailable. - Refined stone-tower visuals — stone sheen, gold capstone, frosted rows, press states — across all six
HomeWidgetStatecases on Small and Medium (HomeWidgetViews.swift,WidgetColors.swift,Assets.xcassets). - A new Large (
.systemLarge) family: tower/streak header, Coach line, a checklist capped at 4 rows plus a "+N more in the app" overflow row, and a closing motivator line. - Three new Lock Screen accessory families —
.accessoryCircular,.accessoryRectangular,.accessoryInline— monochrome-legible, added tosupportedFamiliesunder an iOS-16 availability check. CheckInHabitIntent, anAppIntent(openAppWhenRun = false) living inside the existingHomeWidgetExtensiontarget — no new bundle ID, no new App Group, no signing change. Wired intoFrostedHabitRow(Medium/Large rows) andKindlingCheckButton(Small's CTA).- An App Group pending-checkin queue with exactly-once drain/reconcile on app foreground, guarded by a
hydratedflag so the queue can't clear before a persist is confirmed. kindling_widget_enabledregistered inionic_frontend/src/lib/featureFlags.tsand synced to PostHog at 0% rollout in the same PR that introduced it.- A full
HomeWidgetState×StreakReactiveState×StreakMetaphor× family render-matrix test sweep (HomeWidgetExtensionTests/). docs/features/home-widgets.mdrewritten to supersede its prior "read-only in V1" framing with a full "Kindling refinement (v4.7)" section.- The Android-parity sibling issue (OBJ-1587) filed, which went on to ship as the audited v4.11 milestone.
Phases
| Phase | Name | Status | Plans | Highlights |
|---|---|---|---|---|
| 1 | Snapshot foundation + Coach line | Shipped | 1 | Stable per-habit id + optional coachLine contract; no visible change, gates every later phase |
| 2 | Visual refinement — Small + Medium | Shipped | 1 | Stone-tower visuals across all six states; kindling_widget_enabled registered and synced to PostHog at 0% |
| 3 | Large family | Shipped | 1 | New .systemLarge family — tower, capped checklist, closing motivator |
| 4 | Lock Screen accessories | Shipped | 1 | Three new accessory families, monochrome-legible, all six states |
| 5 | Interactive check-off + App Group write path | Shipped | 1 | Spike-gated AppIntent check-off; shipped ahead of Phase 4 via parallel dispatch |
| 6 | State-coverage sweep + docs | Shipped | 1 | Full render-matrix test coverage; docs/features/home-widgets.md rewrite; Android-parity issue filed |
Key Decisions
- Flag-gated phase-by-phase merges to
master, not a long-lived integration branch — an explicit lesson carried over from v4.2, where an integration branch had drifted. Each of the six phases shipped as its own PR straight tomaster. - All six
HomeWidgetStatecases in scope for Phase 2's UI-SPEC, not just three — a scope tightening directed by Josh on 2026-07-19:firstRun/stale/newDayeach carry real copy today and needed the refined treatment too, not just the three states the initial design draft covered. - Fail-closed, byte-identical flag-off, enforced by CI snapshot coverage, not a visual eyeball — Phase 2's Roy round-1 review caught a capstone-glow leak when the flag was off; the fix was verified with real CI snapshot coverage before merge, reinforcing the fidelity contract for every phase after it.
- Phases 4 and the Phase 5 spike dispatched in parallel — with Phase 3 shipped and Phases 2–5 all depending only on Phase 1, Orion dispatched Lock Screen (Phase 4) and the Phase 5 spike together to compress the timeline and de-risk the highest-risk phase (interactive check-off) early. The spike paid off: Phase 5 shipped ahead of Phase 4.
- Codi does not improvise unspecified states — a fidelity-contract rule set by Josh on 2026-07-19: a state without a Desi UI-SPEC treatment routes back to Desi, never a guess.
Requirements Coverage
14 / 14 requirements satisfied (quoted from the MILESTONE-AUDIT). This milestone tracks deliverables by ad hoc ID rather than a numbered REQUIREMENTS.md, the same convention used by its sibling v4.11 and by v4.17.
| Category | Count | Status |
|---|---|---|
| WIDGET-FOUNDATION-* / COACH-1 (Phase 1) | 3 | All satisfied |
| WIDGET-REFINE-* (Phase 2) | 3 | All satisfied |
| WIDGET-LARGE-* (Phase 3) | 2 | All satisfied |
| WIDGET-LOCK-* (Phase 4) | 2 | All satisfied |
| WIDGET-INTERACT-1..4 (Phase 5) | 1 (grouped) | Satisfied |
| WIDGET-COVERAGE-1 / DOCS-1 / DOCS-2 (Phase 6) | 3 | All satisfied |
Full detail: v4.7-kindling-widgets-MILESTONE-AUDIT.md on GitHub.
Outcomes
The iOS home-screen widget's stone-tower streak, earned Coach line, and tappable check-off exist in master today across Small, Medium, Large, and all three Lock Screen accessory families — but remain invisible to real users. kindling_widget_enabled stayed at 0% rollout for the entirety of this milestone and this audit's writing; flag promotion is a separate, later, human decision, not a milestone-close gap. Android parity followed in the audited v4.11 milestone, using the same flag.
Tech Debt
- (Phase 5, still open) The interactive check-off's tap→cold-kill→foreground path was verified only by code-path re-derivation and CI — no iOS simulator or physical device was available in-env during this milestone. Exactly-once reconciliation is CI-proven; the real device race the
hydratedguard was built to close has not been walked on hardware. Worth a manual device pass beforekindling_widget_enabledramps past 0%. - (Milestone-wide, closed by this page and its audit) This milestone's ROADMAP file was never updated past "Phase 6 dispatched" despite Phase 6 shipping the same day — corrected 2026-08-04 via Orion's ledger reconciliation (OBJ-2150) and this retroactive close.
- (Phase 4, resolved via documentation, non-blocking) Lock Screen accessory gating referenced both
kindling_widget_enabledand the olderreactive_streak_widget_enabled; spun out as OBJ-1580 rather than expanding scope.docs/features/home-widgets.mddocuments the Lock Screen accessories as intentionally ridingreactive_streak_widget_enabledinstead — an accepted design decision, not an open defect, as far as this audit could confirm.
Related Artifacts
- Roadmap: v4.7-kindling-widgets-ROADMAP.md
- Milestone Audit: v4.7-kindling-widgets-MILESTONE-AUDIT.md
- Feature docs: docs/features/home-widgets.md § Kindling refinement (v4.7)
- Source issue: OBJ-1516 · Coordination anchor: OBJ-1517
- Sibling milestone (Android parity): v4.11-kindling-widgets-android-parity.md
- Merge PRs: #1608, #1613, #1637, #1645, #1642, #1654
Related Commits
2282b18db56092ccbdd99207f5dd84d0e47dff33— Snapshot foundation + Coach line (#1608)9ea668b0bedc9aeb374419821a371d8f5c391b32— Visual refinement — Small + Medium (#1613)74b4b0a86d6782a51fbc1654aa9197cef2e0a5c5— Large family (#1637)074f71aeff07878fef0ee024091be77d2e92f765— Lock Screen accessories (#1645)2ee4ebe5256659516adf59d82ae7417ab33591c8— Interactive check-off + App Group write path (#1642)b727c1e0— State-coverage sweep + docs (#1654)
Last updated: 2026-08-04