OpenWatchlist specification
This is the current version. Implementers and AI agents should build against this page — it is always the latest specification. Older versions stay at permanent URLs (/spec-v1.3.html, /spec-v1.2.html); the changelog lists what changed in each. Reading this as an AI? /llms.txt is the condensed edition of this specification — the whole format in one read, maintained in lockstep with this page.
OpenWatchlist is a plain-text format for tracking the films and series you watch. A valid file is readable in any text editor, diffable in any version control system, and useful with no software at all. Apps are viewers and editors over the file — never owners of it.
# My Watchlist
> the July queue — shows first,
> films when there's a free evening
- Dune: Part Two (2024)
- Severance (2022) 4.5/5 * @s2e6 #apple-tv | so creepy
- Aftersun (2022) 4.5/5 * +2026-01-15 | quietly devastating
- The Flash (2023) 2/10 ~
## With John
- Heat (1995) #crime
- Mad Men (2007) 5/5 +
Services: Netflix | Apple TV+
The > lines are a comment — notes to yourself, never parsed as titles. Then four entries: one queued, one being watched (mid s2e6), one watched with a date, one set aside with ~ (still unwatched — archiving hides it, it doesn't rewrite history). Each
line carries its own state. Below them, a shelf called “With John” groups two more titles. The shelf name is organization, nothing else; the + on Mad Men is what says it's been watched. The last line lists the author's
streaming services.
Design principles
- Humans first. Every construct must be writable on a phone keyboard and understandable without documentation. If a feature needs syntax a person wouldn't naturally type, it doesn't go in.
- 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.
- The file is the database. There is no canonical copy elsewhere. Tools must read what humans write and write what humans can read.
- Never destroy what you don't understand. Unknown shelves and unparseable lines are preserved verbatim by conforming writers.
- Order is data. A user's ordering of entries is meaningful (a curated queue, a ranking) and conforming writers preserve it except for user-initiated moves.
- Lenient in, canonical out. Parsers accept entry tokens in any order and several equivalent spellings; writers emit one canonical form.
The entry line
Each title is one line: a bulleted - Title or a plain Title, followed by optional tokens, in this canonical order:
Title (Year) rating favorite masterpiece priority status/progress watch-date archive #tags | notes
Everything except the title is optional. A parser accepts the tokens in any order after the title; a writer always emits the canonical order above. In marks:
Title (YYYY) 4.5 * ^ !!! @s2e6 +2026-01-15 ~ #tags | note
Watch status
A title's watch status is carried on its line — by the marks it does or doesn't have. Only these four determine it:
| Marks on the line | Status |
|---|---|
| nothing | unwatched — a bare title is a queued title |
_ |
unwatched, explicitly — “not watched yet,” on the record |
@ (progress) |
watching — you're somewhere in it |
+ (watched) |
watched — you finished it, optionally dated |
When watch marks combine, the current state wins:
@ > + > _ > nothing. So +2024-11-03 @s3e4 reads as watching — a rewatch in progress, with its history intact.
Everything else — archive ~, favorite *, masterpiece ^, priority ! — is a separate metadata flag, never a watch status.
Archived is a flag, not a status
~ means set aside — dropped, retired, hidden from the active list. It is orthogonal to watch status: archiving something never erases whether you watched it. 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)
A conforming tool reports both facts — “archived” and the underlying watch status — and un-archiving (removing the ~) simply returns the line to its active life with everything intact.
Tokens
| Token | Form | Meaning |
|---|---|---|
| Year | (YYYY) |
Disambiguates remakes — Suspiria (1977) vs (2018). Premiere year for TV |
| Rating | n/5, n/10, or bare n |
Personal rating. Plain numbers 0–5 read out of 5, above 5 read out of 10; explicit forms (4.5/5, 8/10) are always allowed and preferred when clarity matters (see below) |
| Favorite | * |
Flag — independent of rating and status |
| Masterpiece | ^ |
Flag — a higher, narrower indication than favorite: elite, all-time-level work. A title can be * ^ (both), or ^ without * (a masterpiece that isn't a comfort favorite) |
| Priority | ! !! !!! |
Flag — eventually / soon / watch next. Three levels; !!! is the maximum |
| Unwatched | _ |
Status — explicitly not watched yet (a bare line is already unwatched; _ puts it on the record) |
| Progress | @, @s2e6, @s2, @1:20:00, @80m |
Status — where you are: season/episode or a movie timecode; bare @ means watching, position unrecorded. Because progress lives on the line, moving an entry anywhere never loses your place |
| Watched | + or +YYYY-MM-DD |
Status — watched, optionally dated (ISO). Repeat the token for rewatches |
| Archived | ~ |
Flag — set aside, orthogonal to watch status: an archived line keeps its unwatched/watching/watched state (see Archived) |
| Tag | #tag |
Freeform; letters, numbers, -, _. Lowercase kebab-case recommended |
| Note | | text |
Free text to end of line; always last |
Ratings: plain numbers
Ratings are plain numbers: 0–5 defaults to out of 5, greater than 5 defaults to out of 10. A plain 5 means 5/5 — if you mean five out of ten, write 5/10. Explicit forms (4.5/5, 8/10, 2/10) are always allowed and
preferred when clarity matters.
A trailing number is a rating by default. The one exception keeps titles that end in a number intact: a number sitting directly before the year is part of the title — which is exactly where it belongs. Add the year and the title is safe; leave it off and the number reads as a score.
Oppenheimer (2023) 8 rating 8/10 (trailing number → rating)
Heat (1995) 4 rating 4/5 (0–5 reads out of 5)
Ocean's 8 (2018) title "Ocean's 8", no rating (number before the year)
District 9 (2009) title "District 9", no rating
Ocean's 11 title "Ocean's 11" (numbers above 10 are never a rating)
Ocean's 8 title "Ocean's", rating 8/10 (no year → the 8 is read as a score)
Two guardrails make this safe in practice: a plain integer above 10 is never a rating (so Ocean's 11 and Catch 22 stay titles), and a bare decimal like 4.5 is always a rating anywhere — essentially nothing is titled a decimal. When a title ends
in a number, just include its year.
Input leniencies
Real files carry artifacts of the editors that made them. Two spellings are official read-side leniencies — parsers SHOULD accept them; writers MUST emit the canonical form:
| Lenient input | Read as | Why |
|---|---|---|
\* |
favorite * |
Markdown-minded editors and exporters escape a bare * (it could read as emphasis). Without the leniency the unknown token halts right-to-left parsing and silently swallows the year, rating, and favorite into the title |
@YYYY-MM-DD |
watched date +YYYY-MM-DD |
The pre-1.2 spelling of a watch date; files from early tools still carry it |
Bracket marks
The same status marks may be written at the start of a line in task-list brackets — handy when you want your list to render as checkboxes on GitHub or a markdown notes application like Obsidian, or when a leading mark reads better than a trailing one:
- [ ] Heat (1995) #crime unwatched
- [@] Severance (2022) @s2e6 watching
- [x] Mad Men (2007) 5/5 watched
- [ ] The Flash (2023) 2/10 unwatched
- [+] Game of Thrones (2011) ~ watched · archived
- [_] Blow Out (1981) unwatched (explicit)
[ ] and [x] are standard GitHub-Flavored Markdown and render as real checkboxes; [_], [@], and [+] extend the same bracket notation and display as readable text elsewhere. A bracket mark and an inline mark are the same channel:
[@] means what @ means, [+] means what + means, combine it with watch marks to archive something watched or in progress. A line's state is resolved from all its marks together under Watch status: bracket and inline marks should usually
match, and when they conflict the same precedence applies (@ beats +, and ~ is the separate archived flag). Writers preserve whichever spelling (inline or bracket) a line already uses.
Preamble
An OpenWatchlist file is UTF-8 Markdown. Anything before the first
## heading is the preamble — typically an # H1 title, free notes, and (very commonly) entries that simply live at the top level, on no shelf at all. Non-entry preamble text is preserved verbatim.
Comments
Some lines are notes, not titles. A line is a comment when its first characters — after optional leading whitespace, and optionally after a single list bullet (- or * ) — are one of four markers:
> The main style: markdown blockquote lines.
> They read as comments here — and render
> as a tidy quote in any markdown viewer.
// Heat (1995) + a line switched off — kept, but not parsed
-- also a comment
/* a block comment:
every line down to the closing marker
is part of the comment */
| Marker | Meaning |
|---|---|
> |
The recommended style for prose notes — it's a markdown blockquote, so it renders as a quote wherever markdown renders |
// |
The natural spelling for switching a line off: // Heat (1995) keeps the line — and everything on it — while removing it from the list |
-- |
Also a whole-line comment. A markdown horizontal rule (---) begins with --, so pasted notes files with rules between sections parse cleanly |
/* … */ |
Block comment across lines. The line opening with /* begins it; every line up to and including the line containing */ is a comment |
Comments are whole lines only. There are no inline or trailing comments: a // or -- in the middle of a line is ordinary text and stays in the title or note. Commentary that should ride along with an entry belongs in its | note.
Block comments consume whole lines too: anything after */ on the closing line is still comment text, and a one-liner /* like this */ comments out exactly that line. An unclosed /* runs to the end of the file — the behavior every other language
trained your fingers on — and tools should warn, because it comments out everything below it.
Comments are opaque. They produce no entries and no services, they never start or end a shelf (a commented line inside a shelf leaves the shelf intact around it), and they attach to nothing — a comment above an entry is not metadata for that entry. Conforming writers preserve every comment byte-for-byte, in place. Commenting a line out is the format's official way to say “keep this line; it isn't a title.”
Shelves
Every ## Heading starts a shelf, running until the next ## heading or end of file. A shelf is organization — a place you keep things:
## With John
- Heat (1995)
- Blow Out (1981) +2026-02-11
## Rom-coms
- Set It Up (2018)
- Notting Hill (1999) 4/5 +
## Halloween marathon
- The Thing (1982) *
- Suspiria (1977)
Shelves do not decide watch status. Blow Out and Notting Hill above are watched because of their + marks — not because of which shelf they're on, and the unmarked titles around them are unwatched wherever they sit. Entries on any shelf parse under the full
entry grammar; tools preserve every shelf's name, order, and entry style, and never auto-empty one.
This rule has no exceptions: a shelf's name carries no read semantics either. ## Watched is a shelf exactly like ## Rom-coms — a place the author keeps things, whose name means nothing to a parser. An unmarked line is unwatched on every shelf,
and a shelf named ## Archive does not imply ~. Renaming a shelf never changes what a single line means.
+ / @ / ~ to the lines. Tools SHOULD warn when they see unmarked lines under a status-named shelf and SHOULD offer a
one-step normalization that bakes the marks on; they MUST NOT silently rewrite the file or silently re-interpret it. See the changelog for the reasoning.
Name shelves anything: ## With John, ## Rom-coms, ## Favorites, ## Dad's recommendations. Apps may offer to organize a file into shelves for you — grouping by status (## Watched), by genre, by anything — but that's a filing convenience
the user opts into. The marks remain the truth; regrouping lines never changes what they mean.
- Title) or plain lines separated by blank lines. Both are equally valid; a writer preserves the style it finds.Streaming services
The services you subscribe to are one line:
Services: Netflix | Hulu | Max | Apple TV+
- The canonical label is
Services:; readers also acceptStreaming Services:andSubscriptions:, case-insensitive, optionally after a list bullet. - Names are separated by
|with optional surrounding spaces. Each name is plain text, not entry grammar — no token stripping applies, soApple TV+andDisney+survive intact; only|delimits. - The line may appear anywhere in the file — preamble, mid-shelf, anywhere a reader finds it. The conventional home is the last line of the file. If several services lines exist, readers merge them in file order (first occurrence of a name wins).
Legacy form, read forever: before v1.3 services were a special shelf — ## Streaming Services (aliases: Services, Subscriptions), one plain name per line. Conforming readers keep accepting that shelf and merge it with any compact line; conforming
writers emit the compact line when writing services.
Files with no shelves
A list dumped from a notes app usually has no structure at all — and it doesn't need any. Since status lives on the line and shelves are optional organization, a file that is nothing but titles is already a complete OpenWatchlist document:
Heat (1995)
Casablanca (1942)
[x] The Thing (1982) 5/5
Severance @s2e6
parses as two queued titles, one watched, one in progress. Paste anything; structure is never a prerequisite.
Canonical examples
Heat (1995) !!! #crime #mann #pacino #deniro | need to watch
Heat (1995) _ !!! #crime #mann | explicitly unwatched
Severance (2022) 4.5 * @s2e6 #with-john #apple-tv | watching with John, very creepy
The Irishman (2019) @1:20:00 !! #scorsese #netflix | paused halfway through, finish soon
The Batman (2022) 4 * +2024-11-03 #dc #crime #noir | great atmosphere, slightly too long
Mad Men (2007) 5 * ^ + #prestige-tv #amc | watched, date unknown
The Flash (2023) 2/10 ~ #dc #superhero | bad, archived
Children of Men (2006) 5 * ^ !!! +2025-12-18 #sci-fi #dystopian | all-time favorite
Formatting conventions
- Lowercase kebab-case tags:
#with-john #apple-tv #sci-fi #prestige-tv #based-on-book. -
!!!is the maximum priority. Four or more exclamation marks read as title text, not a louder priority. -
A plain rating of
5means5/5. Five out of ten is written5/10.
Identity
Two lines refer to the same title when their title text and year match, case- and punctuation-insensitive. The year is part of a title's identity: Mulan (1998) and Mulan (2020) are different entries. Duplicate identities within a file are an authoring error tools should surface, not
silently merge. Identity is also how tools correlate one title across multiple list files.
Conformance
A conforming reader parses every construct in this document, 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, and entry order byte-for-byte apart from user-initiated changes, preserves each shelf's existing entry style, and round-trips: parsing then writing a canonical file reproduces it exactly.
Versioning
The format is versioned by this document. Backward compatibility is a hard requirement: a valid file must parse identically under every future version. New tokens may be added only where an older parser would already leave them harmlessly in the title. One narrow class of exception is permitted
and must be recorded in the changelog: new line-level constructs may claim line shapes that carry no plausible meaning as titles — v1.3's comment markers claimed lines starting //, --, >, and
/*, and its compact services line claimed lines starting Services:, all of which v1.2 read as (nonsense) titles.
One further, rarer class of exception exists: the retirement of a read-side leniency. v1.4 retired the legacy shelf-name status default (see Shelves) — the one place where something other than a line's own marks influenced how the line read. A retirement is permitted only after a deprecation period in which reference tooling warned every time the behavior fired, only alongside a one-step migration tools can offer (bake explicit marks onto the affected lines), and only with a loud changelog record. The grammar itself never breaks: every token a v1.2 parser read, a current parser reads identically.
Every version of this specification is preserved at a permanent URL of the form /spec-v<version>.html (e.g. /spec-v1.2.html); this page, /spec.html, is always the current version. The changelog lists every version with what changed. Implementers — tools and AI agents alike — should build against this page; files never declare a version and never need to, because a current parser reads every valid older file.
OpenWatchlist is an open format. This document may be reproduced freely. The playground's parser.js is the reference parser for this specification; /llms.txt is its condensed edition for AI agents, updated with every version.