<!--
MAINTAINERS — the six-place format-change rule (binding): any change to the
OpenWatchlist format lands in ONE batch across all six places, this file
included:
  1. site/spec.html         — the normative specification (the source of truth)
  2. site/parser.js         — the reference parser (demonstrates the spec exactly)
  3. site/llms.txt          — THIS FILE: the condensed LLM edition of the spec.
                              Updating it is REQUIRED on every spec change; it
                              must always digest the current version, completely.
  4. lib/format.ts          — the Marquee app's parser (sibling repo)
  5. FORMAT.md              — the app's divergence table (sibling repo)
  6. docs/FORMAT-ROADMAP.md — the app's design-reasoning log (sibling repo)
If this file ever disagrees with spec.html, spec.html wins and this file is
stale — fix this file.
-->

# OpenWatchlist

> OpenWatchlist is a plain-text (Markdown) format for tracking the movies and
> TV shows you watch. One title per line; watch status and every piece of
> metadata live ON THE LINE as compact marks — never in the file's structure.
> This file is the official condensed edition of the OpenWatchlist
> specification, written for AI agents and LLMs: everything needed to read and
> write the format correctly, in one read. It is maintained in lockstep with
> the spec and updated with every version.

This edition digests **spec v1.4** (2026-07-15), the current version. The
normative specification is https://open-watchlist.org/spec.html — it always
self-declares its version; if this file and that page ever disagree, the spec
wins.

## The core model

1. **Humans first.** Every construct is typable on a phone keyboard and
   understandable without documentation.
2. **The line is the record.** Everything true about a title — status, rating,
   history — lives on its own line and travels with it. Moving a line never
   changes its meaning.
3. **The file is the database.** There is no canonical copy elsewhere; tools
   are viewers and editors over the file, never owners of it.
4. **Never destroy what you don't understand.** Unknown constructs and
   unparseable lines are preserved verbatim.
5. **Order is data.** Entry order is meaningful (a queue, a ranking); writers
   preserve it except for user-initiated moves.
6. **Lenient in, canonical out.** Parsers accept tokens in any order and a few
   equivalent spellings; writers emit one canonical form.

## The entry line

Each title is one line — a bulleted `- Title` (or `* Title`) or a plain
`Title` — followed by optional tokens. Canonical order:

    Title (Year) rating * ^ !!! @progress +dates ~ #tags | note

Only the title is required. A parser accepts the tokens in ANY order after the
title (they are stripped from the end of the line; the first unrecognized
token ends stripping and everything left is the title). A writer always emits
the canonical order above.

| Token | Form | Meaning |
|---|---|---|
| Year | `(YYYY)` | Disambiguates remakes (`Suspiria (1977)` vs `(2018)`); premiere year for TV. Part of the title's identity |
| Rating | `n/5`, `n/10`, or a bare number | Personal rating — full rules below |
| Favorite | `*` | Flag — independent of rating and status |
| Masterpiece | `^` | Flag — higher and narrower than favorite: elite, all-time-level. `* ^` together is valid; `^` without `*` too |
| Priority | `!`, `!!`, `!!!` | Flag — eventually / soon / watch next. Three levels; `!!!!` or more reads as title text |
| Unwatched | `_` | Status — explicitly not watched yet (a bare line is already unwatched; `_` puts it on the record) |
| Progress | `@`, `@s2`, `@s2e6`, `@1:20:00`, `@80m` | Status — watching, at this position (season/episode, timecode, or minutes; case-insensitive). Bare `@` = watching, position unrecorded |
| Watched | `+` or `+YYYY-MM-DD` | Status — watched, optionally dated (a real ISO calendar date). Repeat the token for rewatches: `+2020-01-01 +2024-06-15` |
| Archived | `~` | Flag — set aside; ORTHOGONAL to watch status (see below) |
| Tag | `#tag` | Freeform; letters, numbers, `-`, `_` (any language). Lowercase kebab-case recommended |
| Note | `\| text` | Free text to end of line, always last. Begins at the first ` \| ` (pipe with a space on either side) |

## Watch status: the marks decide, nothing else

A title's watch status is carried on its line — by the marks it does or
doesn't have:

- *no mark* → **unwatched** (a bare title is a queued title)
- `_` → **unwatched, explicitly**
- `@` (any progress form) → **watching**
- `+` (with or without date) → **watched**

When watch marks combine, the current state wins — precedence
`@` > `+` > `_` > nothing. So `Mad Men (2007) +2019-05-20 @s3e4` reads as
**watching**: a rewatch in progress with its history intact.

**Archived (`~`) is a flag, not a status.** It means *set aside* — dropped,
retired, hidden from the active list — and it never erases whether you
watched. Every combination is meaningful:

    Heat (1995) ~                   → archived + unwatched (never got to it)
    The Irishman (2019) @1:20:00 ~  → archived + watching (gave up an hour in, place kept)
    Game of Thrones (2011) + ~      → archived + watched (finished, then retired)

Un-archiving (removing the `~`) returns the line to its active life with
everything intact. `*` favorite, `^` masterpiece, and `!`–`!!!` priority are
likewise flags, never statuses.

## Ratings: the bare-number rules

- Explicit `n/5` and `n/10` are always allowed and preferred when clarity
  matters (`4.5/5`, `8/10`).
- A **trailing bare number is a rating by default**: `0–5` reads out of 5,
  above 5 reads out of 10. A plain `5` means `5/5` — write `5/10` if you mean
  five of ten.
- A bare **decimal** (`4.5`) is a rating anywhere — essentially nothing is
  titled a decimal.
- A bare integer **above 10 is never a rating** (protects `Ocean's 11`,
  `Catch 22`).
- A number sitting **directly before the year is title text**, not a rating —
  `Ocean's 8 (2018)` and `District 9 (2009)` keep their numbers. Without the
  year, `Ocean's 8` reads as title `Ocean's` rated 8/10 — when a title ends in
  a number, include its year.
- Reference-parser behavior: ratings normalize to a 0.5–5 half-step scale
  (`8/10` → 4/5); out-of-range values clamp to 5/5 with a warning.

## Bracket marks (checkbox spelling)

The same status marks may be written at the START of a line in task-list
brackets — handy for GitHub/Obsidian checkbox rendering:

    - [ ] Heat (1995)          unwatched        - [x] Mad Men (2007)   watched
    - [_] Blow Out (1981)      unwatched        - [+] Alien (1979)     watched
    - [@] Severance @s2e6      watching         - [~] The Flash (2023) archived

A bracket mark and an inline mark are the SAME channel: `[@]` means what `@`
means, `[+]`/`[x]` mean what `+` means, `[ ]`/`[_]` mean what `_` means, `[~]`
means what `~` means. A line's state resolves from all its marks together
under the precedence above. Writers preserve whichever spelling (inline or
bracket) a line already uses.

## Input leniencies (read-side only)

Parsers SHOULD accept these; writers MUST emit the canonical form:

- `\*` → favorite `*` (markdown-minded editors escape a bare asterisk;
  without this leniency the unknown token would swallow year/rating/favorite
  into the title)
- `@YYYY-MM-DD` → watched date `+YYYY-MM-DD` (the pre-1.2 spelling)

## File structure

An OpenWatchlist file is UTF-8 Markdown.

- **Preamble** — everything before the first `## ` heading: typically an
  `# H1` title, free prose, and entries that live at the top level on no
  shelf. Non-entry preamble text is preserved verbatim.
- **Shelves** — every `## Heading` (H2 exactly) starts a shelf, running to the
  next `## ` or end of file. A shelf is pure organization (`## With John`,
  `## Rom-coms`). **Shelves never decide watch status, and since v1.4 this has
  NO exceptions: a shelf's NAME carries no read semantics either.**
  `## Watched` is a shelf exactly like `## Rom-coms`; an unmarked line is
  unwatched on every shelf; a shelf named `## Archive` does not imply `~`.
  (Versions before 1.4 defaulted unmarked lines from well-known status names —
  that default is retired. Tools SHOULD warn on unmarked lines under a
  status-named shelf and offer a one-step normalization that bakes explicit
  marks on; they MUST NOT silently rewrite or re-interpret a file.)
  Other heading levels (`#`, `###`…) are not shelves and not entries — they
  are preserved as opaque text. Each shelf keeps whichever entry style it
  already uses (bulleted or plain lines); writers preserve the style they find.
- **Comments** — a line is a comment when its first characters (after optional
  whitespace and one optional list bullet) are `>` (recommended for prose;
  renders as a markdown blockquote), `//` (the natural way to switch a line
  off while keeping it), `--`, or `/*` opening a block comment that runs every
  line down to the one containing `*/`. Comments are WHOLE LINES only — a `//`
  or `--` mid-line is ordinary text; per-entry commentary belongs in `| note`.
  Comments are opaque: they produce no entries and no services, never start or
  end a shelf, and attach to nothing. Writers preserve them byte-for-byte, in
  place. An unclosed `/*` runs to end of file — tools should warn.
- **Services** — the author's streaming subscriptions are ONE line:
  `Services: Netflix | Hulu | Max | Apple TV+`. The label `Services:` is
  canonical (readers also accept `Streaming Services:` and `Subscriptions:`,
  case-insensitive, optionally after a bullet). `|` alone delimits; each name
  is plain text with NO token stripping, so `Apple TV+` and `Disney+` survive.
  The line reads anywhere in the file (conventional home: the last line);
  multiple lines merge in file order, first occurrence of a name wins. Legacy
  form, read forever: the pre-1.3 `## Streaming Services` shelf (one plain
  name per line); writers emit the compact line.
- **Bare lists** — a file with no shelves (or no structure at all) is already
  a complete OpenWatchlist document: every non-blank, non-comment line is an
  entry. Paste any list and it just works.

## Identity

Two lines refer to the same title when their title text and year match, case-
and punctuation-insensitive. The year is part of identity: `Mulan (1998)` and
`Mulan (2020)` are different entries. Duplicates within a file are an
authoring error tools should surface — never silently merge. Identity is also
how tools correlate one title across multiple files.

## Conformance

- A **conforming reader** parses every construct in the spec, treats unknown
  constructs and unparseable lines as opaque content, and never errors on
  arbitrary text.
- A **conforming writer** emits canonical token order and spellings, preserves
  the preamble, every shelf, every comment, and entry order byte-for-byte
  apart from user-initiated changes, preserves each shelf's entry style, and
  round-trips: parsing then writing a canonical file reproduces it exactly.

## A worked example

    # My Watchlist

    > the July queue — shows first,
    > films when there's a free evening

    Dune: Part Two (2024) !!!
    Casablanca (1942) _ #classic
    Severance (2022) 4.5 * @s2e6 #apple-tv | so creepy
    Oppenheimer (2023) 8 +2026-01-15
    Mad Men (2007) 5/5 * ^ +2019-05-20 @s3e4 #rewatch
    The Wheel of Time (2021) @s1e3 ~ | not for me — three episodes in
    // Heat (1995) + #crime

    ## From John

    Blow Out (1981) +2026-02-11
    District 9 (2009)

    ## Rom-coms

    - [x] Notting Hill (1999) 4/5
    - [ ] Set It Up (2018)

    Services: Netflix | Apple TV+

How it reads: the `# H1` and the `>` lines are preamble (a title and a
comment — never entries). Then six top-level entries: Dune is unwatched at
maximum priority; Casablanca is explicitly unwatched, tagged; Severance is
watching at s2e6, rated 4.5/5, a favorite, with a note; Oppenheimer is watched
on the date, rated 8/10; Mad Men is a rewatch in progress (`@` beats `+`, the
2019 date is history) — favorite, masterpiece, rated 5/5; The Wheel of Time is
archived while watching (place kept at s1e3); the `//` line is a switched-off
comment, not an entry. `## From John` and `## Rom-coms` are shelves — pure
organization: Blow Out is watched because of its `+` date, District 9 is
unwatched (and keeps its 9 — the year follows it), Notting Hill is watched via
`[x]` and rated 4/5, Set It Up is unwatched via `[ ]`. One shelf uses plain
lines, the other bullets — both valid; writers preserve each. The last line
declares two streaming services.

## Implement in minutes

- Normative specification (build against this page — it is always current):
  https://open-watchlist.org/spec.html
- Reference parser (vanilla JS ES module, zero dependencies):
  https://open-watchlist.org/parser.js
  Exports `parseDocument(text)`, `parseEntryLine(line)`,
  `resolveStatus(entry)`, `entryToCanonical(entry)`. Copy it as-is or port it
  to any language — the spec is the source of truth; the parser is a faithful
  implementation of it.
- Live playground to verify your output against the reference parser:
  https://open-watchlist.org/playground.html

## Versions

Implement against the CURRENT specification at
https://open-watchlist.org/spec.html (it self-declares its version). Files
never declare a version and never need to: backward compatibility is a hard
requirement, so a current parser reads every valid older file. Every version
is preserved at a permanent URL:

- Current (always): https://open-watchlist.org/spec.html — self-declares v1.4
- v1.4 (current): https://open-watchlist.org/spec-v1.4.html (points at
  /spec.html while current; becomes the frozen snapshot when superseded)
- v1.3 (frozen): https://open-watchlist.org/spec-v1.3.html — added comments,
  the compact `Services:` line, and the official input leniencies
- v1.2 (frozen): https://open-watchlist.org/spec-v1.2.html — the launch version
- Version history: https://open-watchlist.org/changelog.html

## Canonical resources

- Home: https://open-watchlist.org/
- Specification (normative, current): https://open-watchlist.org/spec.html
- Changelog / version index: https://open-watchlist.org/changelog.html
- Reference parser: https://open-watchlist.org/parser.js
- Playground: https://open-watchlist.org/playground.html
- This file (canonical URL): https://open-watchlist.org/llms.txt

OpenWatchlist is an open format, free to implement and extend; the
specification may be reproduced freely.
