AI Habit Tracking: How Check-In Data Creates and Refines Dog Care Routines — PupPal
2026-08-26
AI Habit Tracking: How Check-In Data Creates and Refines Dog Care Routines — PupPal
Every habit tracking app asks you to do the same thing: remember to log. Remember to open the app, remember to tap the right tile, remember which habit you meant to build this month. The log grows when your discipline grows, and it rots the moment life gets busy. AI habit tracking flips that contract. In PupPal, the daily check-in photo is the log entry — you take one picture of your dog and the habit layer does the rest: it classifies the moment against your dog's active habits, awards the points, updates the streak, and when no existing habit fits, it quietly creates a new one from the AI summary so the record is never lost. This post walks the entire pipeline as it exists in the PupPal codebase: the two-layer habit model (shared templates versus per-pet instances), the vision classification path, the auto-creation fallback, and the refinement loops that turn a pile of photos into a living care routine.
The design is worth understanding because it solves a real problem. Most pet routines exist in the owner's head: the walk happens, the teeth brushing happens, the supplement happens, and none of it is recorded anywhere. When something changes — the dog starts skipping breakfast, the walk gets shorter — there is no record to compare against. PupPal's habit system was built so the record assembles itself from the check-ins you were going to take anyway, and so the AI, not the owner, does the bookkeeping.
Templates and Instances: The Two Layers of PupPal Habit Tracking
The heart of the system is the Habit model, a single Isar collection in the local database, and it plays two different roles depending on two flags: isTemplate and petId. That split is the most important design decision in the whole habit layer, because it separates shared knowledge from lived reality.
Templates are the shared knowledge. They are curated activity definitions that ship with the app and live in packages/db/assets/habit_templates.json — a library of 49 activities across 10 categories, sourced and ordered by the APPA 2024 US Pet Market Research (food and treats at 43.3 percent of the market, vet and health at 26.2 percent, supplies at 21.9 percent, then grooming, exercise, training, boarding, social, insurance, and special days). A template is identified by a stable sourceId, has petId = null, and is recognized by the query isTemplateEqualTo(true).petIdIsNull(). It is never activated, never completed, never archived — it is a recipe.
What does a recipe contain? Open habit_templates.json and every template carries the full behavioral specification: an emoji icon (daily_feeding rides on 🍲, daily_walk on 🚶), category and tags, a streak frequency (daily, weekly, monthly, and for slower rhythms like vet_checkup or deworming, yearly and quarterly), a completion tracking type (stepByStep versus plain completion), a counting behavior (stopAtGoal versus allowExceeding), a streak goal, completions per period, a completion window in minutes, reminder weekdays and reminder times, reward points, difficulty, and even serviceExamples and marketNotes that document why the template exists (the daily feeding template cites "Chewy subscription, $50/month" and "APPA 2024: food spending is 37 percent of the market"). Templates do not use plain English names — they use i18n keys like daily_feeding_desc that resolve to the user's language, which is how one library serves English and Chinese owners from the same JSON.
Instances are the lived reality. When a template is applied to a dog, the copy() method in HabitRepository builds a new Habit: petId set to the dog's id, sourceId pointing back to the template, remoteId reset, active = true, activatedAt stamped with the current epoch time, and every behavioral field inherited from the recipe — frequency, tracking type, counting behavior, streak goal, completions per period, reminder weekdays and times, window, color, reward points, order index. From that moment on, the instance is the dog's own: it can be completed, archived, restored, renamed in effect by editing, or deleted, and none of that touches the template it came from.
The reason for the split is deduplication and freedom at the same time. The repository keeps templates pristine: addTemplateOfHabits skips any template whose sourceId already exists, and the full library loads once (loadDefaultHabit bails out if template count is already above zero). Meanwhile getHabitsByTemplate checks whether a specific pet already has an instance of a given template — querying sourceIdEqualTo(template.sourceId).petIdEqualTo(pet.id) — so the same recipe never multiplies on one dog. Two dogs in one household, however, can each copy the same template and then diverge entirely: the 10-week-old Border Collie puppy's fetch_ball instance can be tuned to three sessions a day, while the senior Labrador's instance of the same template carries a gentler goal. The phrase "per-pet instance" is literal here: every field lives on the instance, so calibration is per dog, not per breed.
This two-layer design is the foundation of AI habit tracking in PupPal, because it gives the AI a vocabulary. When the vision pipeline looks at a photo and needs to decide "which habit does this moment belong to," it searches a short, clean list of the pet's active instances — not a crowded global pile. And when nothing matches, it can reach back to the template shelf for the closest recipe instead of inventing a configuration from scratch. Shared knowledge stays curated; lived reality stays flexible; the boundary between them is one boolean and one foreign key.
From Photo to Habit: The Check-In Pipeline
The check-in pipeline is where check-in data feeds habit creation, and it starts with the product slogan: photo = check-in. The home screen check-in flow accepts a tap on a habit chip or a photo of the dog, and in both cases the result is a Play record — the actual log entry of the habit system. The Play model is the fact table of AI habit tracking: it stores habitId, petId, date (normalized to the start of the day in local time, which is what makes daily stats and heatmaps cheap to compute), value (the completion count for that day), deltaPoints, checkinType (click, photo, audio, timer, retro, missed, milestone, reward), and a snapshot of habitName, habitColorValue, petName, and petAvatarPath so history renders correctly even offline. AI-specific fields live on the same row: localPhotoPath, remotePhotoUrl, aiMessage (the raw AI JSON), aiTokensUsed, aiCostMicros, and aiChannel — local, hermes, or cloud — recording exactly which engine analyzed the check-in.
The photo channel is the interesting one. When you shoot a photo check-in, the app hands the image to the on-device engine's analyzePhoto, a single Vision call that returns three structured analyses at once — V2 posture (standing, sitting, lying, running, jumping, playing, eating, drinking, sleeping), V4 expression (happy, calm, alert, curious, tired, anxious, sad, playful), and V5 environment (indoor_home, outdoor_park, outdoor_street, car, vet). The token budget is defended hard: the image's long edge is compressed to 1568 pixels before upload, anything over 4MB is forced to JPEG, the call has a 30-second timeout, and the habit classification that follows is deliberately zero-token — it happens locally, on the device, with string matching.
Here is exactly how the classification works, from AiCheckinAnalyzer in lib/pages/home/services/. The engine result is flattened into a summary line by summaryFromAnalysis, which joins the posture, the emotion, the location type, and the model's natural-language message into one string. Then matchHabit(summary, habits) walks the dog's active habit instances and does a case-insensitive contains check of each habit name against that summary. The clever part is bilingual: it matches both the raw template key and its translated name, so a Chinese owner's input like "带狗出去散步" hits the daily_walk instance even though the habit's canonical name is an English i18n key. No match at all? The function falls back to the first habit — a deliberate bias toward "record it somewhere" over "lose it." The result is a PhotoCheckinClassification carrying habitId, points, summary, and tokensUsed, which becomes the Play row that updates the streak and the day's completion totals.
Text check-ins take the sibling path: classifyActivity runs the description through the same engine, receives a JSON contract with habitId, points, and summary, and resolves the same Play fields. Both channels converge on the same recovery logic, which is where the AI starts doing creation rather than classification. The photo check-in deep dive traces the full byte path of a check-in photo through upload, analysis, and state update; the point to hold onto here is that the same photo that feeds the habit log also feeds the health layer — C7 anomaly detection, posture baselines, and the daily review all consume the same check-in stream. One photo, two systems fed.
When the AI Meets Something New: Copy, Then Create
The classification path above assumes the dog's active habit list already covers the moment. But real life produces moments that no habit was created for: the puppy discovers a paddle pool and spends an hour splashing; the owner starts a new training class; the dog develops a medication routine that was never added as a habit. In a traditional tracker, that check-in would be lost, unlabeled, or dumped into an "other" bucket that no one ever reviews. PupPal's recovery service — AiCheckinRecoveryService in lib/pages/home/services/ — treats an unmatched check-in as a signal to create or extend a habit, and it does so in two escalating steps.
The flow begins in resolveWithHabitFallback. The AI's JSON response is parsed; if it already contains a habitId, the check-in resolves normally and the pipeline is done. If the habitId is missing, something new has happened, and the service asks a question in code: does this moment match a template we already ship? copyTemplateFromClassification loads the default template library — the same 49-activity JSON used by the manual template import dialog — runs matchHabit over the combined summary and description text, and if a template hits, calls HabitRepository.copy(template, pet). That is the exact same copy() used when an owner manually activates a template, which means the AI-created habit is not a crippled stub: it inherits the full behavioral configuration — frequency, streak goal, completions per period, reminder weekdays and times, reward points, window — and its sourceId points back to the template, so deduplication queries work identically whether the habit was added by a thumb tap or by the agent.
If the template library does not match — a genuinely novel activity — the service falls back to createHabitFromClassification, which builds a brand-new habit instance from the AI's own words. The naming logic is almost charming in its pragmatism: the habit name is taken from the first 12 characters of the AI summary, stripped of common Chinese verb prefixes (给, 带, 让, 帮, 和, 为 — so "给小狗洗澡" becomes "小狗洗澡", not a permanent habit named "give the puppy a bath"), and trimmed at trailing punctuation; if no summary exists, the description is used; if neither exists, the fallback name is literally "AI Habit." The new instance gets a default gray color (0xFF9E9E9E), the 🐾 paw icon, 10 reward points, the ai category, orderIndex one past the current maximum so it lands at the end of the list, active = true, and activatedAt stamped now. Its tracking type is stepByStep with allowExceeding counting behavior — the most permissive defaults, on the theory that a new habit should collect data before it collects rules.
After either branch, the service patches the AI's JSON with the real new habitId, fills in the default points, and resolves the Play with the completed data. The owner sees a normal check-in; under the hood, the habit library grew by one instance that is fully editable afterwards — retune the reminder, change the goal, archive it later if it never recurs.
There is a third safety net, and it is the one that makes the system feel genuinely reliable: startup recovery. runStartupRecovery scans all Play records stuck in the syncFailed state with a non-empty description — check-ins whose AI classification failed at the time, usually a dead network or an engine timeout — and re-runs the classifier for each one, loading the correct per-pet engine context by pet key so a multi-pet household's records are classified against the right dog. Every retried record flows back through resolveWithHabitFallback, so a failed check-in from last Tuesday can create the habit it should have created then. Nothing the owner recorded is ever silently dropped; it is deferred and eventually resolved.
The design stance is worth naming explicitly: the AI never fails a check-in. It classifies, or it copies a template, or it creates a habit — and each of those outcomes writes a durable, queryable record. That is what check-in data feeding habit creation looks like in practice: the habit list of an active dog is not a static menu the owner configured on day one; it grows in the same direction as the dog's actual life, one photo at a time. The Hermes Agent skills architecture post shows how this classifier behavior sits inside the wider agent skill system — photo-analyze, daily-review, care-monitor, care-handbook, daily-voice all share the same memory, so a habit created by one skill is visible to all the others.
The Refinement Loop: Streaks, Reminders, and Nightly Review
Creation gets a habit born; refinement is what makes it true. PupPal runs several loops that turn raw check-in data into a progressively better routine, and they run on different clocks — instantly, daily, and every night at 21:00.
The instant loop is the completion rule itself. getTodayHabits computes which habits are due today by reading the instance's own configuration: a daily habit is due every day; a weekly habit is due only when reminderWeekDays[weekday] is true for today; a monthly habit is due on the first of the month. When a check-in lands, getHabitsCompletionForDate groups the day's Play records by habitId and sums their value, which answers the question "how many of the two daily feedings have happened so far?" The streak goal on each instance is the target the UI chases: the timeline draws per-habit bars, the heatmap view paints a calendar of completion density, and the habit tree page renders each habit as a horizontal bar of its cumulative play count — a low-fi, honest visualization of which routines actually hold. Small delights reinforce the loop: a completion triggers a fly animation, and the achieved-habits page collects the milestones.
The human-tuning loop is archive and restore. HabitRepository supports archiving an instance (active = false), and the pet page applies a rule worth quoting: a habit with check-in records is archived, never deleted — history must survive; a habit with no records is removed outright. Archive is reversible: restoreHabit flips the instance back to active and returns the refreshed active list, so a routine that died in winter can come back for spring without losing its old streak history. Because the instance carries its own reminderWeekDays, reminderTimeValue, completionsPerPeriod, and streakGoalValue, tuning a routine is editing fields on the dog's copy — it never forks the template, and it never affects another dog.
The slow loop is the nightly review, and it is the part that makes AI habit tracking feel predictive instead of merely record-keeping. Every day at 21:00, the daily-review cron in the Hermes profile wakes, reads the day's photo check-ins, the dog's current state, and the accumulated stats history, then runs trend_calc.py with three analyses: S1 compares today's happiness, energy, and health scores against the 7-day baseline; S3 confirms feeding by counting detected eating scenes against the usual meal count and reports normal, less, more, or unable_to_detect; S4 compares today's activity score against the 7-day average and breaks down high- and low-activity moments. A weighted health score is computed — 25 percent happiness, 30 percent anomaly-free, 15 percent energy, 15 percent feeding normality, 15 percent activity versus baseline — and appended to stats.history. The review's own documentation states that observations like "eating habits changed" are written to long-term memory, which is exactly how a drift in routine becomes a durable, queryable fact rather than a feeling the owner almost noticed.
The thresholds make the loop actionable: if the health score declines three days straight by a cumulative 0.2, or energy stays below 0.3 for three days, the agent pushes a high-severity alert; a single-day happiness drop or an anomaly score above 0.5 gets a medium flag in the report; three consecutive high-severity days triggers a recommendation to book a vet. Every one of these checks is only possible because the habit layer has been accumulating labeled Play records — the review is comparing today's feeding count against the habit instance's usual cadence, and today's activity against the streak history that the check-ins built. The nightly health review post walks S1/S3/S4 and the trend math in full; here the point is the loop: photo check-ins feed habit records, habit records feed baseline statistics, and baseline statistics feed the alerts that refine the routine the owner sees next morning alongside the dog's first-person daily voice message.
Habits That Travel: Caregivers, Foster Care, and the Handbook
Habits live on the dog's instance, not in the owner's head — which means they can be handed to someone else. That one property is what turns the habit system from a personal log into a care infrastructure, and it shows up in three places.
First, the care handbook. When an owner starts a care session and a caregiver types in a nine-digit care code plus PIN, the care-handbook skill generates an instruction document from the dog's memory, and section 4 is explicitly "behavior habits and care tips" — friendliness, leash behavior, known commands, the daily rhythm. The caregiver who has never met the dog learns not the abstract species facts but the particular habits: feeding twice a day with the supplement at breakfast, the walk window, the puzzle toy that burns off evening energy, the command words that actually work. A temporary human being gets the habitual intelligence that the template library and the check-in history have accumulated, without needing an account — the care handbook story shows this from the caregiver side, and the sharing model (code plus PIN, no registration) is the subject of its own deep dive.
Second, foster care. The care-monitor skill runs the same check-in pipeline during a foster or care session, but with deliberately tighter anomaly thresholds — the photo-analyze skill's threshold table states it plainly: in caregiver mode, all alert thresholds are lowered by 40 percent, because a dog in a new environment under someone else's eye needs more sensitivity, not less. The habit layer participates directly: the caregiver's photo check-ins land as Play records on the same instances, so a two-week foster period produces a two-week streak history the owner can read when the dog comes home. When the dog's own habits change under foster care — eating less, sleeping more — the nightly review catches it with the same baseline machinery, and the C7 anomaly check is more eager to flag a deviation because the source is a caregiver. The foster care monitoring post covers the tightened thresholds in depth.
Third, multi-pet households. Because instances are keyed by petId, the same template shelf serves every dog in the house without cross-contamination — and the recovery service loads each pet's engine context by pet key, so a photo of the cat in the same session as a photo of the dog is classified against the right habits. The multi-pet household post examines the coordination side; from the habit layer's perspective the guarantee is architectural: a habit instance belongs to exactly one dog, its history is that dog's history, and its refinement follows that dog's life.
What these three cases share is the same underlying fact: the habit system's output is portable knowledge. A template is a recipe anyone can learn; an instance is a biography only the dog can write; and both can be handed, temporarily and safely, to another human without that human becoming part of the product's social graph. AI habit tracking in PupPal is not a dashboard the owner stares at alone — it is a working memory that the whole care team reads and writes.
What AI Habit Tracking Actually Buys You
Step back from the code and the loop closes into an argument worth stating plainly. A traditional habit tracker sells you a blank grid and your own discipline — the app remembers nothing unless you do the remembering. PupPal's habit layer sells you a system that remembers on your behalf: the photo you take anyway becomes the record, the record feeds the classification, the classification maintains the streak, the streak feeds the nightly baseline, and the baseline catches the drift before it becomes a vet visit. Creation, classification, and refinement all run on the same check-in stream, which is why the feature list of the product — photo check-in, C7 anomaly detection, daily review, daily voice, care codes, care handbook — reads like one pipeline when you look at the source instead of the marketing.
The two-layer model is the quiet genius underneath it. Templates keep the shared knowledge curated and market-grounded — 49 activities, 10 categories, every one with a defensible source; instances keep the lived reality flexible — per-pet goals, per-dog reminder times, archive-and-restore without data loss. The AI sits between them as a translator: matching moments to habits at zero marginal token cost, copying a template when a moment matches a recipe, and creating a fresh instance when the dog's life outruns the catalog. Every habit in the list is either a recipe applied or a story the AI helped write, and the owner is free to edit either one at any time.
That is the practical promise of AI habit tracking with PupPal: start with one photo and let the habit library assemble itself. Take the morning breakfast photo, add the evening walk check-in, and within two weeks the app has a labeled, scored, streak-tracked record of the dog's actual routine — not the routine you intended, the routine that is really happening. When the record is honest, the refinement is honest, and the alerts that come out of it are worth trusting. The first photo is the only onboarding there is.
So set up PupPal, snap the first check-in of the day, and let the agent do the bookkeeping. The template shelf is already loaded with the recipes; your dog's instances are waiting to be written — one photo, one habit, one streak at a time.