Repository navigation
feat(site): the explainer's opening, a CSP on every page, and the calculator fixes (phase 7) - #70
Merged
Merged
Conversation
TMHSDigital
force-pushed
the
site/phase-6-search
branch
from
September 23, 2026 15:01
836ea57 to
f4d21ca
Compare
…files No change in behaviour. This is the first step toward a Content Security Policy with no 'unsafe-inline', and it makes the fixes that follow read as diffs of explainer.js rather than of an HTML file. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…culator fixes (phase 7) The explainer opens with the example report's result as a card: the measured ECE against its floor's 95th percentile on one gauge, and the verdict. build_site.py writes it from the report's own ECE line, so it says what the report says and reads the same with scripts off. The contents list becomes a rail beside the text on wide screens. Every page now carries a Content-Security-Policy that allows only the site's own files. The explainer's inline style and script moved out in the previous commit, so nothing needs 'unsafe-inline' or a hash. The build refuses an explainer without the tag, and check_site_links.mjs fails any page that lacks it or carries an inline script, style block, or style attribute the policy would block. The calculator and planner: - validation messages said "[object Object]"; they now say what is wrong, set aria-invalid and aria-describedby on the field, and focus it (fixes #25); - the result blocks are no longer live regions; the status line announces one summary line (fixes the rest of #53); - each tool has its own worker and a Cancel button, so the planner's long search never queues the calculator; - inputs are kept in the query string, a link carrying them runs the tool on load, and each result has Copy link and Copy result (#57). The worked example says whether its rows were missing or failed to draw; floor.js refuses a bin count of 0 as the Python does, where it used to turn it into 10; and the parity check holds the example's floor to 1e-9, not only the four decimals the report prints (fixes #55). A link to a later section now lands on it after the example has grown the page. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
TMHSDigital
force-pushed
the
site/phase-7-explainer
branch
from
September 23, 2026 15:03
fce54d7 to
32ae325
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #69, which is stacked on #68. Its base is
site/phase-6-search, so the diff shows only this phase. Two commits: the first moves the explainer's inline<style>and<script>intoexplainer.cssandexplainer.jswith no change in behaviour, so the second reads as a diff of real changes.Fixes #25. Fixes #53. Fixes #55. Fixes #57.
(These close when the stack lands on
main.)The explainer's opening
build_site.pywrites the card from the report's own ECE line, into a<!-- site:example-card -->slot. So it says exactly what the report says, it reads the same with scripts off, and it flips wording if the example ever becomes distinguishable.A Content-Security-Policy on every page
default-src 'self', withscript-src,style-src,connect-srcandworker-srcall'self',img-src 'self' data:(for the inline favicon),object-src,frame-src,base-uriandform-actionset to'none', and no'unsafe-inline'.check_site_links.mjsnow fails any page that lacks the tag or carries an inline<script>, a<style>block, or astyle=attribute. I confirmed it catches an injectedstyle=on a doc page.Calculator and planner
[object Object]. They now say what is wrong.aria-invalidand anaria-describedbypointing at the status line, and takes focus.aria-live; the status line announces one summary line instead.n,bins,accuracy,measured, andtarget,plan_bins,plan_accuracy), and a link that carries them runs the tool on load.floor.jsrefuses a bin count of 0, as the Python does, instead of turning it into 10.example-run.jsonnow carries the Python's unroundedcalibration_floor, and the parity check holds the page's floor to 1e-9 against it, not just to the four decimals of the report line./#plannerstopped short, because the worked example grows the page after it loads. It now lands on its target once the example is drawn, unless the reader has already moved.Checked
ruff,mypy --strict, andpytestpass. The new tests cover:floor_golden --check,check_floor_parity(now including the 1e-9 floor),check_site_links(with CSP),check_search, andrender_docs --self-testall pass.0shows the real message, marks the fieldaria-invalidwitharia-describedby="calc-status", and focuses it;?n=300&bins=10&accuracy=0.8&measured=0.09#calculator, and opening that link reproduces the result;#plannerlands 68 px from the top, just under the header, with its rail entry highlighted.Still open in #54: a browser smoke test in CI. It needs a package install in a workflow that has none today.
🤖 Generated with Claude Code