jekyll-theme-zer0 1.13.1 → 1.15.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ad83858a690a41ecdcffeb9d1666bdb477c74f5dc0e203bc0059a56e7ed68a2
4
- data.tar.gz: 88b906b270107cc25861394ff63b9772b9b1758c39315a2758bbe45f42ec00cc
3
+ metadata.gz: 6bd5748efd683917f588e3f0e130536a6ba00727f0d2027445261b1258d2b22d
4
+ data.tar.gz: dcd71edd2118b837980feccce2474ab6936d7838756d4ed3a9aa4a80ef76ff75
5
5
  SHA512:
6
- metadata.gz: a24b1b809afaff0913078bbdbf5ba74799c757d330203ef54f0bccc21e8d4fe82920430ce3d981a540bcea0ac89f8e31b1284eb92db60d0f89c3986b16891126
7
- data.tar.gz: 01dd7a1a0ae1ab4f82936db64ecd509280c76714e80ae55be46c0f67dbca6c59efd5f7590960d11f46237380b6b680873f816e6bb72845d7726c24137b57e4c6
6
+ metadata.gz: 28d42c8f1d75356b055eeeb493d8daf466ef1e8d23eb8aade96f4c77de0f1b1b7d307f821b5ef65716d2379920bd68f9d4862d0ad0fc214b82f9398349041617
7
+ data.tar.gz: 5c2d7da877cdcaa317ef6e90e6c60e5f28098dd1742101c793140801a45d8aa029f8d97960d830bdd4ef00c22f4241f4c2fd9c2e2659c218d2ae69369fd80f9a
data/CHANGELOG.md CHANGED
@@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.15.0] - 2026-06-12
9
+
10
+ ### Changed
11
+ - Version bump: minor release
12
+
13
+ ### Commits in this release
14
+ - 85d0295a feat(quality): implement six backlog tasks — T-003/004/008/014/015/016 (#144)
15
+ - 71b8fe46 fix(gemspec): require Ruby >= 3.2 to match modern dependency floor (#93)
16
+
17
+ ### Added
18
+ - **Contribution templates (T-003)**: bug-report and feature-request issue forms, contact links, and a PR template with the conventional-commit/CHANGELOG/test checklist
19
+ - **Locale-independence guard (T-015)**: lib test suite runs the roadmap/backlog/preflight validators under `LC_ALL=C LANG=C` so the UTF-8 crash class fixed in 1.12.1 cannot return
20
+
21
+ ### Fixed
22
+ - **Theme customizer (T-008)**: YAML export now quotes hex color values in both builders — unquoted `#RRGGBB` parsed as a YAML comment and silently dropped colors; frozen regression test promoted to live
23
+ - **Docs lint baseline (T-004/T-014)**: ~1,600 markdownlint violations fixed or config-tuned to zero (MD060 disabled as post-config stylistic noise, MD025 front-matter handling, MD024 siblings-only); a stray code fence that swallowed the troubleshooting "Advanced Topics" section into a code block repaired; table pipes escaped; 15 dead README links remapped to the reorganized `docs/` tree
24
+ - **site_generation suite (T-016)**: `jekyll build` failures now fail the suite instead of degrading to warnings (missing-bundler skip retained)
25
+
26
+ ### Changed
27
+ - **Docs lint gate (T-014)**: both `|| true` suppressions removed from `docs-validate.yml` — markdownlint now blocks on a zero-violation baseline
28
+
29
+ ## [1.14.0] - 2026-06-11
30
+
31
+ ### Changed
32
+ - Version bump: minor release
33
+
34
+ ### Commits in this release
35
+ - 5de341aa feat(security): sanitize sensitive config lines in admin config-page DOM (T-009) (#140)
36
+ - 42468785 chore(deps): update Ruby gem dependencies (#129)
37
+ - 89e21988 Improve LinkedIn share flow with cleaned article summary (#99)
38
+
39
+ ### Security
40
+ - **Admin config page (T-009 hardening)**: added a pure-Liquid line-redaction layer for the hidden `<pre id="cfg-full-yaml">` element — the `sanitize_config_yaml` plugin filter shipped in 1.13.1 does not run on GitHub Pages builds (safe mode ignores custom plugins, and the unknown filter is a silent no-op), so Pages-built sites were still injecting raw config; the Liquid layer protects every build path, with the plugin filter kept as defense-in-depth
41
+
42
+ ### Fixed
43
+ - **Workflow lint (T-017)**: `version-bump.yml` now passes the repo yamllint config (trailing spaces, bracket spacing, sequence indentation) — these pre-existing violations failed the `auto-version` integration suite on every code PR once the T-012 gate went live; YAML verified semantically identical before/after
44
+ - **Changelog tooling**: `update_changelog_file` normalizes trailing newlines on the entry, guaranteeing exactly one blank line before the next release block even when callers pass entries via command substitution (review feedback on the T-012 PR)
45
+
8
46
  ## [1.13.1] - 2026-06-11
9
47
 
10
48
  ### Changed
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  title: zer0-mistakes
3
3
  sub-title: AI-Native Jekyll Theme
4
4
  description: AI-native Jekyll theme for GitHub Pages — Docker-first development, AI-powered installation, multi-agent integration (Copilot, Codex, Cursor, Claude), AI preview-image generation, and AIEO content optimization with Bootstrap 5.3.
5
- version: 1.13.1
5
+ version: 1.15.0
6
6
  layout: landing
7
7
  tags:
8
8
  - jekyll
@@ -20,7 +20,7 @@ categories:
20
20
  - bootstrap
21
21
  - ai-tooling
22
22
  created: 2024-02-10T23:51:11.480Z
23
- lastmod: 2026-06-11T21:37:24.000Z
23
+ lastmod: 2026-06-12T01:01:59.000Z
24
24
  draft: false
25
25
  permalink: /
26
26
  slug: zer0
@@ -571,7 +571,7 @@ cd <your-username>.github.io
571
571
  docker-compose up
572
572
  ```
573
573
 
574
- See [docs/FORKING.md](docs/FORKING.md) for the full progressive workflow.
574
+ See [docs/FORKING.md](docs/installation/forking.md) for the full progressive workflow.
575
575
 
576
576
  ---
577
577
 
@@ -703,7 +703,7 @@ zer0-mistakes documentation is split across three audiences. Pick the layer that
703
703
  | [📊 Analytics](https://zer0-mistakes.com/docs/analytics/) | PostHog, Google Analytics setup |
704
704
  | [🔍 SEO](https://zer0-mistakes.com/docs/seo/) | Meta tags, sitemap, structured data |
705
705
  | [📓 Jupyter Notebooks](https://github.com/bamr87/zer0-mistakes/blob/main/docs/JUPYTER_NOTEBOOKS.md) | Notebook conversion documentation |
706
- | [📝 PRD](docs/PRD.md) | Product requirements & roadmap |
706
+ | [📝 PRD](docs/architecture/prd-requirements.md) | Product requirements & roadmap |
707
707
  | [🔒 Privacy Policy](https://zer0-mistakes.com/privacy-policy/) | GDPR/CCPA compliant privacy docs |
708
708
 
709
709
  ### Contributor Technical Reference
@@ -712,18 +712,18 @@ These files live in the repo under `docs/` and are intended for theme contributo
712
712
 
713
713
  | File | Topic |
714
714
  |------|-------|
715
- | [docs/design-system.md](docs/design-system.md) | Design system overview |
716
- | [docs/design-tokens.md](docs/design-tokens.md) | Design tokens reference |
717
- | [docs/components.md](docs/components.md) | UI component catalogue |
718
- | [docs/layouts-and-navigation.md](docs/layouts-and-navigation.md) | Layout hierarchy & navigation |
719
- | [docs/theming.md](docs/theming.md) | Sass / Bootstrap theming guide |
720
- | [docs/customization.md](docs/customization.md) | Site customization patterns |
721
- | [docs/configuration.md](docs/configuration.md) | `_config.yml` reference |
722
- | [docs/code-blocks.md](docs/code-blocks.md) | Syntax highlighting setup |
723
- | [docs/extending.md](docs/extending.md) | Adding layouts, includes & plugins |
724
- | [docs/js-api.md](docs/js-api.md) | JavaScript module API |
725
- | [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) | Common issues & fixes |
726
- | [docs/FORKING.md](docs/FORKING.md) | Forking & personalization guide |
715
+ | [docs/design-system.md](docs/ui/design-system.md) | Design system overview |
716
+ | [docs/design-tokens.md](docs/ui/design-tokens.md) | Design tokens reference |
717
+ | [docs/components.md](docs/ui/components.md) | UI component catalogue |
718
+ | [docs/layouts-and-navigation.md](docs/ui/layouts-and-navigation.md) | Layout hierarchy & navigation |
719
+ | [docs/theming.md](docs/ui/theming.md) | Sass / Bootstrap theming guide |
720
+ | [docs/customization.md](docs/ui/customization.md) | Site customization patterns |
721
+ | [docs/configuration.md](docs/ui/configuration.md) | `_config.yml` reference |
722
+ | [docs/code-blocks.md](docs/ui/code-blocks.md) | Syntax highlighting setup |
723
+ | [docs/extending.md](docs/ui/extending.md) | Adding layouts, includes & plugins |
724
+ | [docs/js-api.md](docs/ui/js-api.md) | JavaScript module API |
725
+ | [docs/TROUBLESHOOTING.md](docs/development/troubleshooting.md) | Common issues & fixes |
726
+ | [docs/FORKING.md](docs/installation/forking.md) | Forking & personalization guide |
727
727
 
728
728
  ---
729
729
 
@@ -803,7 +803,7 @@ When you fork the theme as a starter, the workflows come with you. To make them
803
803
 
804
804
  ## 🗺 Roadmap
805
805
 
806
- The diagram and table below are auto-generated from [`_data/roadmap.yml`](_data/roadmap.yml) by [`scripts/generate-roadmap.sh`](scripts/generate-roadmap.sh). See the full [Roadmap page](https://zer0-mistakes.com/roadmap/) for per-version detail and the [PRD](docs/PRD.md) for product context.
806
+ The diagram and table below are auto-generated from [`_data/roadmap.yml`](_data/roadmap.yml) by [`scripts/generate-roadmap.sh`](scripts/generate-roadmap.sh). See the full [Roadmap page](https://zer0-mistakes.com/roadmap/) for per-version detail and the [PRD](docs/architecture/prd-requirements.md) for product context.
807
807
 
808
808
  <!-- ROADMAP_MERMAID:START -->
809
809
 
@@ -830,10 +830,11 @@ gantt
830
830
  v1.9 Installer v2 & Site Scraper :done, 2026-05, 2026-05
831
831
  v1.10 Roadmap Validation :done, 2026-06, 2026-06
832
832
  v1.11 Continuous-Evolution Loop :done, 2026-06, 2026-06
833
+ v1.12 Headless Endpoints :done, 2026-06, 2026-06
834
+ v1.13 Quality Framework — First Wave :done, 2026-06, 2026-06
833
835
  section Current
834
- v1.12 Headless Endpoints :active, 2026-06, 2026-06
836
+ v1.14 Zer0-Mistake Quality Framework :active, 2026-06, 2026-08
835
837
  section Future
836
- v1.13 Zer0-Mistake Quality Framework :2026-06, 2026-08
837
838
  v2.0 CMS Integration :2026-06, 2026-08
838
839
  v2.1 i18n Support :2026-08, 2026-10
839
840
  v2.2 Advanced Analytics :2026-10, 2026-12
@@ -864,8 +865,9 @@ gantt
864
865
  | **v1.9** | ✅ Completed | May 2026 | Modular installer v2 with deploy plugins, AI wizard pipeline, and a site scraper. |
865
866
  | **v1.10** | ✅ Completed | Jun 2026 | Roadmap integrity validation and catch-up milestones so the roadmap tracks the shipped gem. |
866
867
  | **v1.11** | ✅ Completed | Jun 2026 | Self-sustaining backlog loop so AI agents keep improving the repo between human sessions. |
867
- | **v1.12** | 🚧 In Progress | Current (1.12.x) | Machine-readable site endpoints for downstream sites and AI agents. |
868
- | **v1.13** | 🗓 Planned | Q3 2026 | Close the gap between the repo's quality gates and what CI actually enforces no mistake lands green. |
868
+ | **v1.12** | Completed | Jun 2026 | Machine-readable site endpoints for downstream sites and AI agents. |
869
+ | **v1.13** | Completed | Jun 2026 | First shipped wave of the Zer0-Mistake Quality Framework: CI gate parity and DOM sanitization. |
870
+ | **v1.14** | 🚧 In Progress | Current (1.14.x) | Close the gap between the repo's quality gates and what CI actually enforces — no mistake lands green. |
869
871
  | **v2.0** | 🗓 Planned | Q3 2026 | Headless CMS integration with a content API and admin dashboard. |
870
872
  | **v2.1** | 🗓 Planned | Q4 2026 | Multi-language content support with locale-aware routing. |
871
873
  | **v2.2** | 🗓 Planned | Q4 2026 | Visual theme customizer, A/B testing, and conversion funnels. |
@@ -909,7 +911,7 @@ git push origin feature/awesome-feature
909
911
 
910
912
  | Metric | Value |
911
913
  |--------|-------|
912
- | **Current Version** | 1.13.1 ([RubyGems](https://rubygems.org/gems/jekyll-theme-zer0), [CHANGELOG](/CHANGELOG)) |
914
+ | **Current Version** | 1.15.0 ([RubyGems](https://rubygems.org/gems/jekyll-theme-zer0), [CHANGELOG](/CHANGELOG)) |
913
915
  | **Documented Features** | 43 ([Feature Registry](https://github.com/bamr87/zer0-mistakes/blob/main/_data/features.yml)) |
914
916
  | **Setup Time** | 2-5 minutes ([install.sh benchmarks](https://github.com/bamr87/zer0-mistakes/blob/main/install.sh)) |
915
917
  | **Documentation Pages** | 70+ ([browse docs](https://zer0-mistakes.com/pages/)) |
@@ -964,6 +966,6 @@ And these AI partners that make zer0-mistakes truly AI-native:
964
966
 
965
967
  **Built with ❤️ — and a little help from our AI partners — for the Jekyll community**
966
968
 
967
- **v1.13.1** • [Changelog](CHANGELOG.md) • [License](LICENSE) • [Contributing](CONTRIBUTING.md) • [AI Agent Guide](AGENTS.md)
969
+ **v1.15.0** • [Changelog](CHANGELOG.md) • [License](LICENSE) • [Contributing](CONTRIBUTING.md) • [AI Agent Guide](AGENTS.md)
968
970
 
969
971
 
data/_data/backlog.yml CHANGED
@@ -55,8 +55,8 @@
55
55
 
56
56
  meta:
57
57
  title: "zer0-mistakes Backlog"
58
- updated: 2026-06-11
59
- next_id: 18
58
+ updated: 2026-06-12
59
+ next_id: 19
60
60
 
61
61
  tasks:
62
62
  # --- Housekeeping (seeded so the loop has work on day one) ------------------
@@ -106,7 +106,7 @@ tasks:
106
106
 
107
107
  - id: T-003
108
108
  title: "Add GitHub issue templates and a pull-request template"
109
- status: open
109
+ status: done
110
110
  priority: P2
111
111
  area: infra
112
112
  risk: standard
@@ -116,17 +116,21 @@ tasks:
116
116
  The repo has no `.github/ISSUE_TEMPLATE/` or PR template. Add a bug-report
117
117
  and feature-request issue form plus a PR template that nudges contributors
118
118
  toward conventional commits, CHANGELOG updates, and the test checklist.
119
+ Done 2026-06-12: bug-report and feature-request issue forms, config.yml
120
+ with contact links (blank issues stay enabled for the API-driven backlog
121
+ sync), and a PR template with the conventional-commit/CHANGELOG/test
122
+ checklist.
119
123
  acceptance:
120
124
  - "`.github/ISSUE_TEMPLATE/bug_report.yml` and `feature_request.yml` exist and render."
121
125
  - "`.github/pull_request_template.md` exists with a conventional-commit + CHANGELOG + tests checklist."
122
126
  - "Agent-filed backlog issues remain compatible with the new templates (no broken automation)."
123
127
  links: { issue: null, pr: null, roadmap: null }
124
128
  created: 2026-05-31
125
- updated: 2026-05-31
129
+ updated: 2026-06-12
126
130
 
127
131
  - id: T-004
128
132
  title: "Docs-freshness sweep: reconcile docs/ ↔ pages/_docs/ and fix broken links"
129
- status: open
133
+ status: done
130
134
  priority: P2
131
135
  area: docs
132
136
  risk: low
@@ -136,13 +140,18 @@ tasks:
136
140
  Run the markdown-link checker and audit the two-tier docs for drift,
137
141
  stale dates/versions, and any user/technical pages that fell out of sync
138
142
  after the recent alignment commit.
143
+ Done 2026-06-12: markdownlint baseline cleaned to zero violations
144
+ (~1,600 fixed: auto-fix pass, MD060/MD025/MD024 config tuning, fence
145
+ languages, table pipe escapes, a stray fence swallowing a docs section),
146
+ 15 dead README links remapped to the reorganized docs/ tree,
147
+ check-links.sh reports 0 broken links.
139
148
  acceptance:
140
149
  - "`markdown-link-check` (config in `.github/config/`) reports no broken internal links."
141
150
  - "Any stale version/date references in `docs/` and `pages/_docs/` are corrected."
142
151
  - "Cross-links between the user tier and technical tier resolve both ways."
143
152
  links: { issue: null, pr: null, roadmap: null }
144
153
  created: 2026-05-31
145
- updated: 2026-05-31
154
+ updated: 2026-06-12
146
155
 
147
156
  - id: T-005
148
157
  title: "Coverage baseline: identify the lowest-covered subsystems toward the 90% goal"
@@ -192,7 +201,7 @@ tasks:
192
201
 
193
202
  - id: T-008
194
203
  title: "Fix theme-customizer YAML export to quote hex color values"
195
- status: open
204
+ status: done
196
205
  priority: P2
197
206
  area: feat
198
207
  risk: standard
@@ -203,13 +212,17 @@ tasks:
203
212
  YAML without quoting strings that start with `#`. Unquoted `#RRGGBB` is treated as a
204
213
  YAML comment by parsers, silently dropping colour values. A regression guard in
205
214
  `test/visual/theme-colors.spec.js` is frozen as `test.fixme` until the bug is fixed.
215
+ Done 2026-06-12: both YAML builders (theme-customizer.js fallback and
216
+ palette-generator.js commented overrides) quote color values; frozen
217
+ regression test promoted to live test(); export verified parseable by
218
+ Ruby YAML with colors preserved.
206
219
  acceptance:
207
220
  - "JS export wraps every hex colour value (matching `/#[0-9a-fA-F]{3,8}/`) in double quotes in the generated YAML."
208
221
  - "The `test.fixme` in `test/visual/theme-colors.spec.js` is promoted to a live `test()` and passes in CI."
209
222
  - "Exported YAML is valid when parsed by `yaml.safe_load` in Ruby."
210
223
  links: { issue: null, pr: null, roadmap: null }
211
224
  created: 2026-06-01
212
- updated: 2026-06-01
225
+ updated: 2026-06-12
213
226
 
214
227
  - id: T-009
215
228
  title: "Sanitize sensitive config keys from admin config-page DOM injection"
@@ -225,6 +238,9 @@ tasks:
225
238
  (`api_key`, `token`, PostHog `phc_*` keys, etc.) they are exposed in the page
226
239
  DOM. A regression test in `test/visual/security.spec.js` is frozen as
227
240
  `test.fixme` until a sanitisation pass is in place.
241
+ Done 2026-06-11: pure-Liquid line filter redacts matching lines before
242
+ DOM injection (GitHub Pages safe — no custom plugin); regression test
243
+ promoted to a live `test()`; visible Raw-YAML tab untouched.
228
244
  acceptance:
229
245
  - "The Liquid/Ruby that populates `<pre id=\"cfg-full-yaml\">` strips or masks keys matching `api_key`, `secret`, `password`, `token`, and `phc_` prefix before DOM injection."
230
246
  - "The `test.fixme` in `test/visual/security.spec.js` is promoted to a live `test()` and passes in CI."
@@ -339,7 +355,7 @@ tasks:
339
355
 
340
356
  - id: T-014
341
357
  title: "Re-arm the docs link-check gate (remove warn-only || true)"
342
- status: open
358
+ status: done
343
359
  priority: P2
344
360
  area: lint
345
361
  risk: standard
@@ -349,17 +365,20 @@ tasks:
349
365
  `docs-validate.yml` runs markdown-link-check with `|| true` ("warn-only
350
366
  until baseline is clean"), so broken links accumulate. Fix the links it
351
367
  currently reports, then drop the `|| true` so the gate blocks.
368
+ Done 2026-06-12: baseline cleaned to zero violations and both
369
+ warn-only suppressions removed from docs-validate.yml — the lint gate
370
+ now blocks. (The internal link check was already blocking.)
352
371
  acceptance:
353
372
  - "All links reported by the current `docs-validate.yml` run are fixed or allowlisted in `.github/config/.markdown-link-check.json`."
354
373
  - "Both `|| true` suppressions removed from `docs-validate.yml`."
355
374
  - "`docs-validate.yml` passes blocking on a follow-up PR."
356
375
  links: { issue: null, pr: null, roadmap: "1.13" }
357
376
  created: 2026-06-10
358
- updated: 2026-06-10
377
+ updated: 2026-06-12
359
378
 
360
379
  - id: T-015
361
380
  title: "Locale-independence regression guard for Ruby/Bash tooling"
362
- status: open
381
+ status: done
363
382
  priority: P2
364
383
  area: infra
365
384
  risk: standard
@@ -371,17 +390,21 @@ tasks:
371
390
  only reproduced without a UTF-8 locale. Add a guard so the bug class
372
391
  cannot return: run the validators under `LC_ALL=C` in a lib test or a
373
392
  dedicated CI step.
393
+ Done 2026-06-12: scripts/test/lib/test_locale_independence.sh runs
394
+ generate-roadmap --check/--validate, sync-backlog --check, and
395
+ validate --quick under LC_ALL=C LANG=C; verified the guard fails when
396
+ the explicit-UTF-8 read is reverted.
374
397
  acceptance:
375
398
  - "A test (scripts/test/lib/) or CI step runs `generate-roadmap.rb --check`, `sync-backlog.rb --check`, and `validate --quick` with `LC_ALL=C LANG=C`."
376
399
  - "The guard fails when the explicit-UTF-8 reads are reverted (verified once locally)."
377
400
  - "`./scripts/bin/test` stays green."
378
401
  links: { issue: null, pr: null, roadmap: "1.13" }
379
402
  created: 2026-06-10
380
- updated: 2026-06-10
403
+ updated: 2026-06-12
381
404
 
382
405
  - id: T-016
383
406
  title: "site_generation suite: fail on Jekyll build errors instead of warning"
384
- status: open
407
+ status: done
385
408
  priority: P2
386
409
  area: tests
387
410
  risk: standard
@@ -395,17 +418,20 @@ tasks:
395
418
  the suite now gating PRs in CI (working bundler guaranteed), build
396
419
  failures should fail the suite; keep the skip only for genuinely
397
420
  missing toolchain (no bundler).
421
+ Done 2026-06-12: jekyll build failures now fail the suite (bundler-
422
+ missing still skips); verified strict suite passes locally with real
423
+ builds.
398
424
  acceptance:
399
425
  - "A non-zero `jekyll build` exit fails the suite when bundler is available."
400
426
  - "The bundler-missing path still skips with a warning (local minimal environments)."
401
427
  - "Suite passes in CI and via `./test/test_runner.sh --suites site_generation` locally."
402
428
  links: { issue: null, pr: null, roadmap: "1.13" }
403
429
  created: 2026-06-10
404
- updated: 2026-06-10
430
+ updated: 2026-06-12
405
431
 
406
432
  - id: T-017
407
433
  title: "Fix yamllint violations in .github/workflows/version-bump.yml"
408
- status: open
434
+ status: done
409
435
  priority: P2
410
436
  area: lint
411
437
  risk: low
@@ -417,6 +443,9 @@ tasks:
417
443
  integration test (which runs yamllint) to fail in CI on every PR. Discovered
418
444
  while babysitting PR #141 — the file was unchanged by that PR, confirming
419
445
  the failures are pre-existing.
446
+ Done 2026-06-11: full cleanup (trailing spaces, brackets, sequence
447
+ indentation) landed with the T-009 PR; YAML verified semantically
448
+ identical and yamllint exits 0 with the repo config.
420
449
  acceptance:
421
450
  - "`yamllint -c .github/config/.yamllint.yml .github/workflows/version-bump.yml` exits 0."
422
451
  - "`./scripts/test/integration/auto-version` passes the 'version-bump workflow syntax' check."
@@ -425,3 +454,26 @@ tasks:
425
454
  created: 2026-06-11
426
455
  updated: 2026-06-11
427
456
 
457
+ - id: T-018
458
+ title: "Admin config page displays a stale copy of _config.yml — keep it in sync safely"
459
+ status: open
460
+ priority: P2
461
+ area: feat
462
+ risk: standard
463
+ effort: M
464
+ source: audit
465
+ summary: >-
466
+ `pages/_about/settings/config.md` renders `pages/_about/settings/_config.yml`
467
+ via `include_relative` (Liquid cannot reach the repo root), and that copy
468
+ has drifted from the live `_config.yml` (it predates, e.g., the PostHog
469
+ analytics block). Found while implementing T-009. Caution: a naive
470
+ refresh would inject the live `phc_` api_key into the *visible* Raw-YAML
471
+ tab — any sync mechanism must sanitize the visible tab the same way
472
+ T-009 sanitizes the hidden copy element, or redact at sync time.
473
+ acceptance:
474
+ - "The config shown at /about/config/ matches the live `_config.yml` (automated sync step or build-time check that fails on drift)."
475
+ - "The visible Raw-YAML tab applies the same sensitive-line redaction as the hidden `cfg-full-yaml` element."
476
+ - "`test/visual/security.spec.js` raw-tab test passes without its silent skip path (locator matches the actual `code#cfg-raw-yaml` element)."
477
+ links: { issue: null, pr: null, roadmap: "1.13" }
478
+ created: 2026-06-11
479
+ updated: 2026-06-11
data/_data/roadmap.yml CHANGED
@@ -60,7 +60,7 @@
60
60
  meta:
61
61
  title: "zer0-mistakes Roadmap"
62
62
  tagline: "Past releases, current focus, and future plans for the zer0-mistakes Jekyll theme."
63
- updated: 2026-06-10
63
+ updated: 2026-06-12
64
64
 
65
65
  milestones:
66
66
  # --- Completed -------------------------------------------------------------
@@ -313,38 +313,50 @@ milestones:
313
313
  - "`/repo-audit` and `/backlog-implement` agent routines"
314
314
  - "Documentation maintenance system and consolidation"
315
315
 
316
- # --- Current ---------------------------------------------------------------
317
-
318
316
  - version: "1.12"
319
317
  title: "Headless Endpoints"
320
- status: active
321
- section: Current
318
+ status: completed
319
+ section: Completed
322
320
  start: 2026-06
323
321
  end: 2026-06
324
- target: "Current (1.12.x)"
322
+ released: 2026-06-03
325
323
  summary: "Machine-readable site endpoints for downstream sites and AI agents."
326
324
  features:
327
325
  - "Auto-generated `/search.json` endpoint for downstream sites"
328
326
  - "Auto-generated `/sitemap/` endpoint"
329
327
  - "Ruby gem and Mermaid dependency updates"
330
328
 
331
- # --- Future ----------------------------------------------------------------
332
-
333
329
  - version: "1.13"
330
+ title: "Quality Framework — First Wave"
331
+ status: completed
332
+ section: Completed
333
+ start: 2026-06
334
+ end: 2026-06
335
+ released: 2026-06-11
336
+ summary: "First shipped wave of the Zer0-Mistake Quality Framework: CI gate parity and DOM sanitization."
337
+ features:
338
+ - "CI gate parity (T-012) — PRs run the full canonical test entrypoint, not a subset"
339
+ - "Admin config-page DOM sanitization (T-009), Liquid + plugin layers"
340
+ - "Release changelog single source of truth; workflow lint cleanup (T-017)"
341
+ - "Gate-coverage controls contract documented in the workflows README"
342
+
343
+ # --- Current ---------------------------------------------------------------
344
+
345
+ - version: "1.14"
334
346
  title: "Zer0-Mistake Quality Framework"
335
- status: planned
336
- section: Future
347
+ status: active
348
+ section: Current
337
349
  start: 2026-06
338
350
  end: 2026-08
339
- target: "Q3 2026"
351
+ target: "Current (1.14.x)"
340
352
  summary: "Close the gap between the repo's quality gates and what CI actually enforces — no mistake lands green."
341
353
  features:
342
- - "CI gate parity PRs run the full canonical test entrypoint (lib unit, theme, integration, installer e2e), not a subset"
343
- - "Re-armed pixel-snapshot gate (refreshed baselines, no `continue-on-error`)"
344
- - "Re-armed docs link-check gate (clean baseline, no warn-only `|| true`)"
345
- - "Locale-independence regression guard for the Ruby/Bash tooling"
346
- - "Coverage baseline with follow-up tasks for the lowest-covered subsystems"
347
- - "Documented required status checks per branch (the 'controls' contract)"
354
+ - "Re-armed docs lint gate on a zero-violation baseline (T-014)"
355
+ - "Locale-independence regression guard for the Ruby/Bash tooling (T-015)"
356
+ - "Strict site_generation suite build failures fail the gate (T-016)"
357
+ - "Re-armed pixel-snapshot gate (refreshed baselines, no continue-on-error) (T-013)"
358
+ - "Coverage baseline with follow-up tasks for the lowest-covered subsystems (T-005)"
359
+ - "Navbar WCAG 2.1 AA fixes unlocking the axe-core full audit (T-007)"
348
360
 
349
361
  - version: "2.0"
350
362
  title: "CMS Integration"
@@ -32,6 +32,9 @@
32
32
  <!-- Search modal controller -->
33
33
  <script src="{{ '/assets/js/search-modal.js' | relative_url }}"></script>
34
34
 
35
+ <!-- Share helpers -->
36
+ <script src="{{ '/assets/js/share-actions.js' | relative_url }}"></script>
37
+
35
38
  <!-- fffuel-style background customizer (skin switching, toggle, opacity) -->
36
39
  <script src="{{ '/assets/js/background-customizer.js' | relative_url }}"></script>
37
40
 
@@ -46,6 +46,9 @@
46
46
  {% assign file_path = page_dir | append: "/" | append: page.path %}
47
47
  {% assign repo_parts = site.repository | split: "/" %}
48
48
  {% assign repo_owner = repo_parts[0] %}
49
+ {% assign page_absolute_url = page.url | absolute_url %}
50
+ {% assign linkedin_share_title = page.title | default: site.title | strip_html | strip_newlines | strip %}
51
+ {% assign linkedin_share_description = page.description | default: page.excerpt | default: site.description | strip_html | strip_newlines | strip %}
49
52
 
50
53
  {% comment %}
51
54
  Resolve author display name and optional profile URL from page front matter,
@@ -233,12 +236,19 @@
233
236
  </button>
234
237
  <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="shareDropdownBottom">
235
238
  <li>
236
- <a class="dropdown-item" href="https://reddit.com/submit?url={{ site.url | append: page.url | url_encode }}&amp;title={{ page.title | url_encode }}" target="_blank">
239
+ <a class="dropdown-item" href="https://reddit.com/submit?url={{ page_absolute_url | uri_escape }}&amp;title={{ page.title | uri_escape }}" target="_blank" rel="noopener noreferrer">
237
240
  <i class="bi bi-reddit me-2"></i>Share on Reddit
238
241
  </a>
239
242
  </li>
240
243
  <li>
241
- <a class="dropdown-item" href="https://www.linkedin.com/sharing/share-offsite/?url={{ site.url | append: page.url | url_encode }}" target="_blank">
244
+ <a class="dropdown-item js-linkedin-share"
245
+ href="https://www.linkedin.com/sharing/share-offsite/?url={{ page_absolute_url | uri_escape }}"
246
+ target="_blank"
247
+ rel="noopener noreferrer"
248
+ title="Copies a cleaned article summary to your clipboard, then opens LinkedIn sharing"
249
+ data-share-url="{{ page_absolute_url | escape }}"
250
+ data-share-title="{{ linkedin_share_title | escape }}"
251
+ data-share-description="{{ linkedin_share_description | escape }}">
242
252
  <i class="bi bi-linkedin me-2"></i>Share on LinkedIn
243
253
  </a>
244
254
  </li>
@@ -332,4 +342,4 @@
332
342
  </div>
333
343
  </div>
334
344
  </div>
335
- </div>
345
+ </div>
data/_layouts/note.html CHANGED
@@ -1,6 +1,9 @@
1
1
  ---
2
2
  layout: default
3
3
  ---
4
+ {% assign page_absolute_url = page.url | absolute_url %}
5
+ {% assign linkedin_share_title = page.title | default: site.title | strip_html | strip_newlines | strip %}
6
+ {% assign linkedin_share_description = page.description | default: page.excerpt | default: site.description | strip_html | strip_newlines | strip %}
4
7
  <!--
5
8
  ===================================================================
6
9
  NOTE LAYOUT - Quick Notes and Reference Display
@@ -153,11 +156,17 @@ layout: default
153
156
  class="btn btn-outline-primary btn-sm" target="_blank" rel="noopener noreferrer" aria-label="Share on X">
154
157
  <i class="bi bi-twitter-x"></i> X
155
158
  </a>
156
- <a href="https://www.linkedin.com/sharing/share-offsite/?url={{ site.url | append: page.url | uri_escape }}"
157
- class="btn btn-outline-primary btn-sm" target="_blank" rel="noopener noreferrer">
159
+ <a href="https://www.linkedin.com/sharing/share-offsite/?url={{ page_absolute_url | uri_escape }}"
160
+ class="btn btn-outline-primary btn-sm js-linkedin-share"
161
+ data-share-url="{{ page_absolute_url | escape }}"
162
+ data-share-title="{{ linkedin_share_title | escape }}"
163
+ data-share-description="{{ linkedin_share_description | escape }}"
164
+ title="Copies a cleaned article summary to your clipboard, then opens LinkedIn sharing"
165
+ target="_blank" rel="noopener noreferrer">
158
166
  <i class="bi bi-linkedin"></i> LinkedIn
159
167
  </a>
160
- <a href="mailto:?subject={{ page.title | uri_escape }}&body={{ site.url | append: page.url | uri_escape }}"
168
+ <a href="mailto:?subject={{ page.title | uri_escape }}&body={{ page_absolute_url | uri_escape }}"
169
+ title="Share this note by email"
161
170
  class="btn btn-outline-primary btn-sm">
162
171
  <i class="bi bi-envelope"></i> Email
163
172
  </a>
@@ -381,7 +381,7 @@ document.addEventListener('DOMContentLoaded', function () {
381
381
  ['--bs-border-radius', '--bs-border-width', '--bs-body-font-size', '--bs-body-font-weight', '--bs-body-line-height'].forEach(function (v) {
382
382
  if (state.liveOverrides[v]) {
383
383
  var key = v.replace('--bs-', '').replace(/-/g, '_');
384
- lines.push('# ' + key + ': ' + state.liveOverrides[v]);
384
+ lines.push('# ' + key + ': "' + state.liveOverrides[v] + '"');
385
385
  }
386
386
  });
387
387
 
@@ -0,0 +1,156 @@
1
+ (function () {
2
+ 'use strict';
3
+
4
+ function normalizeWhitespace(text) {
5
+ return (text || '').replace(/\s+/g, ' ').trim();
6
+ }
7
+
8
+ function dedupeSections(sections) {
9
+ return sections.filter((section, index) => {
10
+ if (!section) return false;
11
+ const normalized = section.toLowerCase();
12
+ return !sections.slice(0, index).some((previous) => previous && previous.toLowerCase() === normalized);
13
+ });
14
+ }
15
+
16
+ function truncateToSentence(text, maxLength) {
17
+ if (text.length <= maxLength) return text;
18
+
19
+ const trimmed = text.slice(0, maxLength);
20
+ const sentenceBreak = Math.max(
21
+ trimmed.lastIndexOf('. '),
22
+ trimmed.lastIndexOf('! '),
23
+ trimmed.lastIndexOf('? ')
24
+ );
25
+
26
+ if (sentenceBreak > Math.floor(maxLength * 0.6)) {
27
+ return trimmed.slice(0, sentenceBreak + 1).trim();
28
+ }
29
+
30
+ const lastSpace = trimmed.lastIndexOf(' ');
31
+ const endIndex = lastSpace > 0 ? lastSpace : maxLength;
32
+ return `${trimmed.slice(0, endIndex).trim()}…`;
33
+ }
34
+
35
+ function getShareContentRoot() {
36
+ return document.querySelector('[itemprop="articleBody"]')
37
+ || document.querySelector('.bd-content')
38
+ || document.querySelector('main')
39
+ || document.body;
40
+ }
41
+
42
+ function extractCleanExcerpt(description) {
43
+ const contentRoot = getShareContentRoot();
44
+ const normalizedDescription = normalizeWhitespace(description).toLowerCase();
45
+ const paragraphTexts = Array.from(contentRoot.querySelectorAll('p'))
46
+ .map((paragraph) => normalizeWhitespace(paragraph.textContent))
47
+ .filter((paragraph) => paragraph.length > 40)
48
+ .filter((paragraph) => {
49
+ const normalizedParagraph = paragraph.toLowerCase();
50
+ return !normalizedDescription
51
+ || (normalizedParagraph !== normalizedDescription && !normalizedParagraph.includes(normalizedDescription));
52
+ });
53
+
54
+ const combinedParagraphs = paragraphTexts.slice(0, 4).join(' ');
55
+ const fallbackText = normalizeWhitespace(contentRoot.textContent);
56
+ const candidateText = combinedParagraphs || fallbackText;
57
+
58
+ return truncateToSentence(candidateText, 420);
59
+ }
60
+
61
+ function buildLinkedInShareText(anchor) {
62
+ const title = normalizeWhitespace(anchor.dataset.shareTitle || document.title);
63
+ const description = normalizeWhitespace(
64
+ anchor.dataset.shareDescription
65
+ || document.querySelector('meta[name="description"]')?.getAttribute('content')
66
+ || ''
67
+ );
68
+ const excerpt = extractCleanExcerpt(description);
69
+ const url = normalizeWhitespace(anchor.dataset.shareUrl || window.location.href);
70
+
71
+ return dedupeSections([title, description, excerpt, url]).join('\n\n');
72
+ }
73
+
74
+ async function copyShareText(text) {
75
+ if (!navigator.clipboard || typeof navigator.clipboard.writeText !== 'function') {
76
+ return false;
77
+ }
78
+
79
+ try {
80
+ await navigator.clipboard.writeText(text);
81
+ return true;
82
+ } catch (error) {
83
+ console.warn('LinkedIn share copy failed:', error);
84
+ return false;
85
+ }
86
+ }
87
+
88
+ function openShareWindow(href) {
89
+ return window.open(href || 'about:blank', '_blank', 'noopener,noreferrer');
90
+ }
91
+
92
+ function notify(message, type) {
93
+ const notification = document.createElement('div');
94
+ notification.className = `alert alert-${type || 'info'} shadow position-fixed top-0 end-0 m-3`;
95
+ notification.style.zIndex = '1085';
96
+ notification.setAttribute('role', 'status');
97
+ notification.textContent = message;
98
+ document.body.appendChild(notification);
99
+
100
+ window.setTimeout(() => {
101
+ notification.remove();
102
+ }, 4000);
103
+ }
104
+
105
+ function bindLinkedInShare(anchor) {
106
+ if (anchor.dataset.linkedinShareBound === 'true') return;
107
+
108
+ anchor.dataset.linkedinShareBound = 'true';
109
+ anchor.addEventListener('click', async function (event) {
110
+ event.preventDefault();
111
+
112
+ const shareWindow = openShareWindow('', '_blank');
113
+
114
+ const shareText = buildLinkedInShareText(anchor);
115
+ const copied = await copyShareText(shareText);
116
+
117
+ if (shareWindow) {
118
+ shareWindow.location = anchor.href;
119
+ } else {
120
+ window.location.assign(anchor.href);
121
+ }
122
+
123
+ if (copied) {
124
+ notify('A cleaned LinkedIn-ready summary was copied to your clipboard. Paste it on LinkedIn after the share page opens.', 'info');
125
+ } else {
126
+ notify('LinkedIn opened, but clipboard access was unavailable. Copy the summary manually if needed.', 'warning');
127
+ }
128
+ });
129
+ }
130
+
131
+ function bindCopyButton(button) {
132
+ if (button.dataset.copyBound === 'true') return;
133
+
134
+ button.dataset.copyBound = 'true';
135
+ button.addEventListener('click', async function () {
136
+ const copied = await copyShareText(button.dataset.copyText || '');
137
+
138
+ if (copied) {
139
+ notify(button.dataset.copySuccess || 'Copied to clipboard.', 'success');
140
+ } else {
141
+ notify('Clipboard access was unavailable.', 'warning');
142
+ }
143
+ });
144
+ }
145
+
146
+ function initLinkedInShareButtons() {
147
+ document.querySelectorAll('.js-linkedin-share').forEach(bindLinkedInShare);
148
+ document.querySelectorAll('.js-copy-share-link').forEach(bindCopyButton);
149
+ }
150
+
151
+ if (document.readyState === 'loading') {
152
+ document.addEventListener('DOMContentLoaded', initLinkedInShareButtons);
153
+ } else {
154
+ initLinkedInShareButtons();
155
+ }
156
+ })();
@@ -121,7 +121,9 @@ document.addEventListener('DOMContentLoaded', function () {
121
121
  lines.push('');
122
122
  lines.push('theme_color:');
123
123
  document.querySelectorAll('[data-color-key]').forEach(function (el) {
124
- lines.push(' ' + el.dataset.colorKey + ': ' + el.value);
124
+ // Quote values: unquoted #RRGGBB is parsed as a YAML comment,
125
+ // silently dropping the color (T-008).
126
+ lines.push(' ' + el.dataset.colorKey + ': "' + el.value + '"');
125
127
  });
126
128
  var output = document.getElementById('theme-yaml-output');
127
129
  if (output) output.textContent = lines.join('\n');
data/scripts/bin/validate CHANGED
@@ -402,6 +402,7 @@ classified_files = %w[
402
402
  _config_dev.yml
403
403
  _config_secrets_local.yml
404
404
  pages/_about/settings/_config.yml
405
+ .github/ISSUE_TEMPLATE/config.yml
405
406
  ]
406
407
  classified_files.concat(Dir.glob('_data/**/*config*.{yml,yaml}'))
407
408
 
@@ -127,6 +127,7 @@ main() {
127
127
  source "$TEST_DIR/test_changelog.sh"
128
128
  source "$TEST_DIR/test_gem.sh"
129
129
  source "$TEST_DIR/test_analyze_commits.sh"
130
+ source "$TEST_DIR/test_locale_independence.sh"
130
131
 
131
132
  # Summary
132
133
  echo -e "\n${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
@@ -0,0 +1,29 @@
1
+ #!/bin/bash
2
+
3
+ # Locale-independence regression guard (T-015)
4
+ #
5
+ # PR #132 fixed `invalid byte sequence in US-ASCII` crashes in the Ruby
6
+ # tooling that only reproduced when no UTF-8 locale was set (minimal
7
+ # containers, some CI runners). These tests run the validators under
8
+ # LC_ALL=C LANG=C so the bug class cannot silently return: the repo files
9
+ # they read (README.md, _data/*.yml, package.json) contain multibyte
10
+ # characters, so any locale-dependent File.read regresses to a crash here.
11
+
12
+ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
13
+
14
+ echo "Testing locale independence (LC_ALL=C LANG=C)..."
15
+
16
+ assert_true "(cd '$REPO_ROOT' && LC_ALL=C LANG=C ruby scripts/generate-roadmap.rb --check >/dev/null 2>&1)" \
17
+ "generate-roadmap.rb --check survives a C locale"
18
+
19
+ assert_true "(cd '$REPO_ROOT' && LC_ALL=C LANG=C ruby scripts/generate-roadmap.rb --validate >/dev/null 2>&1)" \
20
+ "generate-roadmap.rb --validate survives a C locale"
21
+
22
+ assert_true "(cd '$REPO_ROOT' && LC_ALL=C LANG=C ruby scripts/sync-backlog.rb --check >/dev/null 2>&1)" \
23
+ "sync-backlog.rb --check survives a C locale"
24
+
25
+ # validate --quick covers the package.json read in scripts/bin/validate
26
+ assert_true "(cd '$REPO_ROOT' && LC_ALL=C LANG=C ./scripts/bin/validate --quick >/dev/null 2>&1)" \
27
+ "validate --quick survives a C locale"
28
+
29
+ echo -e "\n${GREEN}locale-independence tests complete${NC}"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-theme-zer0
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.1
4
+ version: 1.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amr Abdel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-11 00:00:00.000000000 Z
11
+ date: 2026-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -306,6 +306,7 @@ files:
306
306
  - assets/js/posts-pagination.js
307
307
  - assets/js/search-modal.js
308
308
  - assets/js/setup-wizard.js
309
+ - assets/js/share-actions.js
309
310
  - assets/js/side-bar-folders.js
310
311
  - assets/js/skin-editor.js
311
312
  - assets/js/stats.js
@@ -482,6 +483,7 @@ files:
482
483
  - scripts/test/lib/test_changelog.sh
483
484
  - scripts/test/lib/test_gem.sh
484
485
  - scripts/test/lib/test_git.sh
486
+ - scripts/test/lib/test_locale_independence.sh
485
487
  - scripts/test/lib/test_validation.sh
486
488
  - scripts/test/lib/test_version.sh
487
489
  - scripts/test/theme/validate
@@ -509,7 +511,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
509
511
  requirements:
510
512
  - - ">="
511
513
  - !ruby/object:Gem::Version
512
- version: 2.7.0
514
+ version: '3.2'
513
515
  required_rubygems_version: !ruby/object:Gem::Requirement
514
516
  requirements:
515
517
  - - ">="