jekyll-theme-zer0 1.9.10 → 1.11.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: e4f18964d4dc8864aa86efd141c457de6f2fbcf175668fc55ff22baf494da2ff
4
- data.tar.gz: 42d783b25bf009fd668afaebc79a7bd0c92c404a4fa9822598a7bb59d5c33191
3
+ metadata.gz: bbe0013679e3fd31006811bfaafd9a011e3e21c2acdc892cefabc2968e5cc542
4
+ data.tar.gz: 678d6fba0883cbb6228d601d11880909ecbb34bf12c861d0ec9f0535ceeccf34
5
5
  SHA512:
6
- metadata.gz: 16cede914562266dc89566362dec725cdfbdca697edead0191540b4987456b42a11e3443c417e5f6b8d42e2fc03d6c2a6c20a6e63dd1a580c41587c6d7765ba9
7
- data.tar.gz: 00636b9287ec7fd044e67e83a28598662be28e534c1b45de8f3e8d69c5a9430ee78ec4cee6e1343a306bc9705719b7ed77940d6d06dc41168d72b52cb0cf297d
6
+ metadata.gz: 8ae41e18796c02009349411e1e9ea45723fd383004560d781a4fcfa266db4fbc6f246755b275b6aabfc86abde0482dcc11380b7065cff0c7a945276f7b48797a
7
+ data.tar.gz: 28b154eda3a2405e6b0e506fb9a293c53956d731d7a4c8239cd1ca3d58a10b6ec28195640d2a8da96853ccd3ea0c5b2c13476ac28f422650bec5259412178ff5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.11.0] - 2026-06-01
4
+
5
+ ### Changed
6
+ - Version bump: minor release
7
+
8
+ ### Commits in this release
9
+ - 8a5ba7e2 feat(ci): add continuous-evolution backlog loop (#114)
10
+
11
+
12
+ ## [Unreleased]
13
+
14
+ ### Added
15
+ - **Continuous-evolution loop**: a self-sustaining backlog mechanism so AI agents can keep improving the repo between human sessions.
16
+ - `_data/backlog.yml` — tactical task queue (single source of truth), mirroring the `_data/roadmap.yml` pattern.
17
+ - `scripts/sync-backlog.rb` (+ `scripts/sync-backlog.sh`) — schema validator and GitHub Issues sync (idempotent via `<!-- backlog-id -->` markers).
18
+ - `.github/workflows/backlog-sync.yml` — syncs the backlog to issues on push to `main`; validates schema on PRs.
19
+ - `.github/workflows/auto-merge.yml` — enables native auto-merge for low-risk (`docs`/`deps`/`lint`) PRs once CI is green.
20
+ - `.github/prompts/repo-audit.prompt.md` (`/repo-audit`) and `.github/prompts/backlog-implement.prompt.md` (`/backlog-implement`) — the audit and implement routines.
21
+ - `.github/instructions/backlog.instructions.md` — file-scoped guidance for the backlog.
22
+ - `docs/systems/continuous-evolution.md` — full design, autonomy policy, and setup.
23
+ - `CLAUDE.md` — Claude Code pointer to `AGENTS.md` (per the documented convention).
24
+ ## [1.10.0] - 2026-06-01
25
+
26
+ ### Changed
27
+ - Version bump: minor release
28
+
29
+ ### Commits in this release
30
+ - 309202f2 feat(roadmap): add --validate mode, catch-up milestones v1.0–1.9, README accuracy fixes (#113)
31
+
32
+
3
33
  ## [1.9.10] - 2026-05-31
4
34
 
5
35
  ### 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.9.10
5
+ version: 1.11.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-05-31T22:40:35.000Z
23
+ lastmod: 2026-06-01T03:33:27.000Z
24
24
  draft: false
25
25
  permalink: /
26
26
  slug: zer0
@@ -217,9 +217,9 @@ Jump to a topic:
217
217
  - [AI-native workflow](#-ai-native-workflow) — multi-agent guidance, AI install wizard, AI preview images, AIEO discovery
218
218
  - [Architecture & technology stack](#-architecture)
219
219
  - [Key features in depth](#-key-features) — installer CLI, site scraper, Docker, analytics, search, navigation, dark mode
220
- - [Installation methods](#-installation-methods) — one-liner, modular CLI, remote theme, fork, Ruby gem
220
+ - [Installation methods](#-installation-methods--reference-card) — one-liner, modular CLI, remote theme, fork, Ruby gem
221
221
  - [Project structure](#-project-structure)
222
- - [Migration utility](#️-migration-utility--add-admin-pages-to-an-existing-site)
222
+ - [Migration utility](#-migration-utility--add-admin-pages-to-an-existing-site)
223
223
  - [Development workflow & testing](#-development-workflow)
224
224
  - [Deployment options](#-deployment)
225
225
  - [Documentation map](#-documentation)
@@ -289,7 +289,7 @@ Clone the repo (or fork) and your editor's AI agent will pick up project context
289
289
 
290
290
  ## 🏗 Architecture
291
291
 
292
- > **Who this is for:** developers, theme customizers, and anyone integrating zer0-mistakes into a larger system. If you only want to publish a blog, you can skip ahead to [Key Features](#-key-features) or [Installation Methods](#-installation-methods).
292
+ > **Who this is for:** developers, theme customizers, and anyone integrating zer0-mistakes into a larger system. If you only want to publish a blog, you can skip ahead to [Key Features](#-key-features) or [Installation Methods](#-installation-methods--reference-card).
293
293
 
294
294
  zer0-mistakes is a layered system: your content (Markdown + YAML) flows through the theme's templates, gets compiled by Jekyll, and is published as a fully static site that any host can serve. Every layer is replaceable without affecting the others.
295
295
 
@@ -407,7 +407,7 @@ docker-compose down -v # clean up volumes and gem cache
407
407
  docker-compose down && docker-compose up --build # full rebuild after Gemfile changes
408
408
  ```
409
409
 
410
- A separate [`docker-compose.test.yml`](docker-compose.test.yml) provides an isolated environment for CI-style test runs, and [`docker-compose.prod.yml`](docker-compose.prod.yml) (referenced in the [Deployment](#-deployment) section) builds a production image.
410
+ A separate [`docker-compose.test.yml`](docker-compose.test.yml) provides an isolated environment for CI-style test runs.
411
411
 
412
412
  ### 🔒 Privacy-First Analytics
413
413
 
@@ -583,7 +583,7 @@ The repository follows Jekyll convention with a few additions for tooling and AI
583
583
 
584
584
  | Directory | Purpose | Key Files |
585
585
  |-----------|---------|-----------|
586
- | `_layouts/` | Page templates | `default.html`, `journals.html`, `landing.html`, `notebook.html` |
586
+ | `_layouts/` | Page templates | `default.html`, `article.html`, `landing.html`, `notebook.html` |
587
587
  | `_includes/` | Reusable components | `core/`, `components/` (see `_includes/components/README.md`), `analytics/`, `navigation/` |
588
588
  | `_sass/` | Stylesheets | `tokens/` (design tokens), `components/`, `layouts/`, `utilities/`, `custom.scss` (legacy barrel), `core/`, `theme/` |
589
589
  | `_data/` | Data-driven content | `landing.yml` (homepage copy), `navigation/*.yml`, `ui-text.yml` (i18n), `features.yml`, `authors.yml`, `theme_backgrounds.yml` |
@@ -647,13 +647,13 @@ git commit -m "feat: add new component"
647
647
 
648
648
  ```bash
649
649
  # Quick validation
650
- ./test/validate_installation.sh
650
+ ./test/test_installation.sh
651
651
 
652
652
  # Full test suite
653
653
  ./test/test_runner.sh --verbose
654
654
 
655
655
  # Docker-specific tests
656
- ./test/test_docker_deployment.sh
656
+ ./test/test_deployment.sh
657
657
 
658
658
  # Frontend smoke tests (Playwright; starts Jekyll on :4011 unless BASE_URL is set)
659
659
  npm run test:smoke
@@ -680,7 +680,7 @@ Three supported targets, in order of recommendation:
680
680
  | Target | Cost | Setup | Best for |
681
681
  |---|---|---|---|
682
682
  | **GitHub Pages user site** | Free | Push to `main` of `<username>.github.io` (see [Path A](#path-a--easiest-fork-on-github-no-terminal-required)) | Most users; personal sites, docs, blogs, portfolios |
683
- | **Docker production image** | Self-hosted | `docker-compose -f docker-compose.prod.yml up -d` | Self-hosted servers, intranet, air-gapped environments |
683
+ | **Docker production image** | Self-hosted | `JEKYLL_ENV=production docker-compose up -d` | Self-hosted servers, intranet, air-gapped environments |
684
684
  | **Custom static host** | Varies | `docker-compose run --rm jekyll jekyll build` → upload `_site/` | Netlify, Vercel, Cloudflare Pages, S3 + CloudFront, Azure Static Web Apps |
685
685
 
686
686
  ---
@@ -706,6 +706,25 @@ zer0-mistakes documentation is split across three audiences. Pick the layer that
706
706
  | [📝 PRD](docs/PRD.md) | Product requirements & roadmap |
707
707
  | [🔒 Privacy Policy](https://zer0-mistakes.com/privacy-policy/) | GDPR/CCPA compliant privacy docs |
708
708
 
709
+ ### Contributor Technical Reference
710
+
711
+ These files live in the repo under `docs/` and are intended for theme contributors and forks:
712
+
713
+ | File | Topic |
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 |
727
+
709
728
  ---
710
729
 
711
730
  ## 🔧 Release System
@@ -784,7 +803,7 @@ When you fork the theme as a starter, the workflows come with you. To make them
784
803
 
785
804
  ## 🗺 Roadmap
786
805
 
787
- 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](/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/PRD.md) for product context.
788
807
 
789
808
  <!-- ROADMAP_MERMAID:START -->
790
809
 
@@ -798,13 +817,23 @@ gantt
798
817
  v0.19 Feature Discovery :done, 2026-01, 2026-01
799
818
  v0.20 Navigation Redesign :done, 2026-02, 2026-02
800
819
  v0.21 Env Switcher :done, 2026-02, 2026-03
820
+ v0.22 AIEO & Customization :done, 2026-04, 2026-04
821
+ v1.0 Modular Installer & First Stable :done, 2026-04, 2026-04
822
+ v1.1 Copilot Agent Prompts :done, 2026-04, 2026-04
823
+ v1.2 Bare-Minimum Starter :done, 2026-04, 2026-04
824
+ v1.3 Obsidian Vault Integration :done, 2026-04, 2026-04
825
+ v1.4 Obsidian Graph View :done, 2026-04, 2026-04
826
+ v1.5 Example Posts & AI Previews :done, 2026-04, 2026-04
827
+ v1.6 About Page & Search Cleanup :done, 2026-04, 2026-04
828
+ v1.7 Build Performance & MathJax 3 :done, 2026-05, 2026-05
829
+ v1.8 Design Tokens & Navigation Chrome :done, 2026-05, 2026-05
801
830
  section Current
802
- v0.22 AIEO & Customization :active, 2026-03, 2026-04
831
+ v1.9 Installer v2 & Site Scraper :active, 2026-05, 2026-05
803
832
  section Future
804
- v0.23 CMS Integration :2026-05, 2026-07
805
- v0.24 i18n Support :2026-07, 2026-09
806
- v0.25 Advanced Analytics :2026-09, 2026-11
807
- v1.0 Stable Release :milestone, 2027-01, 1d
833
+ v2.0 CMS Integration :2026-06, 2026-08
834
+ v2.1 i18n Support :2026-08, 2026-10
835
+ v2.2 Advanced Analytics :2026-10, 2026-12
836
+ v3.0 Stable LTS :milestone, 2027-02, 1d
808
837
  ```
809
838
 
810
839
  <!-- ROADMAP_MERMAID:END -->
@@ -818,11 +847,21 @@ gantt
818
847
  | **v0.19** | ✅ Completed | Jan 2026 | 43 documented features with a comprehensive feature registry. |
819
848
  | **v0.20** | ✅ Completed | Feb 2026 | Local Docker publishing pipeline and CI variable abstraction. |
820
849
  | **v0.21** | ✅ Completed | Feb 2026 | Environment switcher, settings modal redesign, and RubyGems API-key auth. |
821
- | **v0.22** | 🚧 In Progress | Q1–Q2 2026 | AI Engine Optimization (AIEO), structured data, and visual customization tools. |
822
- | **v0.23** | 🗓 Planned | Q2 2026 | Headless CMS integration with a content API and admin dashboard. |
823
- | **v0.24** | 🗓 Planned | Q3 2026 | Multi-language content support with locale-aware routing. |
824
- | **v0.25** | 🗓 Planned | Q4 2026 | Visual theme customizer, A/B testing, and conversion funnels. |
825
- | **v1.0** | 🎯 Milestone | Q1 2027 | Stable public API, 90%+ test coverage, and long-term support commitment. |
850
+ | **v0.22** | Completed | Apr 2026 | AI Engine Optimization (AIEO), structured data, and visual customization tools. |
851
+ | **v1.0** | Completed | Apr 2026 | First stable major release the monolithic installer rewritten as a modular, spec-driven CLI. |
852
+ | **v1.1** | Completed | Apr 2026 | Data-driven Copilot Agent prompt registry focused on frontend and CMS workflows. |
853
+ | **v1.2** | Completed | Apr 2026 | Three-file remote-theme starter with an in-browser configuration wizard. |
854
+ | **v1.3** | Completed | Apr 2026 | Edit content as an Obsidian vault with identical rendering on GitHub Pages. |
855
+ | **v1.4** | ✅ Completed | Apr 2026 | Force-directed knowledge graph mirroring Obsidian's local graph view. |
856
+ | **v1.5** | ✅ Completed | Apr 2026 | Richer sample content and regenerated AI preview images. |
857
+ | **v1.6** | ✅ Completed | Apr 2026 | Expanded About page and removal of the Algolia search dependency. |
858
+ | **v1.7** | ✅ Completed | May 2026 | Significant Jekyll build speedups and a MathJax 3 inline-math fix. |
859
+ | **v1.8** | ✅ Completed | May 2026 | Sass design-token system, refreshed navigation chrome, and a docs overhaul. |
860
+ | **v1.9** | 🚧 In Progress | Current (1.9.x) | Modular installer v2 with deploy plugins, AI wizard pipeline, and a site scraper. |
861
+ | **v2.0** | 🗓 Planned | Q3 2026 | Headless CMS integration with a content API and admin dashboard. |
862
+ | **v2.1** | 🗓 Planned | Q4 2026 | Multi-language content support with locale-aware routing. |
863
+ | **v2.2** | 🗓 Planned | Q4 2026 | Visual theme customizer, A/B testing, and conversion funnels. |
864
+ | **v3.0** | 🎯 Milestone | Q1 2027 | Stable public API, 90%+ test coverage, and long-term support commitment. |
826
865
 
827
866
  <!-- ROADMAP_TABLE:END -->
828
867
 
@@ -862,10 +901,10 @@ git push origin feature/awesome-feature
862
901
 
863
902
  | Metric | Value |
864
903
  |--------|-------|
865
- | **Current Version** | 1.9.10 ([RubyGems](https://rubygems.org/gems/jekyll-theme-zer0), [CHANGELOG](/CHANGELOG)) |
904
+ | **Current Version** | 1.11.0 ([RubyGems](https://rubygems.org/gems/jekyll-theme-zer0), [CHANGELOG](/CHANGELOG)) |
866
905
  | **Documented Features** | 43 ([Feature Registry](https://github.com/bamr87/zer0-mistakes/blob/main/_data/features.yml)) |
867
906
  | **Setup Time** | 2-5 minutes ([install.sh benchmarks](https://github.com/bamr87/zer0-mistakes/blob/main/install.sh)) |
868
- | **Documentation Pages** | 70+ ([browse docs](/pages/)) |
907
+ | **Documentation Pages** | 70+ ([browse docs](https://zer0-mistakes.com/pages/)) |
869
908
  | **RubyGems Downloads** | 3,000+ ([rubygems.org](https://rubygems.org/gems/jekyll-theme-zer0)) |
870
909
  | **Lighthouse Score** | 95+ ([measured via Chrome DevTools](https://developer.chrome.com/docs/lighthouse/)) |
871
910
 
@@ -883,10 +922,10 @@ Key patterns applied (see [glossary entry](https://zer0-mistakes.com/glossary/#a
883
922
  |---|---|
884
923
  | **Structured Data** | JSON-LD `SoftwareApplication`, `WebPage`, `Person`, and `FAQPage` schemas in every page head |
885
924
  | **Entity Density** | Author profiles, technology names, and version numbers linked to canonical sources |
886
- | **E-E-A-T Signals** | Visible [author block](/glossary/#e-e-a-t) on the landing page with social proof links |
887
- | **FAQ Injection** | Dedicated [FAQ page](/faq/) with 12 question-answer pairs and FAQPage schema |
888
- | **Definitional Precision** | Machine-readable [Glossary](/glossary/) with 20+ key term definitions |
889
- | **Temporal Anchoring** | Dated [Roadmap](/roadmap/) with past, present, and future milestones |
925
+ | **E-E-A-T Signals** | Visible [author block](https://zer0-mistakes.com/glossary/#e-e-a-t) on the landing page with social proof links |
926
+ | **FAQ Injection** | Dedicated [FAQ page](https://zer0-mistakes.com/faq/) with 12 question-answer pairs and FAQPage schema |
927
+ | **Definitional Precision** | Machine-readable [Glossary](https://zer0-mistakes.com/glossary/) with 20+ key term definitions |
928
+ | **Temporal Anchoring** | Dated [Roadmap](https://zer0-mistakes.com/roadmap/) with past, present, and future milestones |
890
929
  | **Substantiated Claims** | Project stats table links to RubyGems, CHANGELOG, and Feature Registry as evidence |
891
930
  | **Procedural Clarity** | Step-by-step installation with Mermaid sequence diagrams and comparison tables |
892
931
 
@@ -917,6 +956,6 @@ And these AI partners that make zer0-mistakes truly AI-native:
917
956
 
918
957
  **Built with ❤️ — and a little help from our AI partners — for the Jekyll community**
919
958
 
920
- **v1.9.10** • [Changelog](CHANGELOG.md) • [License](LICENSE) • [Contributing](CONTRIBUTING.md) • [AI Agent Guide](AGENTS.md)
959
+ **v1.11.0** • [Changelog](CHANGELOG.md) • [License](LICENSE) • [Contributing](CONTRIBUTING.md) • [AI Agent Guide](AGENTS.md)
921
960
 
922
961
 
data/_data/backlog.yml ADDED
@@ -0,0 +1,161 @@
1
+ # =============================================================================
2
+ # zer0-mistakes Backlog — Tactical Task Queue (Single Source of Truth)
3
+ # =============================================================================
4
+ #
5
+ # This file is the canonical, machine-readable backlog of *tactical* tasks
6
+ # (the granular, pickup-able work items). It complements `_data/roadmap.yml`,
7
+ # which holds the *strategic* milestones.
8
+ #
9
+ # It powers the continuous-evolution loop:
10
+ #
11
+ # 1. The AUDIT routine (`.github/prompts/repo-audit.prompt.md`) appends new
12
+ # tasks here when it reviews the repo.
13
+ # 2. `scripts/sync-backlog.rb` (run by `.github/workflows/backlog-sync.yml`)
14
+ # mirrors each open task to a GitHub Issue and closes issues for tasks
15
+ # marked `done`.
16
+ # 3. The IMPLEMENT routine (`.github/prompts/backlog-implement.prompt.md`)
17
+ # picks the highest-priority open task, implements it, and opens a PR.
18
+ #
19
+ # See `docs/systems/continuous-evolution.md` for the full design.
20
+ #
21
+ # To add a task by hand:
22
+ # 1. Copy the schema block below; give it the next free `T-NNN` id.
23
+ # 2. Set status/priority/area/risk/effort/source and acceptance criteria.
24
+ # 3. Commit. The sync workflow creates the matching GitHub Issue on push.
25
+ #
26
+ # =============================================================================
27
+ # Schema
28
+ # =============================================================================
29
+ #
30
+ # meta:
31
+ # title: Display title
32
+ # updated: Last-reviewed date (YYYY-MM-DD)
33
+ # next_id: Next free task id number (the audit routine increments this)
34
+ #
35
+ # tasks:
36
+ # - id: Stable task id, "T-NNN" (never reused; matches the GitHub Issue)
37
+ # title: One line — becomes the GitHub Issue title
38
+ # status: open | in-progress | blocked | done
39
+ # priority: P0 (urgent) | P1 | P2 | P3 (nice-to-have)
40
+ # area: tests | docs | feat | infra | a11y | perf | deps | lint
41
+ # risk: low (auto-merge eligible) | standard (human review required)
42
+ # effort: S | M | L
43
+ # source: audit | roadmap | issue | user
44
+ # summary: 1–2 lines of context
45
+ # acceptance: List of checkable done-criteria the implement routine verifies
46
+ # links: { issue: <#|null>, pr: <#|null>, roadmap: "<version>|null" }
47
+ # created: YYYY-MM-DD
48
+ # updated: YYYY-MM-DD
49
+ #
50
+ # `risk: low` + area in {docs, deps, lint} makes a task auto-merge eligible once
51
+ # CI is green (see `.github/prompts/backlog-implement.prompt.md`). Everything
52
+ # else stays PR-only for human review.
53
+ #
54
+ # =============================================================================
55
+
56
+ meta:
57
+ title: "zer0-mistakes Backlog"
58
+ updated: 2026-05-31
59
+ next_id: 7
60
+
61
+ tasks:
62
+ # --- Housekeeping (seeded so the loop has work on day one) ------------------
63
+
64
+ - id: T-001
65
+ title: "Reconcile roadmap milestone numbering with the published gem version"
66
+ status: open
67
+ priority: P2
68
+ area: docs
69
+ risk: standard
70
+ effort: M
71
+ source: user
72
+ summary: >-
73
+ `_data/roadmap.yml` milestones run 0.17–1.0 while the gem ships as 1.9.9.
74
+ Decide on a single numbering scheme (or document the mapping) so the
75
+ roadmap, README, and RubyGems version no longer contradict each other.
76
+ acceptance:
77
+ - "Roadmap version line and gem version no longer contradict (either remapped or an explicit mapping note added to `_data/roadmap.yml` and `pages/roadmap.md`)."
78
+ - "`./scripts/generate-roadmap.sh --check` passes after the change."
79
+ - "No edit to `lib/jekyll-theme-zer0/version.rb`."
80
+ links: { issue: null, pr: null, roadmap: null }
81
+ created: 2026-05-31
82
+ updated: 2026-05-31
83
+
84
+ - id: T-002
85
+ title: "Refresh roadmap `updated:` date and review milestone statuses"
86
+ status: open
87
+ priority: P3
88
+ area: docs
89
+ risk: low
90
+ effort: S
91
+ source: user
92
+ summary: >-
93
+ `_data/roadmap.yml` `meta.updated` is 2026-04-18. Refresh it and confirm
94
+ the 0.22 "active" milestone still reflects reality.
95
+ acceptance:
96
+ - "`meta.updated` set to the current date."
97
+ - "Active/planned statuses reviewed against shipped work."
98
+ - "`./scripts/generate-roadmap.sh --check` passes."
99
+ links: { issue: null, pr: null, roadmap: null }
100
+ created: 2026-05-31
101
+ updated: 2026-05-31
102
+
103
+ - id: T-003
104
+ title: "Add GitHub issue templates and a pull-request template"
105
+ status: open
106
+ priority: P2
107
+ area: infra
108
+ risk: standard
109
+ effort: M
110
+ source: user
111
+ summary: >-
112
+ The repo has no `.github/ISSUE_TEMPLATE/` or PR template. Add a bug-report
113
+ and feature-request issue form plus a PR template that nudges contributors
114
+ toward conventional commits, CHANGELOG updates, and the test checklist.
115
+ acceptance:
116
+ - "`.github/ISSUE_TEMPLATE/bug_report.yml` and `feature_request.yml` exist and render."
117
+ - "`.github/pull_request_template.md` exists with a conventional-commit + CHANGELOG + tests checklist."
118
+ - "Agent-filed backlog issues remain compatible with the new templates (no broken automation)."
119
+ links: { issue: null, pr: null, roadmap: null }
120
+ created: 2026-05-31
121
+ updated: 2026-05-31
122
+
123
+ - id: T-004
124
+ title: "Docs-freshness sweep: reconcile docs/ ↔ pages/_docs/ and fix broken links"
125
+ status: open
126
+ priority: P2
127
+ area: docs
128
+ risk: low
129
+ effort: M
130
+ source: user
131
+ summary: >-
132
+ Run the markdown-link checker and audit the two-tier docs for drift,
133
+ stale dates/versions, and any user/technical pages that fell out of sync
134
+ after the recent alignment commit.
135
+ acceptance:
136
+ - "`markdown-link-check` (config in `.github/config/`) reports no broken internal links."
137
+ - "Any stale version/date references in `docs/` and `pages/_docs/` are corrected."
138
+ - "Cross-links between the user tier and technical tier resolve both ways."
139
+ links: { issue: null, pr: null, roadmap: null }
140
+ created: 2026-05-31
141
+ updated: 2026-05-31
142
+
143
+ - id: T-005
144
+ title: "Coverage baseline: identify the lowest-covered subsystems toward the v1.0 90% goal"
145
+ status: open
146
+ priority: P1
147
+ area: tests
148
+ risk: standard
149
+ effort: L
150
+ source: roadmap
151
+ summary: >-
152
+ The v1.0 milestone targets 90%+ automated test coverage. Produce a
153
+ coverage baseline and file follow-up tasks for the lowest-covered areas
154
+ (start with the modular installer and the Obsidian resolver paths).
155
+ acceptance:
156
+ - "A coverage baseline is recorded (test/coverage or a docs note)."
157
+ - "The 2–3 lowest-covered subsystems are identified and filed as new backlog tasks."
158
+ - "No reduction in existing passing tests (`./scripts/bin/test` stays green)."
159
+ links: { issue: null, pr: null, roadmap: "1.0" }
160
+ created: 2026-05-31
161
+ updated: 2026-05-31
data/_data/roadmap.yml CHANGED
@@ -16,10 +16,23 @@
16
16
  # To update the roadmap:
17
17
  # 1. Edit this file.
18
18
  # 2. Run `./scripts/generate-roadmap.sh` to refresh the README.
19
- # 3. Commit both this file and the resulting README.md changes.
19
+ # 3. Run `./scripts/generate-roadmap.sh --validate` to confirm the roadmap
20
+ # is internally consistent and still tracks the shipped gem version.
21
+ # 4. Commit both this file and the resulting README.md changes.
20
22
  #
21
23
  # A GitHub Actions workflow (`.github/workflows/roadmap-sync.yml`) also runs
22
- # the generator automatically on push when this file changes.
24
+ # the generator and the validator automatically: it regenerates the README on
25
+ # push, fails PRs whose README is stale, and flags whenever the roadmap drifts
26
+ # behind the version in `lib/jekyll-theme-zer0/version.rb`.
27
+ #
28
+ # Tracking rules enforced by `--validate`:
29
+ # - `status` must be one of: completed | active | planned | milestone
30
+ # - `status` must match its `section` (completed=Completed, active=Current,
31
+ # planned/milestone=Future)
32
+ # - exactly one `active` milestone; its version must equal the gem's
33
+ # major.minor series
34
+ # - no `planned`/`milestone` version at or below the shipped gem version
35
+ # - `start`/`end` are valid YYYY-MM with end >= start; `meta.updated` is fresh
23
36
  #
24
37
  # =============================================================================
25
38
  # Schema
@@ -47,7 +60,7 @@
47
60
  meta:
48
61
  title: "zer0-mistakes Roadmap"
49
62
  tagline: "Past releases, current focus, and future plans for the zer0-mistakes Jekyll theme."
50
- updated: 2026-04-18
63
+ updated: 2026-05-30
51
64
 
52
65
  milestones:
53
66
  # --- Completed -------------------------------------------------------------
@@ -119,40 +132,171 @@ milestones:
119
132
  - "RubyGems API-key authentication"
120
133
  - "Dependency updates"
121
134
 
122
- # --- Current ---------------------------------------------------------------
135
+ # --- Completed (continued) -------------------------------------------------
123
136
 
124
137
  - version: "0.22"
125
138
  title: "AIEO & Customization"
126
- status: active
127
- section: Current
128
- start: 2026-03
139
+ status: completed
140
+ section: Completed
141
+ start: 2026-04
129
142
  end: 2026-04
130
- target: "Q1–Q2 2026"
143
+ released: 2026-04-21
131
144
  summary: "AI Engine Optimization (AIEO), structured data, and visual customization tools."
132
145
  features:
133
146
  - "JSON-LD `SoftwareApplication`, `WebPage`, `Person`, and `FAQPage` schemas"
134
147
  - "Author E-E-A-T visibility block on the landing page"
135
- - "FAQ page with FAQPage schema"
136
- - "Glossary with key term definitions"
137
- - "Roadmap page with temporal anchoring"
138
- - "Citation hooks on project stats"
148
+ - "FAQ page with FAQPage schema and glossary with key term definitions"
139
149
  - "Skin editor with live color pickers and palette generator"
140
150
  - "Admin layout and configuration dashboards"
141
151
  - "Playwright visual regression tests"
142
152
  - "Vendored assets (Bootstrap, Icons, Mermaid — no runtime CDN)"
143
153
  - "Copilot Agent prompt button with data-driven prompt registry"
144
- - "Universal installer (remote / github / codespaces modes)"
145
154
  - "Dynamic collection-based navigation fallback for zero-config sites"
146
155
 
156
+ - version: "1.0"
157
+ title: "Modular Installer & First Stable"
158
+ status: completed
159
+ section: Completed
160
+ start: 2026-04
161
+ end: 2026-04
162
+ released: 2026-04-20
163
+ summary: "First stable major release — the monolithic installer rewritten as a modular, spec-driven CLI."
164
+ features:
165
+ - "Modular installer CLI (`scripts/bin/install`) with library modules"
166
+ - "Declarative YAML profiles (full / minimal / fork / remote / github)"
167
+ - "Pluggable deploy targets (GitHub Pages, Azure SWA, Docker)"
168
+ - "`wizard --ai`, `doctor`, `diagnose`, and `upgrade` subcommands"
169
+ - "Hardened version analyzer and release automation"
170
+
171
+ - version: "1.1"
172
+ title: "Copilot Agent Prompts"
173
+ status: completed
174
+ section: Completed
175
+ start: 2026-04
176
+ end: 2026-04
177
+ released: 2026-04-21
178
+ summary: "Data-driven Copilot Agent prompt registry focused on frontend and CMS workflows."
179
+ features:
180
+ - "Prompt registry powering the Copilot Agent button"
181
+ - "Frontend- and CMS-oriented prompt presets"
182
+
183
+ - version: "1.2"
184
+ title: "Bare-Minimum Starter"
185
+ status: completed
186
+ section: Completed
187
+ start: 2026-04
188
+ end: 2026-04
189
+ released: 2026-04-22
190
+ summary: "Three-file remote-theme starter with an in-browser configuration wizard."
191
+ features:
192
+ - "`welcome` layout that detects unconfigured sites"
193
+ - "Embedded setup wizard that generates a personalised `_config.yml`"
194
+ - "Smarter unconfigured-site detection heuristics"
195
+
196
+ - version: "1.3"
197
+ title: "Obsidian Vault Integration"
198
+ status: completed
199
+ section: Completed
200
+ start: 2026-04
201
+ end: 2026-04
202
+ released: 2026-04-24
203
+ summary: "Edit content as an Obsidian vault with identical rendering on GitHub Pages."
204
+ features:
205
+ - "Client-side wiki-link resolver (aliases, embeds, callouts, inline tags)"
206
+ - "Server-side backlinks panel on note layouts"
207
+ - "Optional Ruby converter for vanilla Jekyll forks"
208
+
209
+ - version: "1.4"
210
+ title: "Obsidian Graph View"
211
+ status: completed
212
+ section: Completed
213
+ start: 2026-04
214
+ end: 2026-04
215
+ released: 2026-04-25
216
+ summary: "Force-directed knowledge graph mirroring Obsidian's local graph view."
217
+ features:
218
+ - "Cytoscape.js graph at `/docs/obsidian/graph/`"
219
+ - "Standalone collapsible local graph panel"
220
+
221
+ - version: "1.5"
222
+ title: "Example Posts & AI Previews"
223
+ status: completed
224
+ section: Completed
225
+ start: 2026-04
226
+ end: 2026-04
227
+ released: 2026-04-29
228
+ summary: "Richer sample content and regenerated AI preview images."
229
+ features:
230
+ - "12 example posts across Business, Development, Science, Technology, Tutorial, and World"
231
+ - "All preview images regenerated with the GPT image model"
232
+
233
+ - version: "1.6"
234
+ title: "About Page & Search Cleanup"
235
+ status: completed
236
+ section: Completed
237
+ start: 2026-04
238
+ end: 2026-04
239
+ released: 2026-04-29
240
+ summary: "Expanded About page and removal of the Algolia search dependency."
241
+ features:
242
+ - "About page with prerequisites, quick start, FAQ, and architecture diagram"
243
+ - "Algolia removed in favor of the built-in client-side search"
244
+ - "Jekyll build performance caching"
245
+
246
+ - version: "1.7"
247
+ title: "Build Performance & MathJax 3"
248
+ status: completed
249
+ section: Completed
250
+ start: 2026-05
251
+ end: 2026-05
252
+ released: 2026-05-24
253
+ summary: "Significant Jekyll build speedups and a MathJax 3 inline-math fix."
254
+ features:
255
+ - "Conditional MathJax/Mermaid loading and richer Obsidian cache"
256
+ - "jQuery removed from page loads"
257
+ - "Profiled Docker build reduced from ~119s to ~87s"
258
+
259
+ - version: "1.8"
260
+ title: "Design Tokens & Navigation Chrome"
261
+ status: completed
262
+ section: Completed
263
+ start: 2026-05
264
+ end: 2026-05
265
+ released: 2026-05-25
266
+ summary: "Sass design-token system, refreshed navigation chrome, and a docs overhaul."
267
+ features:
268
+ - "Sass token layers, component/layout partials, and skins"
269
+ - "VS Code-style collapsible sidebar rail and TOC"
270
+ - "Unified Playwright test tiers (smoke / snapshots / regression)"
271
+ - "Design-system developer docs"
272
+
273
+ # --- Current ---------------------------------------------------------------
274
+
275
+ - version: "1.9"
276
+ title: "Installer v2 & Site Scraper"
277
+ status: active
278
+ section: Current
279
+ start: 2026-05
280
+ end: 2026-05
281
+ target: "Current (1.9.x)"
282
+ released: 2026-05-27
283
+ summary: "Modular installer v2 with deploy plugins, AI wizard pipeline, and a site scraper."
284
+ features:
285
+ - "Installer deploy plugins and AI wizard apply pipeline"
286
+ - "Site scraper — BFS-crawl any website into a zer0-mistakes site"
287
+ - "Accessibility skin fixes (WCAG AA contrast); `air` default skin"
288
+ - "Quickstart documentation rewrite with screenshots"
289
+ - "Hardened one-line installer path"
290
+
147
291
  # --- Future ----------------------------------------------------------------
148
292
 
149
- - version: "0.23"
293
+ - version: "2.0"
150
294
  title: "CMS Integration"
151
295
  status: planned
152
296
  section: Future
153
- start: 2026-05
154
- end: 2026-07
155
- target: "Q2 2026"
297
+ start: 2026-06
298
+ end: 2026-08
299
+ target: "Q3 2026"
156
300
  summary: "Headless CMS integration with a content API and admin dashboard."
157
301
  features:
158
302
  - "Headless CMS integration (Decap CMS or Tina)"
@@ -161,13 +305,13 @@ milestones:
161
305
  - "Draft preview workflow"
162
306
  - "Multi-author collaboration"
163
307
 
164
- - version: "0.24"
308
+ - version: "2.1"
165
309
  title: "i18n Support"
166
310
  status: planned
167
311
  section: Future
168
- start: 2026-07
169
- end: 2026-09
170
- target: "Q3 2026"
312
+ start: 2026-08
313
+ end: 2026-10
314
+ target: "Q4 2026"
171
315
  summary: "Multi-language content support with locale-aware routing."
172
316
  features:
173
317
  - "Multi-language content support"
@@ -175,12 +319,12 @@ milestones:
175
319
  - "Translated UI strings via `_data/ui-text.yml`"
176
320
  - "Right-to-left (RTL) layout support"
177
321
 
178
- - version: "0.25"
322
+ - version: "2.2"
179
323
  title: "Advanced Analytics"
180
324
  status: planned
181
325
  section: Future
182
- start: 2026-09
183
- end: 2026-11
326
+ start: 2026-10
327
+ end: 2026-12
184
328
  target: "Q4 2026"
185
329
  summary: "Visual theme customizer, A/B testing, and conversion funnels."
186
330
  features:
@@ -189,12 +333,12 @@ milestones:
189
333
  - "Heatmap visualization"
190
334
  - "Visual theme customizer enhancements"
191
335
 
192
- - version: "1.0"
193
- title: "Stable Release"
336
+ - version: "3.0"
337
+ title: "Stable LTS"
194
338
  status: milestone
195
339
  section: Future
196
- start: 2027-01
197
- end: 2027-01
340
+ start: 2027-02
341
+ end: 2027-02
198
342
  target: "Q1 2027"
199
343
  summary: "Stable public API, 90%+ test coverage, and long-term support commitment."
200
344
  features:
@@ -15,6 +15,7 @@
15
15
  # Usage:
16
16
  # ruby scripts/generate-roadmap.rb # update README.md in place
17
17
  # ruby scripts/generate-roadmap.rb --check # exit non-zero if README is stale
18
+ # ruby scripts/generate-roadmap.rb --validate # check roadmap integrity & version tracking
18
19
  # ruby scripts/generate-roadmap.rb --stdout # print regenerated sections only
19
20
  #
20
21
  # This script has no gem dependencies beyond the Ruby stdlib.
@@ -24,9 +25,21 @@ require 'yaml'
24
25
  require 'date'
25
26
  require 'optparse'
26
27
 
27
- ROOT = File.expand_path('..', __dir__)
28
- DATA_FILE = File.join(ROOT, '_data', 'roadmap.yml')
29
- README = File.join(ROOT, 'README.md')
28
+ ROOT = File.expand_path('..', __dir__)
29
+ DATA_FILE = File.join(ROOT, '_data', 'roadmap.yml')
30
+ README = File.join(ROOT, 'README.md')
31
+ VERSION_RB = File.join(ROOT, 'lib', 'jekyll-theme-zer0', 'version.rb')
32
+
33
+ # Allowed enum values and the section each status must live in.
34
+ VALID_STATUSES = %w[completed active planned milestone].freeze
35
+ SECTION_FOR = {
36
+ 'completed' => 'Completed',
37
+ 'active' => 'Current',
38
+ 'planned' => 'Future',
39
+ 'milestone' => 'Future'
40
+ }.freeze
41
+ # How stale meta.updated may get before validation warns (days).
42
+ FRESHNESS_DAYS = 120
30
43
 
31
44
  MERMAID_START = '<!-- ROADMAP_MERMAID:START -->'
32
45
  MERMAID_END = '<!-- ROADMAP_MERMAID:END -->'
@@ -90,7 +103,7 @@ def target_label(milestone)
90
103
  case milestone['status']
91
104
  when 'completed'
92
105
  if (released = milestone['released'])
93
- Date.parse(released.to_s).strftime('%b %Y')
106
+ Date.parse(released.to_s).strftime('%b %Y') rescue 'Completed'
94
107
  else
95
108
  'Completed'
96
109
  end
@@ -126,6 +139,150 @@ def render_table(data)
126
139
  (header + rows).join("\n")
127
140
  end
128
141
 
142
+ # ---------------------------------------------------------------------------
143
+ # Tracking / validation helpers
144
+ # ---------------------------------------------------------------------------
145
+
146
+ # Reads the canonical gem version (e.g. "1.9.8") from lib/.../version.rb.
147
+ # Returns nil if the file or constant can't be found, so validation degrades
148
+ # gracefully on stripped checkouts.
149
+ def current_gem_version
150
+ return nil unless File.exist?(VERSION_RB)
151
+
152
+ m = File.read(VERSION_RB).match(/VERSION\s*=\s*["']([^"']+)["']/)
153
+ m && m[1]
154
+ end
155
+
156
+ # "1.9.8" -> "1.9" (the major.minor series a milestone version tracks).
157
+ def minor_series(version_string)
158
+ parts = version_string.to_s.split('.')
159
+ "#{parts[0]}.#{parts[1] || '0'}"
160
+ end
161
+
162
+ def parse_month(value)
163
+ Date.strptime(value.to_s, '%Y-%m')
164
+ rescue ArgumentError
165
+ nil
166
+ end
167
+
168
+ # Validates the roadmap data for internal consistency and tracking accuracy.
169
+ # Returns [errors, warnings] as arrays of strings. Errors are integrity
170
+ # violations (fail CI); warnings flag drift that humans should review.
171
+ def validate_roadmap(data)
172
+ errors = []
173
+ warnings = []
174
+ milestones = data['milestones'] || []
175
+
176
+ errors << 'No milestones defined.' if milestones.empty?
177
+
178
+ seen_versions = {}
179
+ milestones.each do |m|
180
+ id = "v#{m['version'] || '?'}"
181
+
182
+ %w[version title status section start].each do |field|
183
+ errors << "#{id}: missing required field '#{field}'." if m[field].nil? || m[field].to_s.empty?
184
+ end
185
+
186
+ status = m['status'].to_s
187
+ unless VALID_STATUSES.include?(status)
188
+ errors << "#{id}: invalid status '#{status}' (expected #{VALID_STATUSES.join(', ')})."
189
+ end
190
+
191
+ if (expected = SECTION_FOR[status]) && m['section'] != expected
192
+ errors << "#{id}: status '#{status}' must use section '#{expected}', found '#{m['section']}'."
193
+ end
194
+
195
+ if (v = m['version'])
196
+ errors << "#{id}: duplicate version '#{v}'." if seen_versions[v]
197
+ seen_versions[v] = true
198
+ end
199
+
200
+ start_d = parse_month(m['start'])
201
+ errors << "#{id}: start '#{m['start']}' is not a valid YYYY-MM date." if m['start'] && start_d.nil?
202
+ if m['end']
203
+ end_d = parse_month(m['end'])
204
+ if end_d.nil?
205
+ errors << "#{id}: end '#{m['end']}' is not a valid YYYY-MM date."
206
+ elsif start_d && end_d < start_d
207
+ errors << "#{id}: end '#{m['end']}' precedes start '#{m['start']}'."
208
+ end
209
+ end
210
+
211
+ if status == 'completed' && (m['released'].nil? || m['released'].to_s.empty?)
212
+ warnings << "#{id}: completed milestone has no 'released' date."
213
+ end
214
+
215
+ if status == 'active' && !m['released'].to_s.empty?
216
+ warnings << "#{id}: active milestone has a 'released' date — should it be marked 'completed'?"
217
+ end
218
+ end
219
+
220
+ # Exactly one in-flight milestone keeps "Current" unambiguous.
221
+ active = milestones.select { |m| m['status'] == 'active' }
222
+ case active.size
223
+ when 0 then warnings << "No 'active' milestone — nothing marks the current focus."
224
+ when 1 then nil
225
+ else errors << "Multiple 'active' milestones (#{active.map { |m| "v#{m['version']}" }.join(', ')}); only one allowed."
226
+ end
227
+
228
+ # Cross-reference the canonical gem version so the roadmap can't drift.
229
+ if (gem_v = current_gem_version)
230
+ require 'rubygems'
231
+ gem_ver = Gem::Version.new(gem_v)
232
+ series = minor_series(gem_v)
233
+
234
+ if active.size == 1 && active.first['version'].to_s != series
235
+ warnings << "Active milestone is v#{active.first['version']} but the gem is at v#{gem_v} " \
236
+ "(series #{series}). Advance the roadmap to track the shipped version."
237
+ end
238
+
239
+ milestones.each do |m|
240
+ next unless %w[planned milestone].include?(m['status'].to_s)
241
+
242
+ begin
243
+ next if Gem::Version.new(m['version'].to_s) > gem_ver
244
+ rescue ArgumentError
245
+ next
246
+ end
247
+ warnings << "v#{m['version']} is still '#{m['status']}' but the gem already shipped v#{gem_v}; " \
248
+ 'mark it completed or renumber it.'
249
+ end
250
+ else
251
+ warnings << "Could not read gem version from #{File.basename(VERSION_RB)} — skipped version tracking."
252
+ end
253
+
254
+ # Freshness of the last-reviewed date.
255
+ if (updated = data.dig('meta', 'updated'))
256
+ updated_d = updated.is_a?(Date) ? updated : (Date.parse(updated.to_s) rescue nil)
257
+ if updated_d.nil?
258
+ warnings << "meta.updated '#{updated}' is not a valid date."
259
+ elsif (Date.today - updated_d).to_i > FRESHNESS_DAYS
260
+ warnings << "meta.updated (#{updated_d}) is more than #{FRESHNESS_DAYS} days old; review the roadmap."
261
+ end
262
+ else
263
+ warnings << 'meta.updated is not set.'
264
+ end
265
+
266
+ [errors, warnings]
267
+ end
268
+
269
+ def run_validation(data)
270
+ errors, warnings = validate_roadmap(data)
271
+
272
+ warnings.each { |w| warn " ⚠ WARN #{w}" }
273
+ errors.each { |e| warn " ✗ FAIL #{e}" }
274
+
275
+ if errors.empty? && warnings.empty?
276
+ puts '✓ Roadmap is valid and tracks the current version.'
277
+ elsif errors.empty?
278
+ puts "✓ Roadmap valid (#{warnings.size} warning(s) — see above)."
279
+ else
280
+ warn "✗ Roadmap validation failed: #{errors.size} error(s), #{warnings.size} warning(s)."
281
+ end
282
+
283
+ errors.empty? ? 0 : 1
284
+ end
285
+
129
286
  def replace_block(content, marker_start, marker_end, replacement)
130
287
  pattern = /(#{Regexp.escape(marker_start)})(.*?)(#{Regexp.escape(marker_end)})/m
131
288
  unless content.match?(pattern)
@@ -146,8 +303,9 @@ end
146
303
  def main
147
304
  options = { mode: :write }
148
305
  OptionParser.new do |opts|
149
- opts.banner = 'Usage: generate-roadmap.rb [--check|--stdout]'
306
+ opts.banner = 'Usage: generate-roadmap.rb [--check|--validate|--stdout]'
150
307
  opts.on('--check', 'Exit non-zero if README would change') { options[:mode] = :check }
308
+ opts.on('--validate', 'Check roadmap integrity & version tracking') { options[:mode] = :validate }
151
309
  opts.on('--stdout', 'Print regenerated sections to stdout') { options[:mode] = :stdout }
152
310
  end.parse!
153
311
 
@@ -162,6 +320,9 @@ def main
162
320
  rescue ArgumentError
163
321
  YAML.safe_load(File.read(DATA_FILE), permitted_classes: [Date, Time], aliases: false)
164
322
  end
323
+
324
+ return run_validation(data) if options[:mode] == :validate
325
+
165
326
  mermaid = render_mermaid(data)
166
327
  table = render_table(data)
167
328
 
@@ -11,6 +11,7 @@
11
11
  # Usage:
12
12
  # ./scripts/generate-roadmap.sh # update README.md
13
13
  # ./scripts/generate-roadmap.sh --check # CI-friendly drift detection
14
+ # ./scripts/generate-roadmap.sh --validate # check integrity & version tracking
14
15
  # ./scripts/generate-roadmap.sh --stdout # print regenerated sections only
15
16
  #
16
17
  # =============================================================================
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # =============================================================================
5
+ # sync-backlog.rb
6
+ # =============================================================================
7
+ #
8
+ # Mirrors `_data/backlog.yml` (the tactical task queue) to GitHub Issues.
9
+ #
10
+ # - Each task with status open|in-progress|blocked -> an OPEN issue.
11
+ # - Each task with status done -> its issue is CLOSED.
12
+ #
13
+ # Issues are matched back to tasks idempotently by a hidden marker embedded in
14
+ # the issue body: `<!-- backlog-id: T-001 -->`. Re-running the sync updates the
15
+ # title/body/labels in place rather than creating duplicates.
16
+ #
17
+ # Managed labels (created with `gh label create --force` if missing):
18
+ # agent-ready · priority:P0..P3 · area:<area> · risk:<low|standard> · agent-hold
19
+ #
20
+ # Usage:
21
+ # ruby scripts/sync-backlog.rb # create/update/close issues via `gh`
22
+ # ruby scripts/sync-backlog.rb --check # validate schema only (no `gh`, CI/PR gate)
23
+ # ruby scripts/sync-backlog.rb --dry-run # print intended `gh` calls, make no changes
24
+ #
25
+ # Requires the `gh` CLI authenticated with `issues: write` for the write path.
26
+ # `--check` needs only Ruby stdlib (used as the pull-request gate).
27
+ # =============================================================================
28
+
29
+ require 'yaml'
30
+ require 'date'
31
+ require 'json'
32
+ require 'optparse'
33
+ require 'open3'
34
+ require 'shellwords'
35
+
36
+ ROOT = File.expand_path('..', __dir__)
37
+ DATA_FILE = File.join(ROOT, '_data', 'backlog.yml')
38
+
39
+ VALID_STATUS = %w[open in-progress blocked done].freeze
40
+ VALID_PRIORITY = %w[P0 P1 P2 P3].freeze
41
+ VALID_AREA = %w[tests docs feat infra a11y perf deps lint].freeze
42
+ VALID_RISK = %w[low standard].freeze
43
+ VALID_EFFORT = %w[S M L].freeze
44
+ VALID_SOURCE = %w[audit roadmap issue user].freeze
45
+
46
+ OPEN_STATUSES = %w[open in-progress blocked].freeze
47
+
48
+ # Labels this script owns. On each sync we reconcile a task's labels to exactly
49
+ # the managed set it should carry, leaving any human-applied labels untouched.
50
+ def managed_labels(task)
51
+ labels = ['agent-ready', "priority:#{task['priority']}", "area:#{task['area']}", "risk:#{task['risk']}"]
52
+ labels << 'agent-hold' if task['status'] == 'blocked'
53
+ labels
54
+ end
55
+
56
+ ALL_MANAGED_LABELS = (
57
+ ['agent-ready', 'agent-hold'] +
58
+ VALID_PRIORITY.map { |p| "priority:#{p}" } +
59
+ VALID_AREA.map { |a| "area:#{a}" } +
60
+ VALID_RISK.map { |r| "risk:#{r}" }
61
+ ).freeze
62
+
63
+ LABEL_COLORS = {
64
+ 'agent-ready' => '0e8a16',
65
+ 'agent-hold' => 'b60205'
66
+ }.freeze
67
+ PRIORITY_COLOR = 'd93f0b'
68
+ AREA_COLOR = '1d76db'
69
+ RISK_COLOR = 'fbca04'
70
+
71
+ # ---------------------------------------------------------------------------
72
+ # Load + validate
73
+ # ---------------------------------------------------------------------------
74
+
75
+ def load_data
76
+ # Mirror generate-roadmap.rb: permit Date/Time, and fall back for the older
77
+ # macOS system Ruby (2.6) whose safe loader signature differs.
78
+ begin
79
+ YAML.load_file(DATA_FILE, permitted_classes: [Date, Time])
80
+ rescue ArgumentError
81
+ YAML.safe_load(File.read(DATA_FILE), permitted_classes: [Date, Time], aliases: false)
82
+ end
83
+ end
84
+
85
+ def validate(data)
86
+ errors = []
87
+ errors << 'Missing top-level `meta:` mapping.' unless data.is_a?(Hash) && data['meta'].is_a?(Hash)
88
+ tasks = data.is_a?(Hash) ? data['tasks'] : nil
89
+ return ['Missing or empty `tasks:` list.'] unless tasks.is_a?(Array) && !tasks.empty?
90
+
91
+ seen_ids = {}
92
+ tasks.each_with_index do |task, i|
93
+ where = "tasks[#{i}]"
94
+ unless task.is_a?(Hash)
95
+ errors << "#{where}: each task must be a mapping."
96
+ next
97
+ end
98
+ id = task['id']
99
+ where = id ? "task #{id}" : where
100
+ errors << "#{where}: missing `id`." if id.to_s.empty?
101
+ errors << "#{where}: `id` must match T-NNN (got #{id.inspect})." if id && id !~ /\AT-\d{3,}\z/
102
+ if id && seen_ids[id]
103
+ errors << "#{where}: duplicate id #{id} (also at #{seen_ids[id]})."
104
+ elsif id
105
+ seen_ids[id] = where
106
+ end
107
+ errors << "#{where}: missing `title`." if task['title'].to_s.strip.empty?
108
+ check_enum(errors, where, task, 'status', VALID_STATUS)
109
+ check_enum(errors, where, task, 'priority', VALID_PRIORITY)
110
+ check_enum(errors, where, task, 'area', VALID_AREA)
111
+ check_enum(errors, where, task, 'risk', VALID_RISK)
112
+ check_enum(errors, where, task, 'effort', VALID_EFFORT) if task['effort']
113
+ check_enum(errors, where, task, 'source', VALID_SOURCE) if task['source']
114
+ unless task['acceptance'].is_a?(Array) && !task['acceptance'].empty?
115
+ errors << "#{where}: `acceptance` must be a non-empty list."
116
+ end
117
+ end
118
+ errors
119
+ end
120
+
121
+ def check_enum(errors, where, task, field, allowed)
122
+ value = task[field]
123
+ return if allowed.include?(value)
124
+
125
+ errors << "#{where}: `#{field}` must be one of #{allowed.join('|')} (got #{value.inspect})."
126
+ end
127
+
128
+ # ---------------------------------------------------------------------------
129
+ # Issue body rendering
130
+ # ---------------------------------------------------------------------------
131
+
132
+ def marker(id)
133
+ "<!-- backlog-id: #{id} -->"
134
+ end
135
+
136
+ def render_body(task)
137
+ accept = (task['acceptance'] || []).map { |a| "- [ ] #{a}" }.join("\n")
138
+ roadmap = task.dig('links', 'roadmap')
139
+ meta_row = [
140
+ "**Priority:** #{task['priority']}",
141
+ "**Area:** #{task['area']}",
142
+ "**Risk:** #{task['risk']}",
143
+ "**Effort:** #{task['effort']}",
144
+ "**Source:** #{task['source']}"
145
+ ].join(' · ')
146
+
147
+ <<~BODY.strip
148
+ #{marker(task['id'])}
149
+ > Auto-managed from [`_data/backlog.yml`](../blob/main/_data/backlog.yml) by `scripts/sync-backlog.rb`.
150
+ > Edit the backlog file, not this issue body — changes here are overwritten on the next sync.
151
+
152
+ #{meta_row}#{roadmap ? " · **Roadmap:** v#{roadmap}" : ''}
153
+
154
+ #{task['summary'].to_s.strip}
155
+
156
+ ## Acceptance criteria
157
+
158
+ #{accept}
159
+
160
+ ---
161
+ Picked up by the IMPLEMENT routine (`.github/prompts/backlog-implement.prompt.md`).
162
+ See [`docs/systems/continuous-evolution.md`](../blob/main/docs/systems/continuous-evolution.md).
163
+ BODY
164
+ end
165
+
166
+ # ---------------------------------------------------------------------------
167
+ # gh helpers
168
+ # ---------------------------------------------------------------------------
169
+
170
+ class Gh
171
+ def initialize(dry_run:)
172
+ @dry_run = dry_run
173
+ end
174
+
175
+ # Read-only call. Always executed (even in dry-run) so we can compute a diff;
176
+ # degrades to a default value if `gh` is unavailable/unauthenticated.
177
+ def read(args, default:)
178
+ out, _err, status = Open3.capture3('gh', *args)
179
+ return default unless status.success?
180
+
181
+ out
182
+ rescue Errno::ENOENT
183
+ default
184
+ end
185
+
186
+ # Mutating call. Printed (not executed) in dry-run mode.
187
+ def write(args)
188
+ if @dry_run
189
+ puts "DRY-RUN gh #{args.map { |a| a.to_s.include?(' ') ? a.inspect : a }.join(' ')}"
190
+ return true
191
+ end
192
+ _out, err, status = Open3.capture3('gh', *args)
193
+ warn "gh #{args.first} failed: #{err.strip}" unless status.success?
194
+ status.success?
195
+ end
196
+ end
197
+
198
+ def ensure_labels(gh)
199
+ LABEL_COLORS.each { |name, color| gh.write(['label', 'create', name, '--color', color, '--force']) }
200
+ VALID_PRIORITY.each { |p| gh.write(['label', 'create', "priority:#{p}", '--color', PRIORITY_COLOR, '--force']) }
201
+ VALID_AREA.each { |a| gh.write(['label', 'create', "area:#{a}", '--color', AREA_COLOR, '--force']) }
202
+ VALID_RISK.each { |r| gh.write(['label', 'create', "risk:#{r}", '--color', RISK_COLOR, '--force']) }
203
+ end
204
+
205
+ # Map of backlog id -> existing issue {number, state, labels} via the body marker.
206
+ def existing_issues(gh)
207
+ raw = gh.read(
208
+ ['issue', 'list', '--label', 'agent-ready', '--state', 'all', '--limit', '500',
209
+ '--json', 'number,body,state,labels'],
210
+ default: '[]'
211
+ )
212
+ index = {}
213
+ JSON.parse(raw).each do |issue|
214
+ next unless issue['body'] =~ /<!-- backlog-id: (T-\d+) -->/
215
+
216
+ index[Regexp.last_match(1)] = {
217
+ 'number' => issue['number'],
218
+ 'state' => issue['state'].to_s.downcase,
219
+ 'labels' => (issue['labels'] || []).map { |l| l['name'] }
220
+ }
221
+ end
222
+ index
223
+ rescue JSON::ParserError
224
+ {}
225
+ end
226
+
227
+ # ---------------------------------------------------------------------------
228
+ # Sync
229
+ # ---------------------------------------------------------------------------
230
+
231
+ def label_args(desired, current)
232
+ desired_set = desired
233
+ # Only remove labels we manage; never touch human-applied ones.
234
+ to_remove = (current & ALL_MANAGED_LABELS) - desired_set
235
+ to_add = desired_set - current
236
+ args = []
237
+ to_add.each { |l| args.push('--add-label', l) }
238
+ to_remove.each { |l| args.push('--remove-label', l) }
239
+ args
240
+ end
241
+
242
+ def sync(data, gh)
243
+ ensure_labels(gh)
244
+ index = existing_issues(gh)
245
+ created = updated = closed = 0
246
+
247
+ (data['tasks'] || []).each do |task|
248
+ id = task['id']
249
+ title = task['title']
250
+ body = render_body(task)
251
+ want_open = OPEN_STATUSES.include?(task['status'])
252
+ issue = index[id]
253
+
254
+ if issue.nil?
255
+ next unless want_open # never create an issue for an already-done task
256
+
257
+ args = ['issue', 'create', '--title', title, '--body', body]
258
+ managed_labels(task).each { |l| args.push('--label', l) }
259
+ created += 1 if gh.write(args)
260
+ next
261
+ end
262
+
263
+ number = issue['number'].to_s
264
+ gh.write(['issue', 'edit', number, '--title', title, '--body', body] +
265
+ label_args(managed_labels(task), issue['labels']))
266
+ updated += 1
267
+
268
+ if want_open && issue['state'] != 'open'
269
+ gh.write(['issue', 'reopen', number])
270
+ elsif !want_open && issue['state'] != 'closed'
271
+ gh.write(['issue', 'close', number, '--reason', 'completed'])
272
+ closed += 1
273
+ end
274
+ end
275
+
276
+ puts "Backlog sync complete: #{created} created, #{updated} updated, #{closed} closed."
277
+ 0
278
+ end
279
+
280
+ # ---------------------------------------------------------------------------
281
+ # Main
282
+ # ---------------------------------------------------------------------------
283
+
284
+ def main
285
+ mode = :sync
286
+ OptionParser.new do |opts|
287
+ opts.banner = 'Usage: sync-backlog.rb [--check|--dry-run]'
288
+ opts.on('--check', 'Validate schema only; make no gh calls') { mode = :check }
289
+ opts.on('--dry-run', 'Print intended gh calls without executing') { mode = :dry_run }
290
+ end.parse!
291
+
292
+ data = load_data
293
+ errors = validate(data)
294
+ unless errors.empty?
295
+ warn '✗ _data/backlog.yml failed validation:'
296
+ errors.each { |e| warn " - #{e}" }
297
+ return 1
298
+ end
299
+ task_count = (data['tasks'] || []).size
300
+
301
+ if mode == :check
302
+ puts "✓ _data/backlog.yml is valid (#{task_count} tasks)."
303
+ return 0
304
+ end
305
+
306
+ sync(data, Gh.new(dry_run: mode == :dry_run))
307
+ end
308
+
309
+ exit main if $PROGRAM_NAME == __FILE__
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env bash
2
+ # =============================================================================
3
+ # sync-backlog.sh
4
+ # =============================================================================
5
+ #
6
+ # Thin wrapper around scripts/sync-backlog.rb.
7
+ #
8
+ # Mirrors `_data/backlog.yml` (the tactical task queue) to GitHub Issues:
9
+ # open tasks become open issues; tasks marked `done` close their issue.
10
+ #
11
+ # Usage:
12
+ # ./scripts/sync-backlog.sh # create/update/close issues via gh
13
+ # ./scripts/sync-backlog.sh --check # validate schema only (CI/PR gate)
14
+ # ./scripts/sync-backlog.sh --dry-run # print intended gh calls only
15
+ #
16
+ # =============================================================================
17
+
18
+ set -euo pipefail
19
+
20
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21
+ exec ruby "${SCRIPT_DIR}/sync-backlog.rb" "$@"
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.9.10
4
+ version: 1.11.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-05-31 00:00:00.000000000 Z
11
+ date: 2026-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -79,6 +79,7 @@ files:
79
79
  - README.md
80
80
  - _data/README.md
81
81
  - _data/authors.yml
82
+ - _data/backlog.yml
82
83
  - _data/content_statistics.yml
83
84
  - _data/features.yml
84
85
  - _data/generate_statistics.rb
@@ -462,6 +463,8 @@ files:
462
463
  - scripts/post-template-setup.sh
463
464
  - scripts/release
464
465
  - scripts/setup.sh
466
+ - scripts/sync-backlog.rb
467
+ - scripts/sync-backlog.sh
465
468
  - scripts/test-auto-version.sh
466
469
  - scripts/test-mermaid.sh
467
470
  - scripts/test-notebook-conversion.sh