feat(habits): Habit-Source — wiederkehrende Candidates (Phase 4 von #360) #366
Labels
No labels
app/archiv
app/einkaufslisten
app/imap-client
app/wissensbasis
arch-answered
arch-question
area/api
area/auth
area/infra
area/mobile
area/shared
area/ui
area/web
portfolio-status
prio/high
prio/low
prio/medium
roadmap/public
size/l
size/m
size/s
size/xl
size/xs
status/blocked
status/needs-info
type/bug
type/chore
type/docs
type/feature
type/idea
type/refactor
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
admin-mrrm/mrrmlabapp#366
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Ziel
Phase 4 von Epic #360. Die Habit-Source erzeugt wiederkehrende Candidates aus Routinen (z.B. wöchentlicher Einkauf, monatliche Kontoabfrage, tägliches Lüften).
Habit ist die erste Source, die zeitgesteuert Candidates produziert — Mail/Tracking reagieren auf ein externes Ereignis, Habit auf "ein Termin in der Zukunft ist erreicht".
Datenmodell
Neue Tabelle
habits:id(uuid)ownerSubtitle(z.B. "Wocheneinkauf")recurrenceDays(integer— alle N Tage; v0-MVP, später cron-like)anchorDate(date— Ausgangspunkt der Folge)nextDueAt(timestamp— wann der nächste Candidate fällig ist)lastSpawnedAt(timestamp| null)createdAtcompletionPolicy(text—manual/time-elapsed, defaultmanual)Spawner-Logik
Service
HabitCandidateWriter.spawnDueCandidates(now):nextDueAt <= now.candidatemitsource = 'habit'sourceRef = ${habitId}:${nextDueAt-YYYY-MM-DD}(Idempotenz: ein Candidate pro Habit pro Fälligkeitstag)title = habit.titlecompletionPolicy = habit.completionPolicynextDueAt += recurrenceDays; setzelastSpawnedAt = now.ON CONFLICT DO NOTHINGauf(ownerSub, source, sourceRef)→ mehrfacher Cron-Run am selben Tag erzeugt keinen Duplicate.Cron-Integration
@Cronläuft (z.B. stündlich) und ruftspawnDueCandidates(new Date()).API (v0, minimal)
POST /habits— neuen Habit anlegenGET /habits— eigene Habits listenPATCH /habits/:id— title / recurrenceDays / anchorDate ändernDELETE /habits/:id(Habit-UI separates Issue, hier nur Backend-Surface.)
Tests
HabitCandidateWriter.spawnDueCandidates:nextDueAt > nownextDueAtnextDueAtin der Vergangenheit setzen, Spawn auslösen, Candidate prüfenOut of scope (für später)
Bezug
PM-Housekeeping (Convention-Falle): Fix wurde am 2026-05-20 via PR #367 (Merge
95312d2) aufmaingemerged. Das Issue blieb offen weil der Merge-Title nurfeat(#366):/fix(366):enthielt, nichtCloses #366.Habit-Source (Phase 4 von #360) implementiert.
Schließe manuell als verifiziert-implementiert. Konvention ist dokumentiert in
convention_commit_close_keywords.md; CI-Guard ist als arch-question #406 in Bearbeitung.