| name | dog-day |
| description | Tempered orchestrator: chains frozen, deterministic decision trees (decide_walk, decide_meal, decide_vet) and keeps only the generative note. Use to run a daily dog-care flow with each decision made by code (no LLM) and only the prose left to the model. |
dog-day — orchestrator (tempered by temper-skills)
You are an assistant.
The decisions below are frozen — extract the features, call each tree, relay the verdict, don't re-derive. Only the generative step(s) are yours to phrase. This is the DMN-vs-BPMN split: the decisions are code, the orchestration and prose stay with you.
1. decide_walk — frozen
Extract hours_since_last_walk, weather, temperature_c, dog_energy, owner_available, is_late, then:
from scripts.decide_walk import decide_walk
decide_walk_verdict = decide_walk({'hours_since_last_walk': hours_since_last_walk, 'weather': weather, 'temperature_c': temperature_c, 'dog_energy': dog_energy, 'owner_available': owner_available, 'is_late': is_late})
- gray zone: measured temperature_c overrides the label — >30°C always skips; the 'heat' label only skips when temperature is unmeasured (None). snow/cold carry no branch (source is silent) and degrade to normal_walk.
- gray zone: late + owner-away falls through to a walk; source says don't postpone when late but never says who walks.
2. decide_meal — frozen
Chained: feed the outcome of decide_walk into the matching feature below.
Extract hours_since_last_meal, time_of_day, last_meal_size, just_exercised, minutes_since_exercise, had_full_meal_today, then:
from scripts.decide_meal import decide_meal
decide_meal_verdict = decide_meal({'hours_since_last_meal': hours_since_last_meal, 'time_of_day': time_of_day, 'last_meal_size': last_meal_size, 'just_exercised': just_exercised, 'minutes_since_exercise': minutes_since_exercise, 'had_full_meal_today': had_full_meal_today})
- gray zone: Evening skip keys on the whole-day had_full_meal_today (added via schema re-gate r1); last_meal_size=='full' is only the fallback when the flag is None. Overlap case [evening + already_full + just_exercised + minutes<30] resolves to treat_only, not wait_then_full_meal, because promising a full meal would violate the 'a treat at most' cap — the four-outcome set can't say 'wait, then a treat', so offer the treat once the dog settles.
- gray zone: Within 30 min of exercise (or unknown timing) rest first; None minutes_since_exercise is treated as still-resting (conservative, honors 'never feed within 30 min').
- gray zone: just_exercised is the governing exercise predicate; a stale minutes_since_exercise with just_exercised False is correctly ignored.
- gray zone: Ate nothing today -> a real meal, not the token light_meal default.
3. decide_vet — frozen
Extract symptom, severity, duration_hours, age_years, then:
from scripts.decide_vet import decide_vet
decide_vet_verdict = decide_vet({'symptom': symptom, 'severity': severity, 'duration_hours': duration_hours, 'age_years': age_years})
- gray zone: puppy(<1)/senior(>8) lower threshold applies even to a mild & brief symptom, so they never reach the monitor branch.
Then — generation, yours
- Write the owner a short, warm summary: the walk call, the meal plan, any vet guidance, and anything to watch. This step is not frozen — phrase it naturally.
Generated by temper-skills decompose --temper-each. Each decision is a pure function — testable (temper-skills validate) and evolvable (temper-skills incremental); regenerate this orchestrator when a tree changes.