This is org-habit-ng.info, produced by makeinfo version 7.3 from
org-habit-ng.texi.

Copyright (C) 2024-2026 Aldric Giacomoni <trevoke@gmail.com>

   You can redistribute this document and/or modify it under the terms
of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any
later version.

   This document is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.
INFO-DIR-SECTION Emacs
START-INFO-DIR-ENTRY
* Org Habit NG Documentation: (org-habit-ng). Tutorials, how-to guides, reference, and explanation for org-habit-ng.
END-INFO-DIR-ENTRY


File: org-habit-ng.info,  Node: Top,  Next: Tutorials,  Up: (dir)

Org Habit NG Documentation
**************************

This is the documentation set for org-habit-ng.  It is organized so each
document serves one purpose for one kind of reader, not a mix.  Find the
quadrant that matches what you are doing right now.

Reader's intent                           Quadrant      Where to start
--------------------------------------------------------------------------------------------
"I want to learn org-habit-ng by doing"   Tutorials     *note Tutorials::
"I have a specific task to accomplish"    How-to        *note How-to guides::
"I need to look up a token or function"   Reference     *note Reference::
"I want to understand why it works"       Explanation   *note Explanation::

   If you are completely new, start with the *note first-habit tutorial:
Your first habit the 2nd Saturday of every month.  If you already know
habits and just need the rule for a pattern, jump straight to the *note
recurrence pattern catalogue: Recurrence pattern catalogue.

* Menu:

* Tutorials::                    Learn org-habit-ng by doing — a guided walkthrough for newcomers
* How-to guides::                Step-by-step recipes for accomplishing specific tasks
* Reference::                    Complete catalogue of patterns, tokens, commands, and API
* Explanation::                  Background, design decisions, and conceptual context
* Index::

-- The Detailed Node Listing --

Tutorials

* Your first habit the 2nd Saturday of every month::

Your first habit: the 2nd Saturday of every month

* Before we start::
* Step 1 create a heading::
* Step 2 run the wizard::
* Step 3 answer the "On a schedule" questions::
* Step 4 see what it wrote::
* Step 5 complete it and watch the date advance::
* What you've built::


How-to guides

* How to install and configure org-habit-ng::
* How to author a habit::
* How to skip instances and take vacations::
* How to read the graph and check your score::

How to install and configure org-habit-ng

* Load the package::
* Enable org-habit's graph::
* Recipe use-package::
* Confirm it is working::
* Optional configuration::
* Where to go next::


How to author a habit

* Use the authoring panel::
* Use the wizard::
* Hand-author the property::
* Choose repeat-from behaviour::
* Bound a limited habit::
* See also::


How to skip instances and take vacations

* Skip a single instance or bucket::
* Hand-type a skip from a mobile device::
* Excuse a whole trip on one habit::
* Pause every habit at once::
* Recover from a missed season-end due date::
* See it on the graph::
* Quick reference::
* See also: See also (1).


How to read the graph and check your score

* Read the consistency graph::
* Check the strength score::
* Agenda score vs. the command: Agenda score vs the command.
* See also: See also (2).


Reference

* Recurrence pattern catalogue:: You want X — here is the rule. The full expressible surface.
* RRULE and extensions reference:: Every token, its values and defaults, and the validation errors.
* Commands, API, and configuration: Commands API and configuration.  Interactive commands, the public engine API, and every defcustom.

Recurrence pattern catalogue

* Point patterns specific due dates::
* Success models what counts as keeping up::
* Authoring shortcuts @-macros::
* See also: See also (3).


RRULE and extensions reference

* Standard RRULE tokens::
* org-habit-ng extension tokens::
* Validation errors::
* See also: See also (4).


Commands, API, and configuration

* Interactive commands::
* Public engine API::
* Configuration variables::
* How it hooks into org-habit::
* See also: See also (5).


Explanation

* About the domain model::
* About the success models::
* About the forgiving score::

About the domain model

* Why a new model at all::
* The three concepts::
* Point versus period::
* The anchor::
* Repeat-from where the next date comes from::
* Further reading::


About the success models

* On a schedule (point + window)::
* So many times each period (period / bucket)::
* At least every so often (ceiling)::
* So many times in a rolling window::
* Skipped::
* How the models relate::
* Further reading: Further reading (1).


About the forgiving score

* The problem with a binary graph::
* What the score does::
* Why it errs toward forgiveness::
* Honesty guards::
* A read-side layer::
* Further reading: Further reading (2).



File: org-habit-ng.info,  Node: Tutorials,  Next: How-to guides,  Prev: Top,  Up: Top

1 Tutorials
***********

Tutorials are guided lessons.  Each one takes you from zero to a working
result on a small example, so you build the muscle memory of using
org-habit-ng before you have to figure out anything on your own.

   If you finish and find yourself wanting to do something different in
your own files, that is the cue to leave the tutorial and look at a
*note how-to guide: How-to guides. instead.

* Menu:

* Your first habit the 2nd Saturday of every month::


File: org-habit-ng.info,  Node: Your first habit the 2nd Saturday of every month,  Up: Tutorials

1.1 Your first habit: the 2nd Saturday of every month
=====================================================

In this tutorial, we will create a recurring habit that standard
org-habit cannot express — _review finances on the 2nd Saturday of every
month_ — using the wizard, and watch it land on the right dates.  By the
end, you will have one working habit heading in an org file and know how
its SCHEDULED date advances when you complete it.

   We will call every command by name with ‘M-x’, so this works no
matter how your configuration is set up.

* Menu:

* Before we start::
* Step 1 create a heading::
* Step 2 run the wizard::
* Step 3 answer the "On a schedule" questions::
* Step 4 see what it wrote::
* Step 5 complete it and watch the date advance::
* What you've built::


File: org-habit-ng.info,  Node: Before we start,  Next: Step 1 create a heading,  Up: Your first habit the 2nd Saturday of every month

1.1.1 Before we start
---------------------

We assume:

   • Emacs is running.
   • org-habit-ng is installed and ‘org-habit-ng-mode’ is on.  If it
     isn't, follow *note How to install and configure org-habit-ng::
     first, then come back.


File: org-habit-ng.info,  Node: Step 1 create a heading,  Next: Step 2 run the wizard,  Prev: Before we start,  Up: Your first habit the 2nd Saturday of every month

1.1.2 Step 1: create a heading
------------------------------

Open any org file and add this heading:

     * TODO Review finances
     SCHEDULED: <2026-07-11 Sat>
     :PROPERTIES:
     :STYLE: habit
     :END:

   The ‘:STYLE: habit’ line is what makes org-mode draw a consistency
graph for this heading.  Unlike standard org-habit, we do _not_ put a
repeater (like ‘.+1d’) in the SCHEDULED timestamp — the recurrence will
live in a property we are about to add.


File: org-habit-ng.info,  Node: Step 2 run the wizard,  Next: Step 3 answer the "On a schedule" questions,  Prev: Step 1 create a heading,  Up: Your first habit the 2nd Saturday of every month

1.1.3 Step 2: run the wizard
----------------------------

With point anywhere on the heading, type:

     M-x org-habit-ng-set-recurrence RET

   The wizard opens and asks its first question, *What kind of habit?*
Choose:

     On a schedule — e.g. every Monday, or the 2nd Tuesday


File: org-habit-ng.info,  Node: Step 3 answer the "On a schedule" questions,  Next: Step 4 see what it wrote,  Prev: Step 2 run the wizard,  Up: Your first habit the 2nd Saturday of every month

1.1.4 Step 3: answer the "On a schedule" questions
--------------------------------------------------

The wizard now walks a short series of prompts.  Answer them in order:

  1. *How often does this repeat?*  → ‘Monthly’
  2. *Every how many months?*  → ‘1’
  3. *Repeat on:* → ‘Nth weekday (e.g., 2nd Saturday)’
  4. *Which day?*  → ‘Saturday’
  5. *Which occurrence?*  → ‘Second’
  6. *Add another ordinal weekday?*  → ‘n’

   After that the wizard asks about flexibility, repeat-from, and
termination.  For our first habit we want the plain behaviour, so accept
each default (press ‘RET’).  The wizard shows a preview of the next few
dates before saving.


File: org-habit-ng.info,  Node: Step 4 see what it wrote,  Next: Step 5 complete it and watch the date advance,  Prev: Step 3 answer the "On a schedule" questions,  Up: Your first habit the 2nd Saturday of every month

1.1.5 Step 4: see what it wrote
-------------------------------

The wizard writes a ‘RECURRENCE’ property onto the heading.  It looks
like:

     * TODO Review finances
     SCHEDULED: <2026-07-11 Sat>
     :PROPERTIES:
     :STYLE: habit
     :RECURRENCE: FREQ=MONTHLY;INTERVAL=1;BYDAY=2SA
     :END:

   That RRULE means "every month on the 2nd Saturday."  July 11 2026 is
the second Saturday of July, so our SCHEDULED date already sits on the
pattern.


File: org-habit-ng.info,  Node: Step 5 complete it and watch the date advance,  Next: What you've built,  Prev: Step 4 see what it wrote,  Up: Your first habit the 2nd Saturday of every month

1.1.6 Step 5: complete it and watch the date advance
----------------------------------------------------

Mark the habit done the way you mark any org TODO — with point on the
heading:

     C-c C-t   (then select DONE)

   org-habit-ng computes the _actual_ next 2nd Saturday — Saturday
August 8 2026 — and updates SCHEDULED to it.  Notice the interval is not
a flat 30 days: from July 11 to August 8 is 28 days, and the following
jump (to September 12) is 35.  The consistency graph shows those real
intervals, not an average.


File: org-habit-ng.info,  Node: What you've built,  Prev: Step 5 complete it and watch the date advance,  Up: Your first habit the 2nd Saturday of every month

1.1.7 What you've built
-----------------------

You now have a habit that recurs on a rule org-habit's plain repeaters
cannot express, and you have seen its SCHEDULED date jump to the true
next occurrence when completed.

   • To express other patterns — quotas, ceilings, seasonal windows,
     nearest-weekday shifts — browse the *note recurrence pattern
     catalogue: Recurrence pattern catalogue.
   • To author habits (by hand or by wizard) in your own files, see
     *note How to author a habit::.
   • To understand _why_ SCHEDULED advances from the completion by
     default, and how point habits differ from period habits, see *note
     About the domain model::.


File: org-habit-ng.info,  Node: How-to guides,  Next: Reference,  Prev: Tutorials,  Up: Top

2 How-to guides
***************

How-to guides answer real-world questions.  Each one assumes you already
have org-habit-ng installed and you know roughly what you are trying to
do; the guide fills in the steps.

   If you don't yet have it installed, start with *note Install and
configure: How to install and configure org-habit-ng.

* Menu:

* How to install and configure org-habit-ng::
* How to author a habit::
* How to skip instances and take vacations::
* How to read the graph and check your score::


File: org-habit-ng.info,  Node: How to install and configure org-habit-ng,  Next: How to author a habit,  Up: How-to guides

2.1 How to install and configure org-habit-ng
=============================================

You have Emacs with org-mode, and you want RRULE-based habits.  This
guide loads org-habit-ng, turns it on, and verifies it end-to-end.

   org-habit-ng requires Emacs 28.1 or newer and org 9.6 or newer.  It
builds on org-mode's own habit support, so it needs the habit
consistency graph enabled (below).

* Menu:

* Load the package::
* Enable org-habit's graph::
* Recipe use-package::
* Confirm it is working::
* Optional configuration::
* Where to go next::


File: org-habit-ng.info,  Node: Load the package,  Next: Enable org-habit's graph,  Up: How to install and configure org-habit-ng

2.1.1 Load the package
----------------------

Clone or download the repository, then add it to your ‘load-path’ and
turn on the global minor mode:

     (add-to-list 'load-path "/path/to/org-habit-ng.el")
     (require 'org-habit-ng)
     (org-habit-ng-mode 1)

   ‘org-habit-ng-mode’ installs the advice that makes the ‘RECURRENCE’
property drive scheduling and the consistency graph.


File: org-habit-ng.info,  Node: Enable org-habit's graph,  Next: Recipe use-package,  Prev: Load the package,  Up: How to install and configure org-habit-ng

2.1.2 Enable org-habit's graph
------------------------------

Consistency graphs come from org-mode's ‘org-habit’ module, which
org-habit-ng extends.  Make sure it is loaded:

     (add-to-list 'org-modules 'org-habit)

   Habits appear in the agenda, so include the files that hold them in
‘org-agenda-files’.


File: org-habit-ng.info,  Node: Recipe use-package,  Next: Confirm it is working,  Prev: Enable org-habit's graph,  Up: How to install and configure org-habit-ng

2.1.3 Recipe: use-package
-------------------------

If you keep the checkout on disk, a ‘use-package’ block with
‘:load-path’ ties it together:

     (use-package org-habit-ng
       :load-path "/path/to/org-habit-ng.el"
       :after org
       :init
       (add-to-list 'org-modules 'org-habit)
       :config
       (org-habit-ng-mode 1))

   Bind the commands you use often — org-habit-ng binds no keys by
default:

     (with-eval-after-load 'org-habit-ng
       (define-key org-mode-map (kbd "C-c h r") #'org-habit-ng-set-recurrence)
       (define-key org-mode-map (kbd "C-c h s") #'org-habit-ng-skip)
       (define-key org-mode-map (kbd "C-c h c") #'org-habit-ng-show-score))


File: org-habit-ng.info,  Node: Confirm it is working,  Next: Optional configuration,  Prev: Recipe use-package,  Up: How to install and configure org-habit-ng

2.1.4 Confirm it is working
---------------------------

  1. In an org file, create a heading with ‘:STYLE: habit’ and a
     SCHEDULED date (no repeater needed).
  2. With point on it, run ‘M-x org-habit-ng-set-recurrence’ and build a
     simple daily rule.
  3. Open the agenda (‘C-c a a’).  The habit shows a consistency graph.
  4. Mark it DONE; SCHEDULED advances to the next occurrence.

   If the wizard cannot find a habit, check that the heading has both
‘:STYLE: habit’ and a SCHEDULED timestamp.


File: org-habit-ng.info,  Node: Optional configuration,  Next: Where to go next,  Prev: Confirm it is working,  Up: How to install and configure org-habit-ng

2.1.5 Optional configuration
----------------------------

   • ‘org-habit-ng-vacation-ranges’ — global vacation ranges suppressing
     every habit.
   • ‘org-habit-ng-skipped-glyph’ — the character drawn for a skipped
     cell (default ‘-’; make it ‘~’ or ‘o’ if it is hard to see in your
     theme).
   • ‘org-habit-ng-use-completing-read’ — force the completing-read
     wizard instead of transient menus.

   See *note Commands, API, and configuration: Commands API and
configuration. for the full list.


File: org-habit-ng.info,  Node: Where to go next,  Prev: Optional configuration,  Up: How to install and configure org-habit-ng

2.1.6 Where to go next
----------------------

   • *note Tutorial your first habit: Your first habit the 2nd Saturday
     of every month. builds a real 2nd-Saturday habit in five minutes.
   • *note How to author a habit:: covers the wizard and hand-authoring.
   • *note Recurrence pattern catalogue:: lists ready-made rules.


File: org-habit-ng.info,  Node: How to author a habit,  Next: How to skip instances and take vacations,  Prev: How to install and configure org-habit-ng,  Up: How-to guides

2.2 How to author a habit
=========================

You want to add or change the recurrence on a habit.  There are three
ways: the *authoring panel*, a persistent menu that shows a live preview
as you build a fresh habit; the *wizard*, which asks a fixed sequence of
questions (and is what you use to modify an existing habit); and
*hand-authoring*, where you type the ‘RECURRENCE’ property directly.
This guide covers all three.  The panel and the wizard emit the same
kind of rule — they are two front ends over one engine.

   Either way, a habit heading needs two things: ‘:STYLE: habit’ in its
properties (so org-mode draws the graph) and a SCHEDULED timestamp (the
series anchor).  No repeater in the timestamp is needed — the
‘RECURRENCE’ property drives scheduling.

* Menu:

* Use the authoring panel::
* Use the wizard::
* Hand-author the property::
* Choose repeat-from behaviour::
* Bound a limited habit::
* See also::


File: org-habit-ng.info,  Node: Use the authoring panel,  Next: Use the wizard,  Up: How to author a habit

2.2.1 Use the authoring panel
-----------------------------

With point on a habit heading, open the panel:

     M-x org-habit-ng-author

   Unlike the wizard's fixed question sequence, the panel is a single
persistent menu.  Its header is a *live preview* — the plain-English
sentence for the rule you are building plus the next few real dates —
that updates every time you press a key, so you see the effect of each
choice as you make it.  It authors a *fresh* habit: to modify a habit
that already has a ‘RECURRENCE’, use the wizard (*note below: Use the
wizard.), which reads the existing rule first.

   Start by choosing the success model with ‘s’ (_On a schedule_, _So
many times each period_, _At least every so often_, or _So many times in
a rolling window_); the groups that follow adapt to that choice.  For an
_On a schedule_ habit:

   • ‘f’ cycles the frequency, ‘i’ sets the interval.
   • ‘d’ opens a weekday multi-toggle (weekly), ‘m’ opens the monthly
     day/ordinal submenu, ‘o’ the yearly submenu.
   • ‘l’ / ‘y’ set late / early tolerance (‘X-FLEX-AFTER’ /
     ‘X-FLEX-BEFORE’).
   • ‘~’ toggles the nearest-weekday shift (‘X-SKIP’), shown only when
     the rule is a MONTHLY/YEARLY day-of-month rule where it applies.
   • ‘e’ sets a ‘COUNT’ or ‘UNTIL’ bound, ‘n’ a seasonal months window,
     ‘k’ adds a vacation range.
   • ‘g’ loads a worked example, ‘RET’ saves, ‘q’ resets the panel.

   *Multi-ordinal months in one habit.*  The monthly submenu (‘m’) is
where "the 2nd _and_ 4th Tuesday" — a set of ordinal weekdays, not just
one — becomes reachable through the guided UI.  Move the ordinal cursor
with ‘1’ ‘2’ ‘3’ ‘4’ ‘l’ (last), then press a weekday letter (‘u m t w h
f s’ = Sun..Sat) to toggle that ordinal-weekday pair.  Set the cursor to
‘2’ and press ‘t’, then set it to ‘4’ and press ‘t’ again, and the panel
builds:

     FREQ=MONTHLY;INTERVAL=1;BYDAY=2TU,4TU

   which reads "Every month on the 2nd Tuesday and 4th Tuesday".  The
chips in the submenu heading show the current set.  (The wizard's
monthly reader accepts the same multi-ordinal set, so both paths express
this habit.)

   The panel needs graphical Emacs with a recent ‘transient’ (Emacs
29+).  When it isn't available (a terminal, or Emacs 28's older
transient), ‘org-habit-ng-author’ falls back to the ‘completing-read’
wizard.


File: org-habit-ng.info,  Node: Use the wizard,  Next: Hand-author the property,  Prev: Use the authoring panel,  Up: How to author a habit

2.2.2 Use the wizard
--------------------

With point on the habit heading (or on its agenda line):

     M-x org-habit-ng-set-recurrence

   In graphical Emacs with a recent ‘transient’ (bundled with Emacs
29+), the wizard uses transient menus; in a terminal, or on Emacs 28's
older transient, it falls back to ‘completing-read’ prompts.  Both ask
the same questions in the same order — there is one wizard with two
front ends.  Set ‘org-habit-ng-use-completing-read’ to force the
prompt-based flow.

   The first question is always *What kind of habit?*, with four types:

   • *On a schedule* — e.g.  every Monday, or the 2nd Tuesday.  Specific
     due dates.  Asks frequency, day/date details, an optional seasonal
     activation window, asymmetric flexibility, repeat-from, and
     termination.
   • *So many times each period* — e.g.  3 times each week.  Once per
     week or month, any day.  Asks period type, week start, an optional
     quota (‘X-TIMES’) and minimum spacing (‘X-MIN-GAP’), and
     termination.
   • *At least every so often* — e.g.  at least every 5 days.  No due
     date, just don't go too long.  Asks the gap unit, an optional warn
     threshold, and termination (‘COUNT’ hidden).
   • *So many times in a rolling window* — e.g.  3 times in any 7 days.
     Asks the window length, an optional quota and minimum spacing, a
     warn threshold, and termination (‘COUNT’ hidden).

   The wizard shows a preview of upcoming dates before it saves.  If the
heading already has a ‘RECURRENCE’ property, you can modify or clear it.

   *The "Custom RRULE…" escape.*  Under the frequency prompt, choosing
‘Custom RRULE...’ lets you type a raw rule string, previews it, and
saves it directly — handy for patterns the wizard has no step for
(‘@-macros’, ‘X-SKIP’, unit-suffixed flex windows).


File: org-habit-ng.info,  Node: Hand-author the property,  Next: Choose repeat-from behaviour,  Prev: Use the wizard,  Up: How to author a habit

2.2.3 Hand-author the property
------------------------------

You can also type the ‘RECURRENCE’ property yourself.  Copy a rule from
the *note pattern catalogue: Recurrence pattern catalogue. into the
heading's property drawer:

     * TODO Team sync
     SCHEDULED: <2026-07-31 Fri>
     :PROPERTIES:
     :STYLE: habit
     :RECURRENCE: FREQ=MONTHLY;BYDAY=-1FR
     :END:

   Hand-authoring is the only way to reach a few features the wizard
does not prompt for: the ‘@daily=/=@weekly=/=@monthly=/=@yearly’ macros,
‘X-SKIP’ nearest-weekday shifting, and the ‘d=/=w’ unit suffixes on
‘X-FLEX-*’.

   Anything you hand-author is validated the moment the habit is parsed.
An impossible combination raises a clear error (see *note the validation
errors: Validation errors.) rather than misbehaving silently.


File: org-habit-ng.info,  Node: Choose repeat-from behaviour,  Next: Bound a limited habit,  Prev: Hand-author the property,  Up: How to author a habit

2.2.4 Choose repeat-from behaviour
----------------------------------

By default the next occurrence is computed from when you *complete* the
habit (‘X-REPEAT-FROM=completion’).  For habits that must stay on a
fixed calendar regardless of when you finish — medication, a standing
meeting — add ‘X-REPEAT-FROM=scheduled’:

     :RECURRENCE: FREQ=DAILY;X-REPEAT-FROM=scheduled

Value                Meaning
-----------------------------------------------------------------------
‘completion’         Next from when you mark DONE (default)
‘scheduled’          Next from the scheduled date
‘scheduled-future’   Like scheduled, but never schedules in the past

   Note that SCHEDULED is the *series anchor* — the graph draws the
pattern through it.  Hand-editing SCHEDULED (to snooze a habit)
re-phases the whole series, so past cells re-colour relative to the new
anchor.


File: org-habit-ng.info,  Node: Bound a limited habit,  Next: See also,  Prev: Choose repeat-from behaviour,  Up: How to author a habit

2.2.5 Bound a limited habit
---------------------------

For a habit that ends — a 30-day challenge, a fixed course of medication
— use ‘COUNT’ (stop after N occurrences) or ‘UNTIL’ (stop after a date):

     :RECURRENCE: FREQ=DAILY;COUNT=30

   When the series is exhausted, completing the habit leaves it DONE and
schedules nothing further.


File: org-habit-ng.info,  Node: See also,  Prev: Bound a limited habit,  Up: How to author a habit

2.2.6 See also
--------------

   • *note Recurrence pattern catalogue:: — a rule for whatever you're
     trying to express.
   • *note RRULE and extensions reference:: — token grammar and
     validation.
   • *note How to skip and take vacations: How to skip instances and
     take vacations. — for instances that don't apply.
   • *note How to read the graph and check your score::.


File: org-habit-ng.info,  Node: How to skip instances and take vacations,  Next: How to read the graph and check your score,  Prev: How to author a habit,  Up: How-to guides

2.3 How to skip instances and take vacations
============================================

Sometimes an instance genuinely doesn't apply — you're sick, travelling,
or the gym is closed.  Marking it DONE would be a lie, but leaving it
red breaks the streak unfairly.  org-habit-ng has a *skipped* status:
streak-neutral, neither satisfied nor missed.  This guide shows the
three ways to produce it.

* Menu:

* Skip a single instance or bucket::
* Hand-type a skip from a mobile device::
* Excuse a whole trip on one habit::
* Pause every habit at once::
* Recover from a missed season-end due date::
* See it on the graph::
* Quick reference::
* See also: See also (1).


File: org-habit-ng.info,  Node: Skip a single instance or bucket,  Next: Hand-type a skip from a mobile device,  Up: How to skip instances and take vacations

2.3.1 Skip a single instance or bucket
--------------------------------------

With point on the habit (or its agenda line):

     M-x org-habit-ng-skip

   This writes a plain-text line into the habit's LOGBOOK drawer and
advances SCHEDULED past exactly one occurrence, without counting a
completion.  For a point habit the line names one day; for a period
habit it spans the whole current bucket:

     :LOGBOOK:
     - Skipped [2026-01-06 Tue]
     - Skipped [2026-01-05 Mon]--[2026-01-11 Sun]
     :END:

   If point is not on a habit the command errors with "Point is not on
an org-habit-ng habit".


File: org-habit-ng.info,  Node: Hand-type a skip from a mobile device,  Next: Excuse a whole trip on one habit,  Prev: Skip a single instance or bucket,  Up: How to skip instances and take vacations

2.3.2 Hand-type a skip from a mobile device
-------------------------------------------

A skip is just a line of text, so you can type it in any org editor that
edits the raw buffer.  Two things must be exact:

   • *Use inactive timestamps* ‘[...]’, not active ‘<...>’.  An active
     timestamp would create its own agenda entry.
   • *No trailing comment.*  The line is matched to end-of-line, so ‘-
     Skipped [2026-01-06 Tue] (out sick)’ is _not_ recognized.  Put any
     note on its own line.


File: org-habit-ng.info,  Node: Excuse a whole trip on one habit,  Next: Pause every habit at once,  Prev: Hand-type a skip from a mobile device,  Up: How to skip instances and take vacations

2.3.3 Excuse a whole trip on one habit
--------------------------------------

For a date range, set the ‘X-VACATION’ property on the habit.  The
grammar is ‘[START]--[END]’ (both bounds required and inclusive — there
is no open-ended form) or a bare ‘[DATE]’ for a single day:

     * TODO Exercise
     SCHEDULED: <2026-07-14 Tue>
     :PROPERTIES:
     :STYLE: habit
     :RECURRENCE: FREQ=WEEKLY;BYDAY=TU
     :X-VACATION: [2026-08-01]--[2026-08-15]
     :END:

   Add ‘X-VACATION+’ lines to accumulate more ranges:

     :X-VACATION: [2026-08-01]--[2026-08-15]
     :X-VACATION+: [2026-12-24]--[2026-12-31]

   A due date or bucket inside a vacation range reads ‘skipped’ instead
of ‘missed’; a completion-anchored projection landing inside re-anchors
past it.  One wart: a vacation covering only _part_ of a quota bucket
(‘X-TIMES=N’) does not shrink N.


File: org-habit-ng.info,  Node: Pause every habit at once,  Next: Recover from a missed season-end due date,  Prev: Excuse a whole trip on one habit,  Up: How to skip instances and take vacations

2.3.4 Pause every habit at once
-------------------------------

For a holiday or an illness that affects your whole routine, add a range
to the global ‘org-habit-ng-vacation-ranges’ — a list of ‘(START . END)’
date-string conses, unioned with each habit's own ‘X-VACATION’:

     (setq org-habit-ng-vacation-ranges
           '(("2026-12-24" . "2026-12-31")))


File: org-habit-ng.info,  Node: Recover from a missed season-end due date,  Next: See it on the graph,  Prev: Pause every habit at once,  Up: How to skip instances and take vacations

2.3.5 Recover from a missed season-end due date
-----------------------------------------------

If you miss the last in-season due date of a seasonal habit (‘BYMONTH’)
— say the final October Monday of an April–October habit — it does not
quietly disappear.  It stays overdue in the agenda through the whole
off-season.  To clear it, either complete it late, or run ‘M-x
org-habit-ng-skip’ on it; both jump SCHEDULED forward to the first
in-season due date next season.


File: org-habit-ng.info,  Node: See it on the graph,  Next: Quick reference,  Prev: Recover from a missed season-end due date,  Up: How to skip instances and take vacations

2.3.6 See it on the graph
-------------------------

A skipped cell is drawn with ‘org-habit-ng-skipped-glyph’ (‘-’ by
default) and its tooltip explains why ("… · skipped" or "… · vacation").
If a bare ‘-’ is hard to see in your theme, customize the glyph to
something more visible:

     (setq org-habit-ng-skipped-glyph ?~)


File: org-habit-ng.info,  Node: Quick reference,  Next: See also (1),  Prev: See it on the graph,  Up: How to skip instances and take vacations

2.3.7 Quick reference
---------------------

You want                                 Do this
-------------------------------------------------------------------------------------
Mark today's due date not applicable     ‘M-x org-habit-ng-skip’ on a point habit
Mark this week's bucket not applicable   ‘M-x org-habit-ng-skip’ on a period habit
Excuse a trip on one habit               ‘:X-VACATION: [START]--[END]’
Add a second vacation range              ‘:X-VACATION+: [START]--[END]’
Pause every habit                        add to ‘org-habit-ng-vacation-ranges’


File: org-habit-ng.info,  Node: See also (1),  Prev: Quick reference,  Up: How to skip instances and take vacations

2.3.8 See also
--------------

   • *note About the success models:: — where _skipped_ sits among
     satisfied, missed, pending, and upcoming.
   • *note How to read the graph and check your score::.
   • *note RRULE and extensions reference: org-habit-ng extension
     tokens. — ‘X-VACATION’ grammar.


File: org-habit-ng.info,  Node: How to read the graph and check your score,  Prev: How to skip instances and take vacations,  Up: How-to guides

2.4 How to read the graph and check your score
==============================================

org-habit-ng shows two views of how a habit is going: the *consistency
graph* in the agenda, and a *strength score* on demand.  This guide
explains how to read each.

* Menu:

* Read the consistency graph::
* Check the strength score::
* Agenda score vs. the command: Agenda score vs the command.
* See also: See also (2).


File: org-habit-ng.info,  Node: Read the consistency graph,  Next: Check the strength score,  Up: How to read the graph and check your score

2.4.1 Read the consistency graph
--------------------------------

Habits appear in the org agenda (‘C-c a a’, or a custom habit view) with
a row of coloured cells to the right.  Each cell is a step back in time.
What a cell means depends on the habit's success model:

   • *Point habits* draw one cell per day, coloured by distance from the
     nearest due date — on time, within the flexibility window, or
     overdue.
   • *Period habits* draw one cell per period (week or month); a period
     with any completion is satisfied, an empty past period is missed.
   • *Off-season* days or buckets (‘BYMONTH’) are drawn dimmed with
     ‘org-habit-ng-off-season-face’ and never count against a streak.
   • *Skipped* cells (from ‘org-habit-ng-skip’ or a vacation) show
     ‘org-habit-ng-skipped-glyph’ (‘-’ by default) and are
     streak-neutral.

   Hover a cell (or leave point on it) to see its ‘help-echo’ tooltip,
which names the date and status; when ‘org-habit-ng-score-in-graph-echo’
is on (the default) the tooltip also carries a one-line strength
summary.


File: org-habit-ng.info,  Node: Check the strength score,  Next: Agenda score vs the command,  Prev: Read the consistency graph,  Up: How to read the graph and check your score

2.4.2 Check the strength score
------------------------------

With point on a habit (in an org buffer or on an agenda line):

     M-x org-habit-ng-show-score

   This messages a breakdown: a percentage in ‘0–100%’, the current
streak, and — if you recently recovered from a miss — how many instances
ago that was.  The score is an exponentially-weighted moving average
indexed by _instance_, not calendar day, so a weekly habit is judged
weekly and a daily habit daily.  A lone miss weeks back barely
registers; two misses in a row cost noticeably more.

   Two honesty notes worth knowing:

   • *Small counts say so.*  A brand-new habit with one completion
     scores "100% · 1 instance" — the instance count is printed whenever
     fewer than 10 instances have been scored, so a fresh 100% can't
     pass for a seasoned one.
   • *No score when there's nothing to score.*  A brand-new habit, or a
     seasonal habit deep in its off-season, shows no score line rather
     than a misleading "0%".


File: org-habit-ng.info,  Node: Agenda score vs the command,  Next: See also (2),  Prev: Check the strength score,  Up: How to read the graph and check your score

2.4.3 Agenda score vs. the command
----------------------------------

The score folded into the graph tooltip is computed from the completions
already in hand while rendering the graph — bounded by
‘org-habit-preceding-days’.  ‘org-habit-ng-show-score’ runs its own,
wider logbook re-scan and reflects the fuller history.  If the agenda
score looks truncated, run ‘M-x org-habit-ng-show-score’ for the
complete picture.

   Tune the decay with ‘org-habit-ng-score-half-life’ (default 13 scored
instances) and the look-back with ‘org-habit-ng-score-window-instances’
(default 52).


File: org-habit-ng.info,  Node: See also (2),  Prev: Agenda score vs the command,  Up: How to read the graph and check your score

2.4.4 See also
--------------

   • *note About the forgiving score:: — the philosophy and the maths
     behind the number.
   • *note About the success models:: — what each cell colour means per
     model.
   • *note Commands, API, and configuration: Commands API and
     configuration. — the scoring functions and their configuration
     variables.


File: org-habit-ng.info,  Node: Reference,  Next: Explanation,  Prev: How-to guides,  Up: Top

3 Reference
***********

Reference describes the machinery.  It is dense, complete, and neutral —
facts you look up while you work.  The *note pattern catalogue:
Recurrence pattern catalogue. is the place most authoring questions get
answered; the *note token reference: RRULE and extensions reference.
backs it with grammar and validation.

* Menu:

* Recurrence pattern catalogue:: You want X — here is the rule. The full expressible surface.
* RRULE and extensions reference:: Every token, its values and defaults, and the validation errors.
* Commands, API, and configuration: Commands API and configuration.  Interactive commands, the public engine API, and every defcustom.


File: org-habit-ng.info,  Node: Recurrence pattern catalogue,  Next: RRULE and extensions reference,  Up: Reference

3.1 Recurrence pattern catalogue
================================

This is the "you want _X_ — here is the rule" catalogue.  It maps a
plain description of a habit onto the exact ‘RECURRENCE’ value that
expresses it, across the whole surface org-habit-ng can express — not
just the common cases.

   Every rule below has been evaluated against the engine: the RRULE
parses, and the dates or English gloss shown are what the engine
actually produces.  Copy a rule into a heading's ‘RECURRENCE’ property
(see *note How to author a habit::) or feed it to the wizard.

   To look up an individual token's grammar, defaults, and the errors it
raises, see *note the RRULE and extensions reference: RRULE and
extensions reference.  For the mental model behind _point vs.  period_
and the five success models, see *note About the domain model:: and
*note About the success models::.

   Two axes run through this catalogue and it helps to keep them apart:

   • A *recurrence rule* answers _when is this due_ — the ‘FREQ’, ‘BY*’,
     and ‘X-SKIP’ parts in the first half.
   • A *success model* answers _what counts as keeping up_ — the
     ‘X-PERIOD’, ‘X-CEILING’, ‘X-WINDOW’, ‘X-FLEX-*’, and vacation parts
     in the second half.

* Menu:

* Point patterns specific due dates::
* Success models what counts as keeping up::
* Authoring shortcuts @-macros::
* See also: See also (3).


File: org-habit-ng.info,  Node: Point patterns specific due dates,  Next: Success models what counts as keeping up,  Up: Recurrence pattern catalogue

3.1.1 Point patterns: specific due dates
----------------------------------------

A point-based habit has a due date.  ‘FREQ’ sets the base unit,
‘INTERVAL’ multiplies it, and the ‘BY*’ parts filter or shape which days
qualify.

* Menu:

* Every day / every N days::
* Weekly on specific weekdays::
* Monthly by day-of-month::
* Monthly Nth weekday::
* Multi-ordinal the 2nd _and_ 4th weekday::
* BYSETPOS selections::
* First / last weekend day of the year::
* The "wednesteenth" a weekday within a day-of-month range::
* Nearest-weekday shift (X-SKIP)::
* Yearly by month + day, or month + ordinal: Yearly by month + day or month + ordinal.


File: org-habit-ng.info,  Node: Every day / every N days,  Next: Weekly on specific weekdays,  Up: Point patterns specific due dates

Every day / every N days
........................

You want        RRULE
-------------------------------------------
Every day       ‘FREQ=DAILY’
Every 3 days    ‘FREQ=DAILY;INTERVAL=3’
Every 45 days   ‘FREQ=DAILY;INTERVAL=45’


File: org-habit-ng.info,  Node: Weekly on specific weekdays,  Next: Monthly by day-of-month,  Prev: Every day / every N days,  Up: Point patterns specific due dates

Weekly on specific weekdays
...........................

You want                  RRULE
---------------------------------------------------------
Every week (anchor day)   ‘FREQ=WEEKLY’
Every 2 weeks             ‘FREQ=WEEKLY;INTERVAL=2’
Mon, Wed, Fri             ‘FREQ=WEEKLY;BYDAY=MO,WE,FR’
Weekends only             ‘FREQ=WEEKLY;BYDAY=SA,SU’

   A plain ‘FREQ=WEEKLY’ with no ‘BYDAY’ repeats on the weekday of the
SCHEDULED anchor.  Weekday codes are ‘MO TU WE TH FR SA SU’.


File: org-habit-ng.info,  Node: Monthly by day-of-month,  Next: Monthly Nth weekday,  Prev: Weekly on specific weekdays,  Up: Point patterns specific due dates

Monthly by day-of-month
.......................

You want                 RRULE
--------------------------------------------------------
The 15th of each month   ‘FREQ=MONTHLY;BYMONTHDAY=15’
The last day of month    ‘FREQ=MONTHLY;BYMONTHDAY’-1=
The 2nd-to-last day      ‘FREQ=MONTHLY;BYMONTHDAY’-2=

   Negative ‘BYMONTHDAY’ counts from the end, so ‘-1’ lands on Jan 31,
Feb 29 (in a leap year), Mar 31, and so on — always the real last day.


File: org-habit-ng.info,  Node: Monthly Nth weekday,  Next: Multi-ordinal the 2nd _and_ 4th weekday,  Prev: Monthly by day-of-month,  Up: Point patterns specific due dates

Monthly Nth weekday
...................

You want           RRULE                       Engine gloss
-----------------------------------------------------------------------------------
The 2nd Saturday   ‘FREQ=MONTHLY;BYDAY=2SA’    "Every month on the 2nd Saturday"
The last Friday    ‘FREQ=MONTHLY;BYDAY’-1FR=   "Every month on the last Friday"
The 1st Monday     ‘FREQ=MONTHLY;BYDAY=1MO’    "Every month on the 1st Monday"

   A number prefix on ‘BYDAY’ means "the Nth occurrence of that weekday
in the month"; a negative prefix counts from the end.


File: org-habit-ng.info,  Node: Multi-ordinal the 2nd _and_ 4th weekday,  Next: BYSETPOS selections,  Prev: Monthly Nth weekday,  Up: Point patterns specific due dates

Multi-ordinal: the 2nd _and_ 4th weekday
........................................

Two ordinals of the same weekday — a common "every other week, pinned to
the calendar" cadence — has two equivalent spellings:

You want                       RRULE
----------------------------------------------------------------------
2nd and 4th Tuesday            ‘FREQ=MONTHLY;BYDAY=2TU,4TU’
2nd and 4th Tuesday (setpos)   ‘FREQ=MONTHLY;BYDAY=TU;BYSETPOS=2,4’

   Both parse to the same series (in 2024: Jan 9, Jan 23, Feb 13, Feb
27, …) and read back identically as "Every month on the 2nd Tuesday and
4th Tuesday."


File: org-habit-ng.info,  Node: BYSETPOS selections,  Next: First / last weekend day of the year,  Prev: Multi-ordinal the 2nd _and_ 4th weekday,  Up: Point patterns specific due dates

BYSETPOS selections
...................

‘BYSETPOS’ picks the Nth element from the set of days a rule already
generates in a period — most useful over a _multi-weekday_ ‘BYDAY’.

You want                     RRULE
-------------------------------------------------------------------------------
First weekday of the month   ‘FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=1’
Last weekday of the month    ‘FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS’-1=

   "First weekday" yields Mon Apr 1 2024, Fri Mar 1 2024, and skips
weekends — ‘BYSETPOS=1’ over the five weekday codes selects whichever of
them comes first in the month.


File: org-habit-ng.info,  Node: First / last weekend day of the year,  Next: The "wednesteenth" a weekday within a day-of-month range,  Prev: BYSETPOS selections,  Up: Point patterns specific due dates

First / last weekend day of the year
....................................

The move that motivated this catalogue: ‘BYSETPOS’ over a _weekend_
‘BYDAY’ selects the first (or last) Saturday-or-Sunday.

You want                        RRULE
-----------------------------------------------------------------------------------
First weekend day of the year   ‘FREQ=YEARLY;BYMONTH=1;BYDAY=SA,SU;BYSETPOS=1’
Last weekend day of the year    ‘FREQ=YEARLY;BYMONTH=12;BYDAY=SA,SU;BYSETPOS’-1=

   The first rule reads "Every year in January on the 1st of Saturday or
Sunday" and lands on Sat Jan 4 2025, Sat Jan 3 2026, Sat Jan 2 2027 —
whichever of the two weekend days falls first in January.  Swap
‘BYSETPOS=1’ for ‘BYSETPOS’-1= and ‘BYMONTH=1’ for ‘BYMONTH=12’ to get
the last weekend day of December instead (Sun Dec 28 2025, Sun Dec 27
2026, …).


File: org-habit-ng.info,  Node: The "wednesteenth" a weekday within a day-of-month range,  Next: Nearest-weekday shift (X-SKIP),  Prev: First / last weekend day of the year,  Up: Point patterns specific due dates

The "wednesteenth": a weekday within a day-of-month range
.........................................................

Intersecting ‘BYDAY’ with a run of ‘BYMONTHDAY’ values pins a weekday to
a band of the month — "the Wednesday of the third full week", spelled as
the Wednesday that falls on the 13th–19th:

You want                         RRULE
-------------------------------------------------------------------------------------------
The Wednesday on the 13th–19th   ‘FREQ=MONTHLY;BYDAY=WE;BYMONTHDAY=13,14,15,16,17,18,19’

   It reads "Every month on the Wednesday that falls on day 13, 14, 15,
16, 17, 18, or 19" and produces Wed Jan 17, Wed Feb 14, Wed Mar 13, Wed
Apr 17 in 2024.


File: org-habit-ng.info,  Node: Nearest-weekday shift (X-SKIP),  Next: Yearly by month + day or month + ordinal,  Prev: The "wednesteenth" a weekday within a day-of-month range,  Up: Point patterns specific due dates

Nearest-weekday shift (‘X-SKIP’)
................................

RRULE can say "the 1st" but not "the 1st, or the nearest weekday if the
1st is a weekend" — cron's ‘W’ flag.  ‘X-SKIP’ adds that shift as a step
applied _after_ the normal calculation, on ‘MONTHLY’ or ‘YEARLY’
‘BYMONTHDAY’ rules only.

You want                                  RRULE
--------------------------------------------------------------------------------------------------------
The 1st, nearest weekday if weekend       ‘FREQ=MONTHLY;BYMONTHDAY=1;X-SKIP=NEAREST’
The 1st, always shift back to Friday      ‘FREQ=MONTHLY;BYMONTHDAY=1;X-SKIP=BACKWARD’
The 1st, always shift forward to Monday   ‘FREQ=MONTHLY;BYMONTHDAY=1;X-SKIP=FORWARD’
Quarterly 15th, forward-shifted           ‘FREQ=YEARLY;BYMONTH=1,4,7,10;BYMONTHDAY=15;X-SKIP=FORWARD’

Mode         Saturday shifts to     Sunday shifts to
-----------------------------------------------------------
‘NEAREST’    the preceding Friday   the following Monday
‘BACKWARD’   the preceding Friday   the preceding Friday
‘FORWARD’    the following Monday   the following Monday

   The shift *never crosses the month boundary*: when a shift would
leave the month (a Saturday on the 1st, a Sunday on the last day) it
reverses direction.  So ‘X-SKIP=NEAREST’ on a Saturday-the-1st (Aug 1
2026) lands on Monday Aug 3, not back in July.  On a ‘FREQ=YEARLY’ rule,
‘X-SKIP’ also requires ‘BYMONTH’.  It is rejected with ‘BYDAY’,
‘BYSETPOS’, and the period/window/ceiling success models.  See *note the
‘X-SKIP’ reference: X-SKIP. for the full rule set.

   *One edge case:* two ‘BYMONTHDAY’ targets that shift onto the _same_
weekday collapse into a single occurrence, not two.  For example
‘BYMONTHDAY=1,2;X-SKIP=NEAREST’ with a Saturday 1st puts both the 1st
and the 2nd on Monday the 3rd.  Prefer a single ‘BYMONTHDAY’ value with
‘X-SKIP’ (cron's ‘W’ is used the same way).


File: org-habit-ng.info,  Node: Yearly by month + day or month + ordinal,  Prev: Nearest-weekday shift (X-SKIP),  Up: Point patterns specific due dates

Yearly by month + day, or month + ordinal
.........................................

You want                     RRULE                                   Lands on
------------------------------------------------------------------------------------------------
Every January 1st            ‘FREQ=YEARLY;BYMONTH=1;BYMONTHDAY=1’    Jan 1 each year
Tax day (April 15)           ‘FREQ=YEARLY;BYMONTH=4;BYMONTHDAY=15’   Apr 15 each year
Last Sunday of December      ‘FREQ=YEARLY;BYMONTH=12;BYDAY’-1SU=     Dec 29 2024, Dec 28 2025
Mother's Day (2nd Sun May)   ‘FREQ=YEARLY;BYMONTH=5;BYDAY=2SU’       May 12 2024, May 11 2025

   A plain month-and-day yearly rule (‘BYMONTH=1;BYMONTHDAY=1’) reads
back simply as "Every year"; the month+ordinal forms get the fuller
gloss ("… in December on the last Sunday").


File: org-habit-ng.info,  Node: Success models what counts as keeping up,  Next: Authoring shortcuts @-macros,  Prev: Point patterns specific due dates,  Up: Recurrence pattern catalogue

3.1.2 Success models: what counts as keeping up
-----------------------------------------------

The rules above all have due dates.  These attach a _success model_ — a
different question about whether completions satisfy the habit.

* Menu:

* Quota per week or month::
* At-least-every-N maintenance ceiling::
* Rolling window K in any N days::
* Seasonal / activation window::
* Early / late tolerance::
* Skip and vacation::
* The forgiving score::


File: org-habit-ng.info,  Node: Quota per week or month,  Next: At-least-every-N maintenance ceiling,  Up: Success models what counts as keeping up

Quota per week or month
.......................

A *period* habit needs to happen once (or N times) per calendar bucket,
any day.  The graph draws one cell per period.

You want                   RRULE
-------------------------------------------------------------------------------
Once per Mon–Sun week      ‘FREQ=WEEKLY;X-PERIOD=WEEK’
Once per Sun–Sat week      ‘FREQ=WEEKLY;X-PERIOD=WEEK;WKST=SU’
Once per two-week period   ‘FREQ=WEEKLY;INTERVAL=2;X-PERIOD=WEEK’
Once per calendar month    ‘FREQ=MONTHLY;X-PERIOD=MONTH’
Gym 3 times per week       ‘FREQ=WEEKLY;X-PERIOD=WEEK;X-TIMES=3’
Gym 3×/week, spread out    ‘FREQ=WEEKLY;X-PERIOD=WEEK;X-TIMES=3;X-MIN-GAP=2’

   ‘X-PERIOD’ must match ‘FREQ’ (‘WEEK’ with ‘WEEKLY’, ‘MONTH’ with
‘MONTHLY’).  ‘X-TIMES=N’ requires N distinct completion days;
‘X-MIN-GAP=N’ additionally requires each counted completion to land at
least N days after the previous counted one.  Quota progress is read
from the LOGBOOK, so these habits need completion logging.


File: org-habit-ng.info,  Node: At-least-every-N maintenance ceiling,  Next: Rolling window K in any N days,  Prev: Quota per week or month,  Up: Success models what counts as keeping up

At-least-every-N maintenance ceiling
....................................

A *ceiling* has no due date — it only fails when too long elapses
between completions.  Doing it early just resets the clock.  The gap is
the rule's ‘FREQ’ + ‘INTERVAL’.

You want                       RRULE
--------------------------------------------------------------------------------
Back up at least monthly       ‘FREQ=MONTHLY;X-CEILING=t’
Visit at least every 6 weeks   ‘FREQ=WEEKLY;INTERVAL=6;X-CEILING=t;X-WARN=1w’
Replace filter every 45 days   ‘FREQ=DAILY;INTERVAL=45;X-CEILING=t’

   A ceiling is always completion-anchored: marking it DONE re-anchors
the horizon.  ‘X-WARN’ sets how many days before the horizon the live
cell flips from ready to alert (‘X-WARN’ applies to every success model
— see the *note ‘X-WARN’ reference: org-habit-ng extension tokens.).
Reads back as "At least every month", "At least every 6 weeks", etc.

   A ceiling reads its last completion from the LOGBOOK, so it needs
completion logging enabled (‘org-log-repeat’, or an ‘org-log-done’
note); without it the horizon can never show overdue.


File: org-habit-ng.info,  Node: Rolling window K in any N days,  Next: Seasonal / activation window,  Prev: At-least-every-N maintenance ceiling,  Up: Success models what counts as keeping up

Rolling window: K in any N days
...............................

A *rolling window* asks for at least K completions in every trailing
N-day window, evaluated every day — no calendar boundary to game.

You want                                RRULE
----------------------------------------------------------------------------------------
At least once in any 5 days             ‘FREQ=DAILY;X-WINDOW=5’
3 sessions in any rolling week          ‘FREQ=DAILY;X-WINDOW=7;X-TIMES=3’
3×/week, spread out (no back-to-back)   ‘FREQ=DAILY;X-WINDOW=7;X-TIMES=3;X-MIN-GAP=2’
Twice in any rolling 10 days            ‘FREQ=DAILY;X-WINDOW=10;X-TIMES=2’

   ‘X-WINDOW’ requires ‘FREQ=DAILY;INTERVAL=1’ and rejects every
calendar ‘BY*’ part — it is pure day arithmetic.  K defaults to 1; reuse
‘X-TIMES=K’ for higher quotas.  Reads back as "At least once in any
rolling 5 days", "3 times in any rolling 7 days", etc.  For the choice
between a ceiling and a K=1 rolling window, see *note About the success
models::.

   Like ceilings, rolling habits read their completions from the
LOGBOOK, so they need completion logging enabled (‘org-log-repeat’, or
an ‘org-log-done’ note); without it the window never advances.


File: org-habit-ng.info,  Node: Seasonal / activation window,  Next: Early / late tolerance,  Prev: Rolling window K in any N days,  Up: Success models what counts as keeping up

Seasonal / activation window
............................

‘BYMONTH’ on a ‘DAILY=/=WEEKLY=/=MONTHLY’ or period rule is an
*activation window*: months outside the set produce no occurrences at
all, and off-season days are drawn dimmed rather than missed.

You want                          RRULE
-----------------------------------------------------------------------------------------------
Mow the lawn April–October        ‘FREQ=WEEKLY;BYDAY=MO;BYMONTH=4,5,6,7,8,9,10’
A cross-year winter habit         ‘FREQ=DAILY;BYMONTH=11,12,1,2’
Once a week, only April–October   ‘FREQ=WEEKLY;X-PERIOD=WEEK;WKST=MO;BYMONTH=4,5,6,7,8,9,10’

   The lawn rule reads "Every week on Monday, April–October" and yields
only April-and-later Mondays.  On a ‘YEARLY’ rule, by contrast,
‘BYMONTH’ shapes the occurrence rather than gating it (see the yearly
patterns above).

   *Straddle-clamp.*  A period bucket that straddles a season boundary
(say the last week of October, mostly in-season) has its ‘X-TIMES’ quota
clamped down to the number of in-season days it actually contains.  At
the default quota of 1 this changes nothing; you only notice it if you
raise ‘X-TIMES’ on a seasonal period habit.


File: org-habit-ng.info,  Node: Early / late tolerance,  Next: Skip and vacation,  Prev: Seasonal / activation window,  Up: Success models what counts as keeping up

Early / late tolerance
......................

‘X-FLEX-BEFORE’ and ‘X-FLEX-AFTER’ widen the "on time" window around a
point habit's due date.

You want                     RRULE
-------------------------------------------------------------------------------------
2 days late still on time    ‘FREQ=WEEKLY;X-FLEX-AFTER=2’
1 day early still on time    ‘FREQ=WEEKLY;X-FLEX-BEFORE=1’
1 early or 2 late            ‘FREQ=WEEKLY;BYDAY=TH;X-FLEX-BEFORE=1;X-FLEX-AFTER=2’
±2 weeks on a yearly habit   ‘FREQ=YEARLY;X-FLEX-BEFORE=2w;X-FLEX-AFTER=2w’

   Both default to 0.  A ‘w’ suffix multiplies by 7, so a once-a-year
habit can tolerate ‘2w’ without hand-counting 14 days; it reads "Every
year (within 2 weeks before and 2 weeks after)".  ‘X-FLEXIBILITY=N’ is
legacy shorthand for ‘X-FLEX-AFTER=N’ (after-only, bare integer).  These
options are rejected with ‘X-PERIOD’, ‘X-CEILING’, and ‘X-WINDOW’.


File: org-habit-ng.info,  Node: Skip and vacation,  Next: The forgiving score,  Prev: Early / late tolerance,  Up: Success models what counts as keeping up

Skip and vacation
.................

A missed instance that genuinely does not apply should be neither
satisfied nor missed but *skipped* — streak-neutral.

You want                                 Do this
-----------------------------------------------------------------------------------------
Mark today's due date not applicable     ‘M-x org-habit-ng-skip’ on a point habit
Mark this week's bucket not applicable   ‘M-x org-habit-ng-skip’ on a period habit
Excuse a whole trip on one habit         ‘:X-VACATION: [START]--[END]’ property
Add a second vacation range              ‘:X-VACATION+: [START]--[END]’ property
Excuse a single day                      ‘:X-VACATION: [DATE]’
Pause _every_ habit (holiday, illness)   add a range to ‘org-habit-ng-vacation-ranges’

   A hand-typed skip is a LOGBOOK line: ‘- Skipped [2026-01-06 Tue]’ for
a point habit, or ‘- Skipped [2026-01-05 Mon]--[2026-01-11 Sun]’ for a
period bucket.  Use inactive timestamps (‘[...]’) and no trailing
comment.  See *note How to skip and take vacations: How to skip
instances and take vacations.


File: org-habit-ng.info,  Node: The forgiving score,  Prev: Skip and vacation,  Up: Success models what counts as keeping up

The forgiving score
...................

There is no RRULE token for the score — it is a read-side computation
over the same instance stream the graph renders.  Run ‘M-x
org-habit-ng-show-score’ on a habit for a 0–100% strength number that
weights recent instances more heavily and treats a lone miss gently.
See *note How to read the graph and check your score:: and *note About
the forgiving score::.


File: org-habit-ng.info,  Node: Authoring shortcuts @-macros,  Next: See also (3),  Prev: Success models what counts as keeping up,  Up: Recurrence pattern catalogue

3.1.3 Authoring shortcuts: @-macros
-----------------------------------

When a habit needs nothing beyond a plain frequency, the cron-style
macros are shorter to type.  They expand at parse time and are one-way
sugar — they compose with everything else but must be the first token
and the only source of ‘FREQ’.

You want             Macro         Equivalent RRULE
------------------------------------------------------
Every day            ‘@daily’      ‘FREQ=DAILY’
Every week           ‘@weekly’     ‘FREQ=WEEKLY’
Every month          ‘@monthly’    ‘FREQ=MONTHLY’
Every year           ‘@yearly’     ‘FREQ=YEARLY’
Every year (alias)   ‘@annually’   ‘FREQ=YEARLY’

   ‘@weekly;X-FLEX-AFTER=1d’ parses identically to
‘FREQ=WEEKLY;X-FLEX-AFTER=1d’.  Unknown macros (‘@hourly’, ‘@reboot’), a
macro after another token, or a macro plus an explicit ‘FREQ’ all raise
an error.  See *note the macro reference: @-macros.


File: org-habit-ng.info,  Node: See also (3),  Prev: Authoring shortcuts @-macros,  Up: Recurrence pattern catalogue

3.1.4 See also
--------------

   • *note RRULE and extensions reference:: — every token, its values
     and defaults, and the validation errors.
   • *note How to author a habit:: — put one of these rules on a
     heading, by hand or with the wizard.
   • *note About the domain model:: and *note About the success models::
     — the concepts these patterns compose from.


File: org-habit-ng.info,  Node: RRULE and extensions reference,  Next: Commands API and configuration,  Prev: Recurrence pattern catalogue,  Up: Reference

3.2 RRULE and extensions reference
==================================

The value of a heading's ‘RECURRENCE’ property is an RFC 5545 RRULE
string — ‘KEY=VALUE’ tokens joined by semicolons — extended with
org-habit-ng's own ‘X-’ tokens.  This reference lists every token the
engine accepts, its allowed values and defaults, and the combinations
that are rejected.

   For worked examples that compose these tokens into real habits, see
*note the recurrence pattern catalogue: Recurrence pattern catalogue.
This document is a lookup table, not a walkthrough.

* Menu:

* Standard RRULE tokens::
* org-habit-ng extension tokens::
* Validation errors::
* See also: See also (4).


File: org-habit-ng.info,  Node: Standard RRULE tokens,  Next: org-habit-ng extension tokens,  Up: RRULE and extensions reference

3.2.1 Standard RRULE tokens
---------------------------

* Menu:

* Frequency and interval::
* BY* shape tokens::
* Termination::


File: org-habit-ng.info,  Node: Frequency and interval,  Next: BY* shape tokens,  Up: Standard RRULE tokens

Frequency and interval
......................

Token        Values                          Default   Meaning
-----------------------------------------------------------------------------------
‘FREQ’       ‘DAILY WEEKLY MONTHLY YEARLY’   —         Base recurrence unit
‘INTERVAL’   positive integer                ‘1’       Repeat every N base units

   Sub-daily frequencies (‘SECONDLY’, ‘MINUTELY’, ‘HOURLY’) are rejected
— org-habit-ng is day-granular.


File: org-habit-ng.info,  Node: BY* shape tokens,  Next: Termination,  Prev: Frequency and interval,  Up: Standard RRULE tokens

BY* shape tokens
................

Token          Values                               Meaning
-------------------------------------------------------------------------------------------------------------------------------------------
‘BYDAY’        ‘MO TU WE TH FR SA SU’               Specific weekdays
‘BYDAY’        ordinal + weekday, e.g.  ‘2SA        The Nth weekday of the period (negative = from end)
               -1FR’
‘BYMONTHDAY’   ‘1=…=31’, ‘-1=…’-31=                 Day of month (negative = from end)
‘BYMONTH’      ‘1=…=12’ (comma list)                Activation window on DAILY/WEEKLY/MONTHLY & period rules; occurrence shape on YEARLY
‘BYSETPOS’     integers, e.g.  ‘1 -1 2,4’           Position(s) within the generated set

   ‘BYWEEKNO’ and ‘BYYEARDAY’ are *not supported* and raise an error
(they were silently ignored in older versions).  ‘WKST’ is documented
with the period tokens below.


File: org-habit-ng.info,  Node: Termination,  Prev: BY* shape tokens,  Up: Standard RRULE tokens

Termination
...........

Token     Values             Meaning
--------------------------------------------------------
‘COUNT’   positive integer   Stop after N occurrences
‘UNTIL’   ‘YYYYMMDD’         Stop after this date

   ‘COUNT’ and ‘UNTIL’ are mutually exclusive.  When the series is
exhausted, completing the habit leaves it DONE and schedules no further
occurrence.


File: org-habit-ng.info,  Node: org-habit-ng extension tokens,  Next: Validation errors,  Prev: Standard RRULE tokens,  Up: RRULE and extensions reference

3.2.2 org-habit-ng extension tokens
-----------------------------------

Token             Values                                        Default                     Meaning
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
‘X-REPEAT-FROM’   ‘completion’ ‘scheduled’ ‘scheduled-future’   ‘completion’                Base for the next occurrence
‘X-WARN’          ‘Nd’ ‘Nw’ ‘Nh’                                —                           Days before a habit's deadline its live cell flips ready→alert (all success models)
‘X-FLEXIBILITY’   bare integer (days)                           ‘0’                         Legacy grace period (= ‘X-FLEX-AFTER’)
‘X-FLEX-BEFORE’   ‘N’ / ‘Nd’ / ‘Nw’                             ‘0’                         Days before due still "on time"
‘X-FLEX-AFTER’    ‘N’ / ‘Nd’ / ‘Nw’                             ‘0’                         Days after due still "on time"
‘X-PERIOD’        ‘WEEK’ ‘MONTH’                                —                           Period-based mode (once per bucket)
‘WKST’            ‘MO’ ‘SU’                                     ‘calendar-week-start-day’   Week start for weekly periods
‘X-TIMES’         positive integer                              ‘1’                         Completions required per period or rolling window
‘X-MIN-GAP’       ‘N’ / ‘Nd’ / ‘Nw’, ≥ 2                        —                           Minimum spacing between counted completions
‘X-CEILING’       ‘t’                                           —                           Maintenance-ceiling mode; gap = ‘FREQ’ + ‘INTERVAL’
‘X-WINDOW’        integer ≥ 2 (days)                            —                           Rolling-window mode: X-TIMES in every trailing N days
‘X-SKIP’          ‘BACKWARD’ ‘FORWARD’ ‘NEAREST’                —                           Weekend shift for a MONTHLY/YEARLY BYMONTHDAY rule
‘X-VACATION’      ‘[START]--[END]’ or ‘[DATE]’                  —                           Per-habit vacation range (property, not RRULE token)

   ‘X-REPEAT-FROM=scheduled-future’ behaves like ‘scheduled’ but never
schedules a date in the past.  ‘X-FLEX-*’ and ‘X-MIN-GAP’ accept an
optional ‘d’ (days, default) or ‘w’ (weeks, ×7) suffix.  ‘X-VACATION’
and ‘X-VACATION+’ are org _properties_ on the heading, not tokens inside
‘RECURRENCE’; ‘X-VACATION+’ lines accumulate additional ranges.  Every
‘X-VACATION’ range needs both a start and an end (or a bare single
‘[DATE]’) — there is no open-ended form.

   ‘X-WARN’ governs the _live_ cell of every success model by one shared
rule: the cell stays *ready* (calm) until TODAY is within ‘X-WARN’ of
its deadline, then turns *alert*.  What counts as the "deadline" depends
on the model.  For a *point* habit it is the due day, and ‘X-WARN’
carves an alert band out of the days approaching due that fall _before_
the flex-ready window opens (so when ‘X-WARN’ is no larger than
‘X-FLEX-BEFORE’ the band is empty — the ready window already covers it).
For a *period* habit it is the current bucket's end.  For *ceiling* and
*rolling* habits it is the next horizon.  Default is ‘0’ (‘X-WARN’
absent).

   Two deliberate differences follow from a point habit's deadline being
a due _day_ you land on (which is itself on-time/ready) rather than a
horizon you race toward.  First, the point band is _inclusive_ at ‘due −
X-WARN’ (that day already shows alert), whereas period/ceiling/rolling
stay ready on the day exactly ‘X-WARN’ before the deadline and flip the
next day — so a point habit lights up one day earlier than an
otherwise-identical ceiling.  Second, a point graph repaints the
approaching-due band before _every_ occurrence it shows (its per-day
proximity colouring), while the other models recolour only their single
live cell.  Both differences are intentional.

   For period habits, with no ‘X-WARN’ the live pending bucket (quota
not yet met this period) renders *ready* for the whole period, flipping
to *alert* only once the period ends unmet.  Set ‘X-WARN’ to at least
the bucket's length for an always-alert bucket.

* Menu:

* @-macros::
* X-SKIP::


File: org-habit-ng.info,  Node: @-macros,  Next: X-SKIP,  Up: org-habit-ng extension tokens

@-macros
........

Cron-style shorthands, expanded at parse time into the equivalent
‘FREQ’.  They are one-way authoring sugar and are never re-emitted.

Macro         Expands to
-------------------------------
‘@daily’      ‘FREQ=DAILY’
‘@weekly’     ‘FREQ=WEEKLY’
‘@monthly’    ‘FREQ=MONTHLY’
‘@yearly’     ‘FREQ=YEARLY’
‘@annually’   ‘FREQ=YEARLY’

   A macro must be the *first* token and the *only* source of ‘FREQ’.
It composes with everything else: ‘@weekly;X-FLEX-AFTER=1d’ is valid.


File: org-habit-ng.info,  Node: X-SKIP,  Prev: @-macros,  Up: org-habit-ng extension tokens

X-SKIP
......

Mode         Saturday shifts to   Sunday shifts to
-----------------------------------------------------
‘NEAREST’    preceding Friday     following Monday
‘BACKWARD’   preceding Friday     preceding Friday
‘FORWARD’    following Monday     following Monday

   The shift never crosses the month boundary; a shift that would leave
the month reverses direction.  ‘X-SKIP’ applies only to ‘MONTHLY’ or
‘YEARLY’ ‘BYMONTHDAY’ rules (yearly additionally requires ‘BYMONTH’),
and is rejected with ‘BYDAY’, ‘BYSETPOS’, and the period/window/ceiling
models.  ‘X-SKIP=OMIT’ is reserved for a future holiday-aware feature
and currently errors.


File: org-habit-ng.info,  Node: Validation errors,  Next: See also (4),  Prev: org-habit-ng extension tokens,  Up: RRULE and extensions reference

3.2.3 Validation errors
-----------------------

‘org-habit-ng-parse-rrule’ signals ‘user-error’ on any of these.  A
configuration must be unambiguous; when two tokens describe incompatible
things, the parser refuses rather than guessing.  The messages below are
verbatim from the engine.

* Menu:

* Macros and X-SKIP values::
* X-SKIP conflicts::
* Period, ceiling, rolling, and flex conflicts: Period ceiling rolling and flex conflicts.
* Quota, count, and value errors: Quota count and value errors.
* Warnings (not errors)::


File: org-habit-ng.info,  Node: Macros and X-SKIP values,  Next: X-SKIP conflicts,  Up: Validation errors

Macros and X-SKIP values
........................

Conflict                   Message
-------------------------------------------------------------------------------------------------------------------
Unknown macro              "Unknown recurrence macro '%s'; use @daily, @weekly, @monthly, @yearly, or @annually"
Macro plus explicit FREQ   "A recurrence macro (@weekly etc.)  already sets FREQ; remove the explicit FREQ"
Macro not first token      "A recurrence macro must be the first token"
‘X-SKIP=OMIT’              "X-SKIP=OMIT (holiday/skip semantics) is reserved but not yet supported"
Bad ‘X-SKIP’ value         "X-SKIP must be BACKWARD, FORWARD, or NEAREST"


File: org-habit-ng.info,  Node: X-SKIP conflicts,  Next: Period ceiling rolling and flex conflicts,  Prev: Macros and X-SKIP values,  Up: Validation errors

X-SKIP conflicts
................

Conflict                                Message
--------------------------------------------------------------------------------------------------------------------------------------------------------
‘X-SKIP’ + period/window/ceiling        "X-SKIP requires a point-based BYMONTHDAY rule; it has no meaning for period/window/ceiling modes"
‘X-SKIP’ without ‘BYMONTHDAY’           "X-SKIP requires BYMONTHDAY; it shifts a day-of-month occurrence off a weekend"
‘X-SKIP’ with FREQ not MONTHLY/YEARLY   "X-SKIP requires FREQ=MONTHLY or FREQ=YEARLY"
‘X-SKIP’ YEARLY without ‘BYMONTH’       "X-SKIP on a yearly rule requires BYMONTH (the engine only honors BYMONTHDAY under BYMONTH for yearly rules)"
‘X-SKIP’ + ‘BYDAY’                      "X-SKIP not allowed with BYDAY; weekday-anchored occurrences never fall on the wrong day"
‘X-SKIP’ + ‘BYSETPOS’                   "X-SKIP not allowed with BYSETPOS in v1; shifting changes positions BYSETPOS selects"

   ‘X-SKIP’ with ‘BYMONTH’ (a seasonal monthly or a yearly rule) is
*allowed* — the season gate runs before the shift and the month-boundary
clamp keeps a shifted date in its month.


File: org-habit-ng.info,  Node: Period ceiling rolling and flex conflicts,  Next: Quota count and value errors,  Prev: X-SKIP conflicts,  Up: Validation errors

Period, ceiling, rolling, and flex conflicts
............................................

Conflict                                       Message
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
‘BYDAY’ + ‘X-PERIOD’                           "BYDAY and X-PERIOD are mutually exclusive.  Use BYDAY for point-based habits, X-PERIOD for period-based"
‘X-REPEAT-FROM=≠scheduled + =X-PERIOD’         "X-REPEAT-FROM must be 'scheduled' (or omitted) with X-PERIOD.  Period boundaries are calendar-fixed"
‘X-FLEX-*’ + ‘X-PERIOD’                        "X-FLEX-BEFORE/AFTER not allowed with X-PERIOD.  Period mode uses the entire period as the completion window"
FREQ ≠ ‘X-PERIOD’ granularity                  "FREQ must match X-PERIOD granularity (FREQ=WEEKLY for X-PERIOD=WEEK, FREQ=MONTHLY for X-PERIOD=MONTH)"
‘X-CEILING’ + ‘BYDAY’                          "BYDAY not allowed with X-CEILING.  A maintenance ceiling has no calendar shape; use FREQ+INTERVAL for the gap"
‘X-CEILING’ + ‘BYMONTHDAY’                     "BYMONTHDAY not allowed with X-CEILING.  A maintenance ceiling has no calendar shape"
‘X-CEILING’ + ‘BYSETPOS’                       "BYSETPOS not allowed with X-CEILING.  A maintenance ceiling has no calendar shape"
‘X-CEILING’ + ‘BYMONTH’                        "BYMONTH not allowed with X-CEILING.  Seasonal ceilings need off-season gap-pause semantics, which are not yet supported"
‘X-CEILING’ + ‘X-PERIOD’                       "X-CEILING and X-PERIOD are mutually exclusive.  A ceiling has no period buckets"
‘X-CEILING’ + ‘X-TIMES’                        "X-TIMES not allowed with X-CEILING.  A ceiling has no period buckets to fill"
‘X-CEILING’ + ‘X-FLEX-*’                       "X-FLEX-BEFORE/AFTER not allowed with X-CEILING.  The window is the whole gap up to the horizon"
‘X-CEILING’ + =X-REPEAT-FROM=≠completion       "X-REPEAT-FROM must be 'completion' (or omitted) with X-CEILING.  The horizon is anchored to the last completion"
‘X-CEILING’ + ‘COUNT’                          "COUNT not allowed with X-CEILING.  A maintenance ceiling has no discrete instances to count"
‘X-CEILING’ + ‘X-MIN-GAP’                      "X-MIN-GAP not allowed with X-CEILING.  A ceiling has no period buckets to space"
‘X-CEILING’ value ≠ t                          "X-CEILING only accepts t"
‘X-WINDOW’ < 2                                 "X-WINDOW must be an integer >= 2 days (a 1-day window is just a plain daily habit)"
‘X-WINDOW’ + FREQ ≠ DAILY                      "X-WINDOW requires FREQ=DAILY.  A rolling window is a trailing day count, not a calendar grid"
‘X-WINDOW’ + ‘INTERVAL’ > 1                    "INTERVAL not allowed with X-WINDOW.  The window length is X-WINDOW's day count"
‘X-WINDOW’ + ‘WKST’                            "WKST not allowed with X-WINDOW.  A rolling window has no week boundaries"
‘X-WINDOW’ + ‘X-PERIOD’                        "X-PERIOD and X-WINDOW are mutually exclusive.  X-PERIOD is calendar buckets; X-WINDOW is a rolling trailing window"
‘X-WINDOW’ + ‘X-CEILING’                       "X-CEILING and X-WINDOW are mutually exclusive.  A ceiling is the FREQ+INTERVAL grid form; X-WINDOW is the rolling day-count form"
‘X-WINDOW’ + ‘BYDAY=/=BYMONTHDAY=/=BYSETPOS’   "…not allowed with X-WINDOW.  A rolling window has no calendar shape"
‘X-WINDOW’ + ‘BYMONTH’                         "BYMONTH not allowed with X-WINDOW.  Seasonal rolling windows need off-season aging semantics, which are not yet supported"
‘X-WINDOW’ + ‘X-FLEX-*’                        "X-FLEX-BEFORE/AFTER not allowed with X-WINDOW.  A rolling window has no due date to flex around"
‘X-WINDOW’ + =X-REPEAT-FROM=≠completion        "X-REPEAT-FROM must be 'completion' (or omitted) with X-WINDOW.  The window is anchored to your completions"
‘X-WINDOW’ + ‘COUNT’                           "COUNT not allowed with X-WINDOW.  A rolling obligation has no discrete instances to count"
‘X-TIMES’ (K) > ‘X-WINDOW’ (N)                 "X-TIMES cannot exceed X-WINDOW: K completions need K distinct days in an N-day window"


File: org-habit-ng.info,  Node: Quota count and value errors,  Next: Warnings (not errors),  Prev: Period ceiling rolling and flex conflicts,  Up: Validation errors

Quota, count, and value errors
..............................

Conflict                            Message
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
‘COUNT’ + ‘UNTIL’                   "COUNT and UNTIL are mutually exclusive.  Use one to bound the recurrence"
‘X-TIMES’ without period/window     "X-TIMES requires X-PERIOD or X-WINDOW"
‘X-TIMES’ < 1                       "X-TIMES must be a positive integer"
‘X-MIN-GAP’ without period/window   "X-MIN-GAP requires X-PERIOD or X-WINDOW"
‘X-MIN-GAP’ without =X-TIMES=≥2     "X-MIN-GAP requires X-TIMES>=2.  Spacing is only meaningful between multiple completions in a bucket; cross-bucket spacing is not supported"
‘X-MIN-GAP’ < 2                     "X-MIN-GAP must be at least 2 days (a 1-day gap is any distinct day, i.e.  plain X-TIMES)"
Bad ‘X-FLEX-*’ value                "X-FLEX-BEFORE/AFTER must be an integer optionally suffixed with d or w (e.g.  3, 2d, 1w)"
‘X-FLEXIBILITY’ with unit suffix    "X-FLEXIBILITY is legacy and takes a bare integer day count; use X-FLEX-AFTER=Nw for units"
‘BYMONTH’ out of range              "BYMONTH months must be integers 1-12"
‘BYMONTH’ without ‘FREQ’            "BYMONTH requires FREQ"
‘BYWEEKNO’ / ‘BYYEARDAY’            "BYWEEKNO is not supported; it was previously ignored" / "BYYEARDAY is not supported; it was previously ignored"
Sub-daily ‘FREQ’                    "Sub-daily FREQ (%s) is not supported; org-habit-ng is day-granular"
Skip / vacation range END < START   "Skip range end precedes start: %s" / "X-VACATION range end precedes start: %S"


File: org-habit-ng.info,  Node: Warnings (not errors),  Prev: Quota count and value errors,  Up: Validation errors

Warnings (not errors)
.....................

A few combinations parse successfully but emit a warning:

   • ‘COUNT’ with ‘X-TIMES=>1’ — ‘COUNT’ counts completions, not
     satisfied buckets, so the series ends after about ⌈COUNT/N⌉ buckets
     and may stop mid-bucket.
   • ‘FREQ=MONTHLY;BYMONTH=…’ with ‘gcd(INTERVAL, 12) > 1’ — the
     INTERVAL grid only visits ‘12 / gcd(INTERVAL, 12)’ distinct months,
     so some anchors never reach an in-season month.
   • ‘X-MIN-GAP’ too tight for ‘X-TIMES’ to fit the bucket or window —
     the quota is clamped down to the achievable spaced maximum.


File: org-habit-ng.info,  Node: See also (4),  Prev: Validation errors,  Up: RRULE and extensions reference

3.2.4 See also
--------------

   • *note Recurrence pattern catalogue:: — these tokens in use.
   • *note Commands, API, and configuration: Commands API and
     configuration. — the functions and variables that consume these
     rules.
   • *note About the success models:: — why period, ceiling, and rolling
     are distinct modes.


File: org-habit-ng.info,  Node: Commands API and configuration,  Prev: RRULE and extensions reference,  Up: Reference

3.3 Commands, API, and configuration
====================================

This reference describes the machinery org-habit-ng exposes: the
interactive commands a user runs, the pure engine functions a package
author can call, and the customization variables that tune behaviour.

   org-habit-ng binds no keys by default.  Commands are run with ‘M-x’
or bound in your own configuration (see *note How to install and
configure: How to install and configure org-habit-ng.).

* Menu:

* Interactive commands::
* Public engine API::
* Configuration variables::
* How it hooks into org-habit::
* See also: See also (5).


File: org-habit-ng.info,  Node: Interactive commands,  Next: Public engine API,  Up: Commands API and configuration

3.3.1 Interactive commands
--------------------------

Command                         Purpose
--------------------------------------------------------------------------------------------------
‘org-habit-ng-mode’             Global minor mode; installs the org-habit advice.
‘org-habit-ng-author’           Persistent authoring panel with a live preview (fresh habit).
‘org-habit-ng-set-recurrence’   Wizard that builds (or modifies) a heading's ‘RECURRENCE’.
‘org-habit-ng-skip’             Mark the current instance/bucket skipped and advance SCHEDULED.
‘org-habit-ng-show-score’       Message the strength-score breakdown for the habit at point.

   ‘org-habit-ng-author’, ‘org-habit-ng-set-recurrence’,
‘org-habit-ng-skip’, and ‘org-habit-ng-show-score’ act on the org
heading at point (or the habit behind an agenda line).
‘org-habit-ng-skip’ errors with "Point is not on an org-habit-ng habit"
when there is no habit there.

   ‘org-habit-ng-author’ opens a single persistent ‘transient’ panel
whose header is a live preview (the plain-English sentence plus the next
few dates), recomputed as you press keys.  It authors a *fresh* habit;
use ‘org-habit-ng-set-recurrence’ to modify an existing one.  The panel
needs graphical Emacs with ‘transient’ ≥ 0.4 (Emacs 29+); when that
isn't available it falls back to the ‘completing-read’ wizard, exactly
as ‘org-habit-ng-set-recurrence’ does.  See *note How to author a
habit:: for the walkthrough.


File: org-habit-ng.info,  Node: Public engine API,  Next: Configuration variables,  Prev: Interactive commands,  Up: Commands API and configuration

3.3.2 Public engine API
-----------------------

The recurrence engine is a set of pure, org-free functions: another
package can parse RRULEs, enumerate occurrences, and evaluate completion
history without loading the habit UI.  All _day_ arguments and results
are *absolute day numbers* — the integer
‘calendar-absolute-from-gregorian’ returns (day 1 = 0001-01-01), the
same scale as ‘time-to-days’.  Convert with the stock
‘calendar-absolute-from-gregorian’ and
‘calendar-gregorian-from-absolute’.

* Menu:

* Parsing and serializing::
* Enumerating occurrences::
* Evaluating completions::
* Scoring::


File: org-habit-ng.info,  Node: Parsing and serializing,  Next: Enumerating occurrences,  Up: Public engine API

Parsing and serializing
.......................

Function                               Returns
---------------------------------------------------------------------------------------
‘(org-habit-ng-parse-rrule RRULE)’     rule plist; signals ‘user-error’ on conflicts
‘(org-habit-ng-build-rrule RULE)’      RRULE string (round-trips a parsed rule)
‘(org-habit-ng-rrule-to-human RULE)’   English description of the rule

     (org-habit-ng-parse-rrule "FREQ=WEEKLY;BYDAY=MO,WE,FR")
     ;; => (:freq weekly :byday (1 3 5) :interval 1)

     (org-habit-ng-build-rrule '(:freq weekly :byday (1 3 5) :interval 1))
     ;; => "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE,FR"

     (org-habit-ng-rrule-to-human (org-habit-ng-parse-rrule "FREQ=MONTHLY;BYDAY=2SA"))
     ;; => "Every month on the 2nd Saturday"


File: org-habit-ng.info,  Node: Enumerating occurrences,  Next: Evaluating completions,  Prev: Parsing and serializing,  Up: Public engine API

Enumerating occurrences
.......................

Function                                             Returns
-------------------------------------------------------------------------------------------------------
‘(org-habit-ng-occurrences RULE ANCHOR START END)’   sorted occurrence days in the inclusive window
‘(org-habit-ng-next-occurrence RULE ANCHOR AFTER)’   first occurrence strictly after ‘AFTER’, or nil
‘(org-habit-ng-next-n-occurrences RULE ANCHOR N)’    the next ‘N’ occurrence days (ascending)

     (let* ((anchor (calendar-absolute-from-gregorian '(1 15 2024)))  ; Jan 15 2024, a Mon
            (start  (calendar-absolute-from-gregorian '(1 1 2024)))
            (end    (calendar-absolute-from-gregorian '(1 31 2024)))
            (rule   (org-habit-ng-parse-rrule "FREQ=WEEKLY;BYDAY=MO")))
       (mapcar #'calendar-gregorian-from-absolute
               (org-habit-ng-occurrences rule anchor start end)))
     ;; => ((1 1 2024) (1 8 2024) (1 15 2024) (1 22 2024) (1 29 2024))

   *ANCHOR defines the pattern.*  For a rule with no ‘BY*’ parts the
anchor's calendar position _is_ the pattern (WEEKLY repeats on the
anchor's weekday, MONTHLY on its day-of-month, YEARLY on its month+day).
Passing an off-pattern anchor silently shifts the whole series.  A nil
result from ‘org-habit-ng-next-occurrence’ is _ambiguous_: it means
either the series is exhausted (COUNT/UNTIL) _or_ the internal
1000-period search cap was hit without a match; bound ‘AFTER’ near the
anchor if you must distinguish.


File: org-habit-ng.info,  Node: Evaluating completions,  Next: Scoring,  Prev: Enumerating occurrences,  Up: Public engine API

Evaluating completions
......................

Function                                                             Returns
-------------------------------------------------------------------------------------------------------------
‘(org-habit-ng-instances RULE ANCHOR COMPLETIONS START END TODAY)’   list of instance plists in the window
‘(org-habit-ng-satisfied-p INSTANCE)’                                non-nil when ‘:status’ is ‘satisfied’

   ‘COMPLETIONS’ and ‘TODAY’ are absolute day numbers.  The guaranteed
instance keys (additions-only across versions) are:

Model    Keys
--------------------------------------------------------------------------------------------
point    ‘:model point’, ‘:due’, ‘:window-start’, ‘:window-end’, ‘:completion’, ‘:status’
period   ‘:model period’, ‘:start’, ‘:end’, ‘:completions’, ‘:status’

   ‘:status’ is one of ‘satisfied’, ‘missed’, ‘pending’, ‘upcoming’.
Statuses are meaningful only for _active_ habits (a terminated series
stays DONE and is not rendered).

   Ceiling and rolling instances carry the same core keys (‘:model’,
‘:due’, ‘:window-start’, ‘:window-end’, ‘:completion’, ‘:status’), plus
‘:ceiling-horizon’ (ceiling) and ‘:live’ (rolling).

     (let* ((rule   (org-habit-ng-parse-rrule "FREQ=WEEKLY;BYDAY=MO"))
            (anchor (calendar-absolute-from-gregorian '(1 15 2024)))
            (start  (calendar-absolute-from-gregorian '(1 1 2024)))
            (end    (calendar-absolute-from-gregorian '(1 31 2024)))
            (today  (calendar-absolute-from-gregorian '(1 20 2024)))
            (comps  (list (calendar-absolute-from-gregorian '(1 8 2024)))))
       (mapcar (lambda (i) (list (plist-get i :status)
                                 (org-habit-ng-satisfied-p i)))
               (org-habit-ng-instances rule anchor comps start end today)))
     ;; => ((missed nil) (satisfied t) (missed nil) (upcoming nil) (upcoming nil))


File: org-habit-ng.info,  Node: Scoring,  Prev: Evaluating completions,  Up: Public engine API

Scoring
.......

Function                                                         Returns
-------------------------------------------------------------------------------------------------------------------------------------------------
‘(org-habit-ng-score RULE ANCHOR COMPLETIONS START END TODAY)’   plist ‘(:score :percent :streak :recovered-after :instances-scored :current)’
‘(org-habit-ng-show-score)’                                      interactive; messages the breakdown for the habit at point

   ‘org-habit-ng-score’ is pure: it reads ‘org-habit-ng-instances’ and
writes nothing.  ‘:score’ is a float in ‘[0.0, 1.0]’ (‘:percent’ the
rounded percentage); both are ‘nil’, never ‘0’, when there is nothing to
score.  See *note About the forgiving score:: for the model.

     (let* ((rule   (org-habit-ng-parse-rrule "FREQ=WEEKLY;BYDAY=MO"))
            (anchor (calendar-absolute-from-gregorian '(1 15 2024)))
            ;; Nine of ten weekly Mondays done; the 6th is missed.
            (comps  (mapcar (lambda (n) (+ anchor (* 7 n))) '(0 1 2 3 4 6 7 8 9)))
            (start  anchor)
            (end    (+ anchor (* 7 9)))
            (today  end))
       (org-habit-ng-score rule anchor comps start end today))
     ;; => (:score 0.958... :percent 96 :streak 4 :recovered-after 1
     ;;     :instances-scored 10 :current nil)


File: org-habit-ng.info,  Node: Configuration variables,  Next: How it hooks into org-habit,  Prev: Public engine API,  Up: Commands API and configuration

3.3.3 Configuration variables
-----------------------------

Variable                                Type      Default   Purpose
----------------------------------------------------------------------------------------------------------------------------------------
‘org-habit-ng-vacation-ranges’          list      ‘nil’     Global ‘(START . END)’ date-string ranges suppressing every habit
‘org-habit-ng-skipped-glyph’            char      ‘?-’      Character drawn for a skipped instance/bucket
‘org-habit-ng-score-half-life’          integer   ‘13’      Scored instances over which the strength score half-decays
‘org-habit-ng-score-window-instances’   integer   ‘52’      Trailing instances the score folds over (‘nil’ = unbounded)
‘org-habit-ng-score-in-graph-echo’      boolean   ‘t’       Fold a strength summary into the graph help-echo
‘org-habit-ng-preview-occurrences’      integer   ‘6’       Future occurrences shown in the authoring preview
‘org-habit-ng-use-completing-read’      boolean   ‘nil’     Force ‘completing-read’ wizard instead of transient (‘nil’ = auto-detect)

   ‘org-habit-ng-vacation-ranges’ is the global counterpart to a habit's
‘X-VACATION’ property; the two are unioned.  Faces: off-season
days/buckets are drawn with ‘org-habit-ng-off-season-face’.


File: org-habit-ng.info,  Node: How it hooks into org-habit,  Next: See also (5),  Prev: Configuration variables,  Up: Commands API and configuration

3.3.4 How it hooks into org-habit
---------------------------------

‘org-habit-ng-mode’ installs advice around three org-habit functions:

  1. ‘org-auto-repeat-maybe’ — after DONE, computes the next occurrence
     with the RRULE engine and updates SCHEDULED.
  2. ‘org-habit-parse-todo’ — returns accurate intervals for org-habit's
     internal calculations.
  3. ‘org-habit-build-graph’ — builds an RRULE-aware consistency graph.

   Unlike standard org-habit, org-habit-ng does *not* require a repeater
(‘.+1d’) in the SCHEDULED timestamp; the ‘RECURRENCE’ property drives
all scheduling.


File: org-habit-ng.info,  Node: See also (5),  Prev: How it hooks into org-habit,  Up: Commands API and configuration

3.3.5 See also
--------------

   • *note RRULE and extensions reference:: — the rule strings these
     functions consume.
   • *note Recurrence pattern catalogue:: — ready-made rules.
   • *note How to author a habit:: — the wizard in practice.


File: org-habit-ng.info,  Node: Explanation,  Next: Index,  Prev: Reference,  Up: Top

4 Explanation
*************

Explanation is reading you do away from the keyboard.  It connects the
dots: why the model splits recurrence from success, why there are five
success models, why the score forgives.  Read it when the package's
behaviour surprises you, or when you want a mental model strong enough
to make your own configuration decisions confidently.

* Menu:

* About the domain model::
* About the success models::
* About the forgiving score::


File: org-habit-ng.info,  Node: About the domain model,  Next: About the success models,  Up: Explanation

4.1 About the domain model
==========================

org-habit-ng is easier to use — and much easier to reason about — once
you see that it separates three questions that org-habit's plain
repeaters run together.  This document lays out that separation.  It is
reflection, not instruction; when you want to _do_ something the
cross-links at the end point at the right guide.

* Menu:

* Why a new model at all::
* The three concepts::
* Point versus period::
* The anchor::
* Repeat-from where the next date comes from::
* Further reading::


File: org-habit-ng.info,  Node: Why a new model at all,  Next: The three concepts,  Up: About the domain model

4.1.1 Why a new model at all
----------------------------

Standard org-habit expresses recurrence as a repeater cookie in the
SCHEDULED timestamp: ‘.+1d’ (every day from completion), ‘.+2d/4d’
(every 2–4 days).  That is elegant for simple cadences and hits a wall
quickly.  There is no way to say "the 2nd Saturday", "once per calendar
week", "at least every six weeks", or "three times in any rolling week".
The cookie conflates _when a habit is due_ with _how its interval is
measured_, and it offers only a single notion of success: did you
complete near the due date.

   org-habit-ng pulls these apart into three concepts you can vary
independently.


File: org-habit-ng.info,  Node: The three concepts,  Next: Point versus period,  Prev: Why a new model at all,  Up: About the domain model

4.1.2 The three concepts
------------------------

A *habit* is a recurring task with three parts:

  1. A *recurrence rule* — _when_ the habit is expected.  Written as an
     RFC 5545 RRULE (‘FREQ’, ‘INTERVAL’, the ‘BY*’ parts), extended with
     org-habit-ng's ‘X-’ tokens.  This is the calendar shape.
  2. A *completion history* — _when it was actually done_, read from the
     heading's LOGBOOK.
  3. A *success model* — _how to decide_ whether those completions
     satisfy the rule.  This is the part org-habit hard-codes and
     org-habit-ng makes explicit.

   Keeping these separate is what lets the same "three times a week"
idea be expressed as a calendar quota _or_ a rolling window, and lets
"every six weeks" be a strict grid _or_ a forgiving ceiling — same
recurrence vocabulary, different success model.


File: org-habit-ng.info,  Node: Point versus period,  Next: The anchor,  Prev: The three concepts,  Up: About the domain model

4.1.3 Point versus period
-------------------------

The deepest split is between two ways of interpreting a recurrence rule.

   *Point-based* habits have *due dates*.  "Every Monday", "the 15th",
"the 2nd Saturday" each name specific days the RRULE generates.  Success
is measured against a *completion window* around each due date: an
*anchor* (the due date), an optional number of days *before* and *after*
that still count as on time, and a deadline past which the instance is
overdue.  With no flexibility the window is the single day;
‘X-FLEX-BEFORE’ and ‘X-FLEX-AFTER’ widen it asymmetrically.

         before        after
     <───────────┼───────────>
               anchor
     [   acceptable window   ]

   *Period-based* habits (‘X-PERIOD’) have no due date at all.  Time is
divided into fixed, calendar-aligned *buckets* — Mon–Sun weeks, calendar
months — and the habit must be done once (or ‘X-TIMES’ times) somewhere
in each bucket.  The window is implicitly the whole period.

     |<─ ─ ─ ─ period ─ ─ ─ ─>|
     boundary              boundary
     [    any completion OK    ]

   The distinction shows up everywhere: point habits draw one graph cell
per _day_; period habits draw one cell per _period_.  A point habit's
SCHEDULED is a real due date; a period habit's SCHEDULED just marks
which bucket is current.  This is why ‘BYDAY’ (which shapes specific
days) and ‘X-PERIOD’ (which asks only "something this week") are
mutually exclusive — they are answers to different questions.


File: org-habit-ng.info,  Node: The anchor,  Next: Repeat-from where the next date comes from,  Prev: Point versus period,  Up: About the domain model

4.1.4 The anchor
----------------

Every habit has a SCHEDULED timestamp that acts as the *series anchor*.
For a rule with no ‘BY*’ parts, the anchor's calendar position _is_ the
pattern — a plain ‘FREQ=WEEKLY’ repeats on the anchor's weekday,
‘FREQ=MONTHLY’ on its day-of-month.  Adding ‘BY*’ parts overrides that
with an explicit shape.

   Because the anchor defines phase, hand-editing SCHEDULED re-phases
the whole series: past graph cells re-colour relative to the new anchor.
This is deliberate — snoozing a habit by moving its date should move the
whole pattern, not leave a contradictory history behind.


File: org-habit-ng.info,  Node: Repeat-from where the next date comes from,  Next: Further reading,  Prev: The anchor,  Up: About the domain model

4.1.5 Repeat-from: where the next date comes from
-------------------------------------------------

When you complete a point habit, the next occurrence can be computed
from two different origins.  ‘X-REPEAT-FROM=completion’ (the default)
measures from the day you actually finished — right for most habits,
where doing it today means the next is relative to today.
‘X-REPEAT-FROM=scheduled’ measures from the due date regardless of when
you finished — right for fixed-calendar obligations like medication,
where completing late must not shift the schedule.  ‘scheduled-future’
is the same but refuses to schedule in the past.

   Period, ceiling, and rolling habits fix this choice by their nature:
periods are always calendar-anchored (scheduled), ceilings and rolling
windows always completion-anchored.  The validator enforces those
constraints rather than letting a contradictory combination through.


File: org-habit-ng.info,  Node: Further reading,  Prev: Repeat-from where the next date comes from,  Up: About the domain model

4.1.6 Further reading
---------------------

   • Try it: *note Your first habit: Your first habit the 2nd Saturday
     of every month.
   • The five ways success is judged: *note About the success models:
     About the success models.
   • The philosophy behind the strength number: *note About the
     forgiving score::.
   • Look it up: *note RRULE and extensions reference::.


File: org-habit-ng.info,  Node: About the success models,  Next: About the forgiving score,  Prev: About the domain model,  Up: Explanation

4.2 About the success models
============================

A recurrence rule says _when_ a habit is expected.  A *success model*
says _what counts as keeping up_.  org-habit-ng ships five, and choosing
the right one is usually more important than the exact RRULE.  This
document walks through each, why it exists, and how they differ — so you
can pick the one that matches how you actually think about a habit.

   For the ready-made rules that express each model, see *note the
recurrence pattern catalogue: Recurrence pattern catalogue.  For the
concepts of point, period, window, and anchor these build on, see *note
About the domain model::.

* Menu:

* On a schedule (point + window)::
* So many times each period (period / bucket)::
* At least every so often (ceiling)::
* So many times in a rolling window::
* Skipped::
* How the models relate::
* Further reading: Further reading (1).


File: org-habit-ng.info,  Node: On a schedule (point + window),  Next: So many times each period (period / bucket),  Up: About the success models

4.2.1 On a schedule (point + window)
------------------------------------

The default.  The habit has a due date, and a completion succeeds if it
falls within a *window* around that date.  With no flexibility the
window is the single due day; ‘X-FLEX-BEFORE’ and ‘X-FLEX-AFTER’ widen
it, and independently, so "fine a day early, tolerable two days late" is
‘X-FLEX-BEFORE=1;X-FLEX-AFTER=2’.

   This is the model for habits with a real calendar meaning: a report
due Thursday, the 2nd-Saturday finance review, weekday medication.  The
window exists because rigid single-day success is punishing for habits
that tolerate slack — the point is the behaviour, not hitting one exact
square.


File: org-habit-ng.info,  Node: So many times each period (period / bucket),  Next: At least every so often (ceiling),  Prev: On a schedule (point + window),  Up: About the success models

4.2.2 So many times each period (period / bucket)
-------------------------------------------------

Some habits aren't tied to a day at all — they just need to happen once
per calendar week or month.  ‘X-PERIOD’ divides time into fixed,
calendar-aligned buckets; any completion inside a bucket satisfies it.
A quota (‘X-TIMES=N’) raises the bar to N distinct days, and ‘X-MIN-GAP’
forbids clustering them ("three times a week, not three days running").

   The defining property is the *hard boundary*.  "Once per Mon–Sun
week" is satisfied by a completion on Sunday and again on Monday — two
different buckets, a day apart — and then licenses a thirteen-day gap
before anything is due.  That is exactly right when the calendar bucket
is what you care about (a weekly report, a monthly backup) and exactly
wrong when you care about _spacing_.  For the latter, reach for a
rolling window.


File: org-habit-ng.info,  Node: At least every so often (ceiling),  Next: So many times in a rolling window,  Prev: So many times each period (period / bucket),  Up: About the success models

4.2.3 At least every so often (ceiling)
---------------------------------------

Some habits have no schedule — they must simply not go too long between
completions.  "Back up the laptop at least monthly."  "Visit at least
every six weeks."  Doing it early is never wrong; an early completion
just resets the clock.  ‘X-CEILING=t’ turns this on, with the gap read
from the rule's ‘FREQ’ + ‘INTERVAL’.

   A ceiling is always completion-anchored: marking it DONE re-anchors
the horizon.  There is no due date; each natural interval you let slip
is one missed cell — an overrun of _k_ intervals shows as _k_ missed
cells, and a skip or vacation *pauses* the clock.  This is a direct
generalization of org-habit's ‘.+2d/4d’ cookie: that means "at most
every 2 days, at least every 4", and ‘X-CEILING’ is the "at least"
(ceiling) half, for any frequency.


File: org-habit-ng.info,  Node: So many times in a rolling window,  Next: Skipped,  Prev: At least every so often (ceiling),  Up: About the success models

4.2.4 So many times in a rolling window
---------------------------------------

A rolling window asks for at least K completions in _every_ trailing
N-day window, evaluated continuously — there is no boundary to game.
‘X-WINDOW=N’ turns it on; K defaults to 1 and ‘X-TIMES=K’ raises it.
Because it is pure day arithmetic it requires ‘FREQ=DAILY;INTERVAL=1’
and rejects every calendar ‘BY*’ part.

   Contrast it with the period bucket.  ‘FREQ=WEEKLY;X-PERIOD=WEEK’
completed Sunday then Monday satisfies two buckets a day apart, then
coasts.  ‘FREQ=DAILY;X-WINDOW=7’ with the same two completions still
slides: the moment seven days pass since the more recent one, the graph
turns red.  There is no boundary to tuck a second completion just
inside.

   A K=1 rolling window _is_ a maintenance ceiling in spirit — "at least
once in any N days".  The difference is shape: a ceiling's gap sits on a
‘FREQ=+=INTERVAL’ calendar grid (monthly, every six weeks); a window is
gridless day arithmetic that also composes with ‘X-TIMES=/=X-MIN-GAP’
for K>1 obligations a ceiling can't express.  Rule of thumb: ceiling for
"at least every N" grid habits, window for "K times in any N days".


File: org-habit-ng.info,  Node: Skipped,  Next: How the models relate,  Prev: So many times in a rolling window,  Up: About the success models

4.2.5 Skipped
-------------

Not a scheduling model but a fifth _status_ that cuts across the others.
An instance or bucket can be *skipped* — suppressed by an explicit skip
mark or a vacation range.  A skipped cell is not-applicable:
streak-neutral, neither satisfied nor missed.  This exists because the
honest answer to "you were in hospital that week" is neither "you did
it" nor "you failed"; both would distort the record.  The four live
statuses are ‘satisfied’, ‘missed’, ‘pending’, and ‘upcoming’; _skipped_
sits outside them, drawn in a neutral glyph.


File: org-habit-ng.info,  Node: How the models relate,  Next: Further reading (1),  Prev: Skipped,  Up: About the success models

4.2.6 How the models relate
---------------------------

Model                               Success criterion                                             Anchoring
---------------------------------------------------------------------------------------------------------------------
On a schedule                       completion within the window around the due date              either
So many times each period           a completion anywhere in the calendar bucket (×N for quota)   calendar (fixed)
At least every so often             a completion within ‘gap’ of the previous one                 completion
So many times in a rolling window   K completions in every trailing N-day window                  completion
Skipped                             (status) instance suppressed — not applicable                 —

   The validator enforces the boundaries between them: you cannot
combine a period with a ceiling, a rolling window with a ‘BY*’ shape, or
flexibility with a period, because each pairing asks two contradictory
questions at once.  Those rejections are catalogued in *note the
validation errors: Validation errors.


File: org-habit-ng.info,  Node: Further reading (1),  Prev: How the models relate,  Up: About the success models

4.2.7 Further reading
---------------------

   • Foundations: *note About the domain model::.
   • The forgiving strength number layered over any model: *note About
     the forgiving score::.
   • Ready-made rules: *note the success-model patterns: Success models
     what counts as keeping up.
   • Handling instances that don't apply: *note How to skip and take
     vacations: How to skip instances and take vacations.


File: org-habit-ng.info,  Node: About the forgiving score,  Prev: About the success models,  Up: Explanation

4.3 About the forgiving score
=============================

The consistency graph is honest but unforgiving: one red cell after
months of green reads, at a glance, like failure.  org-habit-ng adds a
second view — a single *strength score* in 0–100% — designed to reflect
how habit formation actually works rather than how a binary grid looks.
This document explains the idea and the reasoning behind its defaults.

* Menu:

* The problem with a binary graph::
* What the score does::
* Why it errs toward forgiveness::
* Honesty guards::
* A read-side layer::
* Further reading: Further reading (2).


File: org-habit-ng.info,  Node: The problem with a binary graph,  Next: What the score does,  Up: About the forgiving score

4.3.1 The problem with a binary graph
-------------------------------------

A grid of green and red squares treats every miss identically and gives
a lone lapse the same visual weight as a collapse.  But the behavioural
science is clear that a single missed day barely dents habit formation
(Lally et al., 2010): what matters is the trend, and especially whether
you get straight back on.  A view that makes one red square look like
ruin discourages exactly the recovery that keeps a habit alive.

   The score exists to answer a different question from the graph.  The
graph asks "what happened on each day?"; the score asks "how strong is
this habit right now?"


File: org-habit-ng.info,  Node: What the score does,  Next: Why it errs toward forgiveness,  Prev: The problem with a binary graph,  Up: About the forgiving score

4.3.2 What the score does
-------------------------

‘org-habit-ng-score’ folds the same instance stream the graph renders
into one number, using an *exponentially-weighted moving average*.
Three design choices shape it:

   • *Indexed by instance, not day.*  A weekly habit is judged weekly
     and a daily habit daily, so a once-a-week miss isn't diluted across
     seven calendar days of nothing.  The unit of memory is "instances",
     not "dates".
   • *Recent instances weigh more.*  The weight decays with a
     configurable half-life (‘org-habit-ng-score-half-life’, default 13
     scored instances).  A miss four weeks back on a weekly habit barely
     registers; a miss this week is felt.
   • *Consecutive misses compound.*  One miss is cheap; two in a row
     cost noticeably more.  This encodes "never miss twice" — the folk
     rule that matches the science — directly into the number.

   The returned plist also surfaces the current ‘:streak’ and, when you
have just climbed out of a lapse, ‘:recovered-after’ — so the score can
show recovery _even while the graph still shows the red cell_.


File: org-habit-ng.info,  Node: Why it errs toward forgiveness,  Next: Honesty guards,  Prev: What the score does,  Up: About the forgiving score

4.3.3 Why it errs toward forgiveness
------------------------------------

The score is called "forgiving" deliberately.  Its job is not to grade
you harshly but to give an accurate read of momentum, and momentum
survives isolated misses.  A habit that's been kept 9 weeks in 10 is a
strong habit, and the score says so (around 96% in that case) rather
than punishing the one gap.  Forgiveness here is not leniency for its
own sake; it's fidelity to how habits actually decay and recover.


File: org-habit-ng.info,  Node: Honesty guards,  Next: A read-side layer,  Prev: Why it errs toward forgiveness,  Up: About the forgiving score

4.3.4 Honesty guards
--------------------

Forgiveness could tip into flattery, so the score has guards against
looking better than the evidence supports:

   • *A single completion is not mastery.*  An all-satisfied stream is a
     true fixed point of the fold (its seed makes 100% the honest answer
     for a perfect record, not a cold-start penalty) — but every display
     prints the instance count while fewer than ten instances have been
     scored, e.g.  "100% · 1 instance", so a fresh 100% can't masquerade
     as a seasoned one.
   • *Nothing to score means no score.*  For a brand-new habit, or a
     seasonal habit deep in its off-season, ‘:score’ and ‘:percent’ are
     ‘nil’ — never ‘0’ — and the display suppresses the score line
     rather than showing a misleading "0%".
   • *The agenda score is display-bounded.*  Folded into the graph
     tooltip, it is computed from the completions already in hand while
     rendering (bounded by ‘org-habit-preceding-days’).
     ‘org-habit-ng-show-score’ owns a wider logbook re-scan and reflects
     the fuller history — the number to trust when the agenda's looks
     truncated.


File: org-habit-ng.info,  Node: A read-side layer,  Next: Further reading (2),  Prev: Honesty guards,  Up: About the forgiving score

4.3.5 A read-side layer
-----------------------

The score adds no new RRULE syntax and touches no validation.  It is a
pure function over ‘org-habit-ng-instances’ — the same statuses that
colour the graph — so it composes with every success model automatically
and cannot change scheduling.  It is a lens on the record, not a part of
the record.


File: org-habit-ng.info,  Node: Further reading (2),  Prev: A read-side layer,  Up: About the forgiving score

4.3.6 Further reading
---------------------

   • Use it: *note How to read the graph and check your score::.
   • The statuses it folds over: *note About the success models: About
     the success models.
   • The function signature and return plist: *note the scoring API:
     Scoring.


File: org-habit-ng.info,  Node: Index,  Prev: Explanation,  Up: Top

5 Index
*******


Tag Table:
Node: Top790
Node: Tutorials5239
Node: Your first habit the 2nd Saturday of every month5807
Node: Before we start6701
Node: Step 1 create a heading7094
Node: Step 2 run the wizard7739
Node: Step 3 answer the "On a schedule" questions8221
Node: Step 4 see what it wrote9119
Node: Step 5 complete it and watch the date advance9801
Node: What you've built10533
Node: How-to guides11384
Node: How to install and configure org-habit-ng11989
Node: Load the package12672
Node: Enable org-habit's graph13205
Node: Recipe use-package13686
Node: Confirm it is working14548
Node: Optional configuration15237
Node: Where to go next15947
Node: How to author a habit16414
Node: Use the authoring panel17541
Node: Use the wizard20149
Node: Hand-author the property22178
Node: Choose repeat-from behaviour23155
Node: Bound a limited habit24207
Node: See also24702
Node: How to skip instances and take vacations25207
Node: Skip a single instance or bucket26057
Node: Hand-type a skip from a mobile device26823
Node: Excuse a whole trip on one habit27545
Node: Pause every habit at once28631
Node: Recover from a missed season-end due date29206
Node: See it on the graph29878
Node: Quick reference30399
Node: See also (1)31134
Node: How to read the graph and check your score31573
Node: Read the consistency graph32139
Node: Check the strength score33397
Node: Agenda score vs the command34600
Node: See also (2)35370
Node: Reference35871
Node: Recurrence pattern catalogue36653
Node: Point patterns specific due dates38201
Node: Every day / every N days39011
Node: Weekly on specific weekdays39387
Node: Monthly by day-of-month40062
Node: Monthly Nth weekday40690
Node: Multi-ordinal the 2nd _and_ 4th weekday41434
Node: BYSETPOS selections42224
Node: First / last weekend day of the year43057
Node: The "wednesteenth" a weekday within a day-of-month range44140
Node: Nearest-weekday shift (X-SKIP)45063
Node: Yearly by month + day or month + ordinal47278
Node: Success models what counts as keeping up48258
Node: Quota per week or month48904
Node: At-least-every-N maintenance ceiling50112
Node: Rolling window K in any N days51458
Node: Seasonal / activation window52894
Node: Early / late tolerance54302
Node: Skip and vacation55417
Node: The forgiving score56692
Node: Authoring shortcuts @-macros57232
Node: See also (3)58383
Node: RRULE and extensions reference58892
Node: Standard RRULE tokens59742
Node: Frequency and interval60005
Node: BY* shape tokens60606
Node: Termination61713
Node: org-habit-ng extension tokens62209
Node: @-macros66882
Node: X-SKIP67522
Node: Validation errors68311
Node: Macros and X-SKIP values68999
Node: X-SKIP conflicts69786
Node: Period ceiling rolling and flex conflicts71166
Node: Quota count and value errors75694
Node: Warnings (not errors)77614
Node: See also (4)78382
Node: Commands API and configuration78843
Node: Interactive commands79585
Node: Public engine API81226
Node: Parsing and serializing82001
Node: Enumerating occurrences82928
Node: Evaluating completions84619
Node: Scoring86778
Node: Configuration variables88259
Node: How it hooks into org-habit89789
Node: See also (5)90568
Node: Explanation90949
Node: About the domain model91499
Node: Why a new model at all92156
Node: The three concepts92937
Node: Point versus period93930
Node: The anchor95659
Node: Repeat-from where the next date comes from96447
Node: Further reading97521
Node: About the success models98044
Node: On a schedule (point + window)99083
Node: So many times each period (period / bucket)99934
Node: At least every so often (ceiling)101035
Node: So many times in a rolling window102105
Node: Skipped103480
Node: How the models relate104207
Node: Further reading (1)105488
Node: About the forgiving score106038
Node: The problem with a binary graph106758
Node: What the score does107551
Node: Why it errs toward forgiveness108862
Node: Honesty guards109501
Node: A read-side layer110838
Node: Further reading (2)111330
Node: Index111739

End Tag Table


Local Variables:
coding: utf-8
Info-documentlanguage: en
End:
