Skip to content

Close the holes the committee found, and stop testing the file's prose - #74

Merged
peopleworks merged 1 commit into
mainfrom
boot-resilience
Sep 1, 2026
Merged

Close the holes the committee found, and stop testing the file's prose#74
peopleworks merged 1 commit into
mainfrom
boot-resilience

Conversation

@peopleworks

Copy link
Copy Markdown
Owner

Follow-up to #73, which merged while this was being written — so what is deployed right now is the first version, and it carries a regression this PR removes.

Three reviewers, three lenses. Between them, #73 promised more than it delivered.

The regression first, because it is live

With autostart="false" set and the retry living in boot.js, a 503 on boot.js itself means nothing ever starts. Before #73 that file did not exist and Blazor started itself. That is one case where the deployed build is worse than what preceded it, and it is the reason this should not wait.

What the committee found

Copilot measured the scope in a real browser rather than arguing about it. Five files were still a single request each, and a 503 on any of them produced no panel at all:

{"blazor":        requests:1, panel:0}
{"dotnet-main":   requests:1, panel:0}
{"dotnet-native": requests:1, panel:0}

It also executed the misattribution: failure state kept per module meant a file that failed once and recovered was named as the cause of a later, unrelated startup error.

Fable verified the security stance holds — a returned Promise short-circuits the runtime before it sets integrity of its own, so handing the manifest hash to fetch is not hygiene, it is the entire check — and killed the service-worker alternative on grounds specific to this project: a pinned app keeps serving yesterday's published false-positive rate, when publishing today's is the whole claim.

DeepSeek read the copy and found the panel saying more than it knows.

What changed

The panel and a watchdog now live inline in index.html. Inline because the one failure an external file can never explain is its own. The watchdog fires when nothing has advanced for 45 s rather than on a deadline, so a cheap phone on a bad connection is still allowed to be slow. It covers the runtime's ES modules — which must be handed back as a URL and so can never be retried — and a connection that accepts and then hangs. Each fetch also gets its own 25 s abort, since a hung request consumed no attempt and reached no panel.

Failure state is per download, which is the misattribution fixed.

The copy no longer claims what it cannot know. "Almost always a passing server fault" is not a frequency we failed to measure — it is one we can never measure: the panel exists precisely when the app never started, and this project does not phone home. Gone too: exonerating a browser that a school VPN has already disproved once, and a privacy assurance answering a question nobody asked, since the reader has not typed anything yet. The panel now offers the desktop app by absolute URL — every route on this site is served by the page that just failed — tells the reader to copy the technical detail when reporting, and honours the language they chose, which persists in localStorage and outlives the failed startup.

The tests are behaviour now

The old ones searched the file's text, and every one could be satisfied by a comment or by dead code: void undefined; passed the test guarding the ES-module exclusion while breaking every module load.

tests/boot/boot.test.mjs runs the real boot.js against a fake network, in CI. Each guarantee was checked by reintroducing the bug it guards — dropping the integrity handoff, fetching the ES modules, and the original misattribution are all detected. What stays in C# is the wiring those cannot see, asserted against source with comments stripped.

Verified in a browser, all five scenarios

503 on #73 (live) This PR
an assembly panel, names the file unchanged
js/boot.js nothing ever starts panel after 45 s
blazor.webassembly.js eternal spinner panel at once, names the cause
a runtime ES module eternal spinner panel after 45 s
nothing (healthy) boots boots, 0 console errors

Also verified: browser in en-US with the app set to Spanish now gets a Spanish panel.

332 C# tests + 11 boot tests.

🤖 Generated with Claude Code

https://claude.ai/code/session_015PEbbiYSNPw7jE3LrPNhyF

Three reviewers, three lenses, and between them the first version promised
more than it delivered.

Copilot measured the scope in a real browser: five files were still a single
request each, and a 503 on any of them showed no panel at all. One of those was
new — with autostart="false" set and the retry living in boot.js, a 503 on
boot.js itself meant nothing ever started. That was a regression this PR
introduced, so it is fixed here rather than filed.

The panel and a watchdog now live inline in index.html. Inline because the one
failure an external file can never explain is its own. The watchdog fires when
nothing has advanced for 45 s, not on a deadline, so a cheap phone on a bad
connection is still allowed to be slow; it covers the runtime's ES modules,
which must be handed back as a URL and so can never be retried, and a
connection that accepts and then hangs. Each fetch now has its own 25 s abort,
because a hung request consumed no attempt and reached no panel.

The failure state is per download. Kept per module, a file that failed once and
then recovered was named as the cause of a later, unrelated startup error —
found by executing it, not by reading.

DeepSeek read the copy and found the panel saying more than it knows. "Almost
always a passing server fault" is not a frequency we failed to measure, it is
one we can never measure: the panel exists precisely when the app never
started, and this project does not phone home. Gone, along with exonerating a
browser that a school VPN has already disproved once, and a privacy assurance
answering a question nobody asked — the reader has not typed anything yet. The
panel now offers the desktop app by absolute URL, because every route on this
site is served by the page that just failed, and it says to copy the technical
detail when reporting. It also honours the language the reader chose, which
persists in localStorage and outlives the failed startup.

The tests are behaviour now. The old ones searched this file's text, and every
one of them could be satisfied by a comment or by dead code — `void undefined;`
passed the test guarding the ES-module exclusion while breaking every module
load. tests/boot/boot.test.mjs runs the real boot.js against a fake network,
and each guarantee was checked by reintroducing the bug it guards: dropping the
integrity handoff, fetching the ES modules, and the original misattribution are
all detected. What stays in C# is the wiring those cannot see, asserted against
source with comments stripped.

Verified in a browser for all five scenarios: a healthy load, a 503 on
boot.js, on the Blazor script, on a runtime module, and on an assembly. All
five now explain themselves; four of them previously did not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015PEbbiYSNPw7jE3LrPNhyF
@peopleworks
peopleworks merged commit dde7f01 into main Sep 1, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant