M4 — the product UI 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/.
What was done, in short
- The product has a UI over the store.
ProductUi (src/main/kotlin/com/polymarket/ui/) serves the collected events each with its markets, tokens, order-book and trade counts and the recorded span, and a filter; and per event, the order book at a chosen moment T and the trades in a window around it.
- A dead store is reported, not hidden. Where the read fails the UI answers with an explicit store unreachable page — an empty list and a dead store are different facts.
- The product ships as one container image.
Containerfile is multistage: the Kotlin distribution, a JRE, and the pinned ClickHouse 26.9.12.8. The snap is retired (owner's decision, 2026-10-09).
- The UI was captured and checked, on a real browser, against the store's own rows. 14 frames, all matching,
user-verify-m4: OK.
- 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.
User story 1 — "I open the product and see what has been collected"
User story 2 — "I pick an event and see what the book and the trades looked like at a moment"
User story 3 — "I can trust what the UI shows me"
Goal → case → what was observed → result
| Goal | The case | What the run observed | Result |
| The UI serves the collected events with their counts and the recorded span, and narrows the list by a filter | the UI's list beside a query of the store for the same events and counts | frames 1–4; list.order_book rows == store (360=360), list.trade rows == store (179=179); the filter narrows 2→1 | OK, captured and checked |
| For a chosen event the UI shows the order book at a moment T and the trades around T, read from the store | the UI's detail at T beside the store's own book at T | frames 5–10; best_bid 0.001=0.001, best_ask 0.002=0.002, trades around T 20=20 | OK, captured and checked |
| The UI reports its store honestly — a dead store is not an empty success | the UI against a dead store, an empty store, and an event with no rows | frames 11–14, 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 scripts and the same store as the green run.
| Reverse run | What was broken | What went red |
| reverse-a | the layer pointed at a different UI (the empty store) while the store is full | frames miss the Shakhtar event, both counts and the span; no book — user-verify-m4: FAIL |
| reverse-b | the browser driver is absent from the environment | the layer refuses to run: playwright not found — the browser driver belongs to the image (#105) |
| reverse-c | the store is not reachable | cannot compare: the collection store is not reachable at http://127.0.0.1:9999 |
| reverse-d | the UI serves a blank document | every DOM assert reddens and each capture is 0.00% non-white — a blank capture is not evidence |
Layer — the run's own record
Full m4-user-verify.txt (raw file):
== M4 user verification: the product UI beside the collection store, field for field ==
ui : http://127.0.0.1:8899
store : http://127.0.0.1:8123
event : will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578
token : 33339798372916037220786136133406478491116150628220441951753426683441228279665
moment T : 2026-10-07T21:34:39.828Z
window : ±10s
fonts : /tmp/m4-fontconfig/fonts.conf
browser flags : --no-sandbox --disable-gpu
-- the store's own rows (read from ClickHouse, not from the page) --
order_book rows (event token set) : 360
trade rows (event token set) : 179
recorded span : 2026-10-07T21:33:10.552Z .. 2026-10-07T21:36:09.447Z
book at T : best_bid=0.001 best_ask=0.002 @ 2026-10-07T21:34:39.828Z
trades around T : 20
-- the frames (each asserted: DOM before the shot, non-blank after) --
ok us1-01-list.png 2.94% full page http://127.0.0.1:8899/
ok us1-02-list-counts.png 14.77% #events http://127.0.0.1:8899/
ok us1-03-span.png 11.24% #span http://127.0.0.1:8899/
ok us1-04-filter-narrows.png 1.99% full page http://127.0.0.1:8899/?filter=stefanik
ok us2-01-detail-T.png 7.66% full page http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408879828
ok us2-02-orderbook-at-T.png 24.86% #orderbook http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408879828
ok us2-03-trades-around-T.png 25.28% #trades http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408879828
ok us2-04-detail-default-moment.png 6.8% full page http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578
ok us2-05-detail-second-moment.png 6.8% full page http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408969447
ok us2-06-detail-first-instant.png 6.55% full page http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408790552
ok us3-01-store-unreachable.png 0.66% full page http://127.0.0.1:8898/
ok us3-02-empty-store.png 1.1% full page http://127.0.0.1:8897/
ok us3-03-empty-store-detail.png 3.83% full page http://127.0.0.1:8897/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408879828
ok us3-04-event-with-no-rows.png 3.17% full page http://127.0.0.1:8899/?event=will-bernie-sanders-win-the-2028-democratic-presidential-nomination-879&moment=1791408879828
-- field for field: the UI against the store --
match [us1] us1-01-list.png painted non-white 2.94%
match [us1] us1-02-list-counts.png painted non-white 14.77%
match [us1] list.order_book rows == store UI=360 store=360
match [us1] list.trade rows == store UI=179 store=179
match [us1] us1-03-span.png painted non-white 11.24%
match [us1] us1-04-filter-narrows.png painted non-white 1.99%
match [us2] us2-01-detail-T.png painted non-white 7.66%
match [us2] orderbook.at_T best_bid == store UI=0.001 store=0.001
match [us2] orderbook.at_T best_ask == store UI=0.002 store=0.002
match [us2] trades.around_T count == store UI=20 store=20
match [us2] us2-02-orderbook-at-T.png painted non-white 24.86%
match [us2] us2-03-trades-around-T.png painted non-white 25.28%
match [us2] us2-04-detail-default-moment.png painted non-white 6.8%
match [us2] us2-05-detail-second-moment.png painted non-white 6.8%
match [us2] us2-06-detail-first-instant.png painted non-white 6.55%
match [us3] us3-01-store-unreachable.png painted non-white 0.66%
match [us3] us3-02-empty-store.png painted non-white 1.1%
match [us3] us3-03-empty-store-detail.png painted non-white 3.83%
match [us3] us3-04-event-with-no-rows.png painted non-white 3.17%
user-verify-m4: OK
The store's own answers
raw file:
# store side — the store's own answers (ClickHouse 24.8, direct), read by the UI's own reads #
select1 : 1
order_book rows (total) : 364
trade rows (total) : 180
span (min..max) : 1791408790552..1791408969419
-- per event (UI token sets) --
will-shakhtar-done order_book=360 trade=179
will-elise-stefani order_book=4 trade=1
-- the main event's book at T=1791408879828 --
best_bid=0.001 best_ask=0.002 at=1791408879828
trades around T (±10s)=20
-- empty store (schema only, no rows): emptyui --
emptyui.order_book=0 emptyui.trade=0
The raw console log
The layer's console output (raw file):
ok us1-01-list.png 2.94% full page
ok us1-02-list-counts.png 14.77% #events
ok us1-03-span.png 11.24% #span
ok us1-04-filter-narrows.png 1.99% full page
ok us2-01-detail-T.png 7.66% full page
ok us2-02-orderbook-at-T.png 24.86% #orderbook
ok us2-03-trades-around-T.png 25.28% #trades
ok us2-04-detail-default-moment.png 6.8% full page
ok us2-05-detail-second-moment.png 6.8% full page
ok us2-06-detail-first-instant.png 6.55% full page
ok us3-01-store-unreachable.png 0.66% full page
ok us3-02-empty-store.png 1.1% full page
ok us3-03-empty-store-detail.png 3.83% full page
ok us3-04-event-with-no-rows.png 3.17% full page
== M4 user verification: the product UI beside the collection store, field for field ==
ui : http://127.0.0.1:8899
store : http://127.0.0.1:8123
event : will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578
token : 33339798372916037220786136133406478491116150628220441951753426683441228279665
moment T : 2026-10-07T21:34:39.828Z
window : ±10s
fonts : /tmp/m4-fontconfig/fonts.conf
browser flags : --no-sandbox --disable-gpu
-- the store's own rows (read from ClickHouse, not from the page) --
order_book rows (event token set) : 360
trade rows (event token set) : 179
recorded span : 2026-10-07T21:33:10.552Z .. 2026-10-07T21:36:09.447Z
book at T : best_bid=0.001 best_ask=0.002 @ 2026-10-07T21:34:39.828Z
trades around T : 20
-- the frames (each asserted: DOM before the shot, non-blank after) --
ok us1-01-list.png 2.94% full page http://127.0.0.1:8899/
ok us1-02-list-counts.png 14.77% #events http://127.0.0.1:8899/
ok us1-03-span.png 11.24% #span http://127.0.0.1:8899/
ok us1-04-filter-narrows.png 1.99% full page http://127.0.0.1:8899/?filter=stefanik
ok us2-01-detail-T.png 7.66% full page http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408879828
ok us2-02-orderbook-at-T.png 24.86% #orderbook http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408879828
ok us2-03-trades-around-T.png 25.28% #trades http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408879828
ok us2-04-detail-default-moment.png 6.8% full page http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578
ok us2-05-detail-second-moment.png 6.8% full page http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408969447
ok us2-06-detail-first-instant.png 6.55% full page http://127.0.0.1:8899/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408790552
ok us3-01-store-unreachable.png 0.66% full page http://127.0.0.1:8898/
ok us3-02-empty-store.png 1.1% full page http://127.0.0.1:8897/
ok us3-03-empty-store-detail.png 3.83% full page http://127.0.0.1:8897/?event=will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578&moment=1791408879828
ok us3-04-event-with-no-rows.png 3.17% full page http://127.0.0.1:8899/?event=will-bernie-sanders-win-the-2028-democratic-presidential-nomination-879&moment=1791408879828
-- field for field: the UI against the store --
match [us1] us1-01-list.png painted non-white 2.94%
match [us1] us1-02-list-counts.png painted non-white 14.77%
match [us1] list.order_book rows == store UI=360 store=360
match [us1] list.trade rows == store UI=179 store=179
match [us1] us1-03-span.png painted non-white 11.24%
match [us1] us1-04-filter-narrows.png painted non-white 1.99%
match [us2] us2-01-detail-T.png painted non-white 7.66%
match [us2] orderbook.at_T best_bid == store UI=0.001 store=0.001
match [us2] orderbook.at_T best_ask == store UI=0.002 store=0.002
match [us2] trades.around_T count == store UI=20 store=20
match [us2] us2-02-orderbook-at-T.png painted non-white 24.86%
match [us2] us2-03-trades-around-T.png painted non-white 25.28%
match [us2] us2-04-detail-default-moment.png painted non-white 6.8%
match [us2] us2-05-detail-second-moment.png painted non-white 6.8%
match [us2] us2-06-detail-first-instant.png painted non-white 6.55%
match [us3] us3-01-store-unreachable.png painted non-white 0.66%
match [us3] us3-02-empty-store.png painted non-white 1.1%
match [us3] us3-03-empty-store-detail.png painted non-white 3.83%
match [us3] us3-04-event-with-no-rows.png painted non-white 3.17%
user-verify-m4: OK
Reverse runs — raw output
reverse-a.txt
FAIL us1-01-list.png 1.1% full page missing: recorded span: 2026-10-07T21:33:10.552Z .. 2026-10-07T21:36:09.447Z; missing: will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578
FAIL us1-02-list-counts.png 32.54% #events missing: 360; missing: 179
FAIL us1-03-span.png 4.25% #span missing: recorded span: 2026-10-07T21:33:10.552Z .. 2026-10-07T21:36:09.447Z
FAIL us1-04-filter-narrows.png 1.09% full page missing: events: 1/
ok us2-01-detail-T.png 3.83% full page
user-verify-m4: FAIL — page.$eval: Failed to find element matching selector "#orderbook .best_bid"
reverse-b.txt
user-verify-m4: FAIL — playwright not found — the browser driver belongs to the image (mihail812-org/preinstall#105), not to this script; set PLAYWRIGHT_HOME to its install root
reverse-c.txt
user-verify-m4: FAIL — the collection store is not reachable at http://127.0.0.1:9999 — start it (deploy) first
reverse-d.txt
FAIL us1-01-list.png 0% full page missing: Collected events; missing: order_book rows; missing: trade rows; missing: recorded span: 2026-10-07T21:33:10.552Z .. 2026-10-07T21:36:09.447Z; missing: will-shakhtar-donetsk-win-the-2026-27-uefa-champions-league-championship-20260701202025578; blank frame: 0.00% non-white
FAIL us1-02-list-counts.png 0% #events missing: 360; missing: 179; missing selector #events; blank frame: 0.00% non-white
FAIL us1-03-span.png 0% #span missing: recorded span: 2026-10-07T21:33:10.552Z .. 2026-10-07T21:36:09.447Z; missing selector #span; blank frame: 0.00% non-white
FAIL us1-04-filter-narrows.png 0% full page missing: events: 1/; blank frame: 0.00% non-white
FAIL us2-01-detail-T.png 0% full page missing: Order book at a moment; missing: best_bid; missing: best_ask; missing: Trades around the moment; missing: moment T: 2026-10-07T21:34:39.828Z; missing: ±10.0s; blank frame: 0.00% non-white
user-verify-m4: FAIL — page.$eval: Failed to find element matching selector "#orderbook .best_bid"
Layers
SBHOST — this run: the product UI served by the real binary against a live ClickHouse store, the committed fixture loaded into it, 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).
- It is not a hosted-site deliverable: this page is a report of the run's raw evidence, not the product.
Links