docs: correct drifted claims in five posts and the CSP comment - #1293
Conversation
|
Design rationale: why the rule list is deleted rather than corrected, and how the CSP comment was settled Four decisions here are not obvious from the diff. The rule list and the rule count are gone, not fixed. The straightforward repair was to drop the four names that were never in The bullets kept their colon form, against my own earlier note. I had written down that A sixth drifted claim came from the scan, not from reading. I checked every rule-shaped backticked token across The CSP comment was settled by booting an app, not by reading the code. The old comment blamed Tailwind's browser runtime, which is wrong twice over, since a scaffolded app compiles Tailwind to a static file and has no browser runtime at all. Rather than swap one single cause for another, I ran it: an app with One thing I deliberately left alone: the |
vivek7405
left a comment
There was a problem hiding this comment.
Went through this against the source it describes, since a PR that exists to remove wrong claims has to be held to its own standard. The CSP comment is right and I like that it was settled by booting an app rather than by reading; same for the rule names, the light-DOM convention wording, the state.tsCache correction, and the prefetch and X-Webjs-Have corrections. Those all check out.
Five problems, and the pattern in them is worth naming: four are in strip-types-not-esbuild.md and client-router-turbo-drive-style.md, where correcting one paragraph left the paragraphs around it saying something else. The rule for this PR should be that a post is internally consistent when I am done with it, not just that the sentence I targeted is now true.
One is a new wrong claim this PR introduces (the view-transition opt-in), which is the worst kind to ship here. Two are untouched lines that my own edit turned into contradictions. One is a claim I made stricter than the code supports. One is true in dev and false in prod.
Path-level, since it sits outside the diff: strip-types-not-esbuild.md:29 shows import { createRequire, stripTypeScriptTypes } from node:module. dev.js:6 imports only createRequire, and ts-strip.js:28 spells out that the named import of the builtin is forbidden because it is a link-time SyntaxError on a runtime that lacks the export. That is the Bun case the new paragraph at line 64 introduces, so the post now shows a snippet the framework documents as unsafe, twelve lines above the paragraph explaining why Bun needs a different backend.
vivek7405
left a comment
There was a problem hiding this comment.
Second read, scoped to the fix commit and its blast radius. Five more, and they are the same failure as the first round rather than a new one: I fixed the sentence and left the surfaces and paragraphs around it disagreeing with it.
Worth stating plainly, because it is the lesson of this PR. Correcting a claim is not done when the claim is true. It is done when nothing else in the post, the reference docs, or the scaffold still says the old thing, and when the corrected sentence does not quietly depend on a stale paragraph above it.
- The view-transition correction never left the blog. AGENTS.md and the scaffold's own view-transitions demo both still showed the bare meta tag that enables nothing, and the demo contradicted its own
metadata.otherblock two lines above it. A scaffolding agent reads those, not the blog. - The strip-types paragraph I added gave the wrong reason for the namespace import. The hazard
ts-strip.jsdocuments is an older Node crashing the import before the version preflight can report the real problem. A runtime with no stripper is the case the namespace import handles gracefully, so I had named the good case as the failure. - The client-router post still described the pre-keyed boundary format, and the line I added leaned on that section. Sharing a layout chain is not sufficient for the short-circuit, since the route key has to match too, which is the entire reason the entries are keyed.
- The same post said the router needs an ES module import in the root layout. It auto-enables when core loads.
- The rule framing I wrote dropped a category the docs site carries. A reactive prop that silently stops re-rendering is not a crash, a leak, or a strip failure, and several rules catch only that.
All five are fixed. I generated a scaffold app and booted it to confirm the demo route serves and its visible prose now matches the meta it actually emits, since that one is generated code rather than prose.
vivek7405
left a comment
There was a problem hiding this comment.
Third read, scoped to the previous fix commit. Five more, and the shape has shifted in a way worth recording.
Two were the keyed-swap correction being half-applied. I added sentences describing the keyed behaviour to a post whose step list and "how it knows what to swap" section still described the single-tier rule the key replaced, so adjacent paragraphs contradicted each other and following the older one gave the reader exactly the morph the newer one disclaimed. The real rule is two-tier, a changed route key wins first and anchors at the parent of the shallowest change, deepest-shared applies only when no key changed, and the morph is chosen only when that boundary is the leaf on both sides. I had also counted only layout boundaries, where SSR emits one around the page too, which is the pair that makes a bare param change remount.
One was a second phantom rule, and it is the most useful finding of the three rounds. The agent skill named no-scaffold-placeholder as a shipping webjs check rule. It was removed in #969, with a test asserting it is no longer registered. That is precisely the defect this PR exists to remove, sitting on the canonical agent surface that gets bundled into every scaffolded app, and my own phantom-rule scan never saw it because I had scoped the scan to blog/ and the docs site. The scan now covers the skill too.
One was the category I added last round being applied to the blog alone. The same framing is stated in AGENTS.md, in the skill, in the webjs check --rules banner that my corrected sentence explicitly sends readers to as the authority, and in a second post. All four now match the docs site.
All fixed. A generated app passes webjs check clean and boots, and the scaffold gallery tests still pass.
vivek7405
left a comment
There was a problem hiding this comment.
Fourth read. Three findings, down from five and five, and for the first time none of them is a new claim I introduced this round.
Two were the keyed-swap rewrite still not finished. Step 4 promised the swap preserves input values, scroll, popover state and DOM identity, which is true of the morph tier and false of the replace tier that the step directly above had just said a changed route key triggers. A replace tears the live range out and inserts fresh nodes, carrying across only data-webjs-permanent. I had also called the morph the cheaper path when it is the more expensive one, chosen for state preservation rather than speed. And the section on what stays mounted still said replacing only the innermost pair is what preserves it, which is exactly what the keyed rule overrides.
The third is the better find. The api gallery generator told every generated app, in five route files and its own docstring, that webjs check fails while the scaffold marker remains. That is the same removed rule as last round, but this time in GENERATED SOURCE rather than in docs, so a developer running webjs create --template api reads a false instruction in their own repo. The full-stack gallery had already dropped the claim and only its clear is asserted by a test, so nothing covered the api side. A freshly generated api app now passes webjs check clean, which is what the corrected comment says.
Verified by generating an api app, running check on it, and booting two of its routes. All 59 scaffold tests pass.
Three rounds of this PR have now turned up the same failure in three different guises: a claim corrected in one place and left standing in the places around it. The phantom-rule scan I built for this PR only looked at blog/ and the docs site, which is why the skill copy survived round two and the generator copy survived round three. Worth remembering that the scan's SCOPE was the defect both times, not the individual lines.
vivek7405
left a comment
There was a problem hiding this comment.
Fifth read. Seven findings, and one of them falsified the claim this PR leads with, so this round is the most useful of the five.
The CSP comment had the mechanism backwards. style-src is 'self' 'unsafe-inline' and carries no nonce source at all; __NONCE__ appears only in script-src. So the nonce SSR stamps on the framework's own head <style> is INERT under this policy, and that rule is admitted by 'unsafe-inline' exactly like the four app-authored kinds it was being contrasted with. Drop 'unsafe-inline' and the framework's own style is blocked too. The contrast the comment drew does not exist, and my empirical probe did not catch it because it measured whether the attribute was PRESENT, not whether CSP honours it. Presence was the wrong question. The comment now says the allowance covers all inline style, framework included, and enumerates the app-authored shapes as what keeps it non-droppable without claiming WebJs escapes it.
I deleted a statement that was true. The original post's third non-goal was nested-route DATA deduplication, pointing at the HTTP cache and cache() memoization. That is not what X-Webjs-Have does, and it is still accurate. I replaced it with a claim that the mechanism "has since been built", when the post's own # The X-Webjs-Have optimization section was there at first publish. So the issue's premise on that item was wrong, and I introduced drift while removing drift. Restored, with prefetch left as the one that genuinely changed. This is the inverse of every other finding in this PR and the one I would most want flagged.
The rest: the post's "1400-line" figure for router-client.js is off by more than 3x (it is 4777), and a count in prose is the same fuse this PR removed from another post, so it is gone rather than corrected. The api showcase still shipped a webjs-scaffold-placeholder token named after the rule retired in #969, which the full-stack gallery had deleted outright rather than merely reworded; it is gone from generated source now, with a test asserting absence for the api template, since the missing api-side assertion is precisely why the full-stack cleanup never reached it (counterfactual: that test fails against the previous generator). And the widened rule-catches framing reached four surfaces last round and missed three, including check.js itself, the source the other four describe.
Verified: csp.js and check.js remain comment-only against main, csp 16/16 and check 65/65 pass untouched, 13/13 scaffold gallery tests, a generated api app passes webjs check clean and serves, and the docs and blog pages render with the corrected text.
vivek7405
left a comment
There was a problem hiding this comment.
Sixth read, and the last one this cycle takes. Three findings, all fixed in the commit above, and the branch now sits with those three fixes UNREVIEWED. I am stopping here rather than running a seventh round, and I would not merge this without another read over the whole diff.
The findings:
The style-src correction reached csp.js but not no-build-via-jspm-io.md, which stated the same wrong contrast in almost the same words ("The framework's own head <style> carries the nonce. App styles generally do not"). That is the third place this claim lived, and the PR body had asserted only two needed correcting.
The csp.js docstring sentence I wrote in the previous commit was garbled. It listed app-authored style inside an enumeration of WebJs's own output, and its trailing relative clause had no working antecedent, so read literally it said the policy nonces inline style for scripts only.
The scaffold-sync rule swapped one unmet requirement for another. I replaced the retired-marker instruction with "a line naming what to delete when the demo is pruned", which the full-stack gallery deliberately does not carry, since gallery:clear sheds the whole gallery in one step. An author following it would have added prune comments to 26 demos designed not to need them.
On the cycle as a whole. Six rounds found 28 real problems, and the count per round did not converge (5, 5, 3, 7, 3). Two of those findings were mine in a direction worth naming: I introduced a wrong claim about the view-transition opt-in, and I deleted a nested-route data-deduplication non-goal that was true, on a premise from the issue that turned out to be wrong. So this PR both removed drift and briefly added some.
The recurring failure was never a hard fact. It was scope. Every round found the corrected claim still standing somewhere I had not looked: the reference docs, the scaffold, generated source, a sibling post. The phantom-rule scan I built covered blog/ and the docs site only, which is exactly why the skill copy survived round two and the generator copy survived round three. When a claim lives in N places, fixing it in one and verifying that one is not progress.
vivek7405
left a comment
There was a problem hiding this comment.
Short delta round on the three previously unreviewed fixes. Two findings, both mine, both fixed in 33e8cd59.
The CSP correction had one more home. website/app/docs/deployment/page.ts stated the same framework-versus-app nonce contrast the source comment now denies, in nearly the same words as the blog sentence already corrected. I had written the docs site off as N/A in the PR body, which is exactly what let it be skipped, and that line of the body is now fixed too. The related global-error guidance in AGENTS.md and error-handling said an inline style there must carry the nonce; under the default policy that is inert for style and load-bearing only for script, so both now say which is which.
The scaffold-sync rule I rewrote last round overcorrected. I had written that the full-stack demos deliberately carry no prune note, which is false: the auth card, the server-actions card that depends on it, the gallery nav module and the icon route all carry one, and AGENTS.md documents selective pruning that a blanket prohibition contradicted. The rule now describes when a prune note earns its place, which is a card spanning several paths or one another card depends on, rather than forbidding it.
Worth noting for anyone reading this trail cold: that is the third time a correction landed in one place while the same sentence sat in another. The lesson stands at the end of the PR as it did at the start, and it is about search scope rather than about any individual claim.
vivek7405
left a comment
There was a problem hiding this comment.
Fix-check on the previous commit, and it found two, so this is the second consecutive check to come back with something. I am stopping the cycle here rather than running another, which is the rule doing its job and not a judgement that the branch is finished.
Both are fixed in 7b762002, and both were mine. The global-error demo's comment named @webjsdev/server as the source of cspNonce() while the file imports it from @webjsdev/core two lines below, which is also the import every other surface recommends; I rewrote that sentence last commit and carried the stale package name through. And the prune-note rule cited a header on the generated app/page.ts that does not exist, since that instruction is a comment in the generator sitting above the writeFile call and never reaches a generated app. I had swapped one wrong pointer for a more wrong one. It now points only at gallery:clear, which is a claim that holds.
The CSP fact itself is settled. This check swept the repo and found no remaining place claiming an inline <style> needs or is admitted by a nonce, and no remaining contrast between the framework's nonced style and un-nonced app style. That was the PR's headline correction and it is now consistent across the source comment, two blog posts, two docs-site pages, AGENTS.md, and the scaffold.
What is NOT settled is the branch's state. The two fixes above are unreviewed. Eight rounds have now found 34 problems, and the last four rounds were all cleanup of my own corrections rather than of the original drift, which says the corrections themselves need a read more than the original claims do. My recommendation to the owner is one fresh review over the whole diff before merging, not another delta: every delta round since the second has been scoped to a fix commit, and scope is exactly what kept letting the same claim hide one file over.
Six published statements contradicted the code they described, and the blog is the surface search engines and AI agents read most, so a reader was being sent after rules and fallbacks the framework has never had. Four rule names in the posts were never in RULES, and one of them was promised as a linter that would catch unprefixed light-DOM selectors. The rule list and rule count are gone rather than corrected, because a list copied into a post is what drifted in the first place; the posts now name a few real rules and point at `webjs check --rules`. The esbuild fallback was removed from the strip path long ago, and the post describing it was the last place claiming it still existed. The same post showed a TS_CACHE identifier that does not exist. The CSP comment blamed Tailwind's browser runtime for the style-src inline allowance. A scaffolded app compiles Tailwind to a static file and has no browser runtime at all, so the comment now enumerates the four things that genuinely carry no nonce, and contrasts them with the framework's own head style, which is signed. Verified by booting an app with csp on and no Tailwind in it: four style tags, one nonce. Two router non-goals had since been built, prefetch and the per-nav full refetch, the second by a mechanism the same post already explains. Comment-only in csp.js; every directive string is untouched and packages/server/test/csp/csp.test.js passes unedited.
The first pass fixed the sentence it targeted in each post and left the paragraphs around it saying something else. Five follow-ons. The view-transition opt-in needs content="same-origin"; the router reads that value and enables nothing without it, so the bare meta tag the last commit named would have left a reader with no transitions. strip-types-not-esbuild showed a named import of stripTypeScriptTypes, which ts-strip.js documents as unsafe: a named import of a missing builtin export is a link-time SyntaxError, which is exactly the Bun case the same post now explains twelve lines below. It also said amaro would only be installed directly if a future Node dropped it, while the new paragraph says amaro is the Bun backend today. It is an optional dependency now, so the two paragraphs described different worlds. The clean 500 naming the file is the dev response. Production stays terse on purpose so no path leaks, with the detail in the log. "Every rule covers an invariant that crashes in production" was stricter than the rule set: array-prop-uses-array-type says of itself that the wrong one does not crash, and no-server-env-in-components is a leak. AGENTS.md frames the bar as a crash, a leak, or a strip failure.
The blog correction landed without the surfaces around it. AGENTS.md and the scaffold's view-transitions demo both still showed the bare meta tag, which enables nothing, and the demo contradicted its own metadata block two lines above. Both now name content="same-origin". The client-router post described the pre-keyed boundary format, so the line added last commit leaned on a stale section and generalized wrongly: sharing a layout chain is not enough, the route key has to match too, or a dynamic layout held for other params would wrongly short-circuit. The marker format, the X-Webjs-Have entry shape, and that line now agree with ssr.js. The same post also still said the router needs an import in the root layout; it auto-enables when core loads. The strip-types post gave the wrong reason for the namespace import. The hazard is an older Node crashing the import before the version preflight can report the real problem, not a runtime without a stripper, which is the case a namespace import exists to handle gracefully. The rule framing dropped a category the docs site carries: a reactive prop that silently stops re-rendering is neither a crash nor a leak nor a strip failure, and several rules only ever catch that.
The client-router post described the pre-keyed, single-tier swap while the sentences added last commit described the keyed one, so adjacent paragraphs contradicted each other. The real rule is two-tier: a changed route key wins first and anchors a replace at the PARENT of the shallowest change, and only when no key changed does the deepest shared boundary become the target, morphing just when it is the leaf on both sides. Following the old text gave the reader exactly the morph the new text disclaimed. SSR also emits a pair around the page itself, which is what makes a bare param change remount, and the post counted only layouts. The agent skill named `no-scaffold-placeholder` as a shipping check rule. It was removed in #969, and a test asserts it is no longer registered. That file is the canonical agent surface and is bundled into every scaffolded app, so it is the same defect this PR exists to remove, on the surface where it does the most damage. The rule-catches framing that gained a category last commit is stated in four places, and only the blog got it. AGENTS.md, the skill, the `webjs check --rules` banner the corrected sentence points readers to, and a second post now match the docs site.
Step 4 promised that the swap preserves input values, scroll, popover state and DOM identity, but that is the morph tier only. A replace tears the live range out and inserts fresh nodes, carrying across just the elements marked data-webjs-permanent, so the step contradicted the step above it, which had just said a changed route key replaces. The morph was also called the cheaper path when it is the more expensive one, chosen for state preservation rather than for speed. The section on what stays mounted still said replacing only the innermost pair is what preserves it. Under the keyed rule the innermost is exactly not the target when a route key changed. The api gallery generator told every generated app, in five route files and its own docstring, that webjs check fails while the scaffold marker remains. That rule was removed in #969, so a freshly generated api app passes check clean and the comment was false in generated source rather than only in docs. The full-stack gallery had already dropped the claim and only its clear is asserted, so nothing caught the api side.
The style-src comment had the mechanism backwards. That directive carries no nonce source, so the nonce SSR stamps on the framework's own head style is inert under this policy and every inline style, WebJs's own included, is admitted by 'unsafe-inline'. Dropping it would block the head rule too, so the contrast the comment drew between framework style and app style does not exist. What keeps the allowance necessary is inline style with no realistic nonce path, which the comment now enumerates without claiming the framework escapes it. The client-router post lost a non-goal that was accurate. Nested-route DATA deduplication is not what X-Webjs-Have does, and it still does not happen: the HTTP cache and cache() memoization cover that layer. The X-Webjs-Have section also predates the post's publication, so nothing about it "has since been built". Prefetch is the one that changed, and the section now says so and nothing more. The post's 1400-line count for router-client.js was off by a factor of three, and a count in prose is the same fuse this PR removed elsewhere, so it is gone rather than corrected. The api showcase kept shipping a webjs-scaffold-placeholder token named after the rule retired in #969, which the full-stack gallery deleted outright. It is gone from generated source too, with a test asserting absence for the api template, since the missing api-side assertion is why the full-stack cleanup never reached it. The widened rule-catches framing reached four surfaces last time and missed three, including check.js itself, which is the source the others describe.
The style-src fix reached csp.js but not the post that states the same thing in the same words. no-build-via-jspm-io said the framework's head style carries the nonce while app styles do not, which is the contrast that does not exist: style-src has no nonce source, so unsafe-inline admits every inline style including WebJs's own head rule. The csp.js docstring sentence introduced with that fix was garbled. It listed app-authored style as part of WebJs's own output and trailed a relative clause with no antecedent. It now names the head style as the framework's one inline style and states plainly that the nonce covers scripts only. The scaffold-sync rule swapped one unmet requirement for another. The full-stack gallery deliberately carries no per-demo prune comment, because gallery:clear sheds the whole gallery in one step, so telling an author to add one would mean touching 26 demos designed not to need it.
The deployment page carried the same framework-versus-app nonce contrast the source comment now denies, in nearly the same words as the blog sentence already fixed. style-src has no nonce source, so unsafe-inline admits every inline style including WebJs's own head rule, and the page now says that before listing what makes the allowance hard to give up. The global-error guidance said an inline style there must carry the nonce. Under the default policy that is inert for style and load-bearing only for script, so both places now say which is which. The scaffold-sync rule went too far the other way. Several cards do carry a prune note, and they are the ones where removal spans paths or another card depends on them, so the rule now describes that shape instead of forbidding it. AGENTS.md also documents selective pruning, which a blanket prohibition contradicted.
The scaffold's own global-error demo was the third surface of that one piece of guidance, and the previous commit fixed only two. It told the reader an inline style there needs a nonce, and that the nonce is what allows it, when under the default policy unsafe-inline is what allows it and the nonce is inert. The stamped nonce stays, since it is what makes the page keep working if you tighten style-src, and the comment now says that instead of claiming it is load-bearing today. The route-handler demo described the same inert operation. The prune-note rule cited the wrong example. The auth card names its own paths rather than the server-actions card, so it illustrates the spanning-several-paths shape, not the depends-on-another shape. The per-route delete instruction a generated app carries is in its app/page.ts header, not in its AGENTS.md.
The global-error demo's comment named @webjsdev/server while the file imports cspNonce from @webjsdev/core two lines below, which is also the import every other doc surface recommends. The previous commit rewrote that sentence and carried the stale package name through. The prune-note rule cited a header on the generated app/page.ts that does not exist. That instruction is a comment in the generator, above the writeFile call, so it never reaches a generated app. The rule now points only at gallery:clear, which is a claim that holds.
7b76200 to
6f8458a
Compare
Closes #1256
Summary
Published statements about WebJs contradicted the code they described. Most were in blog posts, which is the surface search engines and AI agents read most, and one was the
csp.jscomment that is the upstream cause of a claim already corrected elsewhere. A reader who tried to satisfy awebjs checkrule that never existed, or who expected an esbuild fallback that was deleted, was chasing something the framework has never had.Prose and comments only. No behaviour changes; the two source files touched (
csp.js,check.js) are edited in their comments alone, and one test is added.What changed
webjs checkrules are gone.tests-exist,no-json-data-filesandlight-dom-css-prefixwere never inRULES, andno-server-imports-in-componentswas a wrong spelling ofno-server-import-in-browser-modulewith a wrong description. A second phantom,no-scaffold-placeholder(retired in dogfood: design-distinctness machinery is over-engineered and mis-layered #969), was still named as live in the agent skill and shipped as a token in every generated api app. The enumerated rule list and the "~10 rules" count are gone rather than corrected, since a hand-maintained list inside a post is exactly what drifted; the posts now name real rules and point atwebjs check --rules.strip-types-not-esbuild.mddescribed anesbuild.transformfallback that no longer exists, showed aconst TS_CACHE = new Map()identifier that does not exist, and showed a named import ofstripTypeScriptTypesthatts-strip.jsdocuments as a link-timeSyntaxError. The 500 it describes is now scoped to dev, since production is deliberately terse so no path leaks.csp.jsblamed Tailwind's browser runtime for thestyle-srcinline allowance. The real mechanism is thatstyle-srccarries no nonce source at all, so the nonce SSR stamps on the framework's own head<style>is inert and'unsafe-inline'admits every inline style, WebJs's own included. The comment now says that and enumerates the app-authored shapes that keep the allowance non-droppable. The same wrong contrast was corrected inblog/no-build-via-jspm-io.md.client-router-turbo-drive-style.mddescribed the pre-keyed boundary format, claimed the router needs an import in the root layout, attributed the morph tier's state preservation to the replace tier, and carried a line count off by more than 3x. The marker format, theX-Webjs-Haveentry shape, the two-tier swap rule, and the auto-enable behaviour now agree with the source. Its nested-route data-deduplication non-goal was restored after turning out to be true.<meta name="view-transition" content="same-origin">everywhere it is stated. The bare tag enables nothing, andAGENTS.mdplus the scaffold's own demo were stating the bare form.check.jsitself, after the docs site turned out to carry a category the others dropped.Verification
blog/,website/app/docs/and the agent skill, againstRULESimported fromcheck.js(not counted by eye, and not read off CLI output where long names shift the column spacing). Clean.__NONCE__appears only inscript-src, which is why the nonce is inert for styles. An earlier empirical probe measured only whether the nonce attribute was present, which is the wrong question, and its conclusion has been corrected.csp.jsandcheck.jsare comment-only against main, andcsp.test.js(16/16) andcheck.test.js(65/65) pass with both test files untouched, which is itself the check that no value moved./features/view-transitionswith its prose matching the meta it emits; an api app passeswebjs checkclean and serves its routes. Scaffold tests 13/13.modulepreload, asserting each corrected claim present and each stale one absent.Doc surfaces
packages/server/src/csp.jsandcheck.js(comments),AGENTS.md,.agents/skills/webjs/references/testing.md,website/app/docs/configuration/page.ts,packages/cli/bin/webjs.js(the--rulesbanner),packages/cli/lib/api-gallery.js,packages/cli/templates/gallery/app/features/view-transitions/page.ts,examples/blog/CONVENTIONS.md, and one scaffold test.website/app/docs/deployment/page.tsanderror-handling/page.tscarried the same wrong CSP contrast and are corrected.conventions/page.tsandclient-router/page.tswere already right and are untouched.Tests
One test added (the api marker assertion, with its counterfactual). No test pins blog prose, and none should: pinning sentences to
RULESwould red every future rule addition on an unrelated file.website/test/ssr/pre-block-a11y.test.tsrenders the shared markdown post body from a synthetic string rather than a real post, so nothing pinned the edited sentences. The verification above stands in for a counterfactual on the prose, which is why every claim cites the file and line that governs it.Browser, e2e and Bun layers are N/A for the prose: the only source edits are comments, and
csp.jsis not on the Bun-parity hook's runtime-sensitive list. CI ran all of them anyway and they pass.