Skip to content

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 id on WidgetHabit (from goal public_id) and an optional coachLine: String? on the widget snapshot — both var, not let, so an absent key decodes as nil instead 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 HomeWidgetState cases 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 to supportedFamilies under an iOS-16 availability check.
  • CheckInHabitIntent, an AppIntent (openAppWhenRun = false) living inside the existing HomeWidgetExtension target — no new bundle ID, no new App Group, no signing change. Wired into FrostedHabitRow (Medium/Large rows) and KindlingCheckButton (Small's CTA).
  • An App Group pending-checkin queue with exactly-once drain/reconcile on app foreground, guarded by a hydrated flag so the queue can't clear before a persist is confirmed.
  • kindling_widget_enabled registered in ionic_frontend/src/lib/featureFlags.ts and 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.md rewritten 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

PhaseNameStatusPlansHighlights
1Snapshot foundation + Coach lineShipped1Stable per-habit id + optional coachLine contract; no visible change, gates every later phase
2Visual refinement — Small + MediumShipped1Stone-tower visuals across all six states; kindling_widget_enabled registered and synced to PostHog at 0%
3Large familyShipped1New .systemLarge family — tower, capped checklist, closing motivator
4Lock Screen accessoriesShipped1Three new accessory families, monochrome-legible, all six states
5Interactive check-off + App Group write pathShipped1Spike-gated AppIntent check-off; shipped ahead of Phase 4 via parallel dispatch
6State-coverage sweep + docsShipped1Full 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 to master.
  • All six HomeWidgetState cases in scope for Phase 2's UI-SPEC, not just three — a scope tightening directed by Josh on 2026-07-19: firstRun/stale/newDay each 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.

CategoryCountStatus
WIDGET-FOUNDATION-* / COACH-1 (Phase 1)3All satisfied
WIDGET-REFINE-* (Phase 2)3All satisfied
WIDGET-LARGE-* (Phase 3)2All satisfied
WIDGET-LOCK-* (Phase 4)2All satisfied
WIDGET-INTERACT-1..4 (Phase 5)1 (grouped)Satisfied
WIDGET-COVERAGE-1 / DOCS-1 / DOCS-2 (Phase 6)3All 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 hydrated guard was built to close has not been walked on hardware. Worth a manual device pass before kindling_widget_enabled ramps 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_enabled and the older reactive_streak_widget_enabled; spun out as OBJ-1580 rather than expanding scope. docs/features/home-widgets.md documents the Lock Screen accessories as intentionally riding reactive_streak_widget_enabled instead — an accepted design decision, not an open defect, as far as this audit could confirm.
  • 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

Loading…