Skip to content

colonelpanic8/org-window-habit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

72 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

org-window-habit

https://melpa.org/packages/org-window-habit-badge.svg https://stable.melpa.org/packages/org-window-habit-badge.svg https://github.com/colonelpanic8/org-window-habit/actions/workflows/ci.yml/badge.svg

Why org-window-habit?

Standard org-habit tracks habits using fixed repeater intervals: complete the habit, and it reschedules for the next interval. This works well for habits with predictable schedules, but many real-world habits don’t fit that model:

  • Exercise 5 times per week - You don’t care which 5 days, just that you hit 5 within any 7-day window
  • Read 4 books per month - Finishing one book per week is fine, but so is reading two books in the first week and two in the last
  • Take medication 3 times per day - The exact hours don’t matter, just the count within each 24-hour period

With standard org-habit, missing a single day shows as a “failure” in your consistency graph, even if you’re exceeding your actual goals. This can be demoralizing and doesn’t reflect how habits actually work in practice.

org-window-habit replaces this binary daily model with window-based tracking: instead of asking “did you do it today?”, it asks “have you done it enough times within this time window?”

The Window Habit Concept

A window habit evaluates your compliance over a rolling time window rather than a single day. You define:

  1. Window Duration - How far back to look when counting completions (e.g., 7 days, 1 month)
  2. Repetitions Required - How many completions are needed within that window (e.g., 5 times)
  3. Assessment Interval - How often to re-evaluate, i.e., the step size for the rolling window (e.g., daily)

Note that window duration and assessment interval serve different purposes: the window duration determines how much history counts toward your goal, while the assessment interval determines how granularly the window rolls forward. For example, with a 7-day window and 1-day assessment interval, you get a fresh evaluation each day that looks back at the previous 7 days. If the assessment interval were also 7 days, the window would jump weekly rather than rolling daily.

A practical example where these differ: suppose you want to write 8 blog posts per month, but you don’t want daily nagging - just a weekly check-in. You’d set a 1-month window duration with a 1-week assessment interval. Each week, the system evaluates whether you’re on track for your monthly goal, and the habit only appears in your agenda at weekly boundaries.

Assessment Interval Anchoring

For fixed-length intervals (days, hours), assessment boundaries are anchored to your habit’s start time, not arbitrary calendar boundaries. This means:

  • A habit with a 3-day assessment interval that started on Jan 10 will have periods: Jan 10-13, Jan 13-16, Jan 16-19, etc.
  • Another habit with the same 3-day interval but starting Jan 11 will have different periods: Jan 11-14, Jan 14-17, Jan 17-20, etc.

This ensures consistent, predictable evaluation periods for each habit.

The library tracks your completions and calculates a conforming ratio - are you on track to meet your goal? The agenda graph shows this visually, with colors indicating your conformity status (by default, green when conforming and red when falling behind).

When you complete a habit, org-window-habit automatically reschedules it to the next date when a completion will be needed to maintain conformity, rather than using a fixed repeater interval.

Example: Weekly Exercise

You want to exercise 5 times per week. With org-window-habit:

* TODO Exercise
SCHEDULED: <2024-01-15 Mon .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:days 7) :repetitions 5)))
:END:

If you exercise Monday, Tuesday, Wednesday, Thursday, and Friday, you’ve met your goal for the week. Saturday and Sunday will show as “optional” - you don’t need to exercise, but you can if you want to build buffer.

If you only exercised 3 times so far this week and it’s Friday, the graph shows you’re behind and the habit appears as requiring completion.

Installation

org-window-habit is available on MELPA. With use-package:

(use-package org-window-habit
  :ensure t
  :demand t
  :config
  (org-window-habit-mode +1))

Or install from git using straight.el:

(use-package org-window-habit
  :demand t
  :straight
  (org-window-habit
   :repo "colonelpanic8/org-window-habit"
   :host github
   :files ("org-window-habit.el"))
  :config
  (org-window-habit-mode +1))

Basic Usage

Creating a Window Habit

  1. Create a TODO heading with SCHEDULED or DEADLINE that has any repeater (the repeater value doesn’t matter - org-window-habit uses its own rescheduling logic, but a repeater must be present)
  2. Add :STYLE: habit under :PROPERTIES:
  3. Add the OWH_CONFIG property with your habit configuration
  4. Ensure Org is logging TODO state changes for the entry. org-window-habit reads completion history from standard Org state-change logs whether they are stored inline or inside a LOGBOOK drawer. Setting org-log-into-drawer is optional.

Recommended: Unified CONFIG Property

The OWH_CONFIG property is the recommended way to configure window habits. It uses a single property containing all habit parameters:

* TODO Exercise
SCHEDULED: <2024-01-15 Mon .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:days 7) :repetitions 5)))
:END:

This defines a habit requiring 5 completions per 7-day window.

With additional options:

* TODO Exercise
SCHEDULED: <2024-01-15 Mon .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:days 7) :repetitions 5)) :assessment-interval (:days 1) :only-days (:monday :wednesday :friday))
:END:

Available CONFIG keys:

KeyRequiredDefaultDescription
:window-specsYes-List of window specifications (see below)
:assessment-intervalNo(:days 1)How often to re-evaluate
:reschedule-intervalNo(:days 1)Minimum time after completion before rescheduling
:reschedule-thresholdNo1.0Conforming ratio threshold for rescheduling
:max-reps-per-intervalNo1Max completions counted per assessment interval
:only-daysNo(all days)Restrict to specific days (e.g., (:monday :friday))
:fromNo(unbounded)Ignore completions before this date

Window spec format:

(:duration (:days 7) :repetitions 5)
(:duration (:days 7) :repetitions 5 :value 1.0)  ; with explicit weight

Versioned Configurations (Evolving Habits)

Habits often evolve over time - you might start with 3 workouts per week and gradually increase to 5. The CONFIG property supports versioned configurations that let your habit requirements change while preserving accurate historical tracking.

Example: Increasing workout frequency

* TODO Exercise
SCHEDULED: <2024-01-15 Mon .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: ((:window-specs ((:duration (:days 7) :repetitions 5))) (:until "2024-06-01" :window-specs ((:duration (:days 7) :repetitions 3))))
:END:

This defines:

  • Current requirement: 5 times per week (active from June 1, 2024 onward)
  • Previous requirement: 3 times per week (active until June 1, 2024)

Versioned config rules:

  1. Configs are listed in reverse temporal order (most recent first)
  2. The first config has no :until (active now and forever)
  3. Each config’s :from is implicitly the previous config’s :until
  4. An explicit :from on a config creates a gap where the habit is inactive

With a gap (habit pause):

:OWH_CONFIG: ((:window-specs ((:duration (:days 7) :repetitions 5))) (:until "2024-06-01" :from "2024-01-01" :window-specs ((:duration (:days 7) :repetitions 3))))

This habit was inactive before January 1, 2024 (the :from on the oldest config acts like a reset time).

Legacy: Scattered Properties

For backwards compatibility, you can use individual properties instead of OWH_CONFIG. See “Configuration Properties Reference” below for the full list of legacy properties and defaults.

Additional Examples

Monthly reading example:

* TODO Read a book
SCHEDULED: <2024-01-15 Mon .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:months 1) :repetitions 4)))
:END:

Calendar-aligned monthly task (e.g., paying rent):

For tasks that must happen exactly once per calendar month (not a rolling 30-day window), set both the window duration and assessment interval to (:months 1). The library aligns monthly intervals to the 1st of each month:

* TODO Pay rent
SCHEDULED: <2024-01-01 Mon .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:months 1) :repetitions 1)) :assessment-interval (:months 1))
:END:

This evaluates once per calendar month: did you pay rent in January? In February? The window doesn’t roll daily - it jumps from month to month, aligned to calendar boundaries.

Traditional Org-Habit Style (Fixed Interval from Completion)

If you want a habit that simply repeats a fixed number of days after each completion - like traditional org-habit with a .+3d repeater - you can achieve this with org-window-habit:

* TODO Water plants
SCHEDULED: <2024-01-15 Mon .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:days 3) :repetitions 1)))
:END:

This says “I need to water plants once every 3 days.” After completing, the habit automatically reschedules to the next date when a completion will be needed - approximately 3 days out.

The key insight is that org-window-habit’s rescheduling is based on when you’ll fall out of conformity, not a fixed offset. With a 3-day window requiring 1 completion, you’ll need to complete again within 3 days of your last completion to stay conforming.

Taking medication every 8 hours:

* TODO Take medication
SCHEDULED: <2024-01-15 Mon .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:hours 8) :repetitions 1)) :assessment-interval (:hours 1))
:END:

Weekly task (once per week):

* TODO Review finances
SCHEDULED: <2024-01-15 Mon .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:days 7) :repetitions 1)))
:END:

The advantage over traditional org-habit is that you still get the visual conformity graph showing your history, and the flexibility to complete early without penalty.

Advanced Configuration: Multiple Windows

For more complex requirements, use :window-specs in OWH_CONFIG to define multiple evaluation windows. The habit must satisfy all windows simultaneously.

:PROPERTIES:
:OWH_CONFIG: (:window-specs ((:duration (:days 4) :repetitions 1) (:duration (:days 6) :repetitions 2)))
:END:

This requires:

  • At least 1 completion in any 4-day window, AND
  • At least 2 completions in any 6-day window

This prevents both long gaps and insufficient frequency.

Configuration Properties Reference

All properties use the OWH_ prefix by default (configurable via org-window-habit-property-prefix).

Recommended: CONFIG Property

The OWH_CONFIG property is the recommended approach. It contains all habit parameters in a single plist and supports versioned configurations. See “Unified CONFIG Property” above for details.

Legacy: Scattered Properties

For backwards compatibility, you can use individual properties instead of OWH_CONFIG:

PropertyDefaultDescription
OWH_WINDOW_DURATION(required)Single window length (e.g., 1w, 1m, (:days 7))
OWH_REPETITIONS_REQUIRED(required)Target completions for simple window
OWH_OKAY_REPETITIONS_REQUIREDsame as aboveMinimum acceptable completions
OWH_WINDOW_SPECS(alternative)List of window specs for complex requirements
OWH_ASSESSMENT_INTERVAL(:days 1)How often to re-evaluate (step size for rolling window)
OWH_RESCHEDULE_INTERVAL(:days 1)Minimum time after completion before rescheduling
OWH_MAX_REPETITIONS_PER_INTERVAL1Cap on completions counted per assessment interval
OWH_RESET_TIME(none)Inactive date (e.g., [2024-01-15]) to ignore completions before
OWH_ONLY_DAYS(none)Restrict habit to specific days of week (e.g., M/W/F)

Note: When OWH_CONFIG is present, scattered properties are ignored. You can migrate existing habits using M-x org-window-habit-migrate-to-config.

Reading the Agenda Graph

Like org-habit, org-window-habit displays a consistency graph in the agenda. Each character represents one assessment interval (typically one day):

  • Past intervals: Show whether you were conforming (green) or not (red) at that time
  • Current interval: Shows whether you need to complete today:
    • (box) - Completion needed today to stay on track
    • (checkmark) - Already completed today (or not needed)
  • Future intervals: Project expected conformity based on current trajectory

The colors indicate your conforming ratio:

  • Green shades: On track or ahead
  • Red shades: Falling behind on requirements

Customization

Key customizable variables (M-x customize-group RET org-window-habit):

VariableDefaultDescription
org-window-habit-property-prefix"OWH"Prefix for org properties
org-window-habit-preceding-intervals21Days of history shown in graph
org-window-habit-following-days4Days of future projection in graph
org-window-habit-conforming-color"#4d7085"Color for conforming intervals
org-window-habit-not-conforming-color"#d40d0d"Color for non-conforming intervals
org-window-habit-completion-needed-today-glyph?☐Character for “needed today”
org-window-habit-completed-glyph?✓Character for “completed”

Duration Format

Window durations can be specified as:

  • Shorthand strings: 1d (1 day), 1w (1 week), 1m (1 month), 2w (2 weeks)
  • Property lists: (:days 7), (:weeks 1), (:months 1), or combinations like (:days 1 :hours 12)

Week Alignment

Note that 1w shorthand converts to (:days 7), which does not align to a specific day of the week. For true week-aligned habits, use the :weeks duration type with an optional :start day:

  • (:weeks 1) - Aligns to Monday (default)
  • (:weeks 1 :start :sunday) - Aligns to Sunday
  • (:weeks 1 :start :saturday) - Aligns to Saturday (for habits aligned to the weekend)

Available start days: :sunday, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday

Example: Weekly review every Monday

* TODO Weekly review
SCHEDULED: <2024-01-15 Mon .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:weeks 1 :start :monday) :repetitions 1)) :assessment-interval (:weeks 1 :start :monday))
:END:

Example: Sabbath observance (Friday sundown)

* TODO Shabbat preparation
SCHEDULED: <2024-01-19 Fri .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:weeks 1 :start :friday) :repetitions 1)) :assessment-interval (:weeks 1 :start :friday))
:END:

Day-of-Week Restrictions

Some habits only make sense on specific days of the week. For example, you might go to the gym on Monday, Wednesday, and Friday, or do meal prep only on weekends. Use :only-days in OWH_CONFIG to restrict which days count toward your habit (legacy OWH_ONLY_DAYS still works).

When :only-days is set:

  • Completions on non-allowed days are ignored when counting toward your goal
  • Rescheduling automatically targets the next allowed day

The format is a list of day symbols: (:monday :wednesday :friday)

Available days: :sunday, :monday, :tuesday, :wednesday, :thursday, :friday, :saturday

Example: M/W/F gym habit

* TODO Go to gym
SCHEDULED: <2024-01-15 Mon .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:days 7) :repetitions 3)) :only-days (:monday :wednesday :friday))
:END:

This requires 3 gym visits per week, but only Monday, Wednesday, and Friday completions count. If you complete on Tuesday by mistake, it won’t count toward your goal. After completing on Monday, the habit reschedules to Wednesday (skipping Tuesday).

Example: Weekend meal prep

* TODO Meal prep for the week
SCHEDULED: <2024-01-20 Sat .+1d>
:PROPERTIES:
:STYLE: habit
:OWH_CONFIG: (:window-specs ((:duration (:days 7) :repetitions 1)) :only-days (:saturday :sunday))
:END:

This requires meal prep once per week, but only on weekends. Completing on a weekday won’t count.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages