Blocklists
How tombstones, blackbox, and unresolved block planned adds to prevent repeated failures.
Blocklists are why an item can be planned but still not get added.
CrossWatch blocks retries when it has evidence an item is a bad idea.
What can block an add
Tombstones: it was deleted recently (mostly two-way safety).
Blackbox: it keeps failing, so it’s cooled down.
Unresolved: it failed to apply and is parked.
If an item won’t add
Check if it was deleted recently (tombstones).
Check if it’s in blackbox.
Check unresolved files for that destination.
Watchlist does not use this blocklist path in current wiring. It relies on Phantom Guard.
Related:
Watchlist add flapping: Phantom Guard
File locations: State
This doc explains the shared blocklist logic used to prevent planned ADDs from reattempting known-bad keys.
Implementation notes
Code: cw_platform/orchestrator/_pairs_blocklist.py
Inputs from: _tombstones.py, _unresolved.py, _blackbox.py
Used by: one-way and two-way (mostly non-watchlist features)
What it does
apply_blocklist(state_store, items, dst, feature, pair_key, emit, dbg)
It filters a list of planned items (usually ADDs) by removing items that match any of:
Tombstones (pair-scoped)
Unresolved keys (destination-scoped, optionally cross-feature)
Blackbox keys (destination+feature scoped or pair-scoped, depending on config)
It returns a filtered list and emits a blocked.counts event with breakdowns.
What it does not do
It does not block removals (in current pipeline wiring).
It does not apply PhantomGuard (watchlist uses that separately).
It does not mutate state; it only reads.
Inputs
items
itemsA list of item dicts planned for writing.
Each item should have:
canonical key derivable via
canonical_key(item)idsdict if you want token-based matching to work well
dst
dstDestination provider name (string), e.g. "SIMKL".
feature
featureFeature name (watchlist, ratings, history, playlists).
pair_key
pair_keyProviders sorted and joined (e.g., PLEX-SIMKL). This is used for tombstones and optional blackbox pair scoping.
How matching works
apply_blocklist uses filter_with(tokens_to_block, items) from _tombstones.py.
That function treats a token as matching an item if:
token == canonical key
token matches any ID token derived from
item["ids"]token matches a normalized title-year token:
type|title:<lower>|year:<year>
So even if canonical keys differ, a tombstone written as tmdb:123 can still block an item whose canonical key is imdb:... as long as it includes ids.tmdb = 123.
Blocklist sources
1) Tombstones
Loaded via:
keys_for_feature(state_store, feature, pair=pair_key)
Then TTL-filtered (by whoever calls this; two-way does this explicitly).
In current apply_blocklist usage:
it typically blocks adds for items that have been deleted recently.
2) Unresolved
Loaded via:
load_unresolved_keys(dst, feature, cross_features=True)
This is meant to block adds for keys that keep failing to apply.
Caveat:
writer writes
*.unresolved.pending.jsonloader expects
*.unresolved.json(see Unresolved)
So unresolved may under-block unless you have a promotion step.
3) Blackbox
Loaded via:
load_blackbox_keys(dst, feature, pair=pair_key)
This returns a set of blocked keys (cooldown entries).
Applied as tokens, so it can match by canonical key or ID tokens too.
Output and observability
apply_blocklist emits:
blocked.counts
Typical payload:
Debug mode may also emit:
previews of blocked keys
sample item titles/ids
Where it’s used (current wiring)
One-way
applied only for
feature != "watchlist"(watchlist is excluded)applied primarily to planned ADDs
Two-way
applied similarly for non-watchlist ADDs on each destination side
So if you expect watchlist adds to be blocked by blackbox/unresolved/tombstones via apply_blocklist:
they won’t (today). Watchlist relies on PhantomGuard + two-way tombstone logic.
Operational notes
If an item “won’t add” and you want to know why:
Check tombstones for the feature + pair key
Check blackbox files for destination + feature
Check unresolved files for destination + feature
If you’re implementing a provider and want blocklists to work:
ensure IDs are populated in items (
ids.tmdb,ids.imdb, etc.)so token matching can hit tombstones even if canonical key differs
Related pages
Tombstone tokens: Tombstones
Failure cooldown: Blackbox
One-run failure memory: Unresolved
Where blocklists are applied: One-way sync, Two-way sync
Last updated