A caveat in the documentation reaches one reader. A caveat in the payload reaches every consumer, forever.
Four places you could put the disclosure
Everyone agrees the window should be disclosed. Where it goes is the actual decision, and the options are not equivalent.
In the docs. Free, and reaches only the people who read the docs at the moment they need the caveat. Which is, in practice, the person who wrote them.
In the frontend component. Better — the reader sees it. But it lives in one component. The command line does not have it. A delegation brief quoting the number does not have it. The next surface someone builds does not have it, and will not know it was supposed to.
Computed on demand by each consumer. Now every consumer needs the full history to work out the span, which means every consumer re-implements the same query and one of them gets it subtly wrong.
On the row. The read model computes it once, and every consumer of that row — every surface, every export, every brief, every future integration nobody has thought of — receives it whether they asked or not.
What the row actually carries
Each read-model row holds the value and its data date, the percentile, the count of points behind it, the date the history starts, and the span in years. None of those is optional in the sense that matters: drop any one and the rest overstate what is known.
It also carries the series' own minimum, median and maximum, which do quiet work — a percentile tells you the rank, the distribution tells you the shape. The live CAPE row is the case in point: value 43.19 at the 99.7th percentile, historical median 16.61, historical maximum 44.20. The rank says near the top. The median says about two and a half times the typical reading. The maximum says and not far below the highest ever recorded. Rank alone expresses none of that.
Computed on read, not on write
One structural point that makes this affordable. The percentile and the window are computed on read from the append-only series rather than stored alongside each snapshot.
That is the correct direction, and not only for storage reasons. A percentile is not a property of the observation — it is a property of the observation relative to everything else known so far, and that changes every time a new row arrives. Storing it at write time would freeze a relationship that is still moving, and you would get a table full of percentiles that were true on the day they were computed. Deriving on read means the answer is always relative to the current history, which is the only version of the question anyone is actually asking.