# Binna track record: pre-registration, version 1

Status: registered before any of the calculation code was merged. This file is frozen once it is on the main
branch: it is never edited, deleted or re-added. Any change to these rules is made by adding a new file
(PREREG-v2.md, PREREG-v3.md, ...). A new version applies only to announcements published after that version was
registered; rows published earlier keep being calculated with the version in force when they were published.
The page shows the SHA-256 of every version and the date each one was registered.

## 1. What the track record measures

Two things, and nothing else:

- **A. Announcement lead.** When we publish that a brand has announced a launch at a retailer, how many days
  before the first evidence of the brand being on sale there could the public read it on our site?
- **B. Detection lag.** For every event we publish, how many days after the report did it enter our data?
  This measures how fast we find reports. It is not a measure of being early.

"Early" on the page means only: the day the announcement became visible on our site is before the date of the
first on-sale evidence. It does not mean we predicted anything, and the page does not say so.

## 2. Events in scope

- Every event that has ever been on the main branch in `events/verified-*.jsonl`, or in the initial reviewed set
  `events/golden-2026-10-19.resolved.jsonl`, and that passes the same validation as `collector/compile_events.py`.
  Duplicates are merged with the same key as that script: (brand trimmed and lower-cased, retailer, country,
  source URL, whether the event is a delisting).
- Once an event has been on the main branch it stays in scope for good, even if its line is later removed.
  The fields used for the calculation (status, month, scope, report date, source URL) are taken from the version
  of the line in the commit where the event first appeared, not from later edits. For the initial reviewed set,
  that version is the file as it stood at the end of 2026-10-19 (Toronto), when it was assembled; it was not
  shown on the public site before 2026-10-26.
- Event files and `events/corrections.jsonl` are append-only on the main branch: existing lines are never changed
  or removed. Our checks compare every change with the main branch and refuse anything else. Corrections go to
  `events/corrections.jsonl`; the calculation reads every line that has ever been in that file on the main
  branch, so a removed line still counts.
- Rows are shown only for brands in the public brand data (`data/brands.json`) on the day of the calculation,
  matched by name after Unicode NFKC normalisation, case folding and removal of everything except letters and
  digits. Events about other brands are not shown as rows, under the same publication rules as the rest of the
  site. **They are still counted**: they are calculated with exactly the same rules, included in every headline
  count, median and percentile, and published as anonymous figures (status, lead or lag in days, dates) without
  brand, retailer or link. This matters because brands known only from news reports are published only once a
  store listing is reported, so leaving out hidden announcements would hide exactly the ones that did not come
  true.
- No other selection. Every event in scope is counted, including announcements that never came true, events that
  were later corrected, and events that make the figures look worse.

## 3. Dates

- **published_at** is the date, in America/Toronto, of the earliest commit on the first-parent history of the
  main branch in which a line carrying the event's key is present in an `events/verified-*.jsonl` file. For a line
  that was in the file when the file was first added, this is the committer date of that first-add commit
  (`git log --first-parent --diff-filter=A`). A line added later gets the date of the commit that added it.
  File modification times are never used. Events in the initial reviewed set have published_at 2026-10-19.
  If an event is in more than one file, the earliest date wins. An event never on the main branch is not counted.
- **visible_on** is the first day the public could read the event on our site: the later of published_at plus one
  day (the site is rebuilt each morning at 06:07 Toronto from the main branch) and 2026-10-26 (the day the site
  went live).
- The calculation refuses to run on a shallow clone, where the history would be cut off.

## 4. Metric A: announcement lead

- Eligible: every in-scope event with status `announced`. Its key is brand, retailer and country.
- On-sale evidence for that key is any of:
  1. an in-scope event with status `listed` for the same brand, retailer and country. Its evidence date is the
     earlier of the first day of its listing month (when it has one) and its report date;
  2. a retailer listing in the public brand data for the same brand, retailer and country with status `listed`
     and evidence `checked`, not a distributor. With a start month or year (`since`), its evidence date is the
     earlier of the first day of that month or year and its check date. Without `since`, only the check date is
     known, and it is the most recent check, not the first: such a listing counts as dated evidence only when its
     check date is on or before visible_on (it then shows the brand was already on sale), with the check date as
     its evidence date. A later check date without `since` is "undated on-sale evidence": it never makes an
     announcement fulfilled, and it keeps the row pending only while that check date is on or before the deadline
     (status 5 below).
  Listings with evidence other than `checked` come from the event files and are covered by rule 1.
- Scope: online-only evidence (an event with scope `online`, or a listing of kind `online`) does not count as
  evidence for an announcement whose scope is `in_store` or `both`. Any other evidence counts.
- Each piece of dated evidence has an earliest possible date and a latest possible date. A month counts from its
  first day to its last day ("month precision"), a year from 1 January to 31 December ("year precision"). The
  earliest possible date is the earlier of the start of the listing month or year and the report or check date;
  the latest possible date is the earlier of the end of that month or year and the report or check date. A piece
  of evidence without a month or year has the report or check date as both.
- Evidence date E = the earliest possible date over all dated evidence. Confirmed date L = the latest possible date
  over all dated evidence, taking the smallest such value: the first day by which some evidence shows the brand was
  certainly on sale. E is never after L. The deadline is always compared with L, and the lead is always counted
  from E, so imprecise dates can only make a result look worse, never better.
- Deadline = the last day of the announced month plus 90 days; when the announcement gives no month, the report
  date plus 90 days.
- Status. Write V for visible_on, D for the deadline, E for the evidence date (only when there is dated evidence)
  and T for the calculation date. The first rule that applies decides:
  1. `withdrawn`, in either of two cases. (a) A correction in `events/corrections.jsonl` with action `remove` for
     the same brand, retailer and country and the same source URL as the announcement (URLs compared after
     removing tracking parameters); the date of the correction does not matter, and a correction with another
     source URL never withdraws. An `online_only` correction is never a withdrawal. (b) A verified delisting for
     the same brand, retailer and country dated on or after V and on or before D, when there is no dated on-sale
     evidence with E before the delisting date. A delisting dated before V or after D never withdraws. The scope
     rule above applies to delistings too: an online-only delisting (scope `online`) never withdraws an
     announcement whose scope is `in_store` or `both`.
  2. `already_listed`: L is on or before D, and E is on or before V. No lead is counted.
  3. `fulfilled`: L is on or before D, and E is after V. Lead = E minus V, in days.
  4. `unfulfilled`, evidence not confirmed by the deadline: there is dated evidence but L is after D. No lead is
     counted; the row is marked as having on-sale evidence that does not confirm a listing by the deadline, and
     shows E.
  5. `pending`: there is no dated evidence, and either there is undated on-sale evidence whose check date is on or
     before D, or T is on or before D.
  6. `unfulfilled`: every other case, that is, no dated evidence, T after D, and no undated on-sale evidence
     checked on or before D.
  Unfulfilled means no on-sale evidence in our data that confirms a listing on or before the deadline, not that
  the brand is not on sale.
- A row whose deadline had already passed when it became visible is marked as such.
- Headline: the number of fulfilled announcements and the median lead among them (hidden announcements
  included). Fewer than 10 announcements that are no longer pending is not enough to draw a conclusion; the page
  then says "Sample n=..., not enough to draw a conclusion" instead of presenting the median as a result.

## 5. Metric B: detection lag

- Every in-scope event (listed, announced or delisted): lag = published_at minus the report date, in days.
- Reported: the number of events, the median (the mean of the two middle values when the count is even) and the
  90th percentile by the nearest-rank method (the value at position ceil(0.9 x n) in ascending order).
- Always shown split by how the event entered the data: `daily review` (files named `verified-YYYY-MM-DD.jsonl`)
  and `one-off batch` (the initial reviewed set and files named `verified-YYYY-MM-DD-<label>.jsonl`, which collect
  older reports on purpose). Also grouped by the month of published_at. The overall figure is published but never
  shown on its own without the split.
- Any group with fewer than 10 events is marked as not enough to draw a conclusion.

## 6. Backfill

- Events published on or before the day this file reached the main branch are calculated with exactly the same
  rules and are marked "backfilled". They are not an out-of-sample test: these rules were written while those
  events could already be seen. The page states the backfill range and the known biases, including that the
  initial reviewed set has a fixed date by rule rather than from history, and that one-off batches collect old
  reports, which makes detection lag long by construction.
- Before the first calculation, the history of every event file was checked: each `events/verified-*.jsonl` file
  was added in one commit and never changed; the initial reviewed set was changed only on 2026-10-19.

## 7. Integrity and recalculation

- Each calculation reads these rules from the main branch, not from a working copy, and checks that every commit
  that touched this file left it identical to the version first added. It refuses to run otherwise.
- The calculation is deterministic: the same main branch, the same public brand data and the same calculation
  date give the same output. It is published daily as `data/track_record.json`, on the page and as a download,
  with every row, the anonymous figures for hidden events and the dates used; the page gives a short program that
  recomputes the figures from that file.
- The SHA-256 of this file is written into the data and shown on the page.
