1\section{Data: audit, panel reconstruction and matching}\label{sec:data}23\subsection{Snapshot and provenance}4On 1 November 2026 at 07:57 UTC a consistent backup of the production SQLite database was taken through a read-only connection (SHA-256 recorded in the replication package; the Food-Ka code at commit \texttt{2c76523} was copied alongside to document the data-generating process). Production was not modified at any point. The snapshot holds 124{,}579 listings (50{,}760 active), 300{,}613 price-log rows, 15{,}630 connector passes over 57 feeds (median gap between passes of the same feed: 7.2 hours), 2{,}537 listings linked by Food-Ka's own cross-banner matcher, and auxiliary caches. Table~\ref{tab:coverage} summarises coverage for the largest feeds; Figure~\ref{fig:coverage} shows weekly coverage by channel and active listings by company.56\begin{figure}[htbp]\centering7\includegraphics[width=\textwidth]{figures/fig_coverage.pdf}8\caption{Data coverage. A: listings observed per circular week by channel. B: active listings on 1 November 2026 by company (top 10). Source: Food-Ka snapshot of 1 November 2026; authors' computations.}\label{fig:coverage}9\end{figure}10\input{tables/tab_coverage_feeds}1112The composition is heavily skewed. Four feeds---Giant Tiger, Epipresto, La Boîte à Grains and Marché Nuvo---hold 52\% of active listings because their Shopify catalogues are read exhaustively, whereas the connectors of the large chains read selected aisles (Metro 1{,}540 active listings, Maxi 1{,}086, Walmart 922, Super~C 857, IGA 600, Provigo 450). The large chains nevertheless generate most of the price events (Metro alone 63{,}868; Club Entrepôt 68{,}438) and most of the deactivated listings, for reasons we turn to now.1314\subsection{Source-to-variable inventory}15Table~\ref{tab:inventory} classifies each variable the mandate asked for. Product name, category (18 canonical classes), package label and current price are confirmed for all feeds; brand is missing for the Walmart, Adonis, T\&T, Akhavan, BocoBoco and Costco feeds (the brand sits inside the name); GS1-valid UPCs exist only for Metro and Super~C (100\% check-digit validity, 1{,}954 active listings), while Shopify, Loblaw and Walmart identifiers are internal SKUs (check-digit validity at the 10\% chance level). Package size is derivable for 63\% of active grocery listings after re-parsing labels and names (Food-Ka's own parser: 43\%); it is missing for most circular items and for Giant Tiger and Nuvo. Regular prices, promotion flags and stock status exist only as current state. Store and geography do not exist. Quantities and expenditure weights do not exist.1617\begin{table}[htbp]\centering\small18\begin{threeparttable}19\caption{Source-to-variable inventory}\label{tab:inventory}20\begin{tabular}{p{3.6cm}p{3.2cm}p{8.4cm}}21\toprule22Variable & Status & Notes \\23\midrule24Product name & confirmed & all feeds; French/English mixed \\25Brand & confirmed / partly unavailable & missing for 23\% of active listings (six feeds carry no brand field) \\26UPC / GTIN & confirmed for 2 feeds & Metro, Super~C: GS1-valid; elsewhere internal SKUs; Open Food Facts barcodes attached by name lookup are derived and unreliable \\27Package size, unit & derivable & re-parsed from label or name: 63\% of active grocery listings; missing mostly for circulars, Giant Tiger, Nuvo \\28Category & confirmed & 18 canonical classes; ``Autres'' is a residual (19\%) \\29Retailer / banner / company & derivable & 54 feeds $\to$ 43 banners with grocery listings $\to$ 26 companies \\30Store & unavailable & no store identifier; storefronts answered anonymous sessions; circulars for one postal code \\31Current price & confirmed & event log: first observation + every change \\32Regular / reference price & current state only & historised nowhere; circulars carry a regular price per item-week \\33Unit price & derivable & recomputed; per-kg labels, multi-buy labels flagged \\34Promotion flag & current state only & historical state inferred from price paths (storefront) or implicit (circulars) \\35Observation timestamp & confirmed (feed level) & 15{,}630 passes; listing-level presence between events inferred \\36Circular validity & confirmed & valid-from / valid-to per item \\37Availability / stock & unreliable & NULL for 23\% of active listings; current only \\38Geography, quantities, expenditure weights & unavailable & --- \\39\bottomrule40\end{tabular}41\begin{tablenotes}[flushleft]\footnotesize42\item Classification per the mandate: confirmed, derivable, unavailable, unreliable. Full inventory with per-feed rates in \texttt{reports/phase1\_data\_audit.md}.43\end{tablenotes}44\end{threeparttable}45\end{table}4647\subsection{Panel reconstruction}48Because the log records changes, a listing's price between two events is the last logged value, and the listing was observed at every pass of its feed between its first and last sighting. We reconstruct, for every listing and local day (America/Toronto), the duration-weighted distribution of displayed prices---mode, minimum, maximum, time-weighted mean, open and close, number of changes and number of distinct values---and aggregate to the Quebec circular week (Thursday to Wednesday). Two limitations are inherent: a listing that leaves and re-enters a feed without a price change leaves no trace, and deactivation requires two consecutive misses, so removals are dated to within one pass. Invariant tests verify that covered hours equal the observation span, that daily bounds hold and that single-event listings have a single price. The grocery universe (feeds other than pharmacies and the SAQ; categories other than health and beauty, household, pets, baby and alcohol; at least one observed price; no unrealistic price) spans 723{,}140 listing-weeks for 102{,}835 listings in 43 banners over the 11 complete weeks from 13 August to 22 October 2026 (the partial first and last weeks are dropped).4950\subsection{A measurement finding: store-mixture noise}\label{sec:mixture}51Figure~\ref{fig:mixture}A plots every logged price of one Metro listing. The price alternates, within the same day, among \$5.77, \$6.99, \$7.49, \$7.79 and \$7.99. This is not repricing. In the feeds of Club Entrepôt, Metro, Maxi, Super~C, Provigo and Walmart, 48--68\% of logged changes revert to the previous-but-one value, the median listing with six or more events cycles among four distinct values, and the set of values observed in one week overlaps the next week's set with a median Jaccard index of 0.5. Single-store online shops (Shopify and WooCommerce catalogues) show none of this. The Food-Ka connectors for the large chains reach the sites through residential proxy pools without selecting a store; the sites then serve the price of whatever store they associate with the session. Each logged ``change'' in these feeds is therefore a draw from the banner's distribution of store prices.5253\begin{figure}[htbp]\centering54\includegraphics[width=\textwidth]{figures/fig_store_mixture.pdf}55\caption{Store-mixture noise. A: all logged prices of one Metro listing (mayonnaise, 890 mL), 12 August -- 31 October 2026. B: share of listing-weeks in which more than one price value was displayed, by banner, with the median log range (max/min) on those weeks. Feeds behind anti-bot protection that serve store-specific prices (Club Entrepôt, Maxi, Metro, Super~C, Provigo, Walmart) stand apart from single-store online shops.}\label{fig:mixture}56\end{figure}5758Three consequences follow and were fixed in the preregistration before any estimation. (i) The posted price of a listing in a week is defined as the duration-weighted \emph{modal} price, with minimum, maximum and mean as sensitivity definitions. (ii) Sub-weekly variation in these feeds is not interpreted as price flexibility; H2 is tested on weekly series and the falsification that single-store shops must not display the oscillation is part of the design. (iii) The within-week range of a listing in these feeds is a lower bound on within-banner, across-store dispersion for that product---a quantity we report (Table~\ref{tab:dispersion} note and Figure~\ref{fig:mixture}B) with the caveat that the stores are unidentified: 23\% of storefront listing-weeks in the anti-bot feeds display more than one price value, with a median log range of 0.15--0.24, against 4.7\% in single-store shops.5960\subsection{Attrition}61Table~\ref{tab:attrition} and Figure~\ref{fig:matching}A track listings through the filters. Of 124{,}579 listings, 111{,}575 belong to the grocery universe, 66{,}340 have a parsable package size and are eligible for matching, and 13{,}273 end up in a cross-banner group under tiers A--C (13{,}088 under the primary tiers A+B). The denominator of every cross-banner statistic in this paper is therefore about 11\% of listings, not the 50{,}760 products advertised on the home page.62\input{tables/tab_attrition}6364\subsection{Cross-banner matching}\label{sec:matching}65We match listings across banners in tiers of decreasing confidence. Tier~A is exact equality of GS1-valid UPCs (only Metro and Super~C). Tier~B requires the same brand key, the same package (total quantity in grams, millilitres or units \emph{and} the same multipack count, so that 12$\times$355~mL and 6$\times$710~mL are different packages), identical cleaned token sets after removing size tokens, stop words and the brand, and identical numeric tokens (``2\%'' versus ``3.25\%''). Tier~C relaxes token identity to a token-sort similarity of at least 0.90. Tier~D handles feeds without a brand field by requiring identical full-name token sets, the same package, and the presence of the branded side's brand tokens in the brand-less name. Size-less listings and multi-product circular offers (``X ou Y'', ``assortis'', ``sélectionnés'') are excluded from matching; a first version that allowed them produced chained groups of up to 141 listings.6667The thresholds were not chosen by eye. All 36{,}700 Metro$\times$Super~C candidate pairs sharing a brand and a package were scored and labelled by whether they share a UPC (4{,}328 positives). Token-set similarity---the scorer used by Food-Ka's own matcher and by a first version of ours---scores a name that is a subset of another at 1.0 (``beurre végétal salé'' matches ``beurre végétal non salé''); its precision at threshold 0.90 is far lower (0.61 on a first calibration without multipack-aware package keys). Identical token sets achieve precision 0.932 and recall 0.982; token-sort similarity $\ge 0.90$ cumulatively 0.870 and 0.987. The residual 6.8\% of identical-text pairs with different UPCs are packaging variants invisible in text, so 0.93 is a lower bound on the precision of tier~B relative to what a reader of the two listings could tell apart.6869Figure~\ref{fig:matching}B and Table~\ref{tab:validation} report the validation on a stratified random sample of 60 pairs per tier. Each pair was labelled by Claude (claude-opus-5-5) as a second annotator and then reviewed, pair by pair, by the research operator; the two agreed on all 240 pairs after review. Tiers A and B are correct in 60/60 cases each, tier~D in 59/60 (one unsure), tier~C in only 25/60 (five unsure): the token-sort rule admits flavour, roast, fat-content and colour variants (``douces'' vs.\ ``piquantes'', ``torréfaction moyenne'' vs.\ ``foncée''). The preregistration therefore fixes tiers A+B as the primary specification and uses C and D only in sensitivity analyses. The tiers yield 4{,}413 (A), 4{,}088 (B), 714 (C) and 102 (D) cross-banner pairs, and 5{,}909 groups under A+B covering 13{,}088 listings; the median group spans two banners and the largest seven. Against Food-Ka's own 2{,}033 linked pairs, our tiers recover 42.5\%---the rest are size-less or multi-offer circular links that we exclude by design.7071\begin{figure}[htbp]\centering72\includegraphics[width=\textwidth]{figures/fig_matching.pdf}73\caption{Matching validation and attrition. A: listing-level attrition flow. B: precision by tier from the 240-pair reviewed sample (Wilson 95\% intervals) and from UPC ground truth (Metro$\times$Super~C blocks; none for tier D).}\label{fig:matching}74\end{figure}75\input{tables/tab_match_validation}7677One structural fact emerges from the matched panel and conditions the interpretation of everything that follows: 93\% of matched group-weeks involve banners of the \emph{same} company (Metro vs.\ Super~C; Maxi vs.\ Provigo vs.\ Club Entrepôt). Exactly matched cross-company comparisons number 2{,}103 group-weeks. The apparent diversity of 54 banners collapses, for the purpose of like-for-like comparison, to format differentiation within two chains plus a thin layer of cross-chain pairs.78