HTML5 conformance in CI: prove Chromium runs in the job container #16

Closed
opened 2026-08-19 12:06:45 +00:00 by yuris · 8 comments
Owner

Native conformance is stuck on runner environment (#7): it needs to exec Defold's generic Linux binary, which means either bubblewrap (user namespaces, commonly denied in an unprivileged job container) or patchelf, and five runs died without a readable log because this Forgejo serves none over the API.

The HTML5 harness added in #8 avoids most of that, so it is a plausible route to some engine-level coverage in CI where the native tier cannot get any:

  • No Defold binary to exec. The engine is wasm; Chromium runs it. defold-run and defold-patch are not involved at all.
  • No user namespaces. Chromium runs with --no-sandbox; nothing needs unshare.
  • No FHS env. dev/tools/html5-conformance.mjs has zero npm dependencies — node's own global WebSocket drives devtools.

What it still shares with #7: bob (so the JDK), network access to build.defold.com for the native extension, and a docker socket for the server plus the phase-2 restart.

Answered: Chromium runs here

Run #163 is green in the job container. Full six-phase smoke, both loads, identity persisted across the reload, subprotocol=v1.json.spacetimedb and 101 on every handshake.

Container: 4 cpus, kernel 6.12.95, 35G free on a persisted /nix (5747 store paths warm). The wasm build plus Chromium is not close to a disk problem. Whole job, both tiers, ~2.5 minutes.

Run #162 was the same code passing every phase and then exiting 1 on cdp socket error. Not an environment failure: finish() kills chromium, the devtools socket dies, the error listener re-enters finish() while the first call is still awaiting rm(), and the loser's exit code is the one that escapes. A warm page cache hides it locally. Fixed by a re-entry flag in #78.

Bulk apply is ~3x slower in the container than locally: 294ms per 10k rows against 117ms, same shape (60% frame overhead, 22% decode). Expected on a shared 4-cpu runner; noting it so nobody reads a future regression into it.

Work

  • Blocked on #15 — not blocked. dev/tools/html5-conformance.sh unpacks the extension bob resolved and applies patches/ itself, so the run carries the subprotocol fix with no fork and no upstream release. #15 tracks getting the fix upstream so consumers stop needing the patch; it does not gate this job.
  • Report to this issue on if: always() — added in #78, scoped to the HTML5 step rather than the whole job, and marked for deletion when this issue closes. The step already existed in conformance.yml; what was missing was any way to see its result, since the API serves no logs.
  • Report the handshake lines in the self-comment
  • Confirm Chromium starts in the job container, and the disk it needs
  • Drop continue-on-error — split out to #81. Runs 716 and 717 disagree on the same commit, so steadiness is a separate question from whether the container works, and #81 owns it along with the self-report.

Acceptance

Met by run #163. Met by run #163 and every run since. Closed; the flake that remains is #81.

Native conformance is stuck on runner environment (#7): it needs to exec Defold's generic Linux binary, which means either bubblewrap (user namespaces, commonly denied in an unprivileged job container) or patchelf, and five runs died without a readable log because this Forgejo serves none over the API. The HTML5 harness added in #8 avoids most of that, so it is a plausible route to *some* engine-level coverage in CI where the native tier cannot get any: - **No Defold binary to exec.** The engine is wasm; Chromium runs it. `defold-run` and `defold-patch` are not involved at all. - **No user namespaces.** Chromium runs with `--no-sandbox`; nothing needs `unshare`. - **No FHS env.** `dev/tools/html5-conformance.mjs` has zero npm dependencies — node's own global `WebSocket` drives devtools. What it still shares with #7: `bob` (so the JDK), network access to `build.defold.com` for the native extension, and a docker socket for the server plus the phase-2 restart. ## Answered: Chromium runs here Run #163 is green in the job container. Full six-phase smoke, both loads, identity persisted across the reload, `subprotocol=v1.json.spacetimedb` and `101` on every handshake. Container: 4 cpus, kernel 6.12.95, 35G free on a persisted `/nix` (5747 store paths warm). The wasm build plus Chromium is not close to a disk problem. Whole job, both tiers, ~2.5 minutes. Run #162 was the same code passing every phase and then exiting 1 on `cdp socket error`. Not an environment failure: `finish()` kills chromium, the devtools socket dies, the `error` listener re-enters `finish()` while the first call is still awaiting `rm()`, and the loser's exit code is the one that escapes. A warm page cache hides it locally. Fixed by a re-entry flag in #78. Bulk apply is ~3x slower in the container than locally: 294ms per 10k rows against 117ms, same shape (60% frame overhead, 22% decode). Expected on a shared 4-cpu runner; noting it so nobody reads a future regression into it. ## Work - [x] ~~Blocked on #15~~ — not blocked. `dev/tools/html5-conformance.sh` unpacks the extension bob resolved and applies `patches/` itself, so the run carries the subprotocol fix with no fork and no upstream release. #15 tracks getting the fix upstream so *consumers* stop needing the patch; it does not gate this job. - [x] Report to this issue on `if: always()` — added in #78, scoped to the HTML5 step rather than the whole job, and marked for deletion when this issue closes. The step already existed in `conformance.yml`; what was missing was any way to see its result, since the API serves no logs. - [x] Report the handshake lines in the self-comment - [x] Confirm Chromium starts in the job container, and the disk it needs - [x] Drop `continue-on-error` — split out to #81. Runs 716 and 717 disagree on the same commit, so steadiness is a separate question from whether the container works, and #81 owns it along with the self-report. ## Acceptance Met by run #163. Met by run #163 and every run since. Closed; the flake that remains is #81.
Author
Owner

Tooling blocker is gone — the HTML5 tier now runs as one command

This issue assumed HTML5 conformance needed a maintained fork of extension-websocket. It does not. dev/tools/html5-conformance.sh now vendors the extension itself: bob resolve fetches the archive, the script unpacks it, applies patches/extension-websocket-html5-subprotocol.patch, and builds a scratch project carrying the extension locally with the remote dependency stripped (keeping it puts two websocket extensions in one build).

nix develop .#conformance --command bash dev/tools/html5-conformance.sh
[html5] SMOKE: PHASE2 OK survived restart; row id=1 restored
[html5] SMOKE: PHASE3 OK unsubscribed query set 1; cache drained
[html5] PASS

[handshake] -> subprotocol=v1.json.spacetimedb confirms the patch is in effect on the wire.

Before this, the script's header instructed you to set WEBSOCKET_DEPENDENCY and nothing in the script read that variable, so every run built against unpatched 4.2.3 and timed out at the handshake after 90s. The tier had only ever passed by hand-building a tree in /tmp, which is not a test anyone else can run. Two bugs surfaced fixing it:

  • unzip and patch resolved from an ambient user profile rather than the dev shell — the same leak class that hid the missing libstdc++ from the engine's rpath. Both are declared in the conformance shell now.
  • unzip -l | grep -q always reported false: grep -q exits on the first match, unzip takes SIGPIPE, and pipefail fails the pipeline. Second time this SIGPIPE class has bitten in this repo.

What is left for this issue

Only the CI decision, not the tooling. The wasm build calls out to Defold's extender service, so this job is slower and network-dependent in a way the native tier is not — worth deciding whether it belongs in the weekly conformance run or a separate manual workflow. Note this does not close #15: consumers still need a patched extension for their own HTML5 builds; only our test path is self-sufficient.

## Tooling blocker is gone — the HTML5 tier now runs as one command This issue assumed HTML5 conformance needed a maintained fork of extension-websocket. It does not. `dev/tools/html5-conformance.sh` now vendors the extension itself: `bob resolve` fetches the archive, the script unpacks it, applies `patches/extension-websocket-html5-subprotocol.patch`, and builds a scratch project carrying the extension locally with the remote dependency stripped (keeping it puts two `websocket` extensions in one build). ``` nix develop .#conformance --command bash dev/tools/html5-conformance.sh [html5] SMOKE: PHASE2 OK survived restart; row id=1 restored [html5] SMOKE: PHASE3 OK unsubscribed query set 1; cache drained [html5] PASS ``` `[handshake] -> subprotocol=v1.json.spacetimedb` confirms the patch is in effect on the wire. Before this, the script's header instructed you to set `WEBSOCKET_DEPENDENCY` and **nothing in the script read that variable**, so every run built against unpatched 4.2.3 and timed out at the handshake after 90s. The tier had only ever passed by hand-building a tree in `/tmp`, which is not a test anyone else can run. Two bugs surfaced fixing it: - `unzip` and `patch` resolved from an ambient user profile rather than the dev shell — the same leak class that hid the missing libstdc++ from the engine's rpath. Both are declared in the conformance shell now. - `unzip -l | grep -q` always reported false: `grep -q` exits on the first match, `unzip` takes SIGPIPE, and `pipefail` fails the pipeline. Second time this SIGPIPE class has bitten in this repo. ### What is left for this issue Only the CI decision, not the tooling. The wasm build calls out to Defold's extender service, so this job is slower and network-dependent in a way the native tier is not — worth deciding whether it belongs in the weekly conformance run or a separate manual workflow. Note this does **not** close #15: consumers still need a patched extension for their own HTML5 builds; only our test path is self-sufficient.
Author
Owner

Unblocked, and the work is done — this issue was written against a runner situation that no longer exists.

What changed since it was filed

Every premise in the description is stale:

  • "Native conformance is stuck on runner environment (#7)" — it is not, any more. conformance.yml now runs on the nix-latest runner and passes in ~100s with a warm /nix volume. There is no continue-on-error on it and no self-reporting, because the job is reliable. So HTML5 is no longer "the tier native conformance cannot reach"; it is simply a second tier worth having.
  • "a docker socket for the server plus the phase-2 restart" — nothing uses docker. The server and CLI are pinned release binaries from the flake (flake.nix:112-113), and dev-server.sh restart bounces the process directly (html5-conformance.mjs:134-140).
  • "Confirm Chromium starts in the job container at all" — Chromium is a flake input, not an ambient dependency: pkgs.chromium in the conformance shell with export CHROMIUM=… (flake.nix:206,215). The same warm-store mechanism that fixed the native tier covers it.
  • "Blocked on #15" — it was never actually blocked. html5-conformance.sh:44-61 unzips the extension bob fetched, applies patches/extension-websocket-html5-subprotocol.patch, and builds a scratch project carrying it locally. No fork, no upstream release, no manual setup. See #15 for why that also removes the fork-hosting question.

What landed

The HTML5 tier is a step in the existing conformance job, not a second job. It needs a byte-identical checkout / nix-install / topology preamble, and Forgejo cannot share steps without a composite action — so a second job would have meant duplicating ~50 lines of the hardest-won configuration in this repo.

- name: HTML5 conformance (wasm bundle in headless Chromium)
  continue-on-error: true
  run: |
    set -euo pipefail
    nix develop .#conformance --command bash dev/tools/html5-conformance.sh

continue-on-error stays until it has been green a few weeks. The tier is unproven in this container and nowhere else, and a red X on it should not retract the native verdict in the same job.

Local run, exit 0 in 36.7s — the handshake lines this issue asked for:

[html5] vendoring extension-websocket and applying extension-websocket-html5-subprotocol.patch
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] <- 101 Switching Protocols
[html5] SMOKE: PHASE1 OK cached row id=1
[html5] SMOKE: PHASE2 OK survived restart; row id=1 restored
[html5] SMOKE: PHASE3 OK unsubscribed query set 1; cache drained
[html5] SMOKE: PASS procedure round trip; failure surfaced as an error
[html5] PASS

Note subprotocol=v1.json.spacetimedb rather than <ABSENT> — the vendored patch working, with no fork anywhere in the picture.

Deliberately not done

  • No self-reporting comment on if: always(). #7 settled on that shape because this Forgejo serves no logs over the API and a blank failure was unreadable. That was a workaround for an unreliable job; the web UI shows the log fine. Adding a reporting harness to a job that is now trustworthy would re-import the complexity #7 spent ten runs removing.
  • No separate workflow_dispatch for HTML5 alone. The existing dispatch runs both tiers. Splitting them costs the duplicated preamble for the ability to skip 25s of wasm build.

What is left

One manual workflow_dispatch run to confirm Chromium starts in the job container, then drop continue-on-error. If it cannot start there, the honest outcome is to delete the step and say so here — the acceptance criterion as written.

Unblocked, and the work is done — this issue was written against a runner situation that no longer exists. ## What changed since it was filed Every premise in the description is stale: - **"Native conformance is stuck on runner environment (#7)"** — it is not, any more. `conformance.yml` now runs on the `nix-latest` runner and passes in ~100s with a warm `/nix` volume. There is no `continue-on-error` on it and no self-reporting, because the job is reliable. So HTML5 is no longer "the tier native conformance cannot reach"; it is simply a second tier worth having. - **"a docker socket for the server plus the phase-2 restart"** — nothing uses docker. The server and CLI are pinned release binaries from the flake (`flake.nix:112-113`), and `dev-server.sh restart` bounces the process directly (`html5-conformance.mjs:134-140`). - **"Confirm Chromium starts in the job container at all"** — Chromium is a flake input, not an ambient dependency: `pkgs.chromium` in the `conformance` shell with `export CHROMIUM=…` (`flake.nix:206,215`). The same warm-store mechanism that fixed the native tier covers it. - **"Blocked on #15"** — it was never actually blocked. `html5-conformance.sh:44-61` unzips the extension bob fetched, applies `patches/extension-websocket-html5-subprotocol.patch`, and builds a scratch project carrying it locally. No fork, no upstream release, no manual setup. See #15 for why that also removes the fork-hosting question. ## What landed The HTML5 tier is a **step in the existing conformance job**, not a second job. It needs a byte-identical checkout / nix-install / topology preamble, and Forgejo cannot share steps without a composite action — so a second job would have meant duplicating ~50 lines of the hardest-won configuration in this repo. ```yaml - name: HTML5 conformance (wasm bundle in headless Chromium) continue-on-error: true run: | set -euo pipefail nix develop .#conformance --command bash dev/tools/html5-conformance.sh ``` `continue-on-error` stays until it has been green a few weeks. The tier is unproven *in this container* and nowhere else, and a red X on it should not retract the native verdict in the same job. Local run, exit 0 in 36.7s — the handshake lines this issue asked for: ``` [html5] vendoring extension-websocket and applying extension-websocket-html5-subprotocol.patch [handshake] -> subprotocol=v1.json.spacetimedb [handshake] <- 101 Switching Protocols [html5] SMOKE: PHASE1 OK cached row id=1 [html5] SMOKE: PHASE2 OK survived restart; row id=1 restored [html5] SMOKE: PHASE3 OK unsubscribed query set 1; cache drained [html5] SMOKE: PASS procedure round trip; failure surfaced as an error [html5] PASS ``` Note `subprotocol=v1.json.spacetimedb` rather than `<ABSENT>` — the vendored patch working, with no fork anywhere in the picture. ## Deliberately not done - **No self-reporting comment on `if: always()`.** #7 settled on that shape because this Forgejo serves no logs over the API and a blank failure was unreadable. That was a workaround for an unreliable job; the web UI shows the log fine. Adding a reporting harness to a job that is now trustworthy would re-import the complexity #7 spent ten runs removing. - **No separate `workflow_dispatch` for HTML5 alone.** The existing dispatch runs both tiers. Splitting them costs the duplicated preamble for the ability to skip 25s of wasm build. ## What is left One manual `workflow_dispatch` run to confirm Chromium starts in the job container, then drop `continue-on-error`. If it cannot start there, the honest outcome is to delete the step and say so here — the acceptance criterion as written.
yuris changed title from HTML5 conformance in CI: the tier native conformance cannot reach to HTML5 conformance in CI: prove Chromium runs in the job container 2026-08-19 14:27:19 +00:00
Author
Owner

Acceptance met: run #38 on bfd2b14, workflow_dispatch, PASS. Chromium starts in the job container.

Both tiers in one job, 100s total:

step duration
Headless conformance (native) 51s
HTML5 conformance (wasm in Chromium) 47s

The handshake evidence this issue asked for, from the CI log:

[html5] vendoring extension-websocket and applying extension-websocket-html5-subprotocol.patch
[html5] driving Chromium against ws://127.0.0.1:3000/v1/database/chat/subscribe
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] <- 101 Switching Protocols
[html5] SMOKE: PHASE1 OK cached row id=1
[html5] SMOKE: PHASE2 OK survived restart; row id=1 restored
[html5] SMOKE: PHASE3 OK unsubscribed query set 1; cache drained
[html5] SMOKE: PASS procedure round trip; failure surfaced as an error
[html5] PASS

subprotocol=v1.json.spacetimedb rather than <ABSENT>, against an unpatched upstream — the harness's own vendor-and-patch step, no fork involved. The open unknowns from the description are all closed: no Defold binary exec, no user namespaces, no docker, and the wasm build plus browser fit in the runner's disk without complaint.

One thing worth watching, and why the flag stays

Reconnect needed four attempts in CI versus one locally:

[html5] SMOKE: server dropped us (code 1006)
[html5] SMOKE: reconnecting attempt 1 in 1s
[html5] SMOKE: reconnecting attempt 2 in 2s
[html5] SMOKE: reconnecting attempt 3 in 4s
[html5] SMOKE: reconnecting attempt 4 in 5s
[handshake] <- 101 Switching Protocols
[html5] SMOKE: reconnected, replayed 1 subscription(s)

The server takes materially longer to come back on the runner than on a workstation, so phase 2 spent ~13s of its backoff budget where locally it spends ~1s. It passed, and the backoff is doing precisely what it exists for — but the margin is thinner than the local run suggests, and it is the one part of this tier that could go red on a slow day without anything actually being broken.

So continue-on-error: true stays for now. The unknown it was written to guard (does Chromium run here at all) is resolved, but a single green run does not establish that the reconnect phase is stable under runner load. Revisit after a few weekly runs: consistently green → drop the flag; intermittently red at attempt 5+ → the fix is the retry budget in the harness, not the workflow.

Leaving this open until the flag comes off.

Acceptance met: run [#38](https://git.nokopia.com/yuris/spacetimedb-defold/actions/runs/38) on `bfd2b14`, `workflow_dispatch`, **PASS**. Chromium starts in the job container. Both tiers in one job, 100s total: | step | duration | |---|---| | Headless conformance (native) | 51s | | HTML5 conformance (wasm in Chromium) | 47s | The handshake evidence this issue asked for, from the CI log: ``` [html5] vendoring extension-websocket and applying extension-websocket-html5-subprotocol.patch [html5] driving Chromium against ws://127.0.0.1:3000/v1/database/chat/subscribe [handshake] -> subprotocol=v1.json.spacetimedb [handshake] <- 101 Switching Protocols [html5] SMOKE: PHASE1 OK cached row id=1 [html5] SMOKE: PHASE2 OK survived restart; row id=1 restored [html5] SMOKE: PHASE3 OK unsubscribed query set 1; cache drained [html5] SMOKE: PASS procedure round trip; failure surfaced as an error [html5] PASS ``` `subprotocol=v1.json.spacetimedb` rather than `<ABSENT>`, against an unpatched upstream — the harness's own vendor-and-patch step, no fork involved. The open unknowns from the description are all closed: no Defold binary exec, no user namespaces, no docker, and the wasm build plus browser fit in the runner's disk without complaint. ## One thing worth watching, and why the flag stays Reconnect needed **four attempts** in CI versus one locally: ``` [html5] SMOKE: server dropped us (code 1006) [html5] SMOKE: reconnecting attempt 1 in 1s [html5] SMOKE: reconnecting attempt 2 in 2s [html5] SMOKE: reconnecting attempt 3 in 4s [html5] SMOKE: reconnecting attempt 4 in 5s [handshake] <- 101 Switching Protocols [html5] SMOKE: reconnected, replayed 1 subscription(s) ``` The server takes materially longer to come back on the runner than on a workstation, so phase 2 spent ~13s of its backoff budget where locally it spends ~1s. It passed, and the backoff is doing precisely what it exists for — but the margin is thinner than the local run suggests, and it is the one part of this tier that could go red on a slow day without anything actually being broken. So `continue-on-error: true` stays for now. The unknown it was written to guard (does Chromium run here at all) is resolved, but a single green run does not establish that the reconnect phase is stable under runner load. Revisit after a few weekly runs: consistently green → drop the flag; intermittently red at attempt 5+ → the fix is the retry budget in the harness, not the workflow. Leaving this open until the flag comes off.
Author
Owner

CI history checked, and the API cannot settle this — here is why

This issue turns on "once a few weekly runs have been green". I went to count
them. The answer is more awkward than a count.

conformance.yml has 8 runs total: 6 success, 2 failure (Forgejo's
actions API serves no timestamps or per-step results on this endpoint, so that is
run-level status only). The workflow is workflow_dispatch plus a weekly Monday
cron, so 8 runs is the whole history.

The problem: run-level success does not mean the HTML5 step passed. That is
the entire effect of continue-on-error: true at conformance.yml:116 — a red
HTML5 tier is recorded as a footnote and the job still reports green. So the 6
successes are consistent with HTML5 having passed 6 times and with it having
failed all 6 and been swallowed. Nothing in the API distinguishes them, which is
the same limitation conformance.yml:30-32 already notes for logs: "Read the job
log in the web UI — the API serves none."

So the evidence this issue needs is only reachable from the web UI, by a human
opening those 8 job logs and reading whether the HTML5 step's verdict line says
[html5] PASS. That is not agent-reachable, which upgrades this from "a judgement
on accumulated history" to "a judgement that first needs someone to go and read
6-8 job logs".

One new data point, and it argues for waiting

During the v1.1.0 release gate, html5-conformance.sh failed once locally and
then passed four times in a row (28/29/28s against a 120s timeout). Recorded on
#52 with the caveat that I did not capture the frames, so I cannot attribute it.

I did rule out one thing worth ruling out explicitly: it is not #66's new
PHASE6 pushing the per-load budget. The passing runs sit at roughly 4x headroom,
and a timeout emits its own distinct message rather than a bare FAIL.

But a 1-in-5 local failure is exactly the kind of thing continue-on-error exists
to absorb, and it happened on the tier that is better understood locally than in
the job container. Dropping the flag now would convert an unexplained intermittent
into a red X on a workflow whose native half is described as reliable.

Suggested disposition

Keep the flag. Two concrete steps that would actually close this, in order:

  1. Make the evidence machine-readable. Have the HTML5 step write its verdict
    line to $GITHUB_STEP_SUMMARY, or fail a separate non-blocking step on the
    absence of [html5] PASS, so future runs leave a signal the API can see
    without anyone opening a log. Without this, the same manual log-reading is
    required every time this question is revisited.
  2. Then count green weeks with the flag still on, and drop it once #52's
    intermittent has either recurred with a frame capture or gone quiet across a
    meaningful number of runs.

Step 1 is agent-doable and would make step 2 cheap. Say the word and I will open
it as a PR.

## CI history checked, and the API cannot settle this — here is why This issue turns on "once a few weekly runs have been green". I went to count them. The answer is more awkward than a count. **`conformance.yml` has 8 runs total: 6 `success`, 2 `failure`** (Forgejo's actions API serves no timestamps or per-step results on this endpoint, so that is run-level status only). The workflow is `workflow_dispatch` plus a weekly Monday cron, so 8 runs is the whole history. **The problem: run-level `success` does not mean the HTML5 step passed.** That is the entire effect of `continue-on-error: true` at `conformance.yml:116` — a red HTML5 tier is recorded as a footnote and the job still reports green. So the 6 successes are consistent with HTML5 having passed 6 times *and* with it having failed all 6 and been swallowed. Nothing in the API distinguishes them, which is the same limitation `conformance.yml:30-32` already notes for logs: *"Read the job log in the web UI — the API serves none."* So the evidence this issue needs is only reachable from the web UI, by a human opening those 8 job logs and reading whether the HTML5 step's verdict line says `[html5] PASS`. That is not agent-reachable, which upgrades this from "a judgement on accumulated history" to "a judgement that first needs someone to go and read 6-8 job logs". ## One new data point, and it argues for waiting During the v1.1.0 release gate, `html5-conformance.sh` failed once locally and then passed four times in a row (28/29/28s against a 120s timeout). Recorded on #52 with the caveat that I did not capture the frames, so I cannot attribute it. I did rule out one thing worth ruling out explicitly: it is **not** #66's new PHASE6 pushing the per-load budget. The passing runs sit at roughly 4x headroom, and a timeout emits its own distinct message rather than a bare `FAIL`. But a 1-in-5 local failure is exactly the kind of thing `continue-on-error` exists to absorb, and it happened on the tier that is *better* understood locally than in the job container. Dropping the flag now would convert an unexplained intermittent into a red X on a workflow whose native half is described as reliable. ## Suggested disposition Keep the flag. Two concrete steps that would actually close this, in order: 1. **Make the evidence machine-readable.** Have the HTML5 step write its verdict line to `$GITHUB_STEP_SUMMARY`, or fail a *separate* non-blocking step on the absence of `[html5] PASS`, so future runs leave a signal the API can see without anyone opening a log. Without this, the same manual log-reading is required every time this question is revisited. 2. **Then** count green weeks with the flag still on, and drop it once #52's intermittent has either recurred with a frame capture or gone quiet across a meaningful number of runs. Step 1 is agent-doable and would make step 2 cheap. Say the word and I will open it as a PR.

HTML5 tier, run #162: failure (b28ac1e3af)

Topology:

in-container: yes
hostname: 933e0952dcd6
cpus: 4
kernel: 6.12.95+deb13-amd64
/nix mounted: /dev/sda1 /nix ext4 rw,relatime,discard,errors=remount-ro 0 0
free disk /: overlay          74G   37G   35G  52% /
free disk /nix: /dev/sda1        74G   37G   35G  52% /nix
nix store paths: 5747

Tail:

[html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=1
[html5] SMOKE: server dropped us (code 1006)
[html5] SMOKE: reconnecting attempt 1 in 0.5245s
[html5] SMOKE: reconnecting attempt 2 in 1.034s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 3 in 2.038s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 4 in 2.51s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 5 in 4.9725s
[html5] SMOKE: server dropped us (code 0)
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: reconnected, replayed 1 subscription(s)
[html5] SMOKE: PHASE2 OK survived restart; row id=1 restored
[html5] SMOKE: query set 1 subscribed
[html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended
[html5] SMOKE: procedure returned "echo: from defold"
[html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update
[html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s)
[html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200f870…
[html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema
[html5] SMOKE: BULK rows=10000 run=1 frame=310.0ms pre=164.0 decode=84.0 quote=0.0 build=60.0 post=2.0ms us_per_row=31.00
[html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=310.0 med=310.0 max=310.0ms spread=0% med pre=164.0 decode=84.0 quote=0.0 build=60.0 post=2.0ms share pre=53% dec=27% quo=0% bld=19% post=1% us_per_row=31.00 quant=+/-9.16ms apparatus=8.00ms ONE-SAMPLE(set STDB_BULK_RUNS)
[html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 310.0ms median over 1 run(s); json.decode 27%, big-int quoting 0%, construction 19%, frame overhead outside the apply 54%
[html5] load 1 passed — reloading without a token (identity persistence)
[html5] SMOKE: connecting to ws://127.0.0.1:3000/v1/database/chat/subscribe
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: identity 0xc200f8701d45af8610578b66f33a96a4e81b8f8a6e574300307d9270d9aeedf8
[html5] phase 1 passed — restarting the server to force a reconnect
[html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=4097
[html5] SMOKE: server dropped us (code 1006)
[html5] SMOKE: reconnecting attempt 1 in 0.5245s
[html5] SMOKE: reconnecting attempt 2 in 1.034s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 3 in 2.038s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 4 in 2.51s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 5 in 4.9725s
[html5] SMOKE: server dropped us (code 0)
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: reconnected, replayed 1 subscription(s)
[html5] SMOKE: PHASE2 OK survived restart; row id=4097 restored
[html5] SMOKE: query set 1 subscribed
[html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended
[html5] SMOKE: procedure returned "echo: from defold"
[html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update
[html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s)
[html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200f870…
[html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema
[html5] SMOKE: BULK rows=10000 run=1 frame=274.0ms pre=163.0 decode=61.0 quote=0.0 build=48.0 post=2.0ms us_per_row=27.40
[html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=274.0 med=274.0 max=274.0ms spread=0% med pre=163.0 decode=61.0 quote=0.0 build=48.0 post=2.0ms share pre=59% dec=22% quo=0% bld=18% post=1% us_per_row=27.40 quant=+/-7.81ms apparatus=3.00ms ONE-SAMPLE(set STDB_BULK_RUNS)
[html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 274.0ms median over 1 run(s); json.decode 22%, big-int quoting 0%, construction 18%, frame overhead outside the apply 60%
[html5] cdp socket error
HTML5 tier, run #162: **failure** (b28ac1e3af02a334066b0dd35f441b4e82c7d76b) Topology: ``` in-container: yes hostname: 933e0952dcd6 cpus: 4 kernel: 6.12.95+deb13-amd64 /nix mounted: /dev/sda1 /nix ext4 rw,relatime,discard,errors=remount-ro 0 0 free disk /: overlay 74G 37G 35G 52% / free disk /nix: /dev/sda1 74G 37G 35G 52% /nix nix store paths: 5747 ``` Tail: ``` [html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=1 [html5] SMOKE: server dropped us (code 1006) [html5] SMOKE: reconnecting attempt 1 in 0.5245s [html5] SMOKE: reconnecting attempt 2 in 1.034s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 3 in 2.038s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 4 in 2.51s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 5 in 4.9725s [html5] SMOKE: server dropped us (code 0) [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: reconnected, replayed 1 subscription(s) [html5] SMOKE: PHASE2 OK survived restart; row id=1 restored [html5] SMOKE: query set 1 subscribed [html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended [html5] SMOKE: procedure returned "echo: from defold" [html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update [html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s) [html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200f870… [html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema [html5] SMOKE: BULK rows=10000 run=1 frame=310.0ms pre=164.0 decode=84.0 quote=0.0 build=60.0 post=2.0ms us_per_row=31.00 [html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=310.0 med=310.0 max=310.0ms spread=0% med pre=164.0 decode=84.0 quote=0.0 build=60.0 post=2.0ms share pre=53% dec=27% quo=0% bld=19% post=1% us_per_row=31.00 quant=+/-9.16ms apparatus=8.00ms ONE-SAMPLE(set STDB_BULK_RUNS) [html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 310.0ms median over 1 run(s); json.decode 27%, big-int quoting 0%, construction 19%, frame overhead outside the apply 54% [html5] load 1 passed — reloading without a token (identity persistence) [html5] SMOKE: connecting to ws://127.0.0.1:3000/v1/database/chat/subscribe [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: identity 0xc200f8701d45af8610578b66f33a96a4e81b8f8a6e574300307d9270d9aeedf8 [html5] phase 1 passed — restarting the server to force a reconnect [html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=4097 [html5] SMOKE: server dropped us (code 1006) [html5] SMOKE: reconnecting attempt 1 in 0.5245s [html5] SMOKE: reconnecting attempt 2 in 1.034s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 3 in 2.038s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 4 in 2.51s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 5 in 4.9725s [html5] SMOKE: server dropped us (code 0) [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: reconnected, replayed 1 subscription(s) [html5] SMOKE: PHASE2 OK survived restart; row id=4097 restored [html5] SMOKE: query set 1 subscribed [html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended [html5] SMOKE: procedure returned "echo: from defold" [html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update [html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s) [html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200f870… [html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema [html5] SMOKE: BULK rows=10000 run=1 frame=274.0ms pre=163.0 decode=61.0 quote=0.0 build=48.0 post=2.0ms us_per_row=27.40 [html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=274.0 med=274.0 max=274.0ms spread=0% med pre=163.0 decode=61.0 quote=0.0 build=48.0 post=2.0ms share pre=59% dec=22% quo=0% bld=18% post=1% us_per_row=27.40 quant=+/-7.81ms apparatus=3.00ms ONE-SAMPLE(set STDB_BULK_RUNS) [html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 274.0ms median over 1 run(s); json.decode 22%, big-int quoting 0%, construction 18%, frame overhead outside the apply 60% [html5] cdp socket error ```

HTML5 tier, run #163: success (4ca7c9f1fa)

Topology:

in-container: yes
hostname: 8106c4d1002b
cpus: 4
kernel: 6.12.95+deb13-amd64
/nix mounted: /dev/sda1 /nix ext4 rw,relatime,discard,errors=remount-ro 0 0
free disk /: overlay          74G   37G   35G  52% /
free disk /nix: /dev/sda1        74G   37G   35G  52% /nix
nix store paths: 5747

Tail:

[html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=1
[html5] SMOKE: server dropped us (code 1006)
[html5] SMOKE: reconnecting attempt 1 in 0.8295s
[html5] SMOKE: reconnecting attempt 2 in 1.644s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 3 in 3.258s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 4 in 4.035s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 5 in 3.9975s
[html5] SMOKE: server dropped us (code 0)
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: reconnected, replayed 1 subscription(s)
[html5] SMOKE: PHASE2 OK survived restart; row id=1 restored
[html5] SMOKE: query set 1 subscribed
[html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended
[html5] SMOKE: procedure returned "echo: from defold"
[html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update
[html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s)
[html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200fd18…
[html5] SMOKE: PHASE6 measuring with socket.gettime (tick 8.000ms); sweep 10000 x 1 run(s); hand-written schema
[html5] SMOKE: BULK rows=10000 run=1 frame=313.0ms pre=184.0 decode=63.0 quote=0.0 build=64.0 post=2.0ms us_per_row=31.30
[html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=313.0 med=313.0 max=313.0ms spread=0% med pre=184.0 decode=63.0 quote=0.0 build=64.0 post=2.0ms share pre=59% dec=20% quo=0% bld=20% post=1% us_per_row=31.30 quant=+/-22.45ms apparatus=11.00ms ONE-SAMPLE(set STDB_BULK_RUNS)
[html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 8.000ms): 10000 rows apply in 313.0ms median over 1 run(s); json.decode 20%, big-int quoting 0%, construction 20%, frame overhead outside the apply 59%
[html5] load 1 passed — reloading without a token (identity persistence)
[html5] SMOKE: connecting to ws://127.0.0.1:3000/v1/database/chat/subscribe
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: identity 0xc200fd1845760c657b8fc62a420cd9a19da94dc5c4ac86f4e7f7eda5761968b2
[html5] phase 1 passed — restarting the server to force a reconnect
[html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=4097
[html5] SMOKE: server dropped us (code 1006)
[html5] SMOKE: reconnecting attempt 1 in 0.8295s
[html5] SMOKE: reconnecting attempt 2 in 1.644s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 3 in 3.258s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 4 in 4.035s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 5 in 3.9975s
[html5] SMOKE: server dropped us (code 0)
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: reconnected, replayed 1 subscription(s)
[html5] SMOKE: PHASE2 OK survived restart; row id=4097 restored
[html5] SMOKE: query set 1 subscribed
[html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended
[html5] SMOKE: procedure returned "echo: from defold"
[html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update
[html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s)
[html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200fd18…
[html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema
[html5] SMOKE: BULK rows=10000 run=1 frame=294.0ms pre=177.0 decode=65.0 quote=0.0 build=50.0 post=2.0ms us_per_row=29.40
[html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=294.0 med=294.0 max=294.0ms spread=0% med pre=177.0 decode=65.0 quote=0.0 build=50.0 post=2.0ms share pre=60% dec=22% quo=0% bld=17% post=1% us_per_row=29.40 quant=+/-8.06ms apparatus=3.00ms ONE-SAMPLE(set STDB_BULK_RUNS)
[html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 294.0ms median over 1 run(s); json.decode 22%, big-int quoting 0%, construction 17%, frame overhead outside the apply 61%
[html5] PASS (identity 0xc200fd1845760c657b8fc62a420cd9a19da94dc5c4ac86f4e7f7eda5761968b2 survived a reload)
HTML5 tier, run #163: **success** (4ca7c9f1fa9d070c1f8946ad7adf99673642d79f) Topology: ``` in-container: yes hostname: 8106c4d1002b cpus: 4 kernel: 6.12.95+deb13-amd64 /nix mounted: /dev/sda1 /nix ext4 rw,relatime,discard,errors=remount-ro 0 0 free disk /: overlay 74G 37G 35G 52% / free disk /nix: /dev/sda1 74G 37G 35G 52% /nix nix store paths: 5747 ``` Tail: ``` [html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=1 [html5] SMOKE: server dropped us (code 1006) [html5] SMOKE: reconnecting attempt 1 in 0.8295s [html5] SMOKE: reconnecting attempt 2 in 1.644s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 3 in 3.258s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 4 in 4.035s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 5 in 3.9975s [html5] SMOKE: server dropped us (code 0) [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: reconnected, replayed 1 subscription(s) [html5] SMOKE: PHASE2 OK survived restart; row id=1 restored [html5] SMOKE: query set 1 subscribed [html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended [html5] SMOKE: procedure returned "echo: from defold" [html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update [html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s) [html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200fd18… [html5] SMOKE: PHASE6 measuring with socket.gettime (tick 8.000ms); sweep 10000 x 1 run(s); hand-written schema [html5] SMOKE: BULK rows=10000 run=1 frame=313.0ms pre=184.0 decode=63.0 quote=0.0 build=64.0 post=2.0ms us_per_row=31.30 [html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=313.0 med=313.0 max=313.0ms spread=0% med pre=184.0 decode=63.0 quote=0.0 build=64.0 post=2.0ms share pre=59% dec=20% quo=0% bld=20% post=1% us_per_row=31.30 quant=+/-22.45ms apparatus=11.00ms ONE-SAMPLE(set STDB_BULK_RUNS) [html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 8.000ms): 10000 rows apply in 313.0ms median over 1 run(s); json.decode 20%, big-int quoting 0%, construction 20%, frame overhead outside the apply 59% [html5] load 1 passed — reloading without a token (identity persistence) [html5] SMOKE: connecting to ws://127.0.0.1:3000/v1/database/chat/subscribe [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: identity 0xc200fd1845760c657b8fc62a420cd9a19da94dc5c4ac86f4e7f7eda5761968b2 [html5] phase 1 passed — restarting the server to force a reconnect [html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=4097 [html5] SMOKE: server dropped us (code 1006) [html5] SMOKE: reconnecting attempt 1 in 0.8295s [html5] SMOKE: reconnecting attempt 2 in 1.644s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 3 in 3.258s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 4 in 4.035s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 5 in 3.9975s [html5] SMOKE: server dropped us (code 0) [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: reconnected, replayed 1 subscription(s) [html5] SMOKE: PHASE2 OK survived restart; row id=4097 restored [html5] SMOKE: query set 1 subscribed [html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended [html5] SMOKE: procedure returned "echo: from defold" [html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update [html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s) [html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200fd18… [html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema [html5] SMOKE: BULK rows=10000 run=1 frame=294.0ms pre=177.0 decode=65.0 quote=0.0 build=50.0 post=2.0ms us_per_row=29.40 [html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=294.0 med=294.0 max=294.0ms spread=0% med pre=177.0 decode=65.0 quote=0.0 build=50.0 post=2.0ms share pre=60% dec=22% quo=0% bld=17% post=1% us_per_row=29.40 quant=+/-8.06ms apparatus=3.00ms ONE-SAMPLE(set STDB_BULK_RUNS) [html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 294.0ms median over 1 run(s); json.decode 22%, big-int quoting 0%, construction 17%, frame overhead outside the apply 61% [html5] PASS (identity 0xc200fd1845760c657b8fc62a420cd9a19da94dc5c4ac86f4e7f7eda5761968b2 survived a reload) ```

HTML5 tier, run #167: success (62b3e7784a)

Topology:

in-container: yes
hostname: 01e1089c2688
cpus: 4
kernel: 6.12.95+deb13-amd64
/nix mounted: /dev/sda1 /nix ext4 rw,relatime,discard,errors=remount-ro 0 0
free disk /: overlay          74G   37G   35G  52% /
free disk /nix: /dev/sda1        74G   37G   35G  52% /nix
nix store paths: 5750

Tail:

[html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=1
[html5] SMOKE: server dropped us (code 1006)
[html5] SMOKE: reconnecting attempt 1 in 0.734s
[html5] SMOKE: reconnecting attempt 2 in 1.453s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 3 in 2.876s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 4 in 2.8175s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 5 in 2.78s
[html5] SMOKE: server dropped us (code 0)
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: reconnected, replayed 1 subscription(s)
[html5] SMOKE: PHASE2 OK survived restart; row id=1 restored
[html5] SMOKE: query set 1 subscribed
[html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended
[html5] SMOKE: procedure returned "echo: from defold"
[html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update
[html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s)
[html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200ee5e…
[html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema
[html5] SMOKE: BULK rows=10000 run=1 frame=335.0ms pre=186.0 decode=86.0 quote=0.0 build=61.0 post=2.0ms us_per_row=33.50
[html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=335.0 med=335.0 max=335.0ms spread=0% med pre=186.0 decode=86.0 quote=0.0 build=61.0 post=2.0ms share pre=56% dec=26% quo=0% bld=18% post=1% us_per_row=33.50 quant=+/-9.27ms apparatus=13.00ms ONE-SAMPLE(set STDB_BULK_RUNS)
[html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 335.0ms median over 1 run(s); json.decode 26%, big-int quoting 0%, construction 18%, frame overhead outside the apply 56%
[html5] load 1 passed — reloading without a token (identity persistence)
[html5] SMOKE: connecting to ws://127.0.0.1:3000/v1/database/chat/subscribe
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: identity 0xc200ee5eefa2f6242e0b462589e26cd15475c60da47e49ef8b36d41170f8f746
[html5] phase 1 passed — restarting the server to force a reconnect
[html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=4097
[html5] SMOKE: server dropped us (code 1006)
[html5] SMOKE: reconnecting attempt 1 in 0.734s
[html5] SMOKE: reconnecting attempt 2 in 1.453s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 3 in 2.876s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 4 in 2.8175s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 5 in 2.78s
[html5] SMOKE: server dropped us (code 0)
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: reconnected, replayed 1 subscription(s)
[html5] SMOKE: PHASE2 OK survived restart; row id=4097 restored
[html5] SMOKE: query set 1 subscribed
[html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended
[html5] SMOKE: procedure returned "echo: from defold"
[html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update
[html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s)
[html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200ee5e…
[html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema
[html5] SMOKE: BULK rows=10000 run=1 frame=300.0ms pre=171.0 decode=71.0 quote=0.0 build=56.0 post=2.0ms us_per_row=30.00
[html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=300.0 med=300.0 max=300.0ms spread=0% med pre=171.0 decode=71.0 quote=0.0 build=56.0 post=2.0ms share pre=57% dec=24% quo=0% bld=19% post=1% us_per_row=30.00 quant=+/-8.43ms apparatus=9.00ms ONE-SAMPLE(set STDB_BULK_RUNS)
[html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 300.0ms median over 1 run(s); json.decode 24%, big-int quoting 0%, construction 19%, frame overhead outside the apply 58%
[html5] PASS (identity 0xc200ee5eefa2f6242e0b462589e26cd15475c60da47e49ef8b36d41170f8f746 survived a reload)
HTML5 tier, run #167: **success** (62b3e7784a062e5737bb264329df844ccd94215a) Topology: ``` in-container: yes hostname: 01e1089c2688 cpus: 4 kernel: 6.12.95+deb13-amd64 /nix mounted: /dev/sda1 /nix ext4 rw,relatime,discard,errors=remount-ro 0 0 free disk /: overlay 74G 37G 35G 52% / free disk /nix: /dev/sda1 74G 37G 35G 52% /nix nix store paths: 5750 ``` Tail: ``` [html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=1 [html5] SMOKE: server dropped us (code 1006) [html5] SMOKE: reconnecting attempt 1 in 0.734s [html5] SMOKE: reconnecting attempt 2 in 1.453s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 3 in 2.876s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 4 in 2.8175s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 5 in 2.78s [html5] SMOKE: server dropped us (code 0) [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: reconnected, replayed 1 subscription(s) [html5] SMOKE: PHASE2 OK survived restart; row id=1 restored [html5] SMOKE: query set 1 subscribed [html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended [html5] SMOKE: procedure returned "echo: from defold" [html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update [html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s) [html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200ee5e… [html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema [html5] SMOKE: BULK rows=10000 run=1 frame=335.0ms pre=186.0 decode=86.0 quote=0.0 build=61.0 post=2.0ms us_per_row=33.50 [html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=335.0 med=335.0 max=335.0ms spread=0% med pre=186.0 decode=86.0 quote=0.0 build=61.0 post=2.0ms share pre=56% dec=26% quo=0% bld=18% post=1% us_per_row=33.50 quant=+/-9.27ms apparatus=13.00ms ONE-SAMPLE(set STDB_BULK_RUNS) [html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 335.0ms median over 1 run(s); json.decode 26%, big-int quoting 0%, construction 18%, frame overhead outside the apply 56% [html5] load 1 passed — reloading without a token (identity persistence) [html5] SMOKE: connecting to ws://127.0.0.1:3000/v1/database/chat/subscribe [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: identity 0xc200ee5eefa2f6242e0b462589e26cd15475c60da47e49ef8b36d41170f8f746 [html5] phase 1 passed — restarting the server to force a reconnect [html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=4097 [html5] SMOKE: server dropped us (code 1006) [html5] SMOKE: reconnecting attempt 1 in 0.734s [html5] SMOKE: reconnecting attempt 2 in 1.453s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 3 in 2.876s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 4 in 2.8175s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 5 in 2.78s [html5] SMOKE: server dropped us (code 0) [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: reconnected, replayed 1 subscription(s) [html5] SMOKE: PHASE2 OK survived restart; row id=4097 restored [html5] SMOKE: query set 1 subscribed [html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended [html5] SMOKE: procedure returned "echo: from defold" [html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update [html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s) [html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc200ee5e… [html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema [html5] SMOKE: BULK rows=10000 run=1 frame=300.0ms pre=171.0 decode=71.0 quote=0.0 build=56.0 post=2.0ms us_per_row=30.00 [html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=300.0 med=300.0 max=300.0ms spread=0% med pre=171.0 decode=71.0 quote=0.0 build=56.0 post=2.0ms share pre=57% dec=24% quo=0% bld=19% post=1% us_per_row=30.00 quant=+/-8.43ms apparatus=9.00ms ONE-SAMPLE(set STDB_BULK_RUNS) [html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 300.0ms median over 1 run(s); json.decode 24%, big-int quoting 0%, construction 19%, frame overhead outside the apply 58% [html5] PASS (identity 0xc200ee5eefa2f6242e0b462589e26cd15475c60da47e49ef8b36d41170f8f746 survived a reload) ```

HTML5 tier, run #169: failure (38f14c758b)

Topology:

in-container: yes
hostname: 54651d8bbc80
cpus: 4
kernel: 6.12.95+deb13-amd64
/nix mounted: /dev/sda1 /nix ext4 rw,relatime,discard,errors=remount-ro 0 0
free disk /: overlay          74G   37G   35G  52% /
free disk /nix: /dev/sda1        74G   37G   35G  52% /nix
nix store paths: 5750

Tail:

[html5] SMOKE: reconnecting attempt 4 in 4.7325s
[html5] SMOKE: server dropped us (code 0)
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: reconnected, replayed 1 subscription(s)
[html5] SMOKE: PHASE2 OK survived restart; row id=1 restored
[html5] SMOKE: query set 1 subscribed
[html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended
[html5] SMOKE: procedure returned "echo: from defold"
[html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update
[html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s)
[html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc2000947…
[html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema
[html5] SMOKE: BULK rows=10000 run=1 frame=304.0ms pre=164.0 decode=80.0 quote=0.0 build=58.0 post=2.0ms us_per_row=30.40
[html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=304.0 med=304.0 max=304.0ms spread=0% med pre=164.0 decode=80.0 quote=0.0 build=58.0 post=2.0ms share pre=54% dec=26% quo=0% bld=19% post=1% us_per_row=30.40 quant=+/-8.94ms apparatus=4.00ms ONE-SAMPLE(set STDB_BULK_RUNS)
[html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 304.0ms median over 1 run(s); json.decode 26%, big-int quoting 0%, construction 19%, frame overhead outside the apply 55%
[html5] load 1 passed — reloading without a token (identity persistence)
[html5] SMOKE: connecting to ws://127.0.0.1:3000/v1/database/chat/subscribe
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: identity 0xc20009474e80022b1a97e6970564da45ceecfaf6a5d36584868f9b011ee04652
[html5] phase 1 passed — restarting the server to force a reconnect
[html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=4097
[html5] SMOKE: server dropped us (code 1006)
[html5] SMOKE: reconnecting attempt 1 in 0.969s
[html5] SMOKE: reconnecting attempt 2 in 1.923s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 3 in 3.816s
[html5] SMOKE: server dropped us (code 0)
[html5] SMOKE: reconnecting attempt 4 in 4.7325s
[html5] SMOKE: server dropped us (code 0)
[handshake] -> subprotocol=v1.json.spacetimedb
[handshake] -> authorization=<ABSENT>
[handshake] <- 101 Switching Protocols
[html5] SMOKE: reconnected, replayed 1 subscription(s)
[html5] SMOKE: PHASE2 OK survived restart; row id=4097 restored
[html5] SMOKE: query set 1 subscribed
[html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended
[html5] SMOKE: procedure returned "echo: from defold"
[html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update
[html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s)
[html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc2000947…
[html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema
[html5] SMOKE: BULK rows=10000 run=1 frame=321.0ms pre=186.0 decode=78.0 quote=0.0 build=55.0 post=2.0ms us_per_row=32.10
[html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=321.0 med=321.0 max=321.0ms spread=0% med pre=186.0 decode=78.0 quote=0.0 build=55.0 post=2.0ms share pre=58% dec=24% quo=0% bld=17% post=1% us_per_row=32.10 quant=+/-8.83ms apparatus=2.00ms ONE-SAMPLE(set STDB_BULK_RUNS)
[html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 321.0ms median over 1 run(s); json.decode 24%, big-int quoting 0%, construction 17%, frame overhead outside the apply 59%
node:internal/process/promises:394
    triggerUncaughtException(err, true /* fromPromise */);
    ^

[Error: ENOTEMPTY: directory not empty, rmdir '/tmp/nix-shell.BiwBuN/stdb-chromium-qAkkMs/Default'] {
  errno: -39,
  code: 'ENOTEMPTY',
  syscall: 'rmdir',
  path: '/tmp/nix-shell.BiwBuN/stdb-chromium-qAkkMs/Default'
}

Node.js v24.19.0
HTML5 tier, run #169: **failure** (38f14c758bc150348453a4026b234b928b8d1d43) Topology: ``` in-container: yes hostname: 54651d8bbc80 cpus: 4 kernel: 6.12.95+deb13-amd64 /nix mounted: /dev/sda1 /nix ext4 rw,relatime,discard,errors=remount-ro 0 0 free disk /: overlay 74G 37G 35G 52% / free disk /nix: /dev/sda1 74G 37G 35G 52% /nix nix store paths: 5750 ``` Tail: ``` [html5] SMOKE: reconnecting attempt 4 in 4.7325s [html5] SMOKE: server dropped us (code 0) [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: reconnected, replayed 1 subscription(s) [html5] SMOKE: PHASE2 OK survived restart; row id=1 restored [html5] SMOKE: query set 1 subscribed [html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended [html5] SMOKE: procedure returned "echo: from defold" [html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update [html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s) [html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc2000947… [html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema [html5] SMOKE: BULK rows=10000 run=1 frame=304.0ms pre=164.0 decode=80.0 quote=0.0 build=58.0 post=2.0ms us_per_row=30.40 [html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=304.0 med=304.0 max=304.0ms spread=0% med pre=164.0 decode=80.0 quote=0.0 build=58.0 post=2.0ms share pre=54% dec=26% quo=0% bld=19% post=1% us_per_row=30.40 quant=+/-8.94ms apparatus=4.00ms ONE-SAMPLE(set STDB_BULK_RUNS) [html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 304.0ms median over 1 run(s); json.decode 26%, big-int quoting 0%, construction 19%, frame overhead outside the apply 55% [html5] load 1 passed — reloading without a token (identity persistence) [html5] SMOKE: connecting to ws://127.0.0.1:3000/v1/database/chat/subscribe [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: identity 0xc20009474e80022b1a97e6970564da45ceecfaf6a5d36584868f9b011ee04652 [html5] phase 1 passed — restarting the server to force a reconnect [html5] SMOKE: PHASE1 OK light=true, own update still full; pk change is one update event; cached row id=4097 [html5] SMOKE: server dropped us (code 1006) [html5] SMOKE: reconnecting attempt 1 in 0.969s [html5] SMOKE: reconnecting attempt 2 in 1.923s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 3 in 3.816s [html5] SMOKE: server dropped us (code 0) [html5] SMOKE: reconnecting attempt 4 in 4.7325s [html5] SMOKE: server dropped us (code 0) [handshake] -> subprotocol=v1.json.spacetimedb [handshake] -> authorization=<ABSENT> [handshake] <- 101 Switching Protocols [html5] SMOKE: reconnected, replayed 1 subscription(s) [html5] SMOKE: PHASE2 OK survived restart; row id=4097 restored [html5] SMOKE: query set 1 subscribed [html5] SMOKE: PHASE3 OK dropped query set 1; the row the legacy set still matches survived, handle ended [html5] SMOKE: procedure returned "echo: from defold" [html5] SMOKE: PHASE4 OK procedure round trip; failure surfaced as an error; per-table API: 1 insert(s), 1 update [html5] SMOKE: real delete drained the cache and fired 1 on_delete callback(s) [html5] SMOKE: PHASE5 OK identity-keyed table: one row throughout, one update event, resolved by 0xc2000947… [html5] SMOKE: PHASE6 measuring with socket.gettime (tick 1.000ms); sweep 10000 x 1 run(s); hand-written schema [html5] SMOKE: BULK rows=10000 run=1 frame=321.0ms pre=186.0 decode=78.0 quote=0.0 build=55.0 post=2.0ms us_per_row=32.10 [html5] SMOKE: BULK SUMMARY rows=10000 runs=1 frame min=321.0 med=321.0 max=321.0ms spread=0% med pre=186.0 decode=78.0 quote=0.0 build=55.0 post=2.0ms share pre=58% dec=24% quo=0% bld=17% post=1% us_per_row=32.10 quant=+/-8.83ms apparatus=2.00ms ONE-SAMPLE(set STDB_BULK_RUNS) [html5] SMOKE: PASS PHASE6 hand-written schema, socket.gettime (tick 1.000ms): 10000 rows apply in 321.0ms median over 1 run(s); json.decode 24%, big-int quoting 0%, construction 17%, frame overhead outside the apply 59% node:internal/process/promises:394 triggerUncaughtException(err, true /* fromPromise */); ^ [Error: ENOTEMPTY: directory not empty, rmdir '/tmp/nix-shell.BiwBuN/stdb-chromium-qAkkMs/Default'] { errno: -39, code: 'ENOTEMPTY', syscall: 'rmdir', path: '/tmp/nix-shell.BiwBuN/stdb-chromium-qAkkMs/Default' } Node.js v24.19.0 ```
yuris closed this issue 2026-08-21 04:20:50 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/spacetimedb-defold#16
No description provided.