M4 — the product terminal and the container delivery
Provenance: the frames below are the user-verification layer's own PNGs, and the raw
output is the run's own artifact — nothing here is retyped. Source: mihail812-org/polymarket_wiki,
milestones/M4-report.md and milestones/M4-user-verification/.
Milestone: M4 — the product UI and the container delivery (Kind: feature) Change:m4-ui-and-container-delivery Branch:feat/m4-ui-and-container-delivery Layer:SBHOST — the real binary serving the Kotlin/JS + React terminal over a live ClickHouse store, driven by a real headless chromium Verdict: the terminal is delivered and its user stories are captured and checked; the container image's build/run is not run on this host — the podman-kind runner carries no engine client (preinstall#114).
What was done, in short
The product's UI is a browser terminal. A Kotlin/JS module (ui/) renders React through kotlin-react/kotlin-react-dom, its graph is owned by Koin (koin-core-js), and it charts the recorded price series with the free TradingView lightweight-charts (Apache-2.0).
The terminal reads the product's own read API — GET /api/health, /api/events, /api/event/<slug> — served over the same CollectionStore/MomentQuery reads the collector uses. Read-only by construction; no order path.
A dead store is reported, not hidden — an explicit store unreachable state, not an empty list.
The product ships as one container image.Containerfile is multistage: the Kotlin distribution and the terminal's bundle, a JRE, and the pinned ClickHouse 26.9.12.8. The snap is retired (owner's decision, 2026-10-09).
The terminal was captured and checked, on a real browser, against the store's own rows. 10 frames, 38 field-for-field checks, all matching, user-verify-m4: OK.
The acceptance found and fixed three real defects the offline tests could not see — the read-API GET that never reached the browser, the empty /api/event answer, and the chart that never drew. Detail below the stories.
Each frame is asserted twice — the page must carry the fact (DOM checked before the shot) and the capture must have painted (non-white fraction checked after it). A frame that fails either is not evidence.
The three defects the acceptance caught (and the offline tests did not)
The terminal's own jsNodeTest suite was green the whole time; it drives the components with a fake read API. Only driving the real bundle in a real browser against a real store exposed these:
The read-API GET never reached the browser.Bridge.kt declared httpGet as @JsModule("./http.js") @JsNonModule; the Kotlin/JS UMD rule then binds the module namespace object, and the bundle called that object as a function — TypeError: t is not a function, and the terminal sat in store: unreachable while /api/health on the same origin answered {"reachable":true}. Fixed by inlining the same-origin GET and leaving no relative JS-module seam but the chart's.
/api/event/<slug> answered nothing. The price series was read with a sentinel Long.MAX_VALUE/2 window; the upper bound lands in the year 292278994, ClickHouse rejects the DateTime64 (HTTP 400), and the throw escaped the handler as an empty reply. Fixed by reading the series over the store's real recorded span, and by picking a token the store actually recorded rows for.
The chart never drew, then drew Invalid Date.renderChart was declared but called from nowhere, and lightweight-charts was absent from the bundle (an ES export behind the same UMD binding). Once wired, the series handed it a boxed Kotlin/JS Long and a locale of en-US@posix (Chromium on a LANG=C host), which threw inside the time axis. Fixed by a CommonJS chart bridge with a dynamic import() (the package is ESM-only), integer-seconds series points, and an explicit localization.locale.
User story 1 — "I open the terminal and see what has been collected"
An owner opens the terminal and wants to know, at a glance, which events the collector has recorded and how much of each: the events with their market and token counts, the number of order-book and trade rows, the recorded time span, and a filter that narrows the list to a slug.
the terminal's events panel — the collected events, their markets, tokens, both counts and the recorded span (header: store up, events 4/4)the counts table — val-nrg-loud 10 order-book / 5 trade rows; the three others 2 / 1the recorded span — the min and max recv_time across both tablesthe filter narrows the list — the chosen event's leading token keeps it and drops the rest (events 1/4)
Against the store:list.order_book rows == store and list.trade rows == store for all four events (2=2, 1=1, 10=10, 5=5); the filter narrows 4 → 1. match.
User story 2 — "I pick an event and see its recorded price series, the book and the trades at a moment"
The owner picks an event and a moment T and wants the recorded price series, the order book that stood then and the trades that executed around it. The view names the event, the moment and the window, so the moment is recountable by hand.
the event view at T — the chart of the recorded series, the event name, the moment T (2026-10-10T08:34:25.889Z) and the window (±10s), then the two tablesthe price chart — the recorded series over the recorded span, drawn with lightweight-charts; the layer samples the canvas itself (1839 series-coloured pixels, 53 distinct colours)the order book at T — best_bid 0.57, best_ask 0.58the trades around T — the ±10 s window, in receive-time order (3 rows)
Against the store:orderbook.at_T best_bid == store → 0.57=0.57; best_ask → 0.58=0.58; trades.around_T count == store → 3=3; chart series points == store trades over the span → 5=5. match.
User story 3 — "I can trust what the terminal shows me"
An owner has to be able to tell "the store did not answer" from "the store is empty". The terminal renders each as its own state; the layer captures each and asserts the page says so.
the store unreachable — an explicit state, not an empty list: 'nothing is shown rather than an empty success'a reachable but empty store — events 0/0, span: n/a — a different fact, rendered as such
Goal → case → what was observed → result
Goal
The case
What the run observed
Result
The terminal serves the collected events — markets, tokens, order-book and trade counts and the recorded span — and narrows the list by a filter
the terminal's events panel beside a query of the store for the same events and counts
frames 1–4; list.order_book rows == store and list.trade rows == store for all four events (2=2, 1=1, 10=10, 5=5); the filter narrows 4→1
OK, captured and checked
For a chosen event the terminal charts the recorded price series and shows the order book at a moment T and the trades around T, read from the store
the terminal's event view at T beside the store's own rows at T
frames 5–8; the chart's canvas carries 1839 series pixels; best_bid 0.57=0.57, best_ask 0.58=0.58, trades around T 3=3, series 5=5
OK, captured and checked
The terminal reports its store honestly — a dead store is not an empty success
the terminal against a dead store and an empty store
frames 9–10, each asserted for its own state
OK, captured and checked
The product ships as one container image carrying all of its dependencies, and is built and run as an image
./run-m4.sh deploy on a clean environment (podman build → podman run)
not run — the podman layer carries no engine client (have podman MISSING, preinstall#114); the Containerfile's static checks pass in ./gradlew test
open
The snap is retired
no snap/snapcraft.yaml, no snap scenario, no snap path in scripts or workflows
grep for the snap paths is empty; the REMOVED delta sits in the change
OK
The reverse runs — a green result turned red
Each check must go red on its own broken subject. All were run from the same script as the green run, each pointing it at a deliberately wrong subject (so no subject is mutated in place).
Reverse run
What was broken
What went red
reverse-a
the layer pointed at the readable fallback page instead of the terminal
32 mismatches, user-verify-m4: FAIL — the page is not the terminal (class="terminal" absent), and its events do not carry the terminal's rows or chart
reverse-b
the reference store is a different database than the terminal reads
20 mismatches, user-verify-m4: FAIL — the terminal's rows, span and book do not match the reference the layer reads
reverse-c
the terminal is served by a product whose store does not answer
12 mismatches, user-verify-m4: FAIL — the terminal lists 0 events where the store has rows for 4
== M4 user verification: the product terminal beside the collection store, field for field ==
terminal : http://127.0.0.1:8899 (the Kotlin/JS + React bundle the product serves, driven in a real browser)
store : http://127.0.0.1:8123 / liveui
listed events : 4 (the terminal) / 4 (the store has rows for)
chosen event : val-nrg-loud-2026-10-10
chosen token : 54766275672533937226534147411813036045778021882156237046712863902839579722640
moment T : 2026-10-10T08:34:25.889Z
window : ±10s
fonts : /tmp/m4-fontconfig/fonts.conf
browser flags : --no-sandbox --disable-gpu --disable-dev-shm-usage
-- the store's own rows (read from ClickHouse, not from the DOM) --
cs2-dep5-vita-2026-10-10 order_book=2 trade=1
val-nrg-loud-2026-10-10 order_book=10 trade=5
ja2-jub-con-2026-10-10-more-markets order_book=2 trade=1
auc-sm-vic-2026-10-10-more-markets order_book=2 trade=1
recorded span : 2026-10-10T08:33:53.313Z .. 2026-10-10T08:34:25.889Z
book at T : best_bid=0.57 best_ask=0.58
trades around T : 3
series (span) : 5
-- the frames (each asserted: DOM before the shot, non-blank after) --
ok us1-01-terminal-list.png 100% full page
ok us1-02-list-counts.png 100% #events
ok us1-03-recorded-span.png 100% #span
ok us1-04-filter-narrows.png 100% #events
ok us2-01-event-detail-T.png 100% full page
ok us2-02-chart.png 100% #chart
ok us2-03-orderbook-at-T.png 100% #orderbook
ok us2-04-trades-around-T.png 100% #trades
ok us3-01-store-unreachable.png 100% full page
ok us3-02-empty-store.png 100% full page
-- field for field: the terminal against the store --
match [us1] the page is the product terminal, not the readable fallback class="terminal" present: true
match [us1] row is a store event [auc-sm-vic-2026-10-10-more] store has rows: order_book=2 trade=1
match [us1] list.order_book rows == store [auc-sm-vic-2026-10-10-more] terminal=2 store=2
match [us1] list.trade rows == store [auc-sm-vic-2026-10-10-more] terminal=1 store=1
match [us1] row is a store event [ja2-jub-con-2026-10-10-mor] store has rows: order_book=2 trade=1
match [us1] list.order_book rows == store [ja2-jub-con-2026-10-10-mor] terminal=2 store=2
match [us1] list.trade rows == store [ja2-jub-con-2026-10-10-mor] terminal=1 store=1
match [us1] row is a store event [val-nrg-loud-2026-10-10] store has rows: order_book=10 trade=5
match [us1] list.order_book rows == store [val-nrg-loud-2026-10-10] terminal=10 store=10
match [us1] list.trade rows == store [val-nrg-loud-2026-10-10] terminal=5 store=5
match [us1] row is a store event [cs2-dep5-vita-2026-10-10] store has rows: order_book=2 trade=1
match [us1] list.order_book rows == store [cs2-dep5-vita-2026-10-10] terminal=2 store=2
match [us1] list.trade rows == store [cs2-dep5-vita-2026-10-10] terminal=1 store=1
match [us1] us1-01-terminal-list.png painted non-white 100%
match [us1] us1-02-list-counts.png painted non-white 100%
match [us1] us1-03-recorded-span.png painted non-white 100%
match [us1] the terminal lists at least one event rows=4
match [us1] filter narrows to the named event terminal=["val-nrg-loud-2026-10-10"] want=[val-nrg-loud-2026-10-10]
match [us1] us1-04-filter-narrows.png painted non-white 100%
match [us2] the chosen event row is clickable slug=val-nrg-loud-2026-10-10
match [us2] the product's event answer names a token token=54766275672533937226534147411813036045778021882156237046712863902839579722640
match [us2] detail names the chosen event terminal=val-nrg-loud-2026-10-10 store=val-nrg-loud-2026-10-10
match [us2] detail names the moment T it printed (the store's span end) terminal=moment T: 2026-10-10T08:34:25.889Z · window: ±10s store=2026-10-10T08:34:25.889Z
match [us2] detail names the window it printed terminal=moment T: 2026-10-10T08:34:25.889Z · window: ±10s
match [us2] orderbook.at_T best_bid == store terminal=0.57 store=0.57
match [us2] orderbook.at_T best_ask == store terminal=0.58 store=0.58
match [us2] trades.around_T count == store terminal=3 store=3
match [us2] the chart drew a canvas canvas=614x212
match [us2] the chart canvas painted a series series-coloured pixels=1839 of 614x212
match [us2] the chart canvas is not a flat fill distinct colours=53
match [us2] chart series points == store trades over the span terminal=5 store=5
match [us2] us2-01-event-detail-T.png painted non-white 100%
match [us2] us2-02-chart.png painted non-white 100%
match [us2] us2-03-orderbook-at-T.png painted non-white 100%
match [us2] us2-04-trades-around-T.png painted non-white 100%
match [us3] us3-01-store-unreachable.png painted non-white 100%
match [us3] an empty store lists no event terminal rows=0
match [us3] us3-02-empty-store.png painted non-white 100%
user-verify-m4: OK
# store side — the store's own answers (ClickHouse, direct), read by the terminal's own reads #
select1 : 1
order_book rows (total) : 16
trade rows (total) : 8
-- per event (the store's own slug groups) --
auc-sm-vic-2026-10-10-more-markets order_book=2
cs2-dep5-vita-2026-10-10 order_book=2
ja2-jub-con-2026-10-10-more-markets order_book=2
val-nrg-loud-2026-10-10 order_book=10
auc-sm-vic-2026-10-10-more-markets trade=1
cs2-dep5-vita-2026-10-10 trade=1
ja2-jub-con-2026-10-10-more-markets trade=1
val-nrg-loud-2026-10-10 trade=5
-- the chosen event's book at T (the span end) --
best_bid=0.57 best_ask=0.58 at=1791621264661
-- trades for that token (the chart series) --
5
-- recorded span --
order_book: 1791621233313 .. 1791621265870
trade: 1791621233337 .. 1791621265889
ok us1-01-terminal-list.png 100% full page
ok us1-02-list-counts.png 100% #events
ok us1-03-recorded-span.png 100% #span
ok us1-04-filter-narrows.png 100% #events
ok us2-01-event-detail-T.png 100% full page
ok us2-02-chart.png 100% #chart
ok us2-03-orderbook-at-T.png 100% #orderbook
ok us2-04-trades-around-T.png 100% #trades
ok us3-01-store-unreachable.png 100% full page
ok us3-02-empty-store.png 100% full page
== M4 user verification: the product terminal beside the collection store, field for field ==
terminal : http://127.0.0.1:8899 (the Kotlin/JS + React bundle the product serves, driven in a real browser)
store : http://127.0.0.1:8123 / liveui
listed events : 4 (the terminal) / 4 (the store has rows for)
chosen event : val-nrg-loud-2026-10-10
chosen token : 54766275672533937226534147411813036045778021882156237046712863902839579722640
moment T : 2026-10-10T08:34:25.889Z
window : ±10s
fonts : /tmp/m4-fontconfig/fonts.conf
browser flags : --no-sandbox --disable-gpu --disable-dev-shm-usage
-- the store's own rows (read from ClickHouse, not from the DOM) --
cs2-dep5-vita-2026-10-10 order_book=2 trade=1
val-nrg-loud-2026-10-10 order_book=10 trade=5
ja2-jub-con-2026-10-10-more-markets order_book=2 trade=1
auc-sm-vic-2026-10-10-more-markets order_book=2 trade=1
recorded span : 2026-10-10T08:33:53.313Z .. 2026-10-10T08:34:25.889Z
book at T : best_bid=0.57 best_ask=0.58
trades around T : 3
series (span) : 5
-- the frames (each asserted: DOM before the shot, non-blank after) --
ok us1-01-terminal-list.png 100% full page
ok us1-02-list-counts.png 100% #events
ok us1-03-recorded-span.png 100% #span
ok us1-04-filter-narrows.png 100% #events
ok us2-01-event-detail-T.png 100% full page
ok us2-02-chart.png 100% #chart
ok us2-03-orderbook-at-T.png 100% #orderbook
ok us2-04-trades-around-T.png 100% #trades
ok us3-01-store-unreachable.png 100% full page
ok us3-02-empty-store.png 100% full page
-- field for field: the terminal against the store --
match [us1] the page is the product terminal, not the readable fallback class="terminal" present: true
match [us1] row is a store event [auc-sm-vic-2026-10-10-more] store has rows: order_book=2 trade=1
match [us1] list.order_book rows == store [auc-sm-vic-2026-10-10-more] terminal=2 store=2
match [us1] list.trade rows == store [auc-sm-vic-2026-10-10-more] terminal=1 store=1
match [us1] row is a store event [ja2-jub-con-2026-10-10-mor] store has rows: order_book=2 trade=1
match [us1] list.order_book rows == store [ja2-jub-con-2026-10-10-mor] terminal=2 store=2
match [us1] list.trade rows == store [ja2-jub-con-2026-10-10-mor] terminal=1 store=1
match [us1] row is a store event [val-nrg-loud-2026-10-10] store has rows: order_book=10 trade=5
match [us1] list.order_book rows == store [val-nrg-loud-2026-10-10] terminal=10 store=10
match [us1] list.trade rows == store [val-nrg-loud-2026-10-10] terminal=5 store=5
match [us1] row is a store event [cs2-dep5-vita-2026-10-10] store has rows: order_book=2 trade=1
match [us1] list.order_book rows == store [cs2-dep5-vita-2026-10-10] terminal=2 store=2
match [us1] list.trade rows == store [cs2-dep5-vita-2026-10-10] terminal=1 store=1
match [us1] us1-01-terminal-list.png painted non-white 100%
match [us1] us1-02-list-counts.png painted non-white 100%
match [us1] us1-03-recorded-span.png painted non-white 100%
match [us1] the terminal lists at least one event rows=4
match [us1] filter narrows to the named event terminal=["val-nrg-loud-2026-10-10"] want=[val-nrg-loud-2026-10-10]
match [us1] us1-04-filter-narrows.png painted non-white 100%
match [us2] the chosen event row is clickable slug=val-nrg-loud-2026-10-10
match [us2] the product's event answer names a token token=54766275672533937226534147411813036045778021882156237046712863902839579722640
match [us2] detail names the chosen event terminal=val-nrg-loud-2026-10-10 store=val-nrg-loud-2026-10-10
match [us2] detail names the moment T it printed (the store's span end) terminal=moment T: 2026-10-10T08:34:25.889Z · window: ±10s store=2026-10-10T08:34:25.889Z
match [us2] detail names the window it printed terminal=moment T: 2026-10-10T08:34:25.889Z · window: ±10s
match [us2] orderbook.at_T best_bid == store terminal=0.57 store=0.57
match [us2] orderbook.at_T best_ask == store terminal=0.58 store=0.58
match [us2] trades.around_T count == store terminal=3 store=3
match [us2] the chart drew a canvas canvas=614x212
match [us2] the chart canvas painted a series series-coloured pixels=1839 of 614x212
match [us2] the chart canvas is not a flat fill distinct colours=53
match [us2] chart series points == store trades over the span terminal=5 store=5
match [us2] us2-01-event-detail-T.png painted non-white 100%
match [us2] us2-02-chart.png painted non-white 100%
match [us2] us2-03-orderbook-at-T.png painted non-white 100%
match [us2] us2-04-trades-around-T.png painted non-white 100%
match [us3] us3-01-store-unreachable.png painted non-white 100%
match [us3] an empty store lists no event terminal rows=0
match [us3] us3-02-empty-store.png painted non-white 100%
user-verify-m4: OK
Reverse runs — raw output
reverse-a.txt
FAIL us1-01-terminal-list.png 2.1% full page missing: class="terminal"; missing: trading terminal; missing: store: up
ok us1-02-list-counts.png 19.72% #events
ok us1-03-recorded-span.png 10.01% #span
FAIL us1-04-filter-narrows.png 19.72% #events missing: events: 1/4
FAIL us2-01-event-detail-T.png 2.1% full page missing: Order book at the moment; missing: Trades around the moment; missing: best_bid; missing: best_ask; missing: moment T: 2026-10-10T08:34:25.889Z
FAIL us2-02-chart.png 0% #chart missing selector #chart; blank frame: 0.00% non-white
FAIL us2-03-orderbook-at-T.png 0% #orderbook missing: best_bid; missing: best_ask; missing selector #orderbook; blank frame: 0.00% non-white
FAIL us2-04-trades-around-T.png 0% #trades missing selector #trades; blank frame: 0.00% non-white
== M4 user verification: the product terminal beside the collection store, field for field ==
terminal : http://127.0.0.1:8890 (the Kotlin/JS + React bundle the product serves, driven in a real browser)
store : http://127.0.0.1:8123 / liveui
listed events : 4 (the terminal) / 4 (the store has rows for)
chosen event : val-nrg-loud-2026-10-10
chosen token : none
moment T : 2026-10-10T08:34:25.889Z
window : ±10s
fonts : /tmp/m4-fontconfig/fonts.conf
browser flags : --no-sandbox --disable-gpu --disable-dev-shm-usage
-- the store's own rows (read from ClickHouse, not from the DOM) --
cs2-dep5-vita-2026-10-10 order_book=2 trade=1
val-nrg-loud-2026-10-10 order_book=10 trade=5
ja2-jub-con-2026-10-10-more-markets order_book=2 trade=1
auc-sm-vic-2026-10-10-more-markets order_book=2 trade=1
recorded span : 2026-10-10T08:33:53.313Z .. 2026-10-10T08:34:25.889Z
book at T : best_bid=n/a best_ask=n/a
trades around T : 0
series (span) : 0
-- the frames (each asserted: DOM before the shot, non-blank after) --
FAIL us1-01-terminal-list.png 2.1% full page
ok us1-02-list-counts.png 19.72% #events
ok us1-03-recorded-span.png 10.01% #span
FAIL us1-04-filter-narrows.png 19.72% #events
FAIL us2-01-event-detail-T.png 2.1% full page
FAIL us2-02-chart.png 0% #chart
FAIL us2-03-orderbook-at-T.png 0% #orderbook
FAIL us2-04-trades-around-T.png 0% #trades
-- field for field: the terminal against the store --
MISMATCH [us1] the page is the product terminal, not the readable fallback class="terminal" present: false
match [us1] row is a store event [auc-sm-vic-2026-10-10-more] store has rows: order_book=2 trade=1
match [us1] list.order_book rows == store [auc-sm-vic-2026-10-10-more] terminal=2 store=2
match [us1] list.trade rows == store [auc-sm-vic-2026-10-10-more] terminal=1 store=1
match [us1] row is a store event [ja2-jub-con-2026-10-10-mor] store has rows: order_book=2 trade=1
match [us1] list.order_book rows == store [ja2-jub-con-2026-10-10-mor] terminal=2 store=2
match [us1] list.trade rows == store [ja2-jub-con-2026-10-10-mor] terminal=1 store=1
match [us1] row is a store event [val-nrg-loud-2026-10-10] store has rows: order_book=10 trade=5
match [us1] list.order_book rows == store [val-nrg-loud-2026-10-10] terminal=10 store=10
match [us1] list.trade rows == store [val-nrg-loud-2026-10-10] terminal=5 store=5
match [us1] row is a store event [cs2-dep5-vita-2026-10-10] store has rows: order_book=2 trade=1
match [us1] list.order_book rows == store [cs2-dep5-vita-2026-10-10] terminal=2 store=2
match [us1] list.trade rows == store [cs2-dep5-vita-2026-10-10] terminal=1 store=1
match [us1] us1-01-terminal-list.png painted non-white 2.1%
MISMATCH [us1] us1-01-terminal-list.png holds missing: class="terminal"
MISMATCH [us1] us1-01-terminal-list.png holds missing: trading terminal
MISMATCH [us1] us1-01-terminal-list.png holds missing: store: up
match [us1] us1-02-list-counts.png painted non-white 19.72%
match [us1] us1-03-recorded-span.png painted non-white 10.01%
match [us1] the terminal lists at least one event rows=4
MISMATCH [us1] filter narrows to the named event terminal=[null,"auc-sm-vic-2026-10-10-more-markets","ja2-jub-con-2026-10-10-more-markets","val-nrg-loud-2026-10-10","cs2-dep5-vita-2026-10-10"] want=[val-nrg-loud-2026-10-10]
match [us1] us1-04-filter-narrows.png painted non-white 19.72%
MISMATCH [us1] us1-04-filter-narrows.png holds missing: events: 1/4
match [us2] the chosen event row is clickable slug=val-nrg-loud-2026-10-10
MISMATCH [us2] the product's event answer names a token token=none
MISMATCH [us2] detail names the chosen event terminal=Collected events store=val-nrg-loud-2026-10-10
MISMATCH [us2] detail names the moment T it printed (the store's span end) terminal=MISSING store=2026-10-10T08:34:25.889Z
MISMATCH [us2] detail names the window it printed terminal=MISSING
MISMATCH [us2] orderbook.at_T best_bid == store terminal=undefined store=n/a
MISMATCH [us2] orderbook.at_T best_ask == store terminal=undefined store=n/a
match [us2] trades.around_T count == store terminal=0 store=0
MISMATCH [us2] the chart drew a canvas canvas=none
MISMATCH [us2] the chart canvas painted a series series-coloured pixels=n/a of ?
MISMATCH [us2] the chart canvas is not a flat fill distinct colours=n/a
MISMATCH [us2] chart series points == store trades over the span terminal=n/a store=0
match [us2] us2-01-event-detail-T.png painted non-white 2.1%
MISMATCH [us2] us2-01-event-detail-T.png holds missing: Order book at the moment
MISMATCH [us2] us2-01-event-detail-T.png holds missing: Trades around the moment
MISMATCH [us2] us2-01-event-detail-T.png holds missing: best_bid
MISMATCH [us2] us2-01-event-detail-T.png holds missing: best_ask
MISMATCH [us2] us2-01-event-detail-T.png holds missing: moment T: 2026-10-10T08:34:25.889Z
MISMATCH [us2] us2-02-chart.png painted non-white 0%
MISMATCH [us2] us2-02-chart.png holds missing selector #chart
MISMATCH [us2] us2-02-chart.png holds blank frame: 0.00% non-white
MISMATCH [us2] us2-03-orderbook-at-T.png painted non-white 0%
MISMATCH [us2] us2-03-orderbook-at-T.png holds missing: best_bid
MISMATCH [us2] us2-03-orderbook-at-T.png holds missing: best_ask
MISMATCH [us2] us2-03-orderbook-at-T.png holds missing selector #orderbook
MISMATCH [us2] us2-03-orderbook-at-T.png holds blank frame: 0.00% non-white
MISMATCH [us2] us2-04-trades-around-T.png painted non-white 0%
MISMATCH [us2] us2-04-trades-around-T.png holds missing selector #trades
MISMATCH [us2] us2-04-trades-around-T.png holds blank frame: 0.00% non-white
user-verify-m4: FAIL
reverse-b.txt
FAIL us1-01-terminal-list.png 100% full page missing: span: 2026-10-07T21:33:10.552Z .. 2026-10-07T21:36:09.447Z
ok us1-02-list-counts.png 100% #events
FAIL us1-03-recorded-span.png 100% #span missing: span: 2026-10-07T21:33:10.552Z .. 2026-10-07T21:36:09.447Z
ok us1-04-filter-narrows.png 100% #events
FAIL us2-01-event-detail-T.png 100% full page missing: moment T: 2026-10-07T21:36:09.447Z
ok us2-02-chart.png 100% #chart
ok us2-03-orderbook-at-T.png 100% #orderbook
ok us2-04-trades-around-T.png 100% #trades
== M4 user verification: the product terminal beside the collection store, field for field ==
terminal : http://127.0.0.1:8899 (the Kotlin/JS + React bundle the product serves, driven in a real browser)
store : http://127.0.0.1:8123 / default
listed events : 4 (the terminal) / 2 (the store has rows for)
chosen event : val-nrg-loud-2026-10-10
chosen token : 54766275672533937226534147411813036045778021882156237046712863902839579722640
moment T : 2026-10-07T21:36:09.447Z
window : ±10s
fonts : /tmp/m4-fontconfig/fonts.conf
browser flags : --no-sandbox --disable-gpu --disable-dev-shm-usage
-- the store's own rows (read from ClickHouse, not from the DOM) --
will-elise-stefanik-win-the-2028-republican-presid order_book=4 trade=1
will-shakhtar-donetsk-win-the-2026-27-uefa-champio order_book=360 trade=179
recorded span : 2026-10-07T21:33:10.552Z .. 2026-10-07T21:36:09.447Z
book at T : best_bid=n/a best_ask=n/a
trades around T : 0
series (span) : 0
-- the frames (each asserted: DOM before the shot, non-blank after) --
FAIL us1-01-terminal-list.png 100% full page
ok us1-02-list-counts.png 100% #events
FAIL us1-03-recorded-span.png 100% #span
ok us1-04-filter-narrows.png 100% #events
FAIL us2-01-event-detail-T.png 100% full page
ok us2-02-chart.png 100% #chart
ok us2-03-orderbook-at-T.png 100% #orderbook
ok us2-04-trades-around-T.png 100% #trades
-- field for field: the terminal against the store --
match [us1] the page is the product terminal, not the readable fallback class="terminal" present: true
MISMATCH [us1] row is a store event [auc-sm-vic-2026-10-10-more] store has rows: order_book=0 trade=0
MISMATCH [us1] list.order_book rows == store [auc-sm-vic-2026-10-10-more] terminal=2 store=absent
MISMATCH [us1] list.trade rows == store [auc-sm-vic-2026-10-10-more] terminal=1 store=absent
MISMATCH [us1] row is a store event [ja2-jub-con-2026-10-10-mor] store has rows: order_book=0 trade=0
MISMATCH [us1] list.order_book rows == store [ja2-jub-con-2026-10-10-mor] terminal=2 store=absent
MISMATCH [us1] list.trade rows == store [ja2-jub-con-2026-10-10-mor] terminal=1 store=absent
MISMATCH [us1] row is a store event [val-nrg-loud-2026-10-10] store has rows: order_book=0 trade=0
MISMATCH [us1] list.order_book rows == store [val-nrg-loud-2026-10-10] terminal=10 store=absent
MISMATCH [us1] list.trade rows == store [val-nrg-loud-2026-10-10] terminal=5 store=absent
MISMATCH [us1] row is a store event [cs2-dep5-vita-2026-10-10] store has rows: order_book=0 trade=0
MISMATCH [us1] list.order_book rows == store [cs2-dep5-vita-2026-10-10] terminal=2 store=absent
MISMATCH [us1] list.trade rows == store [cs2-dep5-vita-2026-10-10] terminal=1 store=absent
match [us1] us1-01-terminal-list.png painted non-white 100%
MISMATCH [us1] us1-01-terminal-list.png holds missing: span: 2026-10-07T21:33:10.552Z .. 2026-10-07T21:36:09.447Z
match [us1] us1-02-list-counts.png painted non-white 100%
match [us1] us1-03-recorded-span.png painted non-white 100%
MISMATCH [us1] us1-03-recorded-span.png holds missing: span: 2026-10-07T21:33:10.552Z .. 2026-10-07T21:36:09.447Z
match [us1] the terminal lists at least one event rows=4
match [us1] filter narrows to the named event terminal=["val-nrg-loud-2026-10-10"] want=[val-nrg-loud-2026-10-10]
match [us1] us1-04-filter-narrows.png painted non-white 100%
match [us2] the chosen event row is clickable slug=val-nrg-loud-2026-10-10
match [us2] the product's event answer names a token token=54766275672533937226534147411813036045778021882156237046712863902839579722640
match [us2] detail names the chosen event terminal=val-nrg-loud-2026-10-10 store=val-nrg-loud-2026-10-10
MISMATCH [us2] detail names the moment T it printed (the store's span end) terminal=moment T: 2026-10-10T08:34:25.889Z · window: ±10s store=2026-10-07T21:36:09.447Z
match [us2] detail names the window it printed terminal=moment T: 2026-10-10T08:34:25.889Z · window: ±10s
MISMATCH [us2] orderbook.at_T best_bid == store terminal=0.57 store=n/a
MISMATCH [us2] orderbook.at_T best_ask == store terminal=0.58 store=n/a
MISMATCH [us2] trades.around_T count == store terminal=3 store=0
match [us2] the chart drew a canvas canvas=614x212
match [us2] the chart canvas painted a series series-coloured pixels=1839 of 614x212
match [us2] the chart canvas is not a flat fill distinct colours=53
MISMATCH [us2] chart series points == store trades over the span terminal=5 store=0
match [us2] us2-01-event-detail-T.png painted non-white 100%
MISMATCH [us2] us2-01-event-detail-T.png holds missing: moment T: 2026-10-07T21:36:09.447Z
match [us2] us2-02-chart.png painted non-white 100%
match [us2] us2-03-orderbook-at-T.png painted non-white 100%
match [us2] us2-04-trades-around-T.png painted non-white 100%
user-verify-m4: FAIL
reverse-c.txt
FAIL us1-01-terminal-list.png 100% full page missing: Collected events; missing: store: up; missing: span: 2026-10-10T08:33:53.313Z .. 2026-10-10T08:34:25.889Z
FAIL us1-02-list-counts.png 0% #events missing: events: 0/0; missing selector #events; blank frame: 0.00% non-white
FAIL us1-03-recorded-span.png 0% #span missing: span: 2026-10-10T08:33:53.313Z .. 2026-10-10T08:34:25.889Z; missing selector #span; blank frame: 0.00% non-white
== M4 user verification: the product terminal beside the collection store, field for field ==
terminal : http://127.0.0.1:8898 (the Kotlin/JS + React bundle the product serves, driven in a real browser)
store : http://127.0.0.1:8123 / liveui
listed events : 0 (the terminal) / 4 (the store has rows for)
chosen event : null
chosen token : none
moment T : 2026-10-10T08:34:25.889Z
window : ±10s
fonts : /tmp/m4-fontconfig/fonts.conf
browser flags : --no-sandbox --disable-gpu --disable-dev-shm-usage
-- the store's own rows (read from ClickHouse, not from the DOM) --
cs2-dep5-vita-2026-10-10 order_book=2 trade=1
val-nrg-loud-2026-10-10 order_book=10 trade=5
ja2-jub-con-2026-10-10-more-markets order_book=2 trade=1
auc-sm-vic-2026-10-10-more-markets order_book=2 trade=1
recorded span : 2026-10-10T08:33:53.313Z .. 2026-10-10T08:34:25.889Z
book at T : best_bid=n/a best_ask=n/a
trades around T : 0
series (span) : 0
-- the frames (each asserted: DOM before the shot, non-blank after) --
FAIL us1-01-terminal-list.png 100% full page
FAIL us1-02-list-counts.png 0% #events
FAIL us1-03-recorded-span.png 0% #span
-- field for field: the terminal against the store --
match [us1] the page is the product terminal, not the readable fallback class="terminal" present: true
match [us1] us1-01-terminal-list.png painted non-white 100%
MISMATCH [us1] us1-01-terminal-list.png holds missing: Collected events
MISMATCH [us1] us1-01-terminal-list.png holds missing: store: up
MISMATCH [us1] us1-01-terminal-list.png holds missing: span: 2026-10-10T08:33:53.313Z .. 2026-10-10T08:34:25.889Z
MISMATCH [us1] us1-02-list-counts.png painted non-white 0%
MISMATCH [us1] us1-02-list-counts.png holds missing: events: 0/0
MISMATCH [us1] us1-02-list-counts.png holds missing selector #events
MISMATCH [us1] us1-02-list-counts.png holds blank frame: 0.00% non-white
MISMATCH [us1] us1-03-recorded-span.png painted non-white 0%
MISMATCH [us1] us1-03-recorded-span.png holds missing: span: 2026-10-10T08:33:53.313Z .. 2026-10-10T08:34:25.889Z
MISMATCH [us1] us1-03-recorded-span.png holds missing selector #span
MISMATCH [us1] us1-03-recorded-span.png holds blank frame: 0.00% non-white
MISMATCH [us1] the terminal lists at least one event rows=0
user-verify-m4: FAIL
Layers
SBHOST — this run: the terminal (the Kotlin/JS + React bundle) served by the real binary against a live ClickHouse store, driven by a real headless chromium; the frames are the layer's own.
CIHOST (the podman layer) — the image's build and run: open, blocked by the engine-client gap (preinstall#114); the yc lens the M3 record names is deferred (polymarket#11).
What this run does not cover
The container image's own build and run — the podman layer carries no engine client (have podman MISSING, preinstall#114).
The milestone's CIHOST (yc VM) run — deferred, blocked by the YC quota (polymarket#11).
This page is a report of the run's raw evidence, not the product itself.