Skip to content

Re-vendor the §15 fixture: this copy was two generations behind - #4

Merged
MichalAFerber merged 3 commits into
mainfrom
chore/revendor-15-fixture
Sep 5, 2026
Merged

MichalAFerber merged 3 commits into
mainfrom
chore/revendor-15-fixture

Conversation

@MichalAFerber

@MichalAFerber MichalAFerber commented Sep 5, 2026 •

Copy link
Copy Markdown
Owner

The vendored test/xss-lint-fixture.test.js here was the 80-line original. The template on tgwab-standards origin/main is 221 lines and carries two controls this copy had neither of — the expect(flagged).toContain('case1') guard (standards #123) and the describe('the rule reaches this repo') coverage block (standards #111).

The fixture specimen test/fixtures/xss-lint-fixture.js was already byte-identical to the template, so only the test file moves.

The measurement came first

A stale copy and an inert rule look identical from the config text and have different fixes, so this was measured before anything was vendored — eslint --print-config against real shipped source, read for whether no-restricted-syntax resolves at severity 2 carrying the JSON.stringify selector:

file severity selector
tools.js 2 ✅
features/text-tools.js 2 ✅
features/code-data.js 0 ✅

This repo passes the coverage control, so the stale copy was the whole defect — it is not in the resizewizard-api class. The severity-0 is the repo's own declared §15 opt-out resolving exactly as written (the code-data report is join('\n')-ed and rendered as text, never parsed as HTML), not a gap.

Both new controls were seen to fire

A control that has only ever been observed green is not yet evidence, so each was induced to fail:

A — no-restricted-syntax: 'off' → 3 failed, the guard reporting:

× leaves the two negative controls alone
  → expected [] to include 'case1'

Under the 80-line copy that test held only the two not.toContain assertions, which pass green on a wholly inert rule. That is the hole #123 closed.

B — rule left ON, glob narrowed to src/**/*.js (this repo ships nothing under src/) → 2 passed, 1 failed:

✓ flags all five hazards …
✓ leaves the two negative controls alone
× applies to at least one file this repo actually ships
  → The §15 rule resolves for none of the 11 files ESLint lints in this repo …

This is the sharper one: the first two tests lint the synthetic path src/__xss-lint-fixture__.js and therefore cannot tell a covered repo from an inert one. Only the coverage block sees it. That is the defect #111 closed, reproduced here on purpose.

eslint.config.js was verified byte-identical to its original after both calibrations (git diff empty); no config change ships in this PR.

Gate

  • npm run lint — clean
  • npm test — 3 passed (3)
  • ESLint v10.9.1, vitest v4.1.11

Repo state read (rule 5)

textwizard-tools   chore/revendor-15-fixture off origin/main   0/0
tgwab-standards    main                                        origin/main 0/0  (template 221 lines, DS v2.56.0)

Rule 6: worktrees eslint-v10, lint-adopt (both merged branches); gh pr list --state all --search fixture → #2, #3, both MERGED. No competing work.

Refs MichalAFerber/tgwab-standards#129

🤖 Generated with Claude Code

https://claude.ai/code/session_01CRX8K6Ys4CDRDcdknCuPVh


Amended 2026-09-05 — the coverage control changed under this PR

devops caught that the control this PR vendors was itself a gate reporting coverage it did not have: expect(covered.length).toBeGreaterThan(0) passed at 7 of 29 in resizewizard-api and at 3 of 3 on pure build tooling in uploadwizard-app. Fixed in tgwab-standards#131 before these land, so six repos do not vendor the weak version and get re-fixed next sweep.

The assertion now names files. Each adopter ships test/fixtures/xss-lint-covers.json naming the product source the rule must cover; the test asserts the rule actually resolves for each, and refuses build tooling, kit files, and names that do not exist. The count is still reported on every run so a human sees the shape.

A sidecar rather than a field in the test file, because §15 requires the kit to ship byte-identical and the whole comparability argument depends on that.

A floor proportion would have been worse — uploadwizard-app scores 100%.

Honest about what it is: nothing can check that a named file is genuinely product source. It is a reviewable declaration, not a measurement, and the template says so in place — reading the old check as a measurement is exactly how it failed.
This repo: names tools.js, features/text-tools.js, features/fancy-text.js. Reports 10 of 11. features/code-data.js is deliberately not named — it carries the declared §15 opt-out. Seen to fire: naming it fails with "the rule does NOT resolve for features/code-data.js", which is the check telling a covered file from an exempted one.


Amended 2026-09-05 — the covers declaration was inverted, and is corrected at 62ba7fe

The review is right and the fix is the swap it asked for. No softened justification: the entry is
replaced, not reworded.

covers  BEFORE  tools.js · features/text-tools.js · features/fancy-text.js
covers  AFTER   features/_editor.js · features/diff.js · features/qr-code.js

The full ranking, all 11 linted files, zeros included

Counted with grep -o per construct and summed — not grep -c, so a line carrying two
constructs counts twice.

file innerHTML insertAdjacentHTML outerHTML createElement total
features/_editor.js 3 0 0 6 9 now named
features/diff.js 3 0 0 0 3 now named
features/qr-code.js 1 0 0 2 3 now named
features/analyzers.js 0 0 0 0 0
features/case-converter.js 0 0 0 0 0
features/code-data.js 0 0 0 0 0 declared §15 opt-out
features/fancy-text.js 0 0 0 0 0 was named
features/generators.js 0 0 0 0 0
features/text-tools.js 0 0 0 0 0 was named
features/translators.js 0 0 0 0 0
tools.js 0 0 0 0 0 was named

No omitted file outranks a named one. The three named are the only three with a nonzero score.

The counts were read, not trusted

All 15 nonzero sites are real assignments or document.createElement calls — none in a
comment or a string:

features/_editor.js   13,48,50,117,166,168,275,291,363
features/diff.js      48,88,118
features/qr-code.js   18,57,142

The zeros were read too, and one of them was a trap: tools.js matches <[a-zA-Z/] once, at line
3 — features/<id>.js, inside a comment. Code-shaped text in a comment is exactly what inflates
this kind of tally, so it is named here rather than left in a total.

Rule 4: the instrument produced 9, 3 and 3 on the same corpus in the same run, so the eight
zeros are a measurement and not a dead grep. I also checked the three former entries for markup by
a route the grep cannot see, since the review invited that: zero template literals containing an
HTML tag, zero <tag occurrences outside that one comment, zero append/insertBefore/
replaceChildren/setHTML. tools.js is a 121-line registry with no document.* reference at
all; text-tools.js and fancy-text.js are string transformers, and fancy-text.js's only DOM
touch is one .value. There is no such route. The entry was wrong, not imprecise.

features/code-data.js stays out, and the gate still tells it apart

Its §15 opt-out is unchanged and still correct. Seen to fire: adding it to the corrected
declaration fails with the rule does NOT resolve for features/code-data.js``.

On the MEDIUM about the count never being printed — measured false

The set-level finding says npm test here prints Tests 3 passed (3) and nothing else. That is
true locally and not true in CI, which is the run the sentence is about. This repo's own CI log
at the reviewed head 36930f1:

DS §15 coverage: the rule resolves for 10 of 11 linted files; 3 named as product source.

Full evidence, five repos, and the local/CI mechanism, is on tgwab-standards#131 — where the
report is now written to stdout so the two agree.

Gate

  • npm test — 3 passed (3)
  • npm run lint — clean
  • xss-lint-covers.json parses

Still a draft. The kit re-vendor is not in this push — tgwab-standards#131 moved again today
(template blob 3f6ed048 → b0dc3063), so this repo re-vendors from its merged form.

The vendored `test/xss-lint-fixture.test.js` was the 80-line original. The
template on `tgwab-standards` `origin/main` is 221 lines and carries two
controls this copy had neither of: the `expect(flagged).toContain('case1')`
guard (standards #123) and the `describe('the rule reaches this repo')`
coverage block (standards #111). The fixture specimen itself was already
byte-identical to the template, so only the test file moves.

MEASURED FIRST, because a stale copy and an inert rule look alike and have
different fixes. `eslint --print-config` against real shipped source, read for
whether `no-restricted-syntax` resolves at severity 2 with the JSON.stringify
selector, rather than inferred from the config text:

    tools.js                 severity=2  selector=true
    features/text-tools.js   severity=2  selector=true
    features/code-data.js    severity=0  selector=true   <- declared §15 opt-out

So this repo genuinely passes the coverage control and the stale copy was the
whole defect. The severity=0 is the repo's own named exemption (the code-data
report is joined with newlines and rendered as text, never parsed as HTML)
resolving exactly as written, not a gap.

BOTH NEW CONTROLS WERE SEEN TO FIRE, not merely seen green:

  A. `no-restricted-syntax: 'off'` — 3 failed. The case1 guard reports
     "expected [] to include 'case1'". Under the 80-line copy this test held
     only the two `not.toContain` assertions, which pass green on an inert
     rule; that is the hole #123 closed.

  B. rule left ON, glob narrowed to `src/**/*.js` (this repo ships nothing
     under `src/`) — 2 passed, 1 failed. Only the coverage block sees it,
     because the other two lint the synthetic path `src/__xss-lint-fixture__.js`
     and cannot distinguish a covered repo from an inert one. This is the
     defect #111 closed, reproduced here.

Restored config verified byte-identical after both calibrations.

Gate: `npm run lint` clean, `npm test` 3 passed.

Refs MichalAFerber/tgwab-standards#129

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRX8K6Ys4CDRDcdknCuPVh

--- amended 2026-09-05: the coverage control changed under this PR ---

tgwab-standards#131 replaced the `> 0` coverage assertion with a named-file
declaration, because `> 0` passed at 7 of 29 in resizewizard-api and at 3 of 3
on pure build tooling in uploadwizard-app. This PR now vendors the new template
and ships `test/fixtures/xss-lint-covers.json`.

Named as product source: tools.js, features/text-tools.js, features/fancy-text.js.
Deliberately NOT named: features/code-data.js, which carries this repo's declared
§15 opt-out and correctly resolves to severity 0.

Reported by the new run: the rule resolves for 10 of 11 linted files.

The named-file assertion was seen to fire here, not just pass: pointing one name
at features/code-data.js — the opted-out file — fails with "the rule does NOT
resolve for `features/code-data.js`, which this repo names as covered product
source." That is the check distinguishing a covered file from an exempted one.

Gate re-run: `npm run lint` clean, `npm test` 3 passed.
@MichalAFerber
MichalAFerber force-pushed the chore/revendor-15-fixture branch from 5eded7c to 36930f1 Compare September 5, 2026 02:55

@MichalAFerber MichalAFerber left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stands in for: REQUEST CHANGES. GitHub will not take --request-changes from the account that
opened the PR, so this is a --comment review. Comment-only: I have not approved, marked ready,
pushed, or merged anything.

Rule 5 — branch and offset of everything I read

tgwab-standards         origin/main                     0/0    templates/xss-lint-fixture.test.js  222 lines  sha256 fb7f528f…
tgwab-standards         refs/pull/131/head  bb6bac1     DRAFT, UNMERGED           same path  375 lines  sha256 81195623…
textwizard-tools        refs/pull/4/head    36930f1
resizewizard-api        refs/pull/63/head   df8dbe6
uploadwizard-app        refs/pull/83/head   a12e1ad
wizard-web              refs/pull/103/head  5936e9b     (shared clone parked on fix/resizewizard-pricing-claims, behind 3 / ahead 1 — NOT read)
resizewizard-extension  refs/pull/45/head   bb73c0e     (shared clone parked on fix/clear-data-releases-seat, behind 6 / ahead 1 — NOT read)
mykk.us-extension       refs/pull/40/head   5e9b4fb

No git checkout in any shared clone. Every command ran in a detached worktree under
~/GitHub/.worktrees/<repo>/review-*, at the PR head, with a fresh npm ci / pnpm install,
and each worktree was left byte-clean (git status --porcelain empty) and then removed.


This repo

Reproduced, and it holds up

claim measured
reports 10 of 11 10 of 11 linted files; 3 named — exact
features/code-data.js is the one uncovered file exact — it is the declared §15 opt-out, rule resolves to 0
npm run lint clean, 3 tests pass clean, Tests 3 passed (3)

I also mutated the declaration six ways in a throwaway worktree. Every one fires, so the
control is real and not decorative:

mutation result
name features/code-data.js (the opt-out) ✗ "the rule does NOT resolve for features/code-data.js"
{"covers":[]} with no declaredGap ✗ "declaredGap MUST say why…"
name test/fixtures/xss-lint-fixture.js ✗ "a file this repo GAINS by adopting the kit"
name src/nope.js ✗ "named … but does not exist"
name scripts/build.js ✗ "is build tooling, not product source"
delete the sidecar ✗ "xss-lint-covers.json is missing"

HIGH — the declaration names three files that build no markup, and omits the three that do.

test/fixtures/xss-lint-covers.json:2

"covers": ["tools.js", "features/text-tools.js", "features/fancy-text.js"],
"why": "The tool host and two feature modules that build result markup from user-supplied text."

Measured across all three named files — no innerHTML, no insertAdjacentHTML, no outerHTML,
no createElement, no textContent, and no template literal containing an HTML tag:

$ grep -nE 'innerHTML|insertAdjacentHTML|outerHTML|createElement|textContent' \
      tools.js features/text-tools.js features/fancy-text.js
(no output)
$ grep -nE '`[^`]*<[a-zA-Z/]' tools.js features/text-tools.js features/fancy-text.js
(no output)

Control, because that is two negatives in a row: the same two patterns against
features/_editor.js return 15: ${options.length ?

…
straight away. The instrument works.

tools.js in particular is a static catalog — export const tools = [{id, name, icon, color, category, tagline, description, keywords}, …], 121 lines of data, no DOM at all. Calling it "the
tool host … that builds result markup from user-supplied text" is not a small imprecision; it is
the one sentence a reviewer is being asked to check.

The files that actually do the §15-relevant thing are covered by the rule and unnamed:

features/_editor.js:13    container.innerHTML = `…`
features/diff.js:48       container.innerHTML = `…`
features/diff.js:88       output.innerHTML = renderDiff(parts)   <- parts derives from user text
features/qr-code.js:57    container.innerHTML = `…`

Coverage does not change either way — the rule resolves for all 10 non-opt-out files, so the
assertion passes regardless. What changes is whether the declaration means anything. #131's own
argument for this design is that "a named file can be wrong in an interesting way, and a human can
say so in review." This is that. Please name features/_editor.js, features/diff.js, and
features/qr-code.js, and rewrite why to something that survives a grep.

HIGH — stale kit (see set-level finding 1)

This PR carries the unanchored NOT_PRODUCT_SOURCE[1]. It happens to be harmless for the
current declaration — none of the named paths has a scripts/, test/ or migrations/ segment —
but it is a byte difference from what wizard-web, resizewizard-extension and mykk.us-extension
shipped forty minutes later, and from tgwab-standards #131. Re-vendor after #131 merges.

MEDIUM — the body's own evidence needs one correction

"The count is still reported on every run so a human sees the shape" — measured false here.
npm test in this worktree prints Tests 3 passed (3) and nothing else. See set-level finding 3.


Set-level findings (identical in all six comments)

HIGH — the kit is not byte-identical across the six. It splits two ways, and nobody declared it.

templates/xss-lint-fixture.js — sha256 ff94e2e4… in all six and in both standards refs. Clean.

templates/xss-lint-fixture.test.js:

sha256 repos
81195623… wizard-web #103 · resizewizard-extension #45 · mykk.us-extension #40 — matches tgwab-standards refs/pull/131/head
79920a9c… textwizard-tools #4 · resizewizard-api #63 · uploadwizard-app #83 — matches nothing on either standards ref
fb7f528f… tgwab-standards origin/main (the retired > 0 version)

The whole difference:

 const NOT_PRODUCT_SOURCE = [
   /(^|\/)[^/]*\.config\.(js|mjs|cjs)$/,
-  /(^|\/)(scripts|migrations|tests?|__tests__|e2e|loadtest|test-kit)\//,   <- the three earliest PRs
+  /^(scripts|migrations|tests?|__tests__|e2e|loadtest|test-kit)\//,       <- #131 head, and the three later PRs
 ];

plus the comment paragraph explaining the anchor. Commit times say what happened: #4 22:24, #63
22:27, #83 22:29 all carry the unanchored form; #103 22:32, #45 22:34, #40 22:38 carry the
anchored one; tgwab-standards #131 was amended at 22:47. The anchor was discovered while
doing wizard-web — #131's own comment says so — and propagated forward but never back.

This is live, not cosmetic. I dropped the group-B copy into the wizard-web worktree, left
wizard-web's own covers.json untouched, and ran it:

× resolves for every file this repo NAMES as covered product source
  → DS §15: `../../apps/punctuationwizard/src/scripts/tool-mount.js` is build tooling,
    not product source.

Same declaration, two vendored copies, opposite verdicts, on real browser source. That is exactly
the comparability §15's byte-identity rule exists to protect.

HIGH — all six vendor from an unmerged draft. Merge order is a gate, not a preference.

tgwab-standards #131 is state=OPEN isDraft=true. Until it lands, origin/main templates/
still carries the 222-line > 0 control, so no repo in this set is byte-identical to the
standard as written
, which is the §15 requirement each PR body cites. #131 has already moved
once under three of these PRs. Nothing here should merge before #131 does, and the three group-B
repos must re-vendor from #131's merged form afterwards.

HIGH — "the count is still reported on every run so a human sees the shape" is false in five of six.

Every body carries that sentence. Measured, with controls:

run coverage line printed?
npm test / pnpm test (default reporter, non-TTY) — what CI runs no
CI=true npm test no
same run, but with the test failing yes
vitest run --reporter=verbose yes
turbo run test (wizard-web only — turbo gives the child a pty) yes

Controls, because an empty result is a claim about the instrument: (a) the identical console.log
does print through the same pipe when the assertion fails, so the pipe and the grep both work;
(b) a fresh two-line passing test that only calls console.log also prints nothing under the
default reporter; (c) plain node -e 'console.log(...)' through the same pipe prints. Vitest 4's
default reporter suppresses console.log from passing tests on a non-TTY.

No repo in the set sets a reporter ("test": "vitest run", no reporters in any vitest config).
So the number is visible exactly when the test is red — when it is the least interesting thing on
screen — and invisible on every green CI run, which is the case the sentence is about.

MEDIUM — the sidecar cannot express a partial gap, so the two repos that have one say nothing.

declaredGap is read only when covers is empty. A repo with genuine partial coverage has
nowhere to put it and the test never asks. Consequence inside this very set: uploadwizard-app
declares "our .astro/.ts product surface is invisible to this rule" — and wizard-web, same
Astro stack, 73 .astro + 8 .ts invisible, 2 of them containing JSON.stringify, declares
nothing, purely because its covers is non-empty. Same gap, opposite visibility, decided by a
schema branch rather than by the facts. Worth raising on #131 before it merges.

LOW — two prose defects repeated in all six bodies.

  • "the 221-line template on origin/main" — it is 222 lines (wc -l, file ends \n).
  • why is the only human-reviewable field in the sidecar and nothing reads it. Four of the six
    why strings I checked contain a factual error (see the per-repo sections). A field no gate
    touches is fine; a field no gate touches that the design calls "the whole point" deserves a
    reviewer checklist item in §15.

@MichalAFerber

Copy link
Copy Markdown
Owner Author

Blocking — the covers declaration is inverted, and this holds regardless of where tgwab-standards#131 lands.

The mechanism here is sound; I want that said first, because the six-way mutation matrix on this repo's declaration is what makes the new control credible for the whole set, and it all fires. The problem is the content of the declaration, which no mutation can reach — prose is not measured.

covers names three files with the justification "the tool host and two feature modules that build result markup from user-supplied text." Measured at head 36930f1, counting innerHTML|insertAdjacentHTML|outerHTML|createElement:

named in covers markup constructs
tools.js 0
features/text-tools.js 0
features/fancy-text.js 0
omitted from covers markup constructs
features/_editor.js 9
features/diff.js 3
features/qr-code.js 3

So the declaration names three files that build no markup at all and omits all three that do. Not imprecise — inverted.

Rule 4, because a zero is exactly the reading that should be distrusted: the instrument produces non-zero on the same corpus in the same run — 9, 3, and 3 on the omitted files — so the three zeros are a measurement rather than a broken grep. I also checked textContent, with the same result.

Why this is worse than the gen2 fixture it replaces. The old fixture proved the rule was wired and claimed nothing about coverage. This one makes an affirmative claim about which files carry the hazard, and that claim is green while pointing at the wrong files. A future reader trusts it, and the three files that actually construct DOM from user-supplied text are the ones nobody is watching. An empty covers with a declared gap — which is what uploadwizard-app#83 ships, correctly — is honest. A wrong covers is green and false.

The fix is a swap, not a rewrite: name features/_editor.js, features/diff.js, and features/qr-code.js, and evidence each with the construct count in the PR body. If tools.js and the two feature modules belong in covers for a reason the grep cannot see — string-templated markup handed to a helper, say — then say which construct to look for and I will re-measure against it.

features/code-data.js is fine and should stay out. Its declared §15 opt-out checks out: 19 backticks, zero markup constructs, consistent with a report that is join('\\n')-ed and rendered as text.

Everything else in this PR reproduces exactly: 10 of 11 linted, npm run lint clean, and code-data.js as the single uncovered file by design.

Also blocking, separately and for the whole set: this vendors tgwab-standards#131 while #131 is still an open draft. #131 merges first — otherwise the estate's best-covered adopters end up furthest from the published standard.

@MichalAFerber

Copy link
Copy Markdown
Owner Author

Blocking — this vendors a kit version that exists nowhere in tgwab-standards.

tgwab-standards has exactly two versions of templates/xss-lint-fixture.test.js across every branch and commit on GitHub: origin/main at 222 lines (no NOT_PRODUCT_SOURCE block at all) and origin/ds/comment-accuracy (#131) at 375 lines, sha 81195623. This PR ships 365 lines, sha 79920a9c. It matches neither.

It is not one generation behind. It is an unsourced fork, and the delta is 22 diff lines: a comment paragraph appearing in no tgwab-standards commit, plus a denylist regex that reverts #131's anchor.

this PR, line 289:   /(^|\/)(scripts|migrations|tests?|__tests__|e2e|loadtest|test-kit)\//,
#131,     line 299:   /^(scripts|migrations|tests?|__tests__|e2e|loadtest|test-kit)\//,

History search over templates/, each with a proven-positive control so a "no hits" is a measurement rather than a dead search:

git log --all -S'(^|\/)(scripts|migrations'                        -- templates/  -> NO HITS
git log --all -S'/^(scripts|migrations'                            -- templates/  -> bb6bac1  (control fires)
git log --all -S'THE SECOND PATTERN IS ANCHORED'                   -- templates/  -> bb6bac1  (control fires)

Why this matters more here than it would elsewhere: §15's entire comparability argument rests on the kit shipping byte-identical, and this PR's own body invokes that requirement by name. A re-vendor PR that vendors a file with no canonical source fails at its one job — and it is precisely the outcome the amendment says it exists to prevent, "so six repos do not vendor the weak version and get re-fixed next sweep."

No behavioural impact today — this is latent, not live. The unanchored regex only differs on a mid-path scripts/-like segment, and no covers entry here has one. The fix is mechanical: re-vendor from #131's head.

Three of the six sibling PRs (wizard-web#103, resizewizard-extension#45, mykk.us-extension#40) are already byte-identical to #131, which is what makes this a divergence rather than a sequencing artefact.

Also blocking, and it applies to all six: #131 is still an open draft. Every one of these ships xss-lint-covers.json plus a test that hard-fails when it is missing, and both the sidecar template and the DEV-STANDARDS text defining it exist only on #131's branch. Merging any of these first leaves a repo carrying a mandatory file whose format and semantics are defined nowhere in the published standard.

Correcting my own earlier reading on this PR set: I measured these at "365–375 lines, both markers present, ahead of the template" and treated that range as one population. It is two, and the hyphen was doing the work of hiding the finding.

@MichalAFerber

Copy link
Copy Markdown
Owner Author

Correction to my comment above — the SHAs I quoted were wrong. The finding and the partition are unchanged; the identifiers were not blob SHAs.

Canonical git blob SHAs, read from GitHub's tree API (authoritative — no local hashing, no ambiguity about which function or which content):

blob file
97cbb6e032f68031df965b919659f55513a6f7aa tgwab-standards origin/main — the 222-line version #131 retires
3f6ed048877412145d66f6db973bd98242bac15e tgwab-standards #131 @ 918b571 — the target
b71e006a4564caeb8f62ddab1dcb3178f7217e83 this PR — matches neither

3f6ed048 is also what wizard-web#103, resizewizard-extension#45 and mykk.us-extension#40 carry, so those three are byte-identical to #131 and this one is not.

Two independent instruments agreed on the partition and disagreed on the digits, which is what prompted me to go get the canonical value rather than pick a side. The partition was always the finding; quoting an unverifiable identifier as evidence for it was a defect in my comment, not in the finding.

MichalAFerber and others added 2 commits September 5, 2026 02:31
…e three that build none

The declaration was inverted. It named `tools.js`, `features/text-tools.js` and
`features/fancy-text.js` as "the tool host and two feature modules that build
result markup from user-supplied text." Measured across all 11 linted files,
counting occurrences of innerHTML, insertAdjacentHTML, outerHTML and
createElement:

  features/_editor.js      9   <- omitted
  features/diff.js         3   <- omitted
  features/qr-code.js      3   <- omitted
  the other eight          0   <- three of them were the ones named

So the three named files build no markup at all and all three that do were
missing. Green, and pointing at the wrong files — which is worse than the
generation it replaced, because that one proved the rule was WIRED and claimed
nothing about which files carry the hazard.

Counted with `grep -o` per construct rather than `grep -c`, so a line carrying
two constructs counts twice, and every one of the 15 nonzero sites was read
rather than trusted: all 15 are real assignments or `document.createElement`
calls, none in a comment. The zeros were read too — `tools.js` matched
`<[a-zA-Z/]` once, and it is `features/<id>.js` inside a comment on line 3, not
markup. The instrument produced 9, 3 and 3 on the same corpus in the same run,
so the eight zeros are a measurement rather than a dead grep.

`features/code-data.js` stays out and is still correct: it carries the declared
§15 opt-out, its report is join('\n')-ed and rendered as text, and it scores 0
on every construct. Control: adding it to the corrected declaration fails with
"the rule does NOT resolve for `features/code-data.js`", so the gate still tells
a covered file from an exempted one.

Verified: npm test 3 passed; npm run lint clean; JSON parses.

Not in this commit: the kit re-vendor. tgwab-standards#131 moved again today
(template blob 3f6ed048 -> b0dc3063) and this repo re-vendors after it merges.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRX8K6Ys4CDRDcdknCuPVh
…identical

Vendored from `ds/comment-accuracy` at f022390. Previous copy was b71e006.

Three things changed in the kit since this repo's copy:

1. The count is written with process.stdout.write instead of console.log.
   vitest 4 selects its reporter with `[isAgent ? 'agent' : 'default']`, and the
   agent reporter is MinimalReporter with `silent: 'passed-only'` — console
   output from PASSING tests is dropped. std-env sets isAgent from CLAUDECODE /
   AI_AGENT / CURSOR_AGENT, so every agent-run `npm test` in this estate hid the
   §15 count while every CI run showed it. Hardening, not a repair: the line was
   in this repo's CI log already.

2. Names in xss-lint-covers.json resolve from the cwd first and the repo root
   second. The sidecar was already found from the test file's own directory; the
   names inside it were still cwd-relative, so the robustness stopped at the
   file. wizard-web writes `../../apps/…` and mykk.us-extension writes
   `content.js`; both are valid now.

3. The build-tooling denylist is applied to the repo-root-relative form, which
   closes the `../../scripts/foo.js` escape the file documented as open.

ALSO CLOSES A DIVERGENCE THAT SHOULD NOT HAVE EXISTED. Three of the six adopter
PRs carried blob b71e006, a 365-line copy that appears NOWHERE in the template's
history — a working copy that never landed in templates/, carrying the UNANCHORED
denylist `/(^|\/)(scripts|…)\//` instead of `/^(scripts|…)\//`. Latent in all
three, because no sidecar in any of them names a mid-path `scripts/` segment, so
all three were green. §15's comparability argument is that the kit ships
byte-identical; six repos, three generations, all green is exactly the silence it
exists to prevent.

Verified: npm test 3 passed; lint clean; the vendored file hashes to
e35e98a, byte-identical to the template.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRX8K6Ys4CDRDcdknCuPVh
@MichalAFerber
MichalAFerber marked this pull request as ready for review September 5, 2026 07:32
@MichalAFerber

Copy link
Copy Markdown
Owner Author

Ready for review — devops attestation.

Blob identity verified independently, not taken on report. Read through the git trees API at this PR's head and at tgwab-standards#131's head (c16f65d):

template @ #131   e35e98a8ce02cd54dc7a1386382cc23adec17492
this PR           e35e98a8ce02cd54dc7a1386382cc23adec17492   MATCH

All six adopters carry that same blob. §15's comparability argument rests on byte-identity, and line counts already hid a divergence tonight — 365 and 375 were reported as one range "365–375" when they were two populations, one of which (b71e006a) existed nowhere in tgwab-standards. Hashes, not lengths.

MERGE ORDER: tgwab-standards#131 must merge FIRST. Every one of these ships xss-lint-covers.json plus a test that hard-fails when the sidecar is missing, and both the sidecar template and the DEV-STANDARDS text defining it exist only on #131's branch. Merging an adopter first leaves a repo carrying a mandatory file whose format is defined nowhere in the published standard.

One finding withdrawn on the way here, and it is worth recording why. Three reviewers independently found that a §15 coverage line "does not appear in test output". It was an artefact of a shared instrument: CLAUDECODE=1 and AI_AGENT=* make vitest select MinimalReporter (silent: 'passed-only'), which drops console.log from passing tests. Measured as a 2×2, same worktree, same command:

                       agent env set   agent env cleared
console.log                  0                 1
process.stdout.write         1                 1

So the count did reach CI all along, and the process.stdout.write change these PRs now carry is what makes it visible in both arms rather than incidental hardening. Three agents agreeing was one measurement, not three.

@MichalAFerber
MichalAFerber merged commit f3953b6 into main Sep 5, 2026
1 check passed
@MichalAFerber
MichalAFerber deleted the chore/revendor-15-fixture branch September 5, 2026 07:35
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