@piercebarney/whs-eleventy 2026.9.29 → 2026.9.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/compliance.js CHANGED
@@ -109,6 +109,28 @@ const fromDoctor = (id, extra = "", { strict = false, missingIsFail = false } =
109
109
  // ---- per-chapter checks ------------------------------------------------
110
110
  // Each returns { status, note }. Keep them mechanical; hand judgement to MANUAL.
111
111
 
112
+ // Used by `privacy` below: the third-party list must render from
113
+ // _data/thirdparties.js — either a Nunjucks `for...in` loop or a JS
114
+ // `.map()`/`.forEach()` iteration.
115
+ const THIRD_PARTY_LOOP = /for\s+t\s+in\s+thirdparties|thirdparties\s*\.\s*(?:map|forEach)\s*\(/;
116
+
117
+ function hasThirdPartyLoop() {
118
+ if (grepSrc(THIRD_PARTY_LOOP).length) return true;
119
+ // A privacy page can be a root-level template (a flat-layout adopted
120
+ // project's own convention, outside grepSrc's src/static/scripts roots
121
+ // entirely). Check top-level files at the project root too — never
122
+ // recursive, so node_modules/.git/etc. are never walked (readdirSync with
123
+ // no recursion, filtered to isFile()).
124
+ try {
125
+ return fs
126
+ .readdirSync(ROOT, { withFileTypes: true })
127
+ .filter((e) => e.isFile() && /\.(js|njk)$/.test(e.name))
128
+ .some((e) => THIRD_PARTY_LOOP.test(fs.readFileSync(path.join(ROOT, e.name), "utf8")));
129
+ } catch {
130
+ return false;
131
+ }
132
+ }
133
+
112
134
  const CHECKS = {
113
135
  "repo-hygiene": () => {
114
136
  const docs = ["README.md", "CLAUDE.md"].filter((f) => has(f));
@@ -452,7 +474,7 @@ const CHECKS = {
452
474
  if (!built && !source) return { status: FAIL, note: "no privacy page" };
453
475
  // The third-party disclosure must be generated from _data/thirdparties.js,
454
476
  // never hand-maintained — the loop lives in the privacy shell or an include.
455
- if (grepSrc(/for\s+t\s+in\s+thirdparties/).length === 0)
477
+ if (!hasThirdPartyLoop())
456
478
  return {
457
479
  status: FAIL,
458
480
  note: "privacy third-party list not rendered from _data/thirdparties.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piercebarney/whs-eleventy",
3
- "version": "2026.9.29",
3
+ "version": "2026.9.30",
4
4
  "description": "The web house style's Eleventy + Netlify tooling — the compliance sweep, the infra doctor, the link/CSP integrity check, and the a11y scan, shared by every project on the stack.",
5
5
  "bin": {
6
6
  "whs": "cli.js"
@@ -21,6 +21,29 @@ enforced, new *opt-in* chapters/capabilities, tooling-only changes — is
21
21
 
22
22
  ---
23
23
 
24
+ ## 2026-09-30 [non-breaking]
25
+
26
+ ### packages/whs-eleventy
27
+
28
+ `privacy` in `lib/compliance.js`'s third-party-loop gate
29
+ (`grepSrc(/for\s+t\s+in\s+thirdparties/)`) only recognized Nunjucks
30
+ `for...in` syntax and only scanned `src/`, `static/`, `scripts/` — so a
31
+ root-level, JS-class (`.11ty.js`) privacy page rendering its disclosure list
32
+ via `.map()`/`.forEach()` could pass only by coincidence, via an unrelated
33
+ file elsewhere in the scanned roots happening to contain a matching Nunjucks
34
+ loop. Now also recognizes `.map()`/`.forEach()` iteration, and falls back to
35
+ a non-recursive scan of top-level project-root files when the shared scan
36
+ finds nothing. This resolves the concrete reported case — the check now
37
+ reaches and validates the actual root-level privacy page's own rendering
38
+ instead of passing via a coincidental match — without claiming full
39
+ precision: Eleventy supports too many valid page-rendering shapes for the
40
+ check to reliably identify "the one file that renders this page" in general.
41
+ Local to `privacy`; `grepSrc`'s shared defaults and its other six call sites
42
+ are untouched. Resolves
43
+ `feedback/2026-09-23-privacy-thirdparty-check-scans-wrong-file.md`.
44
+
45
+ ---
46
+
24
47
  ## 2026-09-29 [non-breaking]
25
48
 
26
49
  ### packages/whs-eleventy
package/standard/core.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Web project house style — CORE (stack-agnostic)
2
2
 
3
- **Version:** 2026-09-29 · **Status:** active
3
+ **Version:** 2026-09-30 · **Status:** active
4
4
 
5
5
  This is the stack-agnostic contract every web project follows, regardless of
6
6
  framework, host, or CSS system. It says **what** must be true, with concrete
@@ -1467,7 +1467,7 @@ governs it and follow that chapter — the slugs are the index (a `<script>` →
1467
1467
  - production-url: https://example.com
1468
1468
  - content-type: tool # tool | article (article => feeds)
1469
1469
  - publishing-rate: ~5 pages/week
1470
- - standard-version: 2026-09-29 # recommended — enables standard-version drift tracking (#compliance)
1470
+ - standard-version: 2026-09-30 # recommended — enables standard-version drift tracking (#compliance)
1471
1471
  ```
1472
1472
 
1473
1473
  If the section is absent, the assistant's first action is to run the flow above