Thales
← research journal

An internal research document, published verbatim by the automated daily export — not written for an audience, and better for it. All performance discussed is simulated paper trading; nothing here is investment advice.

STAMP-3 — #132's pre-open→previous-day rule inverts for current-state captures (shortability), and three stamp sites still judge the UTC date

status: open · raised by the monthly research audit, G1 fresh-eyes audit of PR #132 (2026-09-03, research/RESEARCH_AUDIT_LOG.md run 1) · class: moat / loudness · judgement: YES (MAJOR lead + three MINOR riders, same class) · effort ~0.25 pd · horizon: live now — the trigger window is the same displaced-cron regime that fired STAMP-1 three times


Plain-language summary for an owner reading one paragraph. The STAMP fix (#132) stamps every capture with the market session it observed, and its rule says a pre-open instant belongs to yesterday — right for option chains (pre-open, the only chain data that exists is yesterday's close), but backwards for the shortability snapshot, which photographs right now: Alpaca's current ETB/HTB borrow flags. If the 19:00Z skew workflow is ever delayed into the next morning's pre-open window (observed dispatch delays already reach 9 h; 9 h is exactly midnight ET), the run would write this morning's borrow flags into yesterday's file, permanently — and the TIME-GATED flip panel would date an overnight ETB→HTB flip to the wrong session. Before #132 this one stream stamped the current UTC day, which was content-correct; the fix made it wrong. Three smaller leftovers of the same class ride along below.

Why this is a new row, not STAMP-1/2 relitigation (C0). STAMP-1/2 are BUILT and their criteria verified met (queue/built/, audit run 1 M3). This row records a defect their fix introduced (the pre-open rule applied to a current-state stream — a case neither row's registration contemplated) plus sites the fix never reached. Nothing here reopens their verdicts.

Mechanism — code-verified at HEAD (audit run 1)

Lead (MAJOR). utils/calendar.py:66-79: market_session_date maps any pre-09:30-ET instant to previous_trading_day. cli.py:2157 passes that session into capture_shortability(as_of=...) — directly under the comment "the snapshot's as_of IS the present by construction — a threaded historical date would FABRICATE state this current-state-only API cannot serve" (cli.py:2153-2155). borrow_flags.py then keys the parquet filename and the idempotency skip (if f.exists(): return) on that date. Failing scenario: any firing of snapshot-shortability in the 04:00–13:30Z window — a 9h-delayed 19:00Z cron, or a morning manual re-dispatch after a missed day — resolves to yesterday, yesterday's file is missing (the delayed run was yesterday's), and morning-refreshed flags are written to data/shortability/<yesterday>.parquet with nothing to flag the fabrication. No test covers a pre-open instant reaching this writer.

Riders (MINOR, same class — residual UTC judges/stamps after #132):

  1. market_session_date's docstring promises "a holiday stays the holiday", but the pre-open branch on a holiday jumps two calendar days back (2026-09-07T12:00Z Labor Day 08:00 ET → 2026-09-04); the pinned holiday test probes only 14:35Z. Behavior may even be desirable (pre-open holiday catch-up) — but it contradicts the documented design and is unpinned either way.
  2. The per-sleeve digest's no-run canary still judges the UTC date (execution/digest.py:49 via cli.py:4378, today=Nonedate.today()): the backwards fire #132 fixed in the fleet path survives in the manual single-sleeve path, and the retained test_clock_hermeticity entry explicitly sanctions the read.
  3. Failure-alert email subjects stamp date.today() (UTC) (cli.py:3628, :4392): a post-midnight displaced failure emails tomorrow's date in the channel a human reads to decide which session to investigate. (Hygiene, fold into the same PR: duplicate helper import at cli.py:4713/:4760; export-public's date.today() feeds a dead parameter.)

Data plan

Owned and free — no new data. The fix is stamp/judge logic only; the existing committed capture instants (the 20:14–20:43Z band plus the incident instants already pinned in tests/test_utils/ test_market_session_date.py) are the replay corpus.

Test design / fix shape (propose-only; owner decides)

For the lead: the shortability writer must not inherit the chains rule. The design choice to make explicit — REFUSE pre-open (exit non-zero naming both dates; a pre-open borrow snapshot is neither yesterday's close state nor the day's regular capture) or STAMP-CURRENT-ET-DAY (content-correct, degraded basis, logged loudly). Either is defensible; silently writing yesterday is not. Riders: route the per-sleeve digest and the alert subjects through market_session_date(); pin the pre-open-holiday branch to whatever the docstring is amended to say.

Kill criterion — pre-registered, negative-controlled in both directions

On a temp copy with the clock frozen at a pre-open instant (2026-09-04T12:00:00Z, 08:00 ET): thales snapshot-shortability must never produce a file dated the previous trading day — it refuses naming both dates, or writes the current ET day per the chosen design. Negative control: replay the committed capture band (20:14–20:43Z normal days, the 09-01 post-close instant, and the pinned incident instants) and assert byte-identical stamping behavior vs HEAD — the guard fires on none of them; a same-session re-run still takes the idempotent skip. If the guard fires on any normally-timed run, revert it and record the miss rather than tuning the threshold after the data exists. Riders red-first: the per-sleeve digest judged at a frozen post-UTC-midnight instant must name the observed session (red before, green after); the holiday pre-open branch gets one pinned test matching the amended docstring.

Effort ~0.25 pd (one writer guard + two one-line reroutes + three tests). Horizon: decidable immediately; the exposure window is the standing displaced-cron regime, outage-correlated exactly as STAMP-1/2 were.


BUILT — 2026-09-03 (hotfix of the same-day regression, hours after the row was raised)

Route — recorded honestly. This row records a defect that PR #132 (merged 2026-09-03 02:02Z) INTRODUCED on a live capture stream; it was fixed as a hotfix in the same interactive session that built #132, on the owner's standing "let's do it" for the post-audit list — not via queue/approved/. A same-day regression of a just-merged change is hotfix territory rather than research-queue territory, but the research audit's counter stands as written: the approved route has still never been used (0-for-7 counting this).

Design choice made explicit (the row asked for one): the shortability writer SKIPS pre-open (nothing written, loud yellow line, exit 0) rather than exiting non-zero — a pre-open borrow snapshot is neither yesterday's close state nor today's regular capture, and the in-session dispatch (12:00 PT) captures the day; a red exit here would page on every harmlessly delayed backup cron. In session and post-close it stamps the ET calendar date, never the previous session. New helper utils.calendar.session_phase carries the rule; market_session_date's docstring now says plainly it is NOT for current-state captures.

Riders folded in: the per-sleeve digest's gather_digest default and the two failure-alert subjects use the market session; export-public's today too; the four local market_session_date imports in cli.py collapsed to one module import; the pre-open-holiday branch is pinned to what the code does (08:00 ET on Labor Day → the previous trading day) and the docstring amended to match.

Kill criterion — met (tests/test_utils/test_market_session_date.py): session_phase classifies the incident instants (04:00Z = midnight ET is the NEXT date, pre-open); the CLI test proves the writer is never reached pre-open and receives the ET date in session; the UTC-judge lint asserts the riders are gone. Full suite 1357 passed; trading gate green under all three sleeve environments.


Provenance recorded by triage 2026-09-07 (annotation only; the record above is untouched): correcting a misleading reference in the built section above — it names PR #132 as this row's PR, but #132 is the merge that INTRODUCED the pre-open rule this row corrects. STAMP-3 was fixed by the hotfix PR #147 (99656aa, merged 2026-09-03); the row was raised in PR #144.