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 +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +67 -28
- data/_data/backlog.yml +161 -0
- data/_data/roadmap.yml +172 -28
- data/scripts/generate-roadmap.rb +166 -5
- data/scripts/generate-roadmap.sh +1 -0
- data/scripts/sync-backlog.rb +309 -0
- data/scripts/sync-backlog.sh +21 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bbe0013679e3fd31006811bfaafd9a011e3e21c2acdc892cefabc2968e5cc542
|
|
4
|
+
data.tar.gz: 678d6fba0883cbb6228d601d11880909ecbb34bf12c861d0ec9f0535ceeccf34
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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-
|
|
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](
|
|
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
|
|
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`, `
|
|
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/
|
|
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/
|
|
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 | `
|
|
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
|
-
|
|
831
|
+
v1.9 Installer v2 & Site Scraper :active, 2026-05, 2026-05
|
|
803
832
|
section Future
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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** |
|
|
822
|
-
| **
|
|
823
|
-
| **
|
|
824
|
-
| **
|
|
825
|
-
| **v1.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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-
|
|
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
|
-
# ---
|
|
135
|
+
# --- Completed (continued) -------------------------------------------------
|
|
123
136
|
|
|
124
137
|
- version: "0.22"
|
|
125
138
|
title: "AIEO & Customization"
|
|
126
|
-
status:
|
|
127
|
-
section:
|
|
128
|
-
start: 2026-
|
|
139
|
+
status: completed
|
|
140
|
+
section: Completed
|
|
141
|
+
start: 2026-04
|
|
129
142
|
end: 2026-04
|
|
130
|
-
|
|
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
|
|
293
|
+
- version: "2.0"
|
|
150
294
|
title: "CMS Integration"
|
|
151
295
|
status: planned
|
|
152
296
|
section: Future
|
|
153
|
-
start: 2026-
|
|
154
|
-
end: 2026-
|
|
155
|
-
target: "
|
|
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: "
|
|
308
|
+
- version: "2.1"
|
|
165
309
|
title: "i18n Support"
|
|
166
310
|
status: planned
|
|
167
311
|
section: Future
|
|
168
|
-
start: 2026-
|
|
169
|
-
end: 2026-
|
|
170
|
-
target: "
|
|
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: "
|
|
322
|
+
- version: "2.2"
|
|
179
323
|
title: "Advanced Analytics"
|
|
180
324
|
status: planned
|
|
181
325
|
section: Future
|
|
182
|
-
start: 2026-
|
|
183
|
-
end: 2026-
|
|
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: "
|
|
193
|
-
title: "Stable
|
|
336
|
+
- version: "3.0"
|
|
337
|
+
title: "Stable LTS"
|
|
194
338
|
status: milestone
|
|
195
339
|
section: Future
|
|
196
|
-
start: 2027-
|
|
197
|
-
end: 2027-
|
|
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:
|
data/scripts/generate-roadmap.rb
CHANGED
|
@@ -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
|
|
28
|
-
DATA_FILE
|
|
29
|
-
README
|
|
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
|
|
data/scripts/generate-roadmap.sh
CHANGED
|
@@ -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.
|
|
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-
|
|
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
|