From 84a0371025c46650a06265ab24c54a3c35ac7ce0 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Thu, 21 May 2026 16:14:54 -0400 Subject: [PATCH 01/30] fix(labdev): Pass proper arguments for labdev:heal:all task --- gems/docopslab-dev/lib/docopslab/dev.rb | 4 ++-- gems/docopslab-dev/lib/docopslab/dev/tasks.rb | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/gems/docopslab-dev/lib/docopslab/dev.rb b/gems/docopslab-dev/lib/docopslab/dev.rb index 8865c4d5..280944ff 100644 --- a/gems/docopslab-dev/lib/docopslab/dev.rb +++ b/gems/docopslab-dev/lib/docopslab/dev.rb @@ -340,8 +340,8 @@ def run_all_linters end def run_auto_fix - Linters.run_auto_fix - AsciiidocAutoFix.fix_asciidoc_files(self) + Linters.run_auto_fix(self) + AutoFixAsciidoc.fix_asciidoc_files(self) end def run_rubocop_auto_fix path: nil diff --git a/gems/docopslab-dev/lib/docopslab/dev/tasks.rb b/gems/docopslab-dev/lib/docopslab/dev/tasks.rb index 0f3c8f72..35c6efa4 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/tasks.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/tasks.rb @@ -390,14 +390,12 @@ def define_tasks end desc desc_for('heal:adoc') - # Add an optional path argument that defaults to nil task :adoc, %i[path] => [] do |_t, args| Dev.run_adoc_auto_fix(args[:path]) end desc desc_for('heal:all') task :all do - # if the user passed an argument, we wan to tell them this task does not accept any arguments and we want to peaec out of this operation rather than running it if ARGV.any? { |arg| arg.include?('labdev:heal:all') && arg.include?('[') } puts '⚠️ labdev:heal:all does not accept any arguments. Exiting.' puts 'Use labdev:heal:ruby[path] or labdev:heal:adoc[path] to auto-fix specific files.' From d3a4578e1a3bbfcbba15480bd9d6091250bffe9d Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Thu, 21 May 2026 18:29:51 -0400 Subject: [PATCH 02/30] fix: Typos, grammar in blog entries --- _blog/code-to-docs-pipelines.adoc | 4 ++-- _blog/how-to-train-your-agent.adoc | 8 +++++--- _blog/tech-docs-blogs-collection.adoc | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/_blog/code-to-docs-pipelines.adoc b/_blog/code-to-docs-pipelines.adoc index 91c2f4e7..e75af321 100644 --- a/_blog/code-to-docs-pipelines.adoc +++ b/_blog/code-to-docs-pipelines.adoc @@ -183,7 +183,7 @@ And I added a `project_end_date` for good measure -- you can probably already im Suddenly, we're either asking the developers to maintain the user-facing documentation _inside their source code_, or we need to find some way to complement the source code with those enhanced docs. [NOTE] -None of this is to mention that even when product attribute descriptions are staightforward, they often need to be proofread and edited by a professional, who is probably not a developer working in the product codebase. +None of this is to mention that even when product attribute descriptions are straightforward, they often need to be proofread and edited by a professional, who is probably not a developer working in the product codebase. Doing all of this much better than so-far discussed is quite possible, but I needed to get to this point so anyone reading along can appreciate the problem. I often hear developers shrug off the idea that this is "`even a thing`". @@ -238,7 +238,7 @@ In the following sections, I will detail and show examples of the latter three a [[overlay-method]] === The Overlay Method -Once there is a fixed data structure in place that is being used to generate output for reference codes, that system can be "`overlayed`" with suppliementary or overriding data. +Once there is a fixed data structure in place that is being used to generate output for reference codes, that system can be "`overlayed`" with supplementary or overriding data. [source,yaml] ---- diff --git a/_blog/how-to-train-your-agent.adoc b/_blog/how-to-train-your-agent.adoc index ea6cfb4d..2e59a321 100644 --- a/_blog/how-to-train-your-agent.adoc +++ b/_blog/how-to-train-your-agent.adoc @@ -25,7 +25,9 @@ Before you know it, every project needs a curated, streamlined _library_ of docu A good prompt for a complex coding task includes rich context, which is how MCP servers and skills came about. These, too, are largely dependent on documentation. -And once this level of complexity is introduced, and especially when working across codebases on multiple simultaneous projects. + +Once this level of complexity is introduced, especially when working across codebases on multiple simultaneous projects, your project/organizaton are dealing with documentation divergence and all the attendant problems and possible solutions. +Now you are in my world. .Jargon glossary **** @@ -38,7 +40,7 @@ I settled on a strategy of organizing resources into skills, roles, missions, an The concept of "`skills`" was introduced in 2025 by Anthropic, but the rest of these categories are my own twist on the concept. These resources are all represented by Markdown docs available to LLM-backed coding agents like Copilot, Warp, and Cursor. -(I will talk about MCP serves in a separate blog entry somewhere down the line, but for now I have not concluded much about this new protocol.) +(I will talk about MCP servers in a separate blog entry somewhere down the line, but for now I have not concluded much about this new protocol.) An additional twist is that I single source all of this content, alongside (and secondary to) my people-facing docs, and I do it all using AsciiDoc that gets converted to Markdown at build time, as I described in {xref_blog_single-sourcing-for-ai-agents_link}. @@ -114,7 +116,7 @@ For now, you have to roll up your sleeves and incorporate lessons from all the k These are the ways bots need to be adjusted to work well with _your_ tech stack, _your_ framework, _your_ conventions, _your_ styles, and so forth. You will also need a way to distribute and sync this between projects, and to modify or override some of it in particular projects, when the standard conventions do not translate or apply. -Cross-project orchestration is out of scope for this post, but it I touched on it link:{xref_blog_single-sourcing-for-ai-agents_url}#distribution[here]. +Cross-project orchestration is out of scope for this post, but I touched on it link:{xref_blog_single-sourcing-for-ai-agents_url}#distribution[here]. **** Instead, you need to _organize_ your documentation into a taxonomy of resources that the LLM can access as needed. diff --git a/_blog/tech-docs-blogs-collection.adoc b/_blog/tech-docs-blogs-collection.adoc index cefd73ee..dac1f590 100644 --- a/_blog/tech-docs-blogs-collection.adoc +++ b/_blog/tech-docs-blogs-collection.adoc @@ -28,7 +28,7 @@ include::../README.adoc[tags="global-settings"] [[summary]] == What I've Done -I've collected a number of my favorite blogs, podcasts, and newsletters about technical documentation and presented the in these forms: +I've collected a number of my favorite blogs, podcasts, and newsletters about technical documentation and presented them in these forms: * A link:{serials-collection-web-url}[web page with annotated links] to all the services. * an link:{serials-collection-opml-url}[OPML listing] of all the feeds, which you can import into your feed reader of choice. From 354a8d6f66e2ac9f396da55f65439a304f13a6e4 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Sat, 23 May 2026 17:32:00 -0400 Subject: [PATCH 03/30] fix: Typos, grammar in blog entries --- _blog/how-to-train-your-agent.adoc | 4 ++-- _blog/tech-blogging-in-asciidoc.adoc | 2 +- _data/docops-lab-projects.yml | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/_blog/how-to-train-your-agent.adoc b/_blog/how-to-train-your-agent.adoc index 2e59a321..7456d865 100644 --- a/_blog/how-to-train-your-agent.adoc +++ b/_blog/how-to-train-your-agent.adoc @@ -44,7 +44,7 @@ These resources are all represented by Markdown docs available to LLM-backed cod An additional twist is that I single source all of this content, alongside (and secondary to) my people-facing docs, and I do it all using AsciiDoc that gets converted to Markdown at build time, as I described in {xref_blog_single-sourcing-for-ai-agents_link}. -In this post, I want to talk about _what_ to provide for AI agents, and you can refer to the link:{xref_blog_single-sourcing-for-ai-agents_url}[other post] for _how to deliver_ it. +In this post, I want to talk about _what_ to provide for AI agents, and you can refer to the {xref_blog_single-sourcing-for-ai-agents_link} for _how to deliver_ it. [[taming-dragon]] @@ -123,7 +123,7 @@ Instead, you need to _organize_ your documentation into a taxonomy of resources The good news is that LLMs are pretty good at selectively ingesting and applying relevant context from multiple documents. The hard work is in establishing the library of assets; prompting then becomes as easy as saying: -.Prompt pointing to AGENTS.md +.Prompt pointing to `AGENTS.md` ==== _Read the `AGENTS.md` file, then pick a role and skillset appropriate to the task set forth in `.agent/docs/team/add-fancy-feature.md`._ ==== diff --git a/_blog/tech-blogging-in-asciidoc.adoc b/_blog/tech-blogging-in-asciidoc.adoc index 8269e3ac..af6390ac 100644 --- a/_blog/tech-blogging-in-asciidoc.adoc +++ b/_blog/tech-blogging-in-asciidoc.adoc @@ -22,7 +22,7 @@ include::../README.adoc[tags="global-settings"] The styling and effects you see here are not "`vanilla`" AsciiDoc output. After rendering AsciiDoc to HTML, it still needs to be styled with CSS, and most interactive effects need to be defined with CSS or JavaScript. -Styling and effects are outside this post, but you can find the sources for all of this at link:{this_proj_src_www_url}/blob/main/_sass/[_sass directory] and link:{this_proj_src_www_url}/blob/main/assets/js/main.js[main.js]. +Styling and effects are outside this post, but you can find the sources for all of this at link:{this_proj_src_www_url}/blob/main/_sass/[`_sass` directory] and link:{this_proj_src_www_url}/blob/main/assets/js/main.js[main.js]. toc::[] diff --git a/_data/docops-lab-projects.yml b/_data/docops-lab-projects.yml index a610bd59..4fc50cce 100644 --- a/_data/docops-lab-projects.yml +++ b/_data/docops-lab-projects.yml @@ -231,7 +231,7 @@ projects: A Docker image preconfigured with Issuer and ReleaseHx CLIs and their dependencies. For managing issue tickets and release notes/changelogs in Jira, GitHub, and GitLab. line: Docker image preconfigured with Issuer and ReleaseHx CLIs for ticket and change management - vrsn: 0.3.0-0.1.2 + vrsn: 0.3.0-0.2.0 tags: [issues,release history,changelog,CLI,API,Dockerized] tech: [Docker,Bash,Ruby,GitHub Issues,GitLab Issues,Jira] wave: 0 @@ -253,12 +253,12 @@ projects: Define the expected structure of YAML data objects and AsciiDoc documents, link YAML documents, and more. Eventually to include ports for JavaScript, Python, and more. line: A framework for defining, validating, and parsing data and text objects using YAML and AsciiDoc. - memo: Currently a module in ReleaseHx 0.1.0. (Needs spin-off.) + memo: Spun off from ReleaseHx 0.1.0. (Needs spin-off.) star: true deps: [asciisourcerer] done: 100% live: true - vrsn: V1-beta + vrsn: 0.1.0 wave: 1 card: readme page: true @@ -424,7 +424,7 @@ projects: Provides Ruby API for extracting, parsing, and manipulating AsciiDoc content and YAML data programmatically. Jumpstarts and extends primitive dependencies like Psych (YAML), Asciidoctor (AsciiDoc), and Jekyll/Liquid. line: Ruby API for pre-build and build-time AsciiDoc/YAML processing - vrsn: 0.1.0 + vrsn: 0.4.0 tags: [API,documentation,single-sourcing] tech: [Ruby,AsciiDoc,Asciidoctor,YAML,Liquid,Jekyll] wave: 1 @@ -651,7 +651,7 @@ projects: A development utility that centralizes authoring of common configs, styles/rules, and documentation for sharing across all DocOps Lab project repos. Provides a numer of Rake tasks for maintaining consistency of distributed libraries and assets used in _development_ of DocOps Lab tools but not necessary for _using_ said tools. line: Aka `labdev`, a tool for distributing common libraries and assets across DocOps Lab project repos - vrsn: 0.1.0 + vrsn: 0.3.0 tags: [development,environment] tech: [CLI,Ruby,Docker,Rake,Vale,RuboCop,YAML,Bash,ShellCheck] wave: 0 From 318f0fb68ff80c573497e8fbec1818a1a1f7024d Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Fri, 29 May 2026 13:33:58 -0400 Subject: [PATCH 04/30] chore: Update version metadata for projects --- _data/docops-lab-projects.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/_data/docops-lab-projects.yml b/_data/docops-lab-projects.yml index 4fc50cce..dfd4b8ba 100644 --- a/_data/docops-lab-projects.yml +++ b/_data/docops-lab-projects.yml @@ -231,7 +231,7 @@ projects: A Docker image preconfigured with Issuer and ReleaseHx CLIs and their dependencies. For managing issue tickets and release notes/changelogs in Jira, GitHub, and GitLab. line: Docker image preconfigured with Issuer and ReleaseHx CLIs for ticket and change management - vrsn: 0.3.0-0.2.0 + vrsn: 0.3.0-0.2.1 tags: [issues,release history,changelog,CLI,API,Dockerized] tech: [Docker,Bash,Ruby,GitHub Issues,GitLab Issues,Jira] wave: 0 @@ -477,7 +477,7 @@ projects: Includes a Ruby API and CLI, `releasehx`/`rhx`. Includes SchemaGraphy prototype. line: Manage product release notes and changelog with CLI and Ruby API - vrsn: 0.1.2 + vrsn: 0.2.1 tags: [issues,release history,changelog,product management,versioning,CLI,API,Dockerized] tech: [YAML,Ruby,AsciiDoc,GitHub Issues,GitLab Issues,Jira,Liquid,ERB,JMESPath,JSONPath] wave: 0 @@ -487,7 +487,7 @@ projects: page: true docs: user: https://releasehx.docopslab.org/docs - api: https://releasehx.docopslab.org/docs/api/releasehx/ + api: /releasehx/ ## Issuer: - name: Issuer @@ -1060,7 +1060,7 @@ projects: slug: releasehx-demo type: demo line: Files for testing, demonstrating, and learning ReleaseHx configuration - vrsn: '0.0' # unversioned demo + vrsn: 0.2.1 # matches latest releasehx version tags: [tutorial,issues,release history,changelog] tech: [YAML,Liquid] wave: 0 From 6c0785bad3ea4c14afe632261f4bb972f6c397a8 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Fri, 29 May 2026 13:39:07 -0400 Subject: [PATCH 05/30] docs: Use proper 'specs/' base path reference --- _docs/reference/ruby-styles.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_docs/reference/ruby-styles.adoc b/_docs/reference/ruby-styles.adoc index 9f84e403..bc83ce80 100644 --- a/_docs/reference/ruby-styles.adoc +++ b/_docs/reference/ruby-styles.adoc @@ -59,7 +59,7 @@ However, conventions are not exhaustively listed, and deviations are rarely poin ** `lib/.rb` for the main file ** `lib//` for supporting files and modules ** `lib///` for submodules -* Use `spec/` for specifications and tests. +* Use `specs/` for specifications and tests. * Use `docs/` or `_docs/` for documentation. * Use `build/` for pre-runtime artifacts. * Use `_build/` as default in applications that generate files at runtime, unless another path is more appropriate (ex: `_site/` in Jekyll-centric apps). From d49cffec929f6ed52ac736c56ec77521c91ee34d Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Fri, 29 May 2026 13:40:39 -0400 Subject: [PATCH 06/30] docs: Update general release procedure (steps & anchors) --- _docs/task/release.adoc | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/_docs/task/release.adoc b/_docs/task/release.adoc index 877bfc75..818ae823 100644 --- a/_docs/task/release.adoc +++ b/_docs/task/release.adoc @@ -82,7 +82,7 @@ Edit the Markdown file at `docs/release/{tok_majmin}.{tok_patch}.md`. // end::step-history[] -[[step-2-merge-to-main]] +[[release-step-merge-to-main]] === Step 2. Merge to Main // tag::step-merge[] @@ -95,7 +95,7 @@ git push origin main .... // end::step-merge[] -[[step-3-tag-release]] +[[release-step-tag-release]] === Step 3. Tag Release // tag::step-tag[] @@ -194,8 +194,6 @@ git checkout release/1.1 git merge --squash fix/parser-typo git commit -m "fix: correct parser typo" git push origin release/1.1 -git tag -a v1.2 -m "Patch release 1.2" -git push origin v1.2 .... @@ -205,8 +203,6 @@ git checkout release/1.2 git cherry-pick # … TEST … git push origin release/1.2 -git tag -a v1.2.1 -m "Patch release 1.2.1" -git push origin v1.2.1 .... [NOTE] @@ -217,13 +213,15 @@ Repeat for every affected branch then release the patched versions. [NOTE] Between minor versions, patch versions may vary due to inconsistent applicability of patches. -// end::rollback-patching[] - [[patch-releasing]] === Patch Releasing -Perform Steps 1, 4, and 5 of the standard release procedure: +Perform Steps 1 through 5 of the standard release procedure: + +. <> +. <> +. <> +. <> +. <> -* <> -* <> -* <> \ No newline at end of file +// end::rollback-patching[] \ No newline at end of file From 496597e6652c4eac7f02364b39f1ccc59d5c1b2a Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Fri, 29 May 2026 13:41:34 -0400 Subject: [PATCH 07/30] fix(labdev): Improve pre-hook check for local gem-path references --- gems/docopslab-dev/assets/hooks/pre-push | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/gems/docopslab-dev/assets/hooks/pre-push b/gems/docopslab-dev/assets/hooks/pre-push index a7fc6d71..d8298468 100755 --- a/gems/docopslab-dev/assets/hooks/pre-push +++ b/gems/docopslab-dev/assets/hooks/pre-push @@ -36,11 +36,14 @@ echo "📋 Checking commits from $BASE to $LOCAL..." # Run all linters on the codebase echo "🔍 Running comprehensive linting..." -# If the repo has a Gemfile, make sure none of the gems have a path: '../*' even though path: './*' is fine since it won't break for other users +# If the repo has a Gemfile, make sure none of the gems have a path: '../*' +# (while './*' paths are allowed since they won't break for other users) if [ -f "Gemfile" ]; then - if git diff --cached -U0 | grep -E '^\+.*gem [a-zA-Z0-9_-]+ .+ path:\s*['"'"'"]\.\./.+['"'"'"]'; then + if grep -E 'path:\s*['"'"'"]\.\./' Gemfile > /dev/null; then echo "" - echo "❌ Error: Detected gems with local path: '../*' parameters in Gemfile changes. This will break for other users and CI." + echo "❌ Error: Detected gems with parent directory path: '../*' in Gemfile." + echo " This will break for other users and CI." + exit 1 fi fi From e49bd1ac86e42f94ca2d994c17dd095a3b972b7c Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Fri, 29 May 2026 16:00:34 -0400 Subject: [PATCH 08/30] chore(labdev): Bump version to 0.3.1 patch --- gems/docopslab-dev/lib/docopslab/dev/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/docopslab-dev/lib/docopslab/dev/version.rb b/gems/docopslab-dev/lib/docopslab/dev/version.rb index 1a9f4540..28e21d83 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/version.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/version.rb @@ -2,7 +2,7 @@ module DocOpsLab module Dev - VERSION = '0.3.0' + VERSION = '0.3.1' RUBY_TARGET = '3.2.7' end end From 33f9a7ef9a6ea3780d518bee9a73202757bc5fec Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Fri, 29 May 2026 16:03:15 -0400 Subject: [PATCH 09/30] chore(labdev): Update asciisourcerer, other dependencies This patch is a quick bump to reflect latest asciisourcerer updates. Includes monorepo-level Gemfile.lock to reflect local and upstream version changes. --- Gemfile.lock | 4 ++-- gems/docopslab-dev/Gemfile.lock | 4 ++-- gems/docopslab-dev/docopslab-dev.gemspec | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8e6ff92e..b3cc164e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ PATH remote: gems/docopslab-dev specs: - docopslab-dev (0.3.0) + docopslab-dev (0.3.1) asciidoctor (~> 2.0) - asciisourcerer (~> 0.2) + asciisourcerer (~> 0.4) brakeman (~> 7.1) bundler-audit (~> 0.9) debride (~> 1.13) diff --git a/gems/docopslab-dev/Gemfile.lock b/gems/docopslab-dev/Gemfile.lock index 8354e3cb..f57d573c 100644 --- a/gems/docopslab-dev/Gemfile.lock +++ b/gems/docopslab-dev/Gemfile.lock @@ -1,9 +1,9 @@ PATH remote: . specs: - docopslab-dev (0.3.0) + docopslab-dev (0.3.1) asciidoctor (~> 2.0) - asciisourcerer (~> 0.2) + asciisourcerer (~> 0.4) brakeman (~> 7.1) bundler-audit (~> 0.9) debride (~> 1.13) diff --git a/gems/docopslab-dev/docopslab-dev.gemspec b/gems/docopslab-dev/docopslab-dev.gemspec index 3ea78fb3..e02d6e9e 100644 --- a/gems/docopslab-dev/docopslab-dev.gemspec +++ b/gems/docopslab-dev/docopslab-dev.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'yaml', '~> 0.2' # Code quality and linting - spec.add_dependency 'asciisourcerer', '~> 0.2' + spec.add_dependency 'asciisourcerer', '~> 0.4' spec.add_dependency 'debride', '~> 1.13' spec.add_dependency 'fasterer', '~> 0.11' spec.add_dependency 'flog', '~> 4.8' From 69a8e7739b50791598173a87a6560c5f94024aa6 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Fri, 29 May 2026 16:09:20 -0400 Subject: [PATCH 10/30] docs(labdev): Add Git commit/push step and fix link --- gems/docopslab-dev/README.adoc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gems/docopslab-dev/README.adoc b/gems/docopslab-dev/README.adoc index 13c6e634..b554cda0 100644 --- a/gems/docopslab-dev/README.adoc +++ b/gems/docopslab-dev/README.adoc @@ -1181,10 +1181,17 @@ To build the `docopslab/dev` Docker image: [.prompt] bundle exec rake gemdo:build_docker +[[]] + [[publish-artifacts]] === Publish & Deploy Artifacts -For general DocOps Lab release procedures, see link:{xref_docs_release_url}[Release Process (General)]. +Commit changes and push to GitHub before publishing generated artifacts. + +[NOTE] +The `docopslab-dev` gem does not issue official release notices or registrations on GitHub the way other DocOps Lab gems do. + +For general DocOps Lab release procedures, see link:https://docopslab.org/docs/release/[Release Process (General)]. The `docopslab-dev` gem and `docopslab/dev` Docker image follow that process with these specific considerations: From 26c1c144198ca603b6e54870bef8b731e06b66af Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Wed, 3 Jun 2026 02:01:57 -0400 Subject: [PATCH 11/30] fix: Rename DocOps Box description attribute --- README.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index 00ce1ea2..ee871413 100644 --- a/README.adoc +++ b/README.adoc @@ -44,7 +44,7 @@ endif::[] :description: A community resource for free, open-source tools and practices that empower technical writers, project managers, paralegals, researchers, and educators to leverage modern documentation practices through accessible technologies, strategies, and conventions. // end::general[] // tag::projects[] -:docops-box-desc: A Docker-containerized environment and shell script for reducing the complexity of setting up “developer tools”. Non-developers can run a single command (`docksh run`) and instantly access whole runtimes and specialized documentation tools in a pre-configured shell environment. +:box-desc: pass:q[A Docker-containerized environment and shell script for reducing the complexity of setting up “developer tools”. Non-developers can run a single command (`docksh run`) and instantly access whole runtimes and specialized documentation tools in a pre-configured shell environment.] :docs-as-code-school-desc: pass:q[Structured education in modern technical documentation and document processing. Starting with "`Deep Semantics`" (Fall 2025?) and expanding to courses on version management, code-like workflow adoption, and legal document operations, this project uses docs-as-code to teach docs-as-code principles.] :ayl-docstack-desc: pass:q[AsciiDoc. YAML. Liquid. A three-language approach to managing complex, multi-variant documentation. This "`tech stack`" maximizes power while minimizing syntax overhead, making advanced documentation techniques accessible to beginners while remaining powerful enough for enterprise needs.] :schemagraphy-desc: pass:q[Extends YAML through SGYML and accompanying libraries, providing advanced data typing and document transclusion capabilities. Provides a full-featured schema language that allows users to define complex data structures, document structures, and whole interfaces in a single, unified format.] @@ -74,7 +74,7 @@ They know their content inside and out, but they are locked out of the advanced DocOps Box:: // tag::docops-box[] -{docops-box-desc} +{box-desc} // end::docops-box[] Docs-as-Code School:: From 28a566ed1679056fba8a8d34633416e1f634eed0 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Thu, 4 Jun 2026 20:34:46 -0400 Subject: [PATCH 12/30] chore: Add 'I Make Things' blog to serials data --- _data/tech-docs-serials.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/_data/tech-docs-serials.yml b/_data/tech-docs-serials.yml index 184f96aa..41fbcd08 100644 --- a/_data/tech-docs-serials.yml +++ b/_data/tech-docs-serials.yml @@ -244,6 +244,14 @@ services: desc: | Video podcast from Tom Johnson and Fabrizio Ferri Benedetti, covering topics related to API documentation, AI in technical writing, and interviews with industry experts. +- name: I Make Things + site: https://shaunagordon.com/categories/tech/ + feed: https://shaunagordon.com/posts/index.xml + type: Atom + tags: [ai, community, professional-development] + desc: | + Personal blog of Shauna Gordon, with a mix of technical and personal lessons, from AI optimization to work culture. + - name: Instruction Manuel site: https://instructionmanuel.com feed: https://instructionmanuel.com/rss.xml From 6bdf5b6f92cf130b5db7a43c56f751054433476c Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Sat, 13 Jun 2026 10:16:13 -0400 Subject: [PATCH 13/30] docs: Housekeeping updates to projects; minor fixes to lab docs --- _data/docops-lab-projects.yml | 47 ++++---- _data/jekyll-asciidoc-ui-config-def.yml | 140 ++++++++++++++---------- _docs/reference/infrastructure.adoc | 2 - _docs/templates/AGENTS.markdown | 9 +- 4 files changed, 111 insertions(+), 87 deletions(-) diff --git a/_data/docops-lab-projects.yml b/_data/docops-lab-projects.yml index dfd4b8ba..8f0070d2 100644 --- a/_data/docops-lab-projects.yml +++ b/_data/docops-lab-projects.yml @@ -161,7 +161,7 @@ projects: slug: jekyll-supercharge type: tutorial done: 0% - deps: [jekyll-asciidoc-ui] + deps: [jekyll-docops] - name: "README.adoc-Driven Development and Documentation (RADDD)" title: "README.adoc-Driven Development and Documentation" desc: | @@ -187,7 +187,7 @@ projects: Disambiguation and coordination of the core components of an application based in our Jekyll/Asciidoctor-based publishing framework. These utilities make up the Jekyll _publishing_ aspect of a typical Asciidoctor, Jekyll, and Clide-based toolchain, but they can readily be used independent of that toolchain. - This is a unified docs site for the various plugins and themes based on jekyll-asciidoc-ui. + This is a unified docs site for the various plugins and themes based on jekyll-docops. The intention is for this plugin to differentially feed all of these extensions with core options and a means for each plugin to incorporate these libraries into its own gem or application. line: A unified documentation site for Jekyll/AsciiDoc extensions, plugins, and themes. vrsn: '0.0' # unversioned website @@ -196,7 +196,7 @@ projects: wave: 1 done: 80% icon: book-open - deps: [jekyll-asciidoc-ui,jekyll-openapi,adocBook,asciidocsy-jekyll-theme] + deps: [jekyll-docops,jekyll-openapi,adocBook,asciidocsy-jekyll-theme] # ENVIRONMENT @@ -552,7 +552,7 @@ projects: wave: 1 done: 85% icon: notebook-text - deps: [jekyll-just-the-asciidocs,jekyll-asciidoc-ui] + deps: [jekyll-just-the-asciidocs,jekyll-docops] page: true ## graphy: @@ -702,20 +702,23 @@ projects: deps: [schemagraphy, asciisourcerer] page: true -## jekyll-asciidoc-ui: - - name: Jekyll AsciiDoc UI Extensions - slug: jekyll-asciidoc-ui - repo: jekyll-ext - path: gems/jekyll-asciidoc-ui +## jekyll-docops + - name: Jekyll DocOps Extensions + slug: jekyll-docops + repo: jekyll-docops type: jekyll-ext desc: | + Plugin that adds back-end (plugin) and front-end (UI) support and capabilities to Jekyll themes and plugins. + Front-end assets and components that any Jekyll theme could use to add front-end AsciiDoc support. - Includes templates that interpret specified data objects, such as ReleaseHX and glossaries. + Includes templates that interpret specified data objects, such as release histories and glossaries. Adds some common Bootstrap components such as collapse, accordion, button, badge, card, and so forth. + + Also extends Jekyll with core capabilities key to DocOps Lab Jekyll extension projects but broadly useful to Jekyll plugin and theme developers. line: Front-end assets and components for adding AsciiDoc support to Jekyll themes vrsn: 0.1.0 - tags: [Jekyll plugin,documentation,UI,components,Bootstrap] - tech: [Jekyll,AsciiDoc,Liquid,YAML,Bootstrap,HTML,CSS/Sass,JavaScript,OpenAPI] + tags: [Jekyll plugin,documentation,UI,components,assets,configuration] + tech: [Jekyll,Ruby,AsciiDoc,Liquid,YAML,Bootstrap,HTML,CSS/Sass,JavaScript,OpenAPI] wave: 1 done: 70% icon: test-tube-diagonal @@ -723,10 +726,10 @@ projects: page: true card: readme sort: 5 - deps: [releasehx,ayl-docstack,jekyll-ext] + deps: [schemagraphy,asciisourcerer,releasehx,ayl-docstack] memo: | AYL DockStack dependency is mainly for base/default semantics/styles. - This affects: `theme-ext`, `content-type`, + We may wish to source these in jekyll-docops until we build out AYL DocStack enough to support these assets as a standalone dependency. libs: # General Jekyll UI extensions - glossaries @@ -779,7 +782,7 @@ projects: wave: 2 done: 80% icon: cloud-download - deps: [jekyll-asciidoc-ui,jekyll-ext] + deps: [jekyll-docops] ## jekyll-versioneer: - name: jekyll-versioneer @@ -795,7 +798,7 @@ projects: wave: 2 done: 70% icon: git-branch - deps: [SchemaGraphy,jekyll-asciidoc-ui,versioneer] + deps: [SchemaGraphy,jekyll-docops,versioneer] ## jekyll-api-docs: - name: jekyll-api-docs @@ -811,7 +814,7 @@ projects: wave: 4 done: 0% icon: puzzle - deps: [jekyll-asciidoc-ui,jekyll-openapi] + deps: [jekyll-docops,jekyll-openapi] # JEKYLL THEMES & ENHANCEMENTS @@ -822,7 +825,7 @@ projects: desc: | A simple landing page theme for Jekyll sites that use AsciiDoc. Based on the Minimal Mistakes theme. - Includes AsciiDoc-friendly templates and jekyll-asciidoc-ui assets. + Includes AsciiDoc-friendly templates and jekyll-docops assets. line: Simple landing page theme for Jekyll sites using AsciiDoc vrsn: 0.1.0 tags: [Jekyll plugin,landing page,minimal] @@ -830,7 +833,7 @@ projects: wave: 2 done: 10% icon: layout-template - deps: [jekyll-asciidoc-ui] + deps: [jekyll-docops] note: Currently under development in link:/[DocOps LAB] site (THIS very site!). ## AsciiDocsy: @@ -848,7 +851,7 @@ projects: wave: 2 done: 100% icon: panels-top-left - deps: [LiquiDoc, Graphy, SchemaGraphy, jekyll-asciidoc-ui, jekyll-versioneer] + deps: [LiquiDoc, Graphy, SchemaGraphy, jekyll-docops, jekyll-versioneer] note: AsciiDocsy is technically live, but will be refactored for 1.0.0 release. docs: user: https://asciidocsy.netlify.app/docs/theme @@ -897,7 +900,7 @@ projects: wave: 3 done: 0% icon: test-tube-diagonal - deps: [adocBook, jekyll-asciidoc-ui] + deps: [adocBook, jekyll-docops] ## YAML Agency: - name: YAML Agency @@ -924,7 +927,7 @@ projects: # Extends Agency Bootstrap theme with an AsciiDoc-sourced template for a product landing page. # Uses custom AsciiDoc semantics (roles, etc) for definition of page elements. # Quickly add a landing page to your product docs, either for promoting your product itself, or for introducing the docs, or both, or one for each. -# deps: [jekyll-asciidoc-ui] +# deps: [jekyll-docops] # tags: [landing page] # tech: [Jekyll,AsciiDoc,Liquid,HTML,CSS/Sass,JavaScript] # done: 40% diff --git a/_data/jekyll-asciidoc-ui-config-def.yml b/_data/jekyll-asciidoc-ui-config-def.yml index 1715608e..9eb12ae2 100644 --- a/_data/jekyll-asciidoc-ui-config-def.yml +++ b/_data/jekyll-asciidoc-ui-config-def.yml @@ -1,5 +1,5 @@ -# Setting descriptions and defaults for all jekyll-asciidoc-ui configuration options -# This also models the domain-specific language (DSL) enabled by this plugin's own API method: Jekyll. +# Setting descriptions and defaults for jekyll-docops configuration options +# Covers both the jekyll-ext utility layer and the asciidoc-ui frontend layer. properties: jekyll-ext: # root-level block for Jekyll extensions name: Jekyll Plugin-System Extension Settings @@ -53,6 +53,7 @@ properties: End users (site admins) will be able to re-override this setting in their own config files. + asciidoc-ui: # root-level block for AsciiDoc UI configuration name: User Interface Configuration desc: | @@ -61,16 +62,23 @@ properties: The options in this object are used to configure the plugin itself, with `site.asciidoc-ui.components` enabling and configuring the included components and extensions. properties: - container: + selectors: properties: - selector: - dflt: '.asciidoc-html5' + enabler: + dflt: '.asciidoc-ui' type: Selector desc: | - CSS selector for the container element(s) in which to apply the plugin. - Designated elements will be affected by styles and scripts applied by the plugin. + CSS selector for the container element(s) within which or specific elements to which to apply the plugin's styles and JavaScript. Defaults to any element with the class `asciidoc-html5`. + disabler: + dflt: '.no-asciidoc-ui' + type: Selector + desc: | + CSS selector for the container element(s) within which or specific elements to which to _deny_ the plugin's styles and JavaScript. + + Defaults to any element with the class `no-asciidoc-ui`. + libraries: # third-party frontend dependencies name: Third-Party Library Settings @@ -137,7 +145,7 @@ properties: desc: | Array of files to include from the library. Use this to specify only the files you need from the library. - If not specified, the plugin will include all files from the library. + If not specified or if value is `null` or `['*']``, the plugin will include all files from the library. fontawesome: properties: @@ -264,7 +272,7 @@ properties: Non-default components available for enabling: * `+releasehx` - * `+glossary` + * `+glossaries` * `+term-ext` * `+seo-ext` * `+gdpr-dialog` @@ -284,62 +292,76 @@ properties: glossaries: name: Glossaries desc: | - Adds glossary pages or documents to the site. - The glossary page is a single page or series of pages listing and defining any terms added to the collection. + Adds one or more named glossary page sets to the site. + Each top-level key under `glossaries:` is a user-chosen identifier for a single glossary + (e.g. `main`, `api-terms`). + Each named glossary generates its own index page and per-volume sub-pages. - Use <> to auto-detect and classify terms in the document body. + Use <> to auto-detect and annotate terms in the document body. docs: | - Call using `{% include ui/glossary.html %}` in a page or layout file. - - The associated properties are for default/global glossary settings. - - You may pass these as arguments when including/rendering `ui/glossary.html`. - - You may also add arbitrary arguments either as properties of this ([.ppty']`asciidoc-ui.components.glossary`) object in the config file, - or as additional variables to pass in the tag itself. - - For example, `{% include ui/glossary.html source="site.data.my-glossary" my_var="some value" %}`. - - These properties can also be set in any glossary data object, which are usually sourced like `data/glossary/product-a.yml`. - - The order of precedence for these settings is: + Define as many named glossaries as needed: + + [source,yaml] + ---- + asciidoc-ui: + glossaries: + main: + source: site.data.glossary + name: Glossary + permalink: /glossary + api-terms: + source: site.data.api_glossary + name: API Reference Glossary + permalink: /api/glossary + ---- - . The tag itself, which overrides - . The glossary data's `volume` object, which overrides - . The config file + Each glossary's `source` or `sources` must be a `site.data.*` object path or a sequence of same. + Jekyll automatically loads `_data/glossary.yml` as `site.data.glossary`, `_data/api_glossary.yml` as `site.data.api_glossary`, and so on. - Tags alone can additionally specify a `volume`, `categories`, `tags`, or `applications` arguments to filter the glossary terms displayed. + When neither `source` nor `sources` is set, `source` defaults to + `site.data.glossary`. properties: - source: - dflt: "site.data.glossary" - type: String - desc: Object path for glossary data. - sources: - dflt: [] - type: Array - desc: Array of object paths for glossary data, in descending order of preference (which definitions will overwrite which others by default). - name: - dflt: Glossary - type: String - desc: Title to display for the glossary page. - text: - dflt: Glossary of terms used in this documentation - type: String - desc: Description to display for the glossary page. - layout: - dflt: asciidoc - type: Slug - desc: Layout to use for the glossary page. - permalink: - dflt: /glossary-public - type: Path - desc: Permalink URL for the glossary page. - paginate: - dflt: 100 - type: Integer or 'alphabetical' - desc: Number of terms to display per page. - + : + desc: Configuration block for a single named glossary. + docs: | + Replace `` with a unique identifier of your choice (e.g. `main`, `api-terms`). + Each identifier produces its own index page at the configured `permalink`. + properties: + source: + dflt: "site.data.glossary" + type: String + desc: | + Single `site.data.*` object path for glossary data + (e.g. `site.data.glossary`). + Jekyll auto-loads `_data/glossary.yml` as `site.data.glossary`. + sources: + dflt: [] + type: Array + desc: | + Additional `site.data.*` object paths merged after `source`, in order. + Later entries can override terms defined by earlier ones. + name: + dflt: "" + type: String + desc: | + Display title for the generated glossary index page. + Defaults to a humanized form of the glossary key. + layout: + dflt: default + type: Slug + desc: Jekyll layout name used for all generated pages in this glossary. + permalink: + dflt: "" + type: Path + desc: | + URL path for the generated glossary index. + Defaults to `/`. + paginate: + dflt: 100 + type: Integer or 'alphabetical' + desc: Number of terms to display per page. + releasehx: name: ReleaseHX desc: | diff --git a/_docs/reference/infrastructure.adoc b/_docs/reference/infrastructure.adoc index d45f3579..cd1cb43c 100644 --- a/_docs/reference/infrastructure.adoc +++ b/_docs/reference/infrastructure.adoc @@ -5,8 +5,6 @@ description: "Local and cloud assets for DocOps Lab projects" order: 51 --- include::../_local_settings.adoc[] -:vale_off: pass:[] -:vale_on: pass:[] = Development & Deployment Infrastructure This document addresses a standardized codebase structure and deployment configuration that is common across most DocOps Lab projects. diff --git a/_docs/templates/AGENTS.markdown b/_docs/templates/AGENTS.markdown index 05fb9165..fe7df7f6 100644 --- a/_docs/templates/AGENTS.markdown +++ b/_docs/templates/AGENTS.markdown @@ -22,10 +22,11 @@ This file uses a simple template system with two kinds of placeholders: - <% NOTE: description %> : Arbitrary notes for the user of the template Tagging System for Content Synchronization: - - universal-content: Philosophy, operations notes, AsciiDoc preferences - - universal-agent-development: Development patterns, testing, code standards - - universal-agent-responsibilities: Agent behavior and mindset guidelines - - universal-remember: Operational standards and core principles + - universal-agency: Philosophy, operations notes, AsciiDoc preferences + - universal-config: Development patterns, testing, code standards + - universal-approach: Agent behavior and mindset guidelines + - universal-responsibilities: General best practices + - universal-remember: Reiteration Project-specific content (architecture, reading order, scenarios) remains untagged. {% endraw %} From 4c8f46f80ef2eff3cee033934e0bc4def74a98d3 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Sun, 6 Sep 2026 19:34:46 -0400 Subject: [PATCH 14/30] feat(labdev): Add skim:ruby task for API surface skimming Adds labdev:skim:ruby, which skims a gem's lib/ directory for its Ruby API surface (modules, classes, methods) via Sourcerer::SourceSkim. Refactors run_with_format's optional flags into **opts to keep RuboCop's ParameterLists check under six. --- gems/docopslab-dev/lib/docopslab/dev/skim.rb | 19 +++++++++++----- gems/docopslab-dev/lib/docopslab/dev/tasks.rb | 5 +++++ gems/docopslab-dev/specs/data/tasks-def.yml | 22 +++++++++++++++++-- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/gems/docopslab-dev/lib/docopslab/dev/skim.rb b/gems/docopslab-dev/lib/docopslab/dev/skim.rb index 4f29a14c..3b4d662d 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/skim.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/skim.rb @@ -28,17 +28,24 @@ def run_md path, form: nil, syntax: nil run_with_format(path, exts: MD_EXTS, form: form, syntax: syntax, overlay: true) end + # Skim a Ruby application for its API surface (Modules, Classes, Methods, etc.). Form and syntax are auto-detected. + def run_ruby path, form: nil, syntax: nil + # path is path + lib/ + path = File.join(path, 'lib') + run_with_format(path, exts: ['.rb'], form: form, syntax: syntax) + end + private - def run_with_format path, exts:, form: nil, syntax: nil, default_forms: nil, overlay: false + def run_with_format path, exts:, form: nil, syntax: nil, **opts unless path puts '❌ Path is required.' - puts 'Usage: bundle exec rake labdev:skim[path,form,syntax]' + puts 'Usage: bundle exec rake labdev:skim:[path,form,syntax]' return end - forms = form ? parse_forms(form) : default_forms - file_paths = overlay ? resolve_overlay_paths(path, exts) : resolve_paths(path, exts) + forms = form ? parse_forms(form) : opts[:default_forms] + file_paths = opts[:overlay] ? resolve_overlay_paths(path, exts) : resolve_paths(path, exts) if file_paths.empty? ext_desc = exts.size == 1 ? exts.first : exts.join(', ') @@ -51,6 +58,8 @@ def run_with_format path, exts:, form: nil, syntax: nil, default_forms: nil, ove file_paths.each do |fp| skim_opts = { categories: cats } skim_opts[:forms] = forms if forms + skim_opts[:descriptions] = true if opts[:descriptions] + results[fp] = Sourcerer::SourceSkim.skim_file(fp, **skim_opts) end portable = JSON.parse(JSON.generate(results)) @@ -98,7 +107,7 @@ def resolve_syntax syntax, form return :yaml unless form syntax = 'yaml' if syntax == 'yml' - puts + return syntax.to_sym if syntax :json diff --git a/gems/docopslab-dev/lib/docopslab/dev/tasks.rb b/gems/docopslab-dev/lib/docopslab/dev/tasks.rb index 35c6efa4..1d5193fd 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/tasks.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/tasks.rb @@ -429,6 +429,11 @@ def define_tasks task :md, %i[path form syntax] => [] do |_t, args| Skim.run_md(args[:path], form: args[:form], syntax: args[:syntax]) end + + desc desc_for('skim:ruby') + task :ruby, %i[path form syntax] => [] do |_t, args| + Skim.run_ruby(args[:path], form: args[:form], syntax: args[:syntax]) + end end # ============================================================ diff --git a/gems/docopslab-dev/specs/data/tasks-def.yml b/gems/docopslab-dev/specs/data/tasks-def.yml index ea3ed4c4..c55fdae3 100644 --- a/gems/docopslab-dev/specs/data/tasks-def.yml +++ b/gems/docopslab-dev/specs/data/tasks-def.yml @@ -337,7 +337,7 @@ labdev: - bundle exec rake 'labdev:show:rule[rubocop,Layout/LineLength]' - bundle exec rake 'labdev:show:rule[vale,DocOpsLab-Authoring.ExNotEg]' skim: - _desc: Skim source files for machine-oriented structured outlines (AsciiDoc + Markdown autodetect) + _desc: Skim source files for machine-oriented structured outlines (AsciiDoc, Markdown, Ruby) _args: path: summ: | @@ -402,7 +402,8 @@ labdev: form: summ: | Output shape: tree, flat, or tree,flat (comma-separated). - Defaults to flat for Markdown. When form is explicitly passed, output defaults to JSON. + Defaults to flat for Markdown. + When form is explicitly passed, output defaults to JSON. required: false syntax: summ: | @@ -413,6 +414,23 @@ labdev: - bundle exec rake 'labdev:skim:md[.agent/docs/]' - bundle exec rake 'labdev:skim:md[.agent/docs/,flat,json]' - bundle exec rake 'labdev:skim:md[.agent/docs/:_docs/agent/,flat,json]' + ruby: + _desc: Use ri to detect API surface (Modules, Classes, Methods, etc.). + _args: + path: + summ: | + Optional path to Ruby gem directory to skim. (Default: '.') + required: false + form: + summ: | + Output shape: tree, flat, or tree,flat (comma-separated). + Defaults to tree. When form is explicitly passed, output defaults to JSON. + required: false + syntax: + summ: | + Override output format: json or yaml. + Default: yaml when form is omitted; json when form is explicitly passed. + required: false # ADDING: help: _desc: Show help information about labdev tasks From cb2a9650ad5d3c65341785b8b636c144c10ad2db Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Mon, 7 Sep 2026 13:51:21 -0400 Subject: [PATCH 15/30] feat(labdev): Add CLAUDE.md pointing to AGENTS.md States a basic redirect for Claude Code's default. Change is reflected in default manifest and a .gitignore *.md exception. --- .gitignore | 1 + gems/docopslab-dev/assets/templates/CLAUDE.md | 1 + gems/docopslab-dev/specs/data/default-manifest.yml | 2 ++ 3 files changed, 4 insertions(+) create mode 100644 gems/docopslab-dev/assets/templates/CLAUDE.md diff --git a/.gitignore b/.gitignore index 62a911ba..9b126f81 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ vendor/ # Markdown files (except important docs) *.md ./!AGENTS.md +!gems/docopslab-dev/assets/templates/CLAUDE.md # DocOps Lab vendor files .config/.vendor/ diff --git a/gems/docopslab-dev/assets/templates/CLAUDE.md b/gems/docopslab-dev/assets/templates/CLAUDE.md new file mode 100644 index 00000000..d3dd8171 --- /dev/null +++ b/gems/docopslab-dev/assets/templates/CLAUDE.md @@ -0,0 +1 @@ +Read, respect, and defer to the local `AGENTS.md` file. \ No newline at end of file diff --git a/gems/docopslab-dev/specs/data/default-manifest.yml b/gems/docopslab-dev/specs/data/default-manifest.yml index 3e33e28d..9aa48f8f 100644 --- a/gems/docopslab-dev/specs/data/default-manifest.yml +++ b/gems/docopslab-dev/specs/data/default-manifest.yml @@ -20,6 +20,8 @@ templates: target: .gitignore - source: templates/README.asciidoc target: README.adoc + - source: templates/CLAUDE.md + target: CLAUDE.md tools: - tool: rubocop From 1782608c86d1c62ee6904a4668e10c08b0878773 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Mon, 7 Sep 2026 17:50:16 -0400 Subject: [PATCH 16/30] edit(docs): Update commit-message instructions The verb form of commit summaries was mis-reported; now standardize on capitalized imperative. Also adds `edit`, `create`, and `revert` commit types while removing `docs`, which is a scope. --- _docs/reference/git-commit-styles.adoc | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/_docs/reference/git-commit-styles.adoc b/_docs/reference/git-commit-styles.adoc index 9c5ef3a0..617ec4e5 100644 --- a/_docs/reference/git-commit-styles.adoc +++ b/_docs/reference/git-commit-styles.adoc @@ -11,11 +11,11 @@ This document outlines the protocols for authoring Git commit messages in DocOps [[general-style]] -== General Style (Conventional Commits) +== General Style DocOps Lab _loosely_ follows the link:https://www.conventionalcommits.org/en/v1.0.0/[Conventional Commits] specification for Git commit messages. -Enforcement is not strict, but using Conventional Commits style is encouraged for consistency and clarity. +Enforcement is not strict, but using modified Conventional Commits style is encouraged for consistency and clarity. [NOTE] Most DocOps Lab projects do not base Changelog/Release Notes generation on commit messages. @@ -23,7 +23,7 @@ Most DocOps Lab projects do not base Changelog/Release Notes generation on commi The basic outline for a Conventional Commit message is: .... -[optional scope]: +[optional scope]: [optional body] @@ -31,27 +31,30 @@ The basic outline for a Conventional Commit message is: .... -[[commit-description]] -== Commit Description +[[commit-summary]] +== Commit Summary The commit description should be concise and to the point, summarizing the change in 50 characters or less. -Use the _past tense_ rather than imperative mood (e.g., "Added feature X" instead of "Add feature X"). +Use the imperative mood (e.g., "Add feature X" instead of "Added feature X"). [[commit-types]] == Commit Types + // tag::commit-types[] -* Use present-tense descriptive verbs ("`adds widget`", not "`added`" or "`add`") +* Use imperative verbs ("`Add widget`", not "`Added`" or "`add`") +* `create: ...` for new docs or UI content +* `edit: ...` for minor edits to docs or UI content * `feat: ...` for new features OR improvements * `fix: ...` for bugfixes * `chore: ...` for version bumps and sundry tasks with no product impact -* `docs: ...` for documentation changes * `test: ...` for test code changes * `refactor: ...` for code restructuring with no functional changes * `style: ...` for formatting, missing semi-colons, etc; no functional changes * `perf: ...` for performance improvements * `auto: ...` for changes to CI/CD pipelines and build system +* `revert: ...` for reverting previous changes // end::commit-types[] From 488305fd47c67f846dcab4943007a50975833477 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Tue, 8 Sep 2026 16:31:03 -0400 Subject: [PATCH 17/30] fix(labdev): Skims print relative instead of absolute paths --- gems/docopslab-dev/lib/docopslab/dev/skim.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/gems/docopslab-dev/lib/docopslab/dev/skim.rb b/gems/docopslab-dev/lib/docopslab/dev/skim.rb index 3b4d662d..ce5ab9f5 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/skim.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/skim.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'json' +require 'pathname' require 'asciisourcerer' require 'sourcerer/util/pathifier' @@ -60,7 +61,7 @@ def run_with_format path, exts:, form: nil, syntax: nil, **opts skim_opts[:forms] = forms if forms skim_opts[:descriptions] = true if opts[:descriptions] - results[fp] = Sourcerer::SourceSkim.skim_file(fp, **skim_opts) + results[relative_path(fp)] = Sourcerer::SourceSkim.skim_file(fp, **skim_opts) end portable = JSON.parse(JSON.generate(results)) @@ -94,6 +95,11 @@ def build_relative_map dir_path, exts end end + # Render an absolute file path as relative to the current working directory. + def relative_path fp + Pathname.new(fp).relative_path_from(Pathname.pwd).to_s + end + def parse_forms form form.split(',').map { |f| f.strip.to_sym } end From 417bded7639652710625da4c56da5608db05354c Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Tue, 8 Sep 2026 17:08:11 -0400 Subject: [PATCH 18/30] feat(labdev): Add git-lint commit-message linting Introduces a git-lint integration so commit subjects and bodies are checked against shared DocOps Lab commit conventions, both locally and in CI. - Add a `commit-conventions` config-pack (types, scopes, subject/body rules) that both generates the git-lint config and drives standalone subject validation (capitalized imperative verb, allowed type/scope, no terminal punctuation, no agent authorship signatures). - Add `labdev:lint:commit[path]` (for the commit-msg hook) and `labdev:lint:commits[target]` (for branch/CI linting), plus a `labdev:run:git_lint` raw wrapper. - Distribute a `commit-msg` hook template that runs the new lint task. - Wire git-lint config generation into `labdev:sync:configs` and add a `git-lint` CI job to the QA workflow. - Document the new config layering and usage in the gem README. --- .config/commit-conventions.yml | 10 ++ .config/docopslab-dev.yml | 15 ++ .config/git-lint.local.yml | 6 + .github/workflows/qa.yml | 42 ++++- .gitignore | 3 + gems/docopslab-dev/README.adoc | 34 +++- .../config-packs/commit-conventions/base.yml | 79 ++++++++++ .../commit-conventions/project.yml | 6 + .../assets/config-packs/git-lint/base.yml | 39 +++++ .../assets/config-packs/git-lint/project.yml | 6 + gems/docopslab-dev/assets/hooks/commit-msg | 20 +++ gems/docopslab-dev/assets/templates/gitignore | 1 + gems/docopslab-dev/docopslab-dev.gemspec | 1 + gems/docopslab-dev/lib/docopslab/dev.rb | 16 ++ .../lib/docopslab/dev/config_manager.rb | 109 +++++++++++++ .../lib/docopslab/dev/git_hooks.rb | 2 + .../lib/docopslab/dev/linters.rb | 145 ++++++++++++++++++ gems/docopslab-dev/lib/docopslab/dev/paths.rb | 1 + .../lib/docopslab/dev/sync_ops.rb | 1 + gems/docopslab-dev/lib/docopslab/dev/tasks.rb | 29 ++++ .../specs/data/default-manifest.yml | 17 +- gems/docopslab-dev/specs/data/tasks-def.yml | 26 +++- gems/docopslab-dev/specs/data/tools.yml | 9 +- 23 files changed, 611 insertions(+), 6 deletions(-) create mode 100644 .config/commit-conventions.yml create mode 100644 .config/git-lint.local.yml create mode 100644 gems/docopslab-dev/assets/config-packs/commit-conventions/base.yml create mode 100644 gems/docopslab-dev/assets/config-packs/commit-conventions/project.yml create mode 100644 gems/docopslab-dev/assets/config-packs/git-lint/base.yml create mode 100644 gems/docopslab-dev/assets/config-packs/git-lint/project.yml create mode 100644 gems/docopslab-dev/assets/hooks/commit-msg diff --git a/.config/commit-conventions.yml b/.config/commit-conventions.yml new file mode 100644 index 00000000..68c67695 --- /dev/null +++ b/.config/commit-conventions.yml @@ -0,0 +1,10 @@ +# Project-local commit convention overrides. +--- +inherit_from: .vendor/docopslab/commit-conventions.yml + +conventions: + scopes: + labdev: + name: docopslab-dev + summ: Changes to the docopslab-dev gem and distributed development tooling + akas: [docopslab-dev, dev, gem] diff --git a/.config/docopslab-dev.yml b/.config/docopslab-dev.yml index 16607d2e..9babd022 100644 --- a/.config/docopslab-dev.yml +++ b/.config/docopslab-dev.yml @@ -85,6 +85,21 @@ tools: target: .config/actionlint.yml synced: false + - tool: git-lint + files: + - source: commit-conventions/base.yml + target: .config/.vendor/docopslab/commit-conventions.yml + synced: true + - source: commit-conventions/project.yml + target: .config/commit-conventions.yml + synced: false + - source: git-lint/base.yml + target: .config/.vendor/docopslab/git-lint.yml + synced: true + - source: git-lint/project.yml + target: .config/git-lint.local.yml + synced: false + # Local library declaration used by lab release and local tests library: enabled: true diff --git a/.config/git-lint.local.yml b/.config/git-lint.local.yml new file mode 100644 index 00000000..7e1c7558 --- /dev/null +++ b/.config/git-lint.local.yml @@ -0,0 +1,6 @@ +# Project-local git-lint overrides. +# +# This file is copied once to `.config/git-lint.local.yml`. +# Leave it empty to use DocOps Lab defaults, or override keys from +# `.config/.vendor/docopslab/git-lint.yml`. +--- diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 34d456a1..1e43331b 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -35,6 +35,10 @@ on: description: "Skip HTML-Proofer link checking" type: boolean default: false + skip_git_lint: + description: "Skip git-lint commit message checking" + type: boolean + default: false jobs: config-sync: @@ -92,6 +96,31 @@ jobs: bundle exec rake labdev:lint:ruby fi + git-lint: + runs-on: ubuntu-latest + needs: config-sync + timeout-minutes: 5 + if: ${{ !inputs.skip_git_lint }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler-cache: ${{ inputs.enable_cache }} + + - name: Sync configurations + run: bundle exec rake labdev:sync:configs + + - name: Run git-lint + run: | + COMMIT_SHA="${{ github.event.pull_request.head.sha || github.sha }}" + bundle exec rake "labdev:lint:commits[$COMMIT_SHA]" + vale: runs-on: ubuntu-latest needs: config-sync @@ -160,7 +189,7 @@ jobs: summary: runs-on: ubuntu-latest - needs: [config-sync, rubocop, vale, htmlproofer] + needs: [config-sync, rubocop, git-lint, vale, htmlproofer] if: always() timeout-minutes: 2 steps: @@ -184,6 +213,15 @@ jobs: else echo "❌ **RuboCop**: Failed" >> $GITHUB_STEP_SUMMARY fi + + # git-lint + if [ "${{ inputs.skip_git_lint }}" = "true" ]; then + echo "⏭️ **git-lint**: Skipped" >> $GITHUB_STEP_SUMMARY + elif [ "${{ needs.git-lint.result }}" = "success" ]; then + echo "✅ **git-lint**: Passed" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **git-lint**: Failed" >> $GITHUB_STEP_SUMMARY + fi # Vale if [ "${{ inputs.skip_vale }}" = "true" ]; then @@ -201,4 +239,4 @@ jobs: echo "✅ **HTML-Proofer**: Passed" >> $GITHUB_STEP_SUMMARY else echo "❌ **HTML-Proofer**: Failed" >> $GITHUB_STEP_SUMMARY - fi \ No newline at end of file + fi diff --git a/.gitignore b/.gitignore index 9b126f81..9ddda98a 100644 --- a/.gitignore +++ b/.gitignore @@ -37,9 +37,12 @@ vendor/ # DocOps Lab vendor files .config/.vendor/ +# tag::universal-docopslab-dev-configs[] # Generated config files (merged from base + local) .config/vale.ini .config/htmlproofer.yml +.config/git-lint.yml +# end::universal-docopslab-dev-configs[] # Gem content paths gems/**/pkg/ diff --git a/gems/docopslab-dev/README.adoc b/gems/docopslab-dev/README.adoc index b554cda0..d52cbdcb 100644 --- a/gems/docopslab-dev/README.adoc +++ b/gems/docopslab-dev/README.adoc @@ -525,6 +525,33 @@ For full Vale configuration settings ("`keys`") reference, see the link:https:// // end::config-vale[] +[[git-lint]] +=== git-lint + +Git commit message checking for local hooks and CI. + +Convention data:: `.config/.vendor/docopslab/commit-conventions.yml` +Project convention overrides:: `.config/commit-conventions.yml` (inherits via `inherit_from`) +Base config:: `.config/.vendor/docopslab/git-lint.yml` +Project config overrides:: `.config/git-lint.local.yml` +Ephemeral config:: `.config/git-lint.yml` +Sync command:: `bundle exec rake labdev:sync:configs` +Branch lint command:: `bundle exec rake labdev:lint:commits` +Commit-message hook command:: `bundle exec rake labdev:lint:commit[path/to/COMMIT_EDITMSG]` + +The generated git-lint config is derived from shared commit convention data so other commit-message linters can reuse the same types, scopes, and prose rules later. +Commit subjects must follow `[optional scope]: Subject`, use a configured type and scope, start the subject with a capitalized imperative verb, and omit terminal punctuation. + +Commit convention overrides are mapping-based. +Local projects can add or overwrite keys under `rules`, `conventions.types`, and `conventions.scopes`. +Set a key to `null` in the local file to drop an inherited convention. + +`git-lint` 6.x expects an XDG config path named `git-lint/configuration.yml`. +The `labdev` wrappers bridge `.config/git-lint.yml` into that path at runtime, so use the rake tasks instead of invoking `git-lint` directly. + +The distributed `commit-msg` hook runs `labdev:lint:commit` against the unsaved commit message. +CI should use `labdev:lint:commits` on feature branches or pull request heads. + [[htmlproofer]] === HTMLProofer // tag::config-htmlproofer[] @@ -1024,6 +1051,11 @@ HTMLProofer:: + bundle exec htmlproofer --ignore-urls "/www.github.com/,/foo.com/" ./_site +git-lint:: ++ + bundle exec rake "labdev:lint:commit[.git/COMMIT_EDITMSG]" + bundle exec rake labdev:lint:commits + // end::standard-usage[] // end::usage[] // end::workflow[] @@ -1266,4 +1298,4 @@ Documentation released under Creative Commons Attribution 4.0 International (CC === Bill of Materials No externally sourced content or code is contained in this project. -All third-party dependencies are permissively licensed and are downloaded independently, never provided by DocOps Lab. \ No newline at end of file +All third-party dependencies are permissively licensed and are downloaded independently, never provided by DocOps Lab. diff --git a/gems/docopslab-dev/assets/config-packs/commit-conventions/base.yml b/gems/docopslab-dev/assets/config-packs/commit-conventions/base.yml new file mode 100644 index 00000000..eeb047e2 --- /dev/null +++ b/gems/docopslab-dev/assets/config-packs/commit-conventions/base.yml @@ -0,0 +1,79 @@ +conventions: + types: + edit: + summ: Minor edits to content, documentation, or UI text + create: + summ: New content items, documentation, or UI surfaces + chore: + summ: Routine tasks and maintenance + feat: + summ: New downstream-facing features or functionality + fix: + summ: Bug fixes and error corrections + perf: + summ: Performance improvements + refactor: + summ: Code restructuring without changing behavior + revert: + summ: Reverting previous changes + style: + summ: Code or documentation style and formatting changes + test: + summ: Test code changes + + scopes: + auto: + name: Automation + summ: Changes to automation and CI/CD scripts, workflows, and pipelines + akas: [auto, ci, cd, ci-cd, cicd, pipeline, devops, ops, scripts] + cli: + name: CLI + summ: Changes to a command-line interface + config: + name: Config + summ: Changes to configuration files and settings + content: + name: Content + summ: Changes to content + core: + name: Core + summ: Changes to native API, engine logic, or core behavior + data: + name: Data + summ: Changes to data structures, schemas, and formats + docs: + name: Documentation + summ: Changes to user-facing or internal documentation, including README and inline docs + akas: [doc, documentation] + infra: + name: Infrastructure + summ: Changes to infrastructure, deployment, and CI/CD pipelines + akas: [infrastructure] + lib: + name: Libraries + summ: Changes to libraries, modules, and dependencies + akas: [library, libraries, module, dependency, dependencies, package, packages] + server: + name: Server + summ: Changes to server-side configuration or remote services + specs: + name: Specifications + summ: Changes to specifications, design docs, requirements, and planning artifacts + akas: [spec, specification, definition, design, plan, requirement, requirements] + tests: + name: Tests + summ: Changes to test cases, test suites, and testing frameworks + akas: [test, testing] + +rules: + subject: + maximum: 72 + require_capitalized_imperative: true + reject_terminal_punctuation: true + allowed_scope_separator: "+" + body: + maximum_line_length: 90 + require_one_sentence_per_line: true + text: + reject_em_dash: true + reject_agent_signature: true diff --git a/gems/docopslab-dev/assets/config-packs/commit-conventions/project.yml b/gems/docopslab-dev/assets/config-packs/commit-conventions/project.yml new file mode 100644 index 00000000..050f4d08 --- /dev/null +++ b/gems/docopslab-dev/assets/config-packs/commit-conventions/project.yml @@ -0,0 +1,6 @@ +# Project-local commit convention overrides. +# +# This file is copied once to `.config/commit-conventions.yml`. +# Local keys merge over inherited DocOps Lab defaults. +--- +inherit_from: .vendor/docopslab/commit-conventions.yml diff --git a/gems/docopslab-dev/assets/config-packs/git-lint/base.yml b/gems/docopslab-dev/assets/config-packs/git-lint/base.yml new file mode 100644 index 00000000..9f6ebbae --- /dev/null +++ b/gems/docopslab-dev/assets/config-packs/git-lint/base.yml @@ -0,0 +1,39 @@ +commits: + body: + line_length: + enabled: true + severity: error + maximum: 90 + presence: + enabled: false + severity: warn + minimum: 1 + phrase: + enabled: true + severity: error + excludes: + - "—" + - "Co-authored by Codex" + - "Generated with Claude Code" + signature: + enabled: false + severity: error + includes: + - "" + subject: + length: + enabled: true + severity: error + maximum: 72 + prefix: + enabled: true + severity: error + delimiter: "[[:upper:]]" + includes: [] + suffix: + enabled: true + severity: error + excludes: + - "\\." + - "\\?" + - "\\!" diff --git a/gems/docopslab-dev/assets/config-packs/git-lint/project.yml b/gems/docopslab-dev/assets/config-packs/git-lint/project.yml new file mode 100644 index 00000000..7e1c7558 --- /dev/null +++ b/gems/docopslab-dev/assets/config-packs/git-lint/project.yml @@ -0,0 +1,6 @@ +# Project-local git-lint overrides. +# +# This file is copied once to `.config/git-lint.local.yml`. +# Leave it empty to use DocOps Lab defaults, or override keys from +# `.config/.vendor/docopslab/git-lint.yml`. +--- diff --git a/gems/docopslab-dev/assets/hooks/commit-msg b/gems/docopslab-dev/assets/hooks/commit-msg new file mode 100644 index 00000000..79a78662 --- /dev/null +++ b/gems/docopslab-dev/assets/hooks/commit-msg @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# DocOps Lab commit-msg hook template +# Managed by docopslab-dev gem + +set -e + +MESSAGE_FILE="${1:-}" + +if [[ -z "$MESSAGE_FILE" ]]; then + echo "❌ Missing commit message file path." + exit 1 +fi + +if ! command -v bundle > /dev/null 2>&1; then + echo "⚠️ Bundler not available; skipping commit message lint." + exit 0 +fi + +echo "🪝 Linting commit message..." +bundle exec rake "labdev:lint:commit[$MESSAGE_FILE]" diff --git a/gems/docopslab-dev/assets/templates/gitignore b/gems/docopslab-dev/assets/templates/gitignore index b7fb617b..1fe4d1c8 100644 --- a/gems/docopslab-dev/assets/templates/gitignore +++ b/gems/docopslab-dev/assets/templates/gitignore @@ -43,6 +43,7 @@ tmp/ # Generated config files (merged from base + local) .config/vale.ini .config/htmlproofer.yml +.config/git-lint.yml # end::universal-docopslab-dev-configs[] # tag::universal-docopslab-agent[] .agent/* diff --git a/gems/docopslab-dev/docopslab-dev.gemspec b/gems/docopslab-dev/docopslab-dev.gemspec index e02d6e9e..7000f060 100644 --- a/gems/docopslab-dev/docopslab-dev.gemspec +++ b/gems/docopslab-dev/docopslab-dev.gemspec @@ -36,6 +36,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'debride', '~> 1.13' spec.add_dependency 'fasterer', '~> 0.11' spec.add_dependency 'flog', '~> 4.8' + spec.add_dependency 'git-lint', '~> 6.2' spec.add_dependency 'reek', '~> 6.5' spec.add_dependency 'rubocop', '~> 1.80' spec.add_dependency 'rubocop-rake', '~> 0.7' diff --git a/gems/docopslab-dev/lib/docopslab/dev.rb b/gems/docopslab-dev/lib/docopslab/dev.rb index 280944ff..13812ac7 100644 --- a/gems/docopslab-dev/lib/docopslab/dev.rb +++ b/gems/docopslab-dev/lib/docopslab/dev.rb @@ -313,6 +313,14 @@ def generate_htmlproofer_config ConfigManager.generate_htmlproofer_config(self) end + def generate_git_lint_config + ConfigManager.generate_git_lint_config(self) + end + + def load_commit_conventions + ConfigManager.load_commit_conventions + end + def load_htmlproofer_config ConfigManager.load_htmlproofer_config end @@ -335,6 +343,14 @@ def run_actionlint opts_string='' Linters.run_actionlint(self, opts_string) end + def run_git_lint target=nil, opts_string='' + Linters.run_git_lint(self, target, opts_string) + end + + def run_git_lint_hook message_file + Linters.run_git_lint_hook(self, message_file) + end + def run_all_linters Linters.run_all_linters(self) end diff --git a/gems/docopslab-dev/lib/docopslab/dev/config_manager.rb b/gems/docopslab-dev/lib/docopslab/dev/config_manager.rb index e4847f7e..96eeb1a6 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/config_manager.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/config_manager.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require 'fileutils' +require 'yaml' + module DocOpsLab module Dev module ConfigManager @@ -55,6 +58,51 @@ def generate_htmlproofer_config _context end end + def generate_git_lint_config _context + default_config = load_git_lint_defaults + return false unless default_config + + base_config = File.join(Paths.config_vendor_dir, 'git-lint.yml') + project_config = '.config/git-lint.local.yml' + generated_config = Paths::CONFIG_FILES[:git_lint] + + merged_content = default_config + if File.exist?(base_config) + merged_content = deep_merge_configs(merged_content, YAML.load_file(base_config) || {}) + end + if File.exist?(project_config) + merged_content = deep_merge_configs(merged_content, YAML.load_file(project_config) || {}) + end + + conventions = load_commit_conventions + merged_content = apply_commit_conventions_to_git_lint(merged_content, conventions) if conventions + + rendered = YAML.dump(merged_content) + FileUtils.mkdir_p(File.dirname(generated_config)) + + if !File.exist?(generated_config) || File.read(generated_config) != rendered + File.write(generated_config, rendered) + puts " 📝 Generated #{generated_config} from git-lint defaults + DocOps Lab conventions" + true + else + false + end + end + + def load_commit_conventions + base_path = File.join(Paths.config_vendor_dir, 'commit-conventions.yml') + local_path = '.config/commit-conventions.yml' + + if File.exist?(local_path) + load_inheritable_yaml_config(local_path) + elsif File.exist?(base_path) + YAML.load_file(base_path) || {} + end + rescue StandardError => e + warn "⚠️ Failed to load commit conventions: #{e.message}" + nil + end + def load_htmlproofer_config config_path=nil, policy: 'merge' config_paths = if config_path && File.exist?(config_path) [config_path] @@ -99,6 +147,67 @@ def process_htmlproofer_patterns config config.transform_keys(&:to_sym) end + def load_git_lint_defaults + spec = Gem.loaded_specs['git-lint'] || Gem::Specification.find_all_by_name('git-lint').first + unless spec + warn "⚠️ git-lint is not installed. Run 'bundle install'." + return nil + end + + YAML.load_file(File.join(spec.full_gem_path, 'lib/git/lint/configuration/defaults.yml')) || {} + rescue StandardError => e + warn "⚠️ Failed to load git-lint defaults: #{e.message}" + nil + end + + def apply_commit_conventions_to_git_lint config, conventions + types = convention_slugs(conventions, 'types') + scopes = convention_slugs(conventions, 'scopes') + separator = conventions.dig('rules', 'subject', 'allowed_scope_separator') || '+' + max_length = conventions.dig('rules', 'subject', 'maximum') + body_max = conventions.dig('rules', 'body', 'maximum_line_length') + + subject = config.dig('commits', 'subject') + if subject && types.any? + scope_pattern = git_lint_scope_pattern(scopes, separator) + subject['prefix']['includes'] = types.sort.map { |type| "#{Regexp.escape(type)}#{scope_pattern}: " } + end + + subject['length']['maximum'] = max_length if subject && max_length + body = config.dig('commits', 'body') + body['line_length']['maximum'] = body_max if body&.dig('line_length') && body_max + + config + end + + def git_lint_scope_pattern scopes, separator + return '(?:\\([a-z0-9_-]+\\))?' if scopes.empty? + + escaped_scopes = scopes.sort.map { |scope| Regexp.escape(scope) } + escaped_separator = Regexp.escape(separator) + "(?:\\((?:#{escaped_scopes.join('|')})(?:#{escaped_separator}(?:#{escaped_scopes.join('|')}))*\\))?" + end + + def convention_slugs conventions, key + values = conventions.dig('conventions', key) + case values + when Hash + values.keys + else + Array(values).filter_map { |entry| entry['slug'] if entry.is_a?(Hash) } + end + end + + def load_inheritable_yaml_config path + local = YAML.load_file(path) || {} + inherit_from = local.delete('inherit_from') + return local unless inherit_from + + base_path = File.expand_path(inherit_from, File.dirname(path)) + base = File.exist?(base_path) ? YAML.load_file(base_path) || {} : {} + deep_merge_configs(base, local) + end + def merge_yaml_configs base_path, local_path # Implement RuboCop-style inheritance for YAML files require 'yaml' diff --git a/gems/docopslab-dev/lib/docopslab/dev/git_hooks.rb b/gems/docopslab-dev/lib/docopslab/dev/git_hooks.rb index 42f7df99..8d7ae7ab 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/git_hooks.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/git_hooks.rb @@ -119,6 +119,8 @@ def list_hook_templates description = case hook_name when 'pre-commit' 'Advisory checks & syntax validation (non-blocking)' + when 'commit-msg' + 'Commit message style gate (blocking)' when 'pre-push' 'Comprehensive linting & quality gate (blocking)' else diff --git a/gems/docopslab-dev/lib/docopslab/dev/linters.rb b/gems/docopslab-dev/lib/docopslab/dev/linters.rb index 28f3cdf5..1488b455 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/linters.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/linters.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true require 'open3' +require 'fileutils' require 'pathname' require 'sourcerer/util/pathifier' +require 'tmpdir' module DocOpsLab module Dev @@ -185,6 +187,149 @@ def run_actionlint context, opts_string='' success end + def run_git_lint context, target=nil, opts_string='' + puts '🧾 Running git-lint...' + context.generate_git_lint_config if context.respond_to?(:generate_git_lint_config) + + unless File.exist?(Paths::CONFIG_FILES[:git_lint]) + puts "❌ No git-lint config found. Run 'labdev:sync:configs' to create one." + return false + end + + cmd = %w[bundle exec git-lint analyze] + if target + cmd += ['--commit', target] + else + cmd << '--branch' + end + cmd += opts_string.split unless opts_string.empty? + + success = with_git_lint_config_env do |env| + system(env, *cmd) + end + + if success + puts '✅ git-lint passed' + else + puts '❌ git-lint found commit message issues' + end + + success + end + + def run_git_lint_hook context, message_file + puts "🧾 Checking commit message: #{message_file}" + context.generate_git_lint_config if context.respond_to?(:generate_git_lint_config) + + unless File.exist?(message_file) + puts "❌ Commit message file not found: #{message_file}" + return false + end + + convention_success = validate_commit_message_subject(context, message_file) + git_lint_success = with_git_lint_config_env do |env| + system(env, 'bundle', 'exec', 'git-lint', '--hook', message_file) + end + + if convention_success && git_lint_success + puts '✅ Commit message passed' + true + else + puts '❌ Commit message failed' + false + end + end + + def validate_commit_message_subject context, message_file + subject = File.readlines(message_file, chomp: true).find { |line| !line.strip.empty? } + return true if subject.nil? || subject.match?(/\A(?:fixup|squash|amend)!\s/) + + conventions = context.load_commit_conventions if context.respond_to?(:load_commit_conventions) + return true unless conventions + + types = convention_slugs(conventions, 'types').sort + scopes = convention_slugs(conventions, 'scopes').sort + separator = conventions.dig('rules', 'subject', 'allowed_scope_separator') || '+' + reject_punctuation = conventions.dig('rules', 'subject', 'reject_terminal_punctuation') + reject_agent_signature = conventions.dig('rules', 'text', 'reject_agent_signature') + + match = subject.match(/\A(?[a-z]+)(?:\((?[^)]+)\))?: (?.+)\z/) + unless match + puts '❌ Commit subject must match `[optional scope]: Subject`.' + return false + end + + valid = true + valid = false unless validate_commit_type(match[:type], types) + valid = false unless validate_commit_scopes(match[:scope], scopes, separator) + valid = false unless validate_subject_text(match[:subject], reject_punctuation) + valid = false if reject_agent_signature && agent_signature?(File.read(message_file)) + valid + end + + def with_git_lint_config_env + Dir.mktmpdir('docopslab-dev-git-lint') do |dir| + config_dir = File.join(dir, 'git-lint') + FileUtils.mkdir_p(config_dir) + FileUtils.cp(Paths::CONFIG_FILES[:git_lint], File.join(config_dir, 'configuration.yml')) + yield({ 'XDG_CONFIG_HOME' => dir }) + end + end + + def convention_slugs conventions, key + values = conventions.dig('conventions', key) + case values + when Hash + values.keys + else + Array(values).filter_map { |entry| entry['slug'] if entry.is_a?(Hash) } + end + end + + def validate_commit_type type, types + return true if types.include?(type) + + puts "❌ Commit type `#{type}` is not allowed. Use one of: #{types.join(', ')}." + false + end + + def validate_commit_scopes scope_text, scopes, separator + return true if scope_text.nil? || scopes.empty? + + invalid = scope_text.split(separator).reject { |scope| scopes.include?(scope) } + return true if invalid.empty? + + puts "❌ Commit scope(s) not allowed: #{invalid.join(', ')}." + puts " Use configured scopes joined with `#{separator}` when multiple scopes are needed." + false + end + + def validate_subject_text subject_text, reject_punctuation + valid = true + unless subject_text.match?(/\A[[:upper:]]/) + puts '❌ Commit subject must start with a capitalized imperative verb.' + valid = false + end + + if reject_punctuation && subject_text.match?(/[.?!]\z/) + puts '❌ Commit subject must not end with terminal punctuation.' + valid = false + end + + valid + end + + def agent_signature? message + patterns = [ + /Co-authored-by:\s*(?:Codex|Claude|ChatGPT)/i, + /Generated with (?:Claude Code|Codex|ChatGPT)/i + ] + return false unless patterns.any? { |pattern| message.match?(pattern) } + + puts '❌ Commit message must not include agent authorship signatures.' + true + end + def run_vale context, file_path=nil, opts_string='', output_format: :cli, filter: nil, style_override: nil scope = file_path ? :file : :project running_on = file_path ? "file: #{file_path}" : scope.to_s diff --git a/gems/docopslab-dev/lib/docopslab/dev/paths.rb b/gems/docopslab-dev/lib/docopslab/dev/paths.rb index e751f5c0..6cce3e75 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/paths.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/paths.rb @@ -18,6 +18,7 @@ def self.config_vendor_dir CONFIG_FILES = { vale: '.config/vale.ini', htmlproofer: '.config/htmlproofer.yml', + git_lint: '.config/git-lint.yml', rubocop: '.config/rubocop.yml' }.freeze diff --git a/gems/docopslab-dev/lib/docopslab/dev/sync_ops.rb b/gems/docopslab-dev/lib/docopslab/dev/sync_ops.rb index a31d0151..1f93ef97 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/sync_ops.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/sync_ops.rb @@ -383,6 +383,7 @@ def sync_config_files context, tool_filter: :all, offline: false generated_count = 0 generated_count += 1 if context.generate_vale_config generated_count += 1 if context.generate_htmlproofer_config + generated_count += 1 if context.generate_git_lint_config puts ' ✅ All runtime configs up to date' if generated_count.zero? diff --git a/gems/docopslab-dev/lib/docopslab/dev/tasks.rb b/gems/docopslab-dev/lib/docopslab/dev/tasks.rb index 1d5193fd..9c30b334 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/tasks.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/tasks.rb @@ -146,6 +146,12 @@ def define_tasks opts = args[:opts] || '' Dev.run_actionlint(opts) end + + desc desc_for('run:git_lint') + task :git_lint, [:opts] => [] do |_t, args| + opts = args[:opts] || '' + Dev.run_git_lint(nil, opts) + end end # ============================================================ @@ -339,6 +345,29 @@ def define_tasks end end + desc desc_for('lint:commits') + task :commits, %i[target opts] => [] do |_t, args| + success = Dev.run_git_lint(args[:target], args[:opts] || '') + exit(1) unless success + end + + desc desc_for('lint:commit') + task :commit, [:path] => [] do |_t, args| + unless args[:path] + puts 'Usage: bundle exec rake labdev:lint:commit[path/to/COMMIT_EDITMSG]' + exit(1) + end + + success = Dev.run_git_lint_hook(args[:path]) + exit(1) unless success + end + + desc 'Deprecated alias for labdev:lint:commit' + task :commit_msg, [:path] => [] do |_t, args| + warn '⚠️ labdev:lint:commit_msg is deprecated; use labdev:lint:commit.' + Rake::Task['labdev:lint:commit'].invoke(args[:path]) + end + desc desc_for('lint:spellcheck') task :spellcheck, %i[path opts] => [] do |_t, args| path = args[:path] diff --git a/gems/docopslab-dev/specs/data/default-manifest.yml b/gems/docopslab-dev/specs/data/default-manifest.yml index 9aa48f8f..240f2d59 100644 --- a/gems/docopslab-dev/specs/data/default-manifest.yml +++ b/gems/docopslab-dev/specs/data/default-manifest.yml @@ -77,8 +77,23 @@ tools: target: .config/actionlint.yml synced: false + - tool: git-lint + files: + - source: commit-conventions/base.yml + target: .config/.vendor/docopslab/commit-conventions.yml + synced: true + - source: commit-conventions/project.yml + target: .config/commit-conventions.yml + synced: false + - source: git-lint/base.yml + target: .config/.vendor/docopslab/git-lint.yml + synced: true + - source: git-lint/project.yml + target: .config/git-lint.local.yml + synced: false + library: enabled: true source: repo: DocOps/lab - ref: labdev-library \ No newline at end of file + ref: labdev-library diff --git a/gems/docopslab-dev/specs/data/tasks-def.yml b/gems/docopslab-dev/specs/data/tasks-def.yml index c55fdae3..2f791779 100644 --- a/gems/docopslab-dev/specs/data/tasks-def.yml +++ b/gems/docopslab-dev/specs/data/tasks-def.yml @@ -113,6 +113,14 @@ labdev: _test: - bundle exec rake 'labdev:run:actionlint' - bundle exec rake 'labdev:run:actionlint["-oneline -verbose"]' + git_lint: + _desc: Run the base git-lint branch analyzer and options. + _args: + opts: + summ: Additional arguments to pass to the git-lint command. + required: false + _test: + - bundle exec rake 'labdev:run:git_lint' sync: _desc: Sync all managed files (configs, scripts, docs, styles, etc) all: @@ -270,6 +278,23 @@ labdev: opts: summ: Additional options to pass to actionlint required: false + commits: + _desc: Run git-lint on current branch commits or specific commit SHAs + _args: + target: + summ: Optional commit SHA or comma-separated commit SHAs to lint + required: false + opts: + summ: Additional options to pass to git-lint + required: false + commit: + _desc: Run git-lint on an unsaved commit message file + _args: + path: + summ: Path to the commit message file passed by the commit-msg hook + required: true + commit_msg: + _alias: "labdev:lint:commit" spellcheck: _desc: Generate spellcheck report on content files _args: @@ -448,4 +473,3 @@ labdev: When a task_string is passed, shows all docstrings from this file for that task. required: false _test: [] - diff --git a/gems/docopslab-dev/specs/data/tools.yml b/gems/docopslab-dev/specs/data/tools.yml index 1749b77d..da1fc8ee 100644 --- a/gems/docopslab-dev/specs/data/tools.yml +++ b/gems/docopslab-dev/specs/data/tools.yml @@ -57,4 +57,11 @@ config: inheritance: native # Supports standard YAML patterns uses_vendor_base: true # Syncs base config to .vendor/ - format: YAML \ No newline at end of file + format: YAML + +- slug: git-lint + name: git-lint + config: + inheritance: generated_yaml # Builds .config/git-lint.yml from shared conventions + uses_vendor_base: true + format: YAML From 723a66b9b9b39fda6f732c665b73910396b419ed Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Tue, 8 Sep 2026 17:08:28 -0400 Subject: [PATCH 19/30] fix(labdev): Run Docker linter containers without an interactive TTY `-it` requires an attached terminal, so any invocation of the docopslab/dev container from CI or another non-interactive context would fail outright. Dropping it keeps stdout/stderr passthrough without assuming a TTY is present. --- gems/docopslab-dev/lib/docopslab/dev/tool_execution.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gems/docopslab-dev/lib/docopslab/dev/tool_execution.rb b/gems/docopslab-dev/lib/docopslab/dev/tool_execution.rb index 3c7fa1c0..eff44c71 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/tool_execution.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/tool_execution.rb @@ -58,7 +58,7 @@ def run_in_docker command # Run command in docopslab/dev container # Handle both String and Array command formats cmd_str = command.is_a?(Array) ? command.shelljoin : command - docker_cmd = "docker run -it --rm -v \"$(pwd):/workspace\" -w /workspace docopslab/dev #{cmd_str}" + docker_cmd = "docker run --rm -v \"$(pwd):/workspace\" -w /workspace docopslab/dev #{cmd_str}" puts "🐳 Running in Docker: #{cmd_str}" system(docker_cmd) end From 42937484d0ca20207a60fe5585d8273dc83f2715 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Tue, 8 Sep 2026 17:08:41 -0400 Subject: [PATCH 20/30] edit(docs): Refine commit message convention wording - Rename "commit summary" to "commit subject" throughout, matching the git-lint rule set. - Require a capitalized imperative verb and show full example subjects for every commit type. - Add "never sign commits as a co-author" to the basics checklist. - Fix a stray quote in an include directive and other small typos. --- _docs/agent/skills/git.adoc | 4 ++- _docs/reference/git-commit-styles.adoc | 37 +++++++++++++------------- _docs/task/development.adoc | 14 +++++----- _docs/task/lab-dev-setup.adoc | 2 +- _docs/task/release.adoc | 6 ++--- 5 files changed, 33 insertions(+), 30 deletions(-) diff --git a/_docs/agent/skills/git.adoc b/_docs/agent/skills/git.adoc index 9c648aae..73ab78fd 100644 --- a/_docs/agent/skills/git.adoc +++ b/_docs/agent/skills/git.adoc @@ -19,10 +19,12 @@ This document describes protocols for committing and pushing changes to a git Do . Commit messages should be concise and easy for users to edit. + See <> for guidance. -. Always prompt user to approve commits before pushing. +. Always prompt Operator to approve commits before pushing. . Use `gh` for interacting with GitHub whenever possible. + See <> for more information. + +. NEVER sign commits as a co-author. // end::basics-snippet[] include::../../task/development.adoc[tag=repo-state] diff --git a/_docs/reference/git-commit-styles.adoc b/_docs/reference/git-commit-styles.adoc index 617ec4e5..a65a5f8d 100644 --- a/_docs/reference/git-commit-styles.adoc +++ b/_docs/reference/git-commit-styles.adoc @@ -23,7 +23,7 @@ Most DocOps Lab projects do not base Changelog/Release Notes generation on commi The basic outline for a Conventional Commit message is: .... -[optional scope]: +[optional scope]: [optional body] @@ -31,30 +31,31 @@ The basic outline for a Conventional Commit message is: .... -[[commit-summary]] -== Commit Summary +[[commit-subject]] +== Commit Subject -The commit description should be concise and to the point, summarizing the change in 50 characters or less. +The commit subject is the text after `[optional scope]:`. +It should be concise and to the point, summarizing the change in 50 characters or less. -Use the imperative mood (e.g., "Add feature X" instead of "Added feature X"). +Start the subject with a capitalized imperative verb. +For example, use "`feat: Add widget`" instead of "`feat: add widget`" or "`feat: Added widget`". [[commit-types]] == Commit Types // tag::commit-types[] -* Use imperative verbs ("`Add widget`", not "`Added`" or "`add`") -* `create: ...` for new docs or UI content -* `edit: ...` for minor edits to docs or UI content -* `feat: ...` for new features OR improvements -* `fix: ...` for bugfixes -* `chore: ...` for version bumps and sundry tasks with no product impact -* `test: ...` for test code changes -* `refactor: ...` for code restructuring with no functional changes -* `style: ...` for formatting, missing semi-colons, etc; no functional changes -* `perf: ...` for performance improvements -* `auto: ...` for changes to CI/CD pipelines and build system -* `revert: ...` for reverting previous changes +* `create: Add installation guide` for new docs or UI content +* `edit: Clarify setup notes` for minor edits to docs or UI content +* `feat: Add widget` for new features OR improvements +* `fix: Handle missing widget config` for bugfixes +* `chore: Bump gem version` for version bumps and sundry tasks with no product impact +* `test: Cover widget validation` for test code changes +* `refactor: Extract widget parser` for code restructuring with no functional changes +* `style: Format widget examples` for formatting, missing semi-colons, etc; no functional changes +* `perf: Cache widget lookup` for performance improvements +* `auto: Update release workflow` for changes to CI/CD pipelines and build system +* `revert: Restore previous widget behavior` for reverting previous changes // end::commit-types[] @@ -69,4 +70,4 @@ Use the imperative mood (e.g., "Add feature X" instead of "Added feature X"). ** keep sentences short // end::body-conventions[] -// end::commit-styles[] \ No newline at end of file +// end::commit-styles[] diff --git a/_docs/task/development.adoc b/_docs/task/development.adoc index 498a9ee9..87c8b42b 100644 --- a/_docs/task/development.adoc +++ b/_docs/task/development.adoc @@ -35,7 +35,7 @@ git checkout main git pull origin main git checkout -b dev/1.2 git checkout -b chore/bump-version-1.2.0 -git commit -am "Bumped version attributes in README" +git commit -am "chore: Bump version attributes" git checkout dev/1.2 git merge chore/bump-version-1.2.0 git push -u origin dev/1.2 @@ -54,9 +54,9 @@ git checkout dev/1.2 git checkout -b feat/add-widget … implement … git add . -git commit -m "feat: add widget" +git commit -m "feat: Add widget" git push -u origin feat/add-widget -gh pr create --base dev/1.2 --title "feat: add widget" --body "Adds a new widget to the dashboard." +gh pr create --base dev/1.2 --title "feat: Add widget" --body "Adds a new widget to the dashboard." .... Branch naming conventions:: @@ -69,7 +69,7 @@ Branch naming conventions:: [[commit-message-conventions]] === Commit Message Conventions -Description (first line) conventions:: +Subject conventions:: include::../reference/git-commit-styles.adoc[tag=commit-types] @@ -91,9 +91,9 @@ git checkout dev/1.2 git checkout -b feat/add-widget … implement … git add . -git commit -m "feat: add widget" +git commit -m "feat: Add widget" git merge --squash feat/add-widget -git commit -m "feat: add widget" +git commit -m "feat: Add widget" git push origin dev/1.2 .... @@ -378,4 +378,4 @@ rake pr_test # Often includes verbose options include::../policy/generative-ai-usage.adoc[tags=tldr] -For the complete policy, see {xref_docs_generative-ai-usage_link}. \ No newline at end of file +For the complete policy, see {xref_docs_generative-ai-usage_link}. diff --git a/_docs/task/lab-dev-setup.adoc b/_docs/task/lab-dev-setup.adoc index 93c69e9c..e40ef73e 100644 --- a/_docs/task/lab-dev-setup.adoc +++ b/_docs/task/lab-dev-setup.adoc @@ -10,7 +10,7 @@ include::../_local_settings.adoc[] include::../partials/_docopslab-dev-context-notice.adoc[] -include::../../gems/docopslab-dev/README.adoc[tags="global-settings,setup",leveloffset="-1""] +include::../../gems/docopslab-dev/README.adoc[tags="global-settings,setup",leveloffset="-1"] [NOTE] For configuration details, see {xref_docs_lab-dev-config_link}. diff --git a/_docs/task/release.adoc b/_docs/task/release.adoc index 818ae823..6b97aeb3 100644 --- a/_docs/task/release.adoc +++ b/_docs/task/release.adoc @@ -187,12 +187,12 @@ git checkout release/1.1 git checkout -b fix/parser-typo # … FIX … git add . -git commit -m "fix: correct parser typo" +git commit -m "fix: Correct parser typo" git push origin fix/parser-typo # … TEST … git checkout release/1.1 git merge --squash fix/parser-typo -git commit -m "fix: correct parser typo" +git commit -m "fix: Correct parser typo" git push origin release/1.1 .... @@ -224,4 +224,4 @@ Perform Steps 1 through 5 of the standard release procedure: . <> . <> -// end::rollback-patching[] \ No newline at end of file +// end::rollback-patching[] From 8d551ca04884ebf62a64d6ccb8ce230327232aae Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Tue, 8 Sep 2026 17:08:58 -0400 Subject: [PATCH 21/30] feat: Wire AsciiSourcerer doc partials into the Jekyll build - Add `copy_asciisourcerer_doc_partials`, resolving the source location from an env var, a sibling checkout, or the installed gem. - Split `build_site` into a reusable `build_site_html` (HTML only, used by both the site build and agent-doc generation) and the full `build_site` that also regenerates agent docs. - Add `gemdo:push:library:docs:local` to build, stage, and load agent docs into the local cache for downstream repo testing (documented in the README, with the chain-of-truth from `_docs/agent/` down to the local cache). - Adapt `gen_agent_docs.rb` to AsciiSourcerer's renamed `MarkDownGrade.convert_html` and its new `convert_dls_to_markdown` option. - Un-publish `_docs/partials/` from the Jekyll site and add a permalink for agent docs. - Depend on `asciisourcerer` directly (via a local sibling-repo path) so the Rakefile and script above can load it outside of the docopslab-dev gem chain. --- Gemfile | 3 +- README.adoc | 9 ++++++ Rakefile | 59 ++++++++++++++++++++++++++++++++------ _config.yml | 11 +++++-- gems/docopslab-dev/Gemfile | 2 +- scripts/gen_agent_docs.rb | 7 +++-- 6 files changed, 77 insertions(+), 14 deletions(-) diff --git a/Gemfile b/Gemfile index e8ec4337..c411a6a3 100644 --- a/Gemfile +++ b/Gemfile @@ -6,12 +6,13 @@ source 'https://rubygems.org' gem 'docopslab-dev', path: './gems/docopslab-dev' # 3rd Party gem 'colorize', '~> 1.1' -gem 'jekyll', '~> 4.r' +gem 'jekyll', '~> 4.4' gem 'pathspec', '~> 2.1' gem 'reverse_markdown' gem 'rubyzip', '~> 2.3' # For Vale package building gem 'sass' # DocOps Lab +gem 'asciisourcerer', path: '../asciisourcerer' gem 'schemagraphy' group :jekyll_plugins do diff --git a/README.adoc b/README.adoc index ee871413..35e4b4d2 100644 --- a/README.adoc +++ b/README.adoc @@ -574,6 +574,9 @@ See link:https://vale.sh/docs/styles[Vale's Style documentation] for details on ==== Asset Library Publishing These tasks live in the `DocOps/lab` Rakefile and are used to stage and publish library assets to the `labdev-library` branch. +Generated agent docs follow this chain of truth: +`_docs/agent/` -> `_site/docs/agent/` -> `gems/docopslab-dev/docs/agent/` -> `.library/` -> `~/.cache/docopslab/dev/library/current/`. +Downstream projects running `bundle exec rake labdev:sync:docs` read the local cache first when it is available. .Stage assets from `gems/docopslab-dev/assets/` and generated docs into `.library/` [.prompt] @@ -591,6 +594,12 @@ These tasks live in the `DocOps/lab` Rakefile and are used to stage and publish [.prompt] bundle exec rake gemdo:push:library +.Build the latest agent docs and load them into the local cache for downstream repo testing +[.prompt] + bundle exec rake gemdo:push:library:docs:local + +Use `gemdo:push:library:docs:local` before running `bundle exec rake labdev:sync:docs` in a local downstream checkout such as AsciiSourcerer. +This avoids testing against a stale `~/.cache/docopslab/dev/library/current/` copy. [[troubleshooting]] == Troubleshooting diff --git a/Rakefile b/Rakefile index 5406b2db..4a7babab 100644 --- a/Rakefile +++ b/Rakefile @@ -230,6 +230,39 @@ task :write_rubocop_styles do puts '✅ RuboCop styles written to _docs/partials/built/_rubocop-styles.adoc' end +def asciisourcerer_doc_partials_dir + candidates = [] + candidates << ENV['ASCIISOURCERER_DOC_PARTIALS_DIR'] if ENV['ASCIISOURCERER_DOC_PARTIALS_DIR'] + candidates << File.expand_path('../asciisourcerer/lib/sourcerer/_docs/partials', __dir__) + + if Gem.loaded_specs['asciisourcerer'] + candidates << File.join(Gem.loaded_specs['asciisourcerer'].full_gem_path, 'lib/sourcerer/_docs/partials') + end + + candidates.find { |path| Dir.exist?(path) } +end + +desc 'Copy AsciiSourcerer documentation partials for site includes' +task :copy_asciisourcerer_doc_partials do + source_dir = asciisourcerer_doc_partials_dir + unless source_dir + abort '❌ AsciiSourcerer documentation partials not found. Expected lib/sourcerer/_docs/partials ' \ + 'in ../asciisourcerer or the bundled asciisourcerer gem.' + end + + partials = Dir.glob(File.join(source_dir, '*.adoc')) + abort "❌ No AsciiSourcerer documentation partials found in #{source_dir}" if partials.empty? + + dest_dir = '_docs/partials/built' + FileUtils.mkdir_p(dest_dir) + partials.each do |source| + dest = File.join(dest_dir, File.basename(source)) + FileUtils.cp(source, dest) + puts " ✓ Copied #{dest}" + end + puts "✅ Copied #{partials.count} AsciiSourcerer documentation partial(s)" +end + desc 'Render an AsciiDoc file of universal attributes' # use _data/docops-lab-projects.yml data and the Liquid template at _includes/docpslab-universal-attributes.asciidoc to produce a file at _docs/partials/built/_docopslab-universal-attributes.adoc task :generate_universal_attributes do @@ -264,11 +297,17 @@ task :build_vale_package do system('bundle exec ruby scripts/build_vale_package.rb') or raise 'Failed to build Vale package' end -desc 'Build the Jekyll site (with single-sourced cards and project pages)' -task build_site: %i[extract_readme_attrs generate_project_pages generate_metadata copy_jekyll_ui_config - write_rubocop_styles generate_universal_attributes gemdo:gen_agent_docs] do +desc 'Build Jekyll HTML used by the site and generated docs' +task build_site_html: %i[extract_readme_attrs generate_project_pages generate_metadata copy_jekyll_ui_config + write_rubocop_styles copy_asciisourcerer_doc_partials generate_universal_attributes] do puts '🔨 Building Jekyll site...' system('bundle exec jekyll build') or raise 'Jekyll build failed' + puts '✅ Jekyll HTML build complete' +end + +desc 'Build the Jekyll site (with single-sourced cards, project pages, and agent docs)' +task build_site: [:build_site_html] do + Rake::Task['gemdo:gen_agent_docs'].invoke puts '✅ Build complete' end @@ -652,11 +691,7 @@ namespace :gemdo do task :gen_agent_docs do require_relative 'scripts/gen_agent_docs' - # Build Jekyll site if not already built (for agent docs HTML) - unless Dir.exist?(BUILD_DIR) && !Dir.empty?(BUILD_DIR) - puts '📄 Building Jekyll site for agent docs HTML...' - system('bundle exec jekyll build') or raise 'Jekyll build failed' - end + Rake::Task['build_site_html'].invoke # Run the generation script GenAgentDocs.run(BUILD_DIR) @@ -713,6 +748,14 @@ namespace :gemdo do puts ' Run `bundle exec rake labdev:show:library` in any downstream project to verify.' end + namespace :docs do + desc 'Build latest agent docs, stage library, and load it into the host cache for downstream sync' + task local: %w[gemdo:push:library:local] do + puts '✅ Latest generated docs are available in the local library cache.' + puts ' Downstream projects can now run `bundle exec rake labdev:sync:docs`.' + end + end + desc "Force-push #{LibraryManager::STAGE_DIR}/ to the #{LIBRARY_BRANCH} branch on GitHub" task :remote do require 'tmpdir' diff --git a/_config.yml b/_config.yml index 1f24723c..a5538350 100644 --- a/_config.yml +++ b/_config.yml @@ -100,12 +100,13 @@ defaults: values: type: procedure indexed: true - - scope: + - scope: path: "_docs/agent/" values: type: agent indexed: false noindex: true + permalink: /docs/agent/:slug/ - scope: path: "_docs/agent/topics" values: @@ -122,6 +123,12 @@ defaults: path: "_docs/agent/missions" values: group: missions + - scope: + path: "_docs/partials/" + values: + published: false + indexed: false + noindex: true # end::defaults-snippet[] highlighting: @@ -202,4 +209,4 @@ doc_types_order: - template xref_attrs: - outfile: _docs/partials/built/xref_attrs.adoc \ No newline at end of file + outfile: _docs/partials/built/xref_attrs.adoc diff --git a/gems/docopslab-dev/Gemfile b/gems/docopslab-dev/Gemfile index 76803852..19f22ecf 100644 --- a/gems/docopslab-dev/Gemfile +++ b/gems/docopslab-dev/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' -gem 'asciisourcerer' +gem 'asciisourcerer', path: '../../../asciisourcerer' gemspec diff --git a/scripts/gen_agent_docs.rb b/scripts/gen_agent_docs.rb index 5bafc5fd..4d4fae3f 100644 --- a/scripts/gen_agent_docs.rb +++ b/scripts/gen_agent_docs.rb @@ -10,7 +10,10 @@ def self.run build_dir # Setup ReverseMarkdown extensions for better conversion # Strip internal anchor links and disable anchor IDs for LLM consumption - Sourcerer::MarkDownGrade.bootstrap!(strip_internal_links: true, preserve_heading_ids: false) + Sourcerer::MarkDownGrade.bootstrap!( + strip_internal_links: true, + preserve_heading_ids: false, + convert_dls_to_markdown: true) # Manage paths source_dir = File.expand_path(File.join(build_dir, 'docs', 'agent')) @@ -90,7 +93,7 @@ def self.convert_and_write html_file, dest_file body_div = doc.at_css('div.document-body') if body_div - markdown_content = MarkDownGrade.convert(body_div.inner_html, github_flavored: true) + markdown_content = Sourcerer::MarkDownGrade.convert_html(body_div.inner_html, github_flavored: true) title = h1 ? "# #{h1.text.strip}\n\n" : '' File.write(dest_file, title + markdown_content) From 0b4a892e223b9dcad933ad40e23ef63258dc9c54 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Tue, 8 Sep 2026 17:09:11 -0400 Subject: [PATCH 22/30] chore(labdev): Regenerate lockfiles and bump version to 0.4.0 Reflects the new git-lint dependency, the local asciisourcerer path overrides, and the docopslab-dev version bump for this release cycle. --- Gemfile.lock | 96 +++++++++++++++++-- gems/docopslab-dev/Gemfile.lock | 95 ++++++++++++++++-- .../lib/docopslab/dev/version.rb | 2 +- 3 files changed, 172 insertions(+), 21 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b3cc164e..2ed7a049 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,20 @@ +PATH + remote: ../asciisourcerer + specs: + asciisourcerer (0.5.0) + asciidoctor (~> 2.0) + asciidoctor-html5s (~> 0.5) + jekyll (~> 4.4) + jekyll-asciidoc (~> 3.0) + kramdown-asciidoc (~> 2.1) + liquid (~> 4.0) + reverse_markdown (~> 2.1) + yard (~> 0.9) + PATH remote: gems/docopslab-dev specs: - docopslab-dev (0.3.1) + docopslab-dev (0.4.0) asciidoctor (~> 2.0) asciisourcerer (~> 0.4) brakeman (~> 7.1) @@ -9,6 +22,7 @@ PATH debride (~> 1.13) fasterer (~> 0.11) flog (~> 4.8) + git-lint (~> 6.2) html-proofer (~> 5.2) inch (~> 0.8) rake (~> 13.0) @@ -31,14 +45,6 @@ GEM asciidoctor-html5s (0.5.1) asciidoctor (>= 1.5.7, < 3.0) thread_safe (~> 0.3.4) - asciisourcerer (0.4.0) - asciidoctor (~> 2.0) - asciidoctor-html5s (~> 0.5) - jekyll (~> 4.4) - jekyll-asciidoc (~> 3.0) - kramdown-asciidoc (~> 2.1) - liquid (~> 4.0) - reverse_markdown (~> 2.1) ast (2.4.3) async (2.32.0) console (~> 1.29) @@ -55,6 +61,11 @@ GEM bundler (>= 1.2.0, < 3) thor (~> 1.0) coderay (1.1.3) + cogger (0.14.0) + core (~> 0.1) + refinements (~> 11.0) + tone (~> 0.3) + zeitwerk (~> 2.6) colorator (1.1.0) colorize (1.1.0) concurrent-ruby (1.3.5) @@ -62,6 +73,7 @@ GEM fiber-annotation fiber-local (~> 1.1) json + core (0.1.6) crass (1.0.6) csv (3.3.5) date (3.5.1) @@ -73,6 +85,8 @@ GEM dry-configurable (1.3.0) dry-core (~> 1.1) zeitwerk (~> 2.6) + dry-container (0.11.0) + concurrent-ruby (~> 1.0) dry-core (1.1.0) concurrent-ruby (~> 1.0) logger @@ -84,6 +98,10 @@ GEM concurrent-ruby (~> 1.0) dry-core (~> 1.1) zeitwerk (~> 2.6) + dry-monads (1.10.0) + concurrent-ruby (~> 1.0) + dry-core (~> 1.1) + zeitwerk (~> 2.6) dry-schema (1.14.1) concurrent-ruby (~> 1.0) dry-configurable (~> 1.0, >= 1.0.1) @@ -102,6 +120,14 @@ GEM em-websocket (0.5.3) eventmachine (>= 0.12.9) http_parser.rb (~> 0) + etcher (0.5.1) + cogger (~> 0.12) + core (~> 0.1) + dry-monads (~> 1.6) + dry-types (~> 1.7) + refinements (~> 11.0) + versionaire (~> 12.1) + zeitwerk (~> 2.6) ethon (0.15.0) ffi (>= 1.15.0) eventmachine (1.2.7) @@ -121,6 +147,26 @@ GEM ruby_parser (~> 3.1, > 3.1.0) sexp_processor (~> 4.8) forwardable-extended (2.6.0) + git-lint (6.2.1) + cogger (~> 0.12) + core (~> 0.1) + dry-container (~> 0.11) + dry-monads (~> 1.6) + dry-schema (~> 1.13) + etcher (~> 0.2) + gitt (~> 2.0) + infusible (~> 2.2) + refinements (~> 11.0) + runcom (~> 10.0) + sod (~> 0.0) + spek (~> 2.0) + tone (~> 0.3) + zeitwerk (~> 2.6) + gitt (2.2.0) + core (~> 0.1) + dry-monads (~> 1.6) + refinements (~> 11.0) + zeitwerk (~> 2.6) google-protobuf (4.32.0-x86_64-linux-gnu) bigdecimal rake (>= 13) @@ -147,6 +193,9 @@ GEM sparkr (>= 0.2.0) term-ansicolor yard (~> 0.9.12) + infusible (2.2.1) + marameters (~> 2.0) + zeitwerk (~> 2.6) io-event (1.14.0) jekyll (4.4.1) addressable (~> 2.4) @@ -198,6 +247,9 @@ GEM loofah (2.25.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) + marameters (2.1.1) + refinements (~> 11.0) + zeitwerk (~> 2.6) mercenary (0.4.0) method_source (1.1.0) metrics (0.15.0) @@ -241,6 +293,7 @@ GEM parser (~> 3.3.0) rainbow (>= 2.0, < 4.0) rexml (~> 3.1) + refinements (11.1.3) regexp_parser (2.11.3) reverse_markdown (2.1.1) nokogiri @@ -274,6 +327,10 @@ GEM racc (~> 1.5) sexp_processor (~> 4.16) rubyzip (2.4.1) + runcom (10.2.1) + refinements (~> 11.0) + xdg (~> 7.1) + zeitwerk (~> 2.6) safe_yaml (1.0.5) sass (3.7.4) sass-listen (~> 4.0.0) @@ -297,7 +354,19 @@ GEM simplecov_json_formatter (~> 0.1) simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) + sod (0.3.1) + cogger (~> 0.12) + dry-container (~> 0.11) + infusible (~> 2.2) + refinements (~> 11.0) + tone (~> 0.3) + zeitwerk (~> 2.6) sparkr (0.4.1) + spek (2.1.1) + dry-monads (~> 1.6) + refinements (~> 11.0) + versionaire (~> 12.0) + zeitwerk (~> 2.6) stringio (3.2.0) subtxt (0.3.0) sync (0.5.0) @@ -313,13 +382,19 @@ GEM mize (~> 0.6) sync to_regexp (0.2.1) + tone (0.4.2) + refinements (~> 11.0) + zeitwerk (~> 2.6) traces (0.18.2) ttfunk (1.8.0) bigdecimal (~> 3.1) typhoeus (1.5.0) ethon (>= 0.9.0, < 0.16.0) unicode-display_width (2.6.0) + versionaire (12.1.1) + refinements (~> 11.0) webrick (1.9.1) + xdg (7.1.3) yaml (0.4.0) yard (0.9.37) yell (2.2.2) @@ -329,12 +404,13 @@ PLATFORMS x86_64-linux DEPENDENCIES + asciisourcerer! colorize (~> 1.1) docopslab-dev! feedjira (~> 3.2) http_parser.rb (~> 0.6.0) httparty (~> 0.21) - jekyll (~> 4.r) + jekyll (~> 4.4) jekyll-asciidoc (~> 3.0) jekyll-redirect-from (~> 0.16) jekyll-seo-tag (~> 2.8) diff --git a/gems/docopslab-dev/Gemfile.lock b/gems/docopslab-dev/Gemfile.lock index f57d573c..943a26c1 100644 --- a/gems/docopslab-dev/Gemfile.lock +++ b/gems/docopslab-dev/Gemfile.lock @@ -1,7 +1,20 @@ +PATH + remote: ../../../asciisourcerer + specs: + asciisourcerer (0.5.0) + asciidoctor (~> 2.0) + asciidoctor-html5s (~> 0.5) + jekyll (~> 4.4) + jekyll-asciidoc (~> 3.0) + kramdown-asciidoc (~> 2.1) + liquid (~> 4.0) + reverse_markdown (~> 2.1) + yard (~> 0.9) + PATH remote: . specs: - docopslab-dev (0.3.1) + docopslab-dev (0.4.0) asciidoctor (~> 2.0) asciisourcerer (~> 0.4) brakeman (~> 7.1) @@ -9,6 +22,7 @@ PATH debride (~> 1.13) fasterer (~> 0.11) flog (~> 4.8) + git-lint (~> 6.2) html-proofer (~> 5.2) inch (~> 0.8) rake (~> 13.0) @@ -31,14 +45,6 @@ GEM asciidoctor-html5s (0.5.1) asciidoctor (>= 1.5.7, < 3.0) thread_safe (~> 0.3.4) - asciisourcerer (0.4.0) - asciidoctor (~> 2.0) - asciidoctor-html5s (~> 0.5) - jekyll (~> 4.4) - jekyll-asciidoc (~> 3.0) - kramdown-asciidoc (~> 2.1) - liquid (~> 4.0) - reverse_markdown (~> 2.1) ast (2.4.3) async (2.32.1) console (~> 1.29) @@ -55,12 +61,18 @@ GEM bundler (>= 1.2.0, < 3) thor (~> 1.0) coderay (1.1.3) + cogger (0.14.0) + core (~> 0.1) + refinements (~> 11.0) + tone (~> 0.3) + zeitwerk (~> 2.6) colorator (1.1.0) concurrent-ruby (1.3.6) console (1.34.0) fiber-annotation fiber-local (~> 1.1) json + core (0.1.6) csv (3.3.5) debride (1.13.0) path_expander (~> 1.0) @@ -71,6 +83,8 @@ GEM dry-configurable (1.3.0) dry-core (~> 1.1) zeitwerk (~> 2.6) + dry-container (0.11.0) + concurrent-ruby (~> 1.0) dry-core (1.1.0) concurrent-ruby (~> 1.0) logger @@ -82,6 +96,10 @@ GEM concurrent-ruby (~> 1.0) dry-core (~> 1.1) zeitwerk (~> 2.6) + dry-monads (1.10.0) + concurrent-ruby (~> 1.0) + dry-core (~> 1.1) + zeitwerk (~> 2.6) dry-schema (1.14.1) concurrent-ruby (~> 1.0) dry-configurable (~> 1.0, >= 1.0.1) @@ -100,6 +118,14 @@ GEM em-websocket (0.5.3) eventmachine (>= 0.12.9) http_parser.rb (~> 0) + etcher (0.5.1) + cogger (~> 0.12) + core (~> 0.1) + dry-monads (~> 1.6) + dry-types (~> 1.7) + refinements (~> 11.0) + versionaire (~> 12.1) + zeitwerk (~> 2.6) ethon (0.15.0) ffi (>= 1.15.0) eventmachine (1.2.7) @@ -115,6 +141,26 @@ GEM ruby_parser (~> 3.1, > 3.1.0) sexp_processor (~> 4.8) forwardable-extended (2.6.0) + git-lint (6.2.1) + cogger (~> 0.12) + core (~> 0.1) + dry-container (~> 0.11) + dry-monads (~> 1.6) + dry-schema (~> 1.13) + etcher (~> 0.2) + gitt (~> 2.0) + infusible (~> 2.2) + refinements (~> 11.0) + runcom (~> 10.0) + sod (~> 0.0) + spek (~> 2.0) + tone (~> 0.3) + zeitwerk (~> 2.6) + gitt (2.2.0) + core (~> 0.1) + dry-monads (~> 1.6) + refinements (~> 11.0) + zeitwerk (~> 2.6) google-protobuf (4.34.1-x86_64-linux-gnu) bigdecimal rake (~> 13.3) @@ -137,6 +183,9 @@ GEM sparkr (>= 0.2.0) term-ansicolor yard (~> 0.9.12) + infusible (2.2.1) + marameters (~> 2.0) + zeitwerk (~> 2.6) io-event (1.14.0) jekyll (4.4.1) addressable (~> 2.4) @@ -180,6 +229,9 @@ GEM rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) + marameters (2.1.1) + refinements (~> 11.0) + zeitwerk (~> 2.6) mercenary (0.4.0) method_source (1.1.0) metrics (0.15.0) @@ -216,6 +268,7 @@ GEM parser (~> 3.3.0) rainbow (>= 2.0, < 4.0) rexml (~> 3.1) + refinements (11.1.3) regexp_parser (2.11.3) reverse_markdown (2.1.1) nokogiri @@ -259,6 +312,10 @@ GEM ruby_parser (3.21.1) racc (~> 1.5) sexp_processor (~> 4.16) + runcom (10.2.1) + refinements (~> 11.0) + xdg (~> 7.1) + zeitwerk (~> 2.6) safe_yaml (1.0.5) sass-embedded (1.99.0-x86_64-linux-gnu) google-protobuf (~> 4.31) @@ -269,7 +326,19 @@ GEM simplecov_json_formatter (~> 0.1) simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) + sod (0.3.1) + cogger (~> 0.12) + dry-container (~> 0.11) + infusible (~> 2.2) + refinements (~> 11.0) + tone (~> 0.3) + zeitwerk (~> 2.6) sparkr (0.4.1) + spek (2.1.1) + dry-monads (~> 1.6) + refinements (~> 11.0) + versionaire (~> 12.0) + zeitwerk (~> 2.6) subtxt (0.3.0) sync (0.5.0) term-ansicolor (1.11.3) @@ -282,13 +351,19 @@ GEM bigdecimal mize (~> 0.6) sync + tone (0.4.2) + refinements (~> 11.0) + zeitwerk (~> 2.6) traces (0.18.2) ttfunk (1.8.0) bigdecimal (~> 3.1) typhoeus (1.5.0) ethon (>= 0.9.0, < 0.16.0) unicode-display_width (2.6.0) + versionaire (12.1.1) + refinements (~> 11.0) webrick (1.9.2) + xdg (7.1.3) yaml (0.4.0) yard (0.9.37) yell (2.2.2) @@ -298,7 +373,7 @@ PLATFORMS x86_64-linux DEPENDENCIES - asciisourcerer + asciisourcerer! docopslab-dev! rspec (~> 3.0) diff --git a/gems/docopslab-dev/lib/docopslab/dev/version.rb b/gems/docopslab-dev/lib/docopslab/dev/version.rb index 28e21d83..3ee8954f 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/version.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/version.rb @@ -2,7 +2,7 @@ module DocOpsLab module Dev - VERSION = '0.3.1' + VERSION = '0.4.0' RUBY_TARGET = '3.2.7' end end From c02b700f8d575bda45455496f92cfe1a0728f90c Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Tue, 8 Sep 2026 17:09:23 -0400 Subject: [PATCH 23/30] chore: Add AsciiDoc FabLab project entry; link SGYML spec repos - Add the AsciiDoc FabLab front-end component library under the new Frameworks (Front-End / Design) section. - Record each SGYML spec's owning repo (releasehx, schemagraphy, versioneer, openpathyml, issuer) and add the UserStoryML spec. --- _data/docops-lab-projects.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/_data/docops-lab-projects.yml b/_data/docops-lab-projects.yml index 8f0070d2..436f132f 100644 --- a/_data/docops-lab-projects.yml +++ b/_data/docops-lab-projects.yml @@ -336,6 +336,24 @@ projects: icon: folder-tree libs: [OpenPathYML] +# FRAMEWORKS (FRONT-END / DESIGN) + +## AsciiDoc FabLab: + - name: AsciiDoc FabLab + slug: asciidoc-fablab + type: framework + desc: | + CSS, JavaScript, and other front-end affecting assets for designing and styling rich-text output from AsciiDoc source files, with a focus on accessibility. + line: Component library and methods for designing AsciiDoc-sourced output + vrsn: 0.1.0 + tags: [design,styling,front-end] + tech: [CSS,Sass,JavaScript,Node.js,HTML,Asciidoctor,Asciidoctor.js] + wave: 1 + done: 90% + icon: palette + deps: [] + libs: [OpenFormYML] + # FRAMEWORKS (DOCUMENT[ATION] OPS) ## Clientele-as-Code: @@ -950,28 +968,40 @@ projects: - name: OpenHXY desc: YAML-based syntax for product release history, notes, changelog slug: open-release-history-sgyml + repo: releasehx - name: OpenSGGY desc: YAML-based syntax for style guide / glossary definition format (Open Style Guides) slug: open-style-guide-glossary-sgyml + repo: schemagraphy - name: OpenVMY desc: YAML-based syntax for version (source and output) divergence mapping system slug: open-version-management-sgyml + repo: versioneer - name: OpenCLY desc: YAML-based syntax for defining & documenting CLIs slug: open-cli-definition-sgyml + repo: schemagraphy - name: OpenFormY desc: YAML-based syntax for defining & documenting Web forms slug: open-form-definition-sgyml + repo: schemagraphy - name: OpenCFGY desc: YAML-based syntax for defining config files slug: open-config-definition-sgyml + repo: schemagraphy - name: OpenPathY desc: | A YAML-based syntax for defining, documenting, validating, & initiating fileset structures slug: open-pathtree-sgyml + repo: openpathyml - name: OpenIMY desc: YAML-based syntax for defining issue tickets slug: open-issues-definition-sgyml + repo: issuer + - name: UserStoryML + desc: YAML-based syntax for defining user stories + slug: open-user-story-definition-sgyml + repo: schemagraphy packs: schemagraphy-specs: desc: | @@ -986,6 +1016,7 @@ projects: - schemagraphy-open-config-spec-ruby - schemagraphy-open-pathy-spec-ruby - schemagraphy-open-issues-spec-ruby + - schemagraphy-open-user-story-spec-ruby schemagraphy-specs-py: desc: | Specification schemas as Python libraries. @@ -999,6 +1030,7 @@ projects: - schemagraphy-open-config-spec-python - schemagraphy-open-pathy-spec-python - schemagraphy-open-issues-spec-python + - schemagraphy-open-user-story-spec-python schemagraphy-specs-npm: desc: | Specification schemas as JavaScript libraries. @@ -1012,6 +1044,7 @@ projects: - schemagraphy-open-config-spec-node - schemagraphy-open-pathy-spec-node - schemagraphy-open-issues-spec-node + - schemagraphy-open-user-story-spec-node tech: [SGYML,YAML,JSON,JSON Schema,JMESPath,JSONPath,Ruby,Python,JavaScript] wave: 2 done: 30% From ffd1c7ab1d3cc2cb81dea410a984383f96c32497 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Tue, 8 Sep 2026 17:09:32 -0400 Subject: [PATCH 24/30] chore: Add Geri Reid blog to serials data --- _data/tech-docs-serials.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/_data/tech-docs-serials.yml b/_data/tech-docs-serials.yml index 41fbcd08..7c6aaf61 100644 --- a/_data/tech-docs-serials.yml +++ b/_data/tech-docs-serials.yml @@ -214,6 +214,14 @@ services: desc: | Sarah Maddox’s technical writing (and occasional fiction) blog offering practical insights on technical communication, documentation community events, and writing in the tech world (with a dash of whimsy). +- name: Geri Reid + site: https://gerireid.com/blog + feed: https://gerireid.com/rss.xml + type: RSS + tags: [accessibility, technical-writing, ai, best-practices] + desc: | + Blog of an accessibility specialist and designer, covering design systems and user experience. + - name: Heroic Technical Writing site: https://heroictechwriting.com feed: https://heroictechwriting.com/feed From 8394532f1cb9adbab2d4283174f2a2f13ee64324 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Tue, 8 Sep 2026 17:09:47 -0400 Subject: [PATCH 25/30] create: Add Liquid templating and SchemaGraphy SGYML reference docs - Document DocOps Lab's Liquid 4 environment: custom tags, the AsciiSourcerer filter compendium, house indentation styles for HTML/AsciiDoc/YAML targets, and how to invoke rendering directly. - Document SchemaGraphy's YAML/SGYML loaders (`load_yaml_with_tags`, `load_yaml_with_attributes`) and the `TagUtils.detag` helper. - Add matching agent-facing skill/topic docs that transclude the new reference content for AI agents. --- _docs/agent/skills/liquid-templating.adoc | 8 + _docs/agent/skills/yaml-loading.adoc | 13 ++ .../topics/liquid-filters-reference.adoc | 10 ++ _docs/reference/liquid-templating.adoc | 162 ++++++++++++++++++ _docs/reference/schemagraphy-sgyml.adoc | 56 ++++++ 5 files changed, 249 insertions(+) create mode 100644 _docs/agent/skills/liquid-templating.adoc create mode 100644 _docs/agent/skills/yaml-loading.adoc create mode 100644 _docs/agent/topics/liquid-filters-reference.adoc create mode 100644 _docs/reference/liquid-templating.adoc create mode 100644 _docs/reference/schemagraphy-sgyml.adoc diff --git a/_docs/agent/skills/liquid-templating.adoc b/_docs/agent/skills/liquid-templating.adoc new file mode 100644 index 00000000..26cc67b8 --- /dev/null +++ b/_docs/agent/skills/liquid-templating.adoc @@ -0,0 +1,8 @@ +--- +permalink: /docs/agent/liquid-templating/ +indexed: false +--- += Liquid Templating in DocOps Lab Projects +include::../_agent_settings.adoc[] + +include::../../reference/liquid-templating.adoc[tags="content"] \ No newline at end of file diff --git a/_docs/agent/skills/yaml-loading.adoc b/_docs/agent/skills/yaml-loading.adoc new file mode 100644 index 00000000..70d17d29 --- /dev/null +++ b/_docs/agent/skills/yaml-loading.adoc @@ -0,0 +1,13 @@ +--- +permalink: /docs/agent/yaml-loading/ +indexed: false +--- += Loading YAML/SGYML in DocOps Lab Projects +include::../_agent_settings.adoc[] + +As an AI agent, you can help DocOps Lab developers write code that properly loads YAML and SGYML data. + +include::../../reference/schemagraphy-sgyml.adoc[tag="loading-yaml"] + +.Detagging loaded data +include::../../reference/schemagraphy-sgyml.adoc[tag="detagging-example"] diff --git a/_docs/agent/topics/liquid-filters-reference.adoc b/_docs/agent/topics/liquid-filters-reference.adoc new file mode 100644 index 00000000..1ea7c919 --- /dev/null +++ b/_docs/agent/topics/liquid-filters-reference.adoc @@ -0,0 +1,10 @@ +--- +permalink: /docs/agent/liquid-filters-reference/ +indexed: false +--- += Liquid Filters Reference for DocOps Lab Projects +include::../_agent_settings.adoc[] + +Custom AsciiSourcerer/DocOps Lab filters are located under `## Filters from AsciiSourcerer` below. + +include::../../partials/built/liquid-filters-by-source.adoc[] diff --git a/_docs/reference/liquid-templating.adoc b/_docs/reference/liquid-templating.adoc new file mode 100644 index 00000000..7755974a --- /dev/null +++ b/_docs/reference/liquid-templating.adoc @@ -0,0 +1,162 @@ +--- +tags: ["reference", "liquid", "templating", "development", "styles", "syntax"] +description: "Liquid 4++ templating methods for DocOps Lab projects." +docs-group: technical +order: 66 +permalink: /docs/liquid-templating/ +--- +include::../_local_settings.adoc[] += Liquid Templating Guide + +// tag::content[] +DocOps Lab's Liquid 4 environment is invoked through a special bootstrapping procedure. +It introduces numerous custom Liquid tags and filters. + +Throughout DocOps Lab applications, Jekyll's extended and modified version of Liquid 4 is standard. + +This means all Jekyll's filters and its special `include` tag are available within and outside Jekyll sites. + + +[[custom-tags]] +== Custom Tags + +Not all custom tags are always available, but the following are generally available in DocOps Lab projects: + +.Table of custom tags, their souurce gems, and usage notes. +[cols="1,1,4",options="header"] +|=== +| Tag Name | Source Gem | Usage Notes + +| `include` +| jekyll +| Transcludes the content of another file, with optional variable arguments going in but no transcendent affect on the context. + +| `embed` +| asciisourcerer +| Transcludes another file, with full pass-through of variables (no arguments needed) with full write access to the context (can add/change variables permanently). + +|=== + + +[[liquid-filters]] +== Liquid Filters + +A canonical listing of custom filters is available in the AsciiSourcerer gem at link:https://github.com/DocOps/asciisourcerer/blob/main/lib/specs/data/liquid-filters.yml[`lib/specs/data/liquid-filters.yml`]. + +The full compendium includes _all_ filters available in Liquid 5 and Jekyll 4, even though officially Jekyll and AsciiSourcerer standardize on Liquid 4. +Shopify's novel Liquid 5 filters are hard-coded in AsciiSourcerer. + +[IMPORTANT] +All other Shopify-sourced filters are conveyed directly from their Liquid 4.0.4 versions. + +Where official Jekyll filters override their Liquid 4 counterparts, the Jekyll versions are used. +Where an AsciiSourcerer filter overrides either upstream counterpart, the AsciiSourcerer version is used. + +[[custom-filters]] +=== Custom Filters + +ifndef::audience-agent[] +include::../partials/built/liquid-filters-by-source.adoc[tags="asciisourcerer"] +endif::[] +ifdef::audience-agent[] +See `.agent/docs/topics/liquid-filters-reference.md` for a listing of filters available to projects with AsciiSourcerer available. +See the section starting at `// tag::asciisourcerer[]` for custom filters available downstream of AsciiSourcerer. +endif::[] + +[[liquid-syntax-styles]] +== Liquid Syntax Styles + +DocOps Lab contributions should standardize around certain Liquid syntax conventions, namely around indentation and integration with various markup languages we generate from templates. + +[[html-rendering-style]] +=== HTML Rendering Style + +HTML is by far the most common output target for Liquid templates, but DocOps Lab house style prefers a certain form of indentation. + +Our preference is to indent the Liquid tags in cadence with the surrounding HTML tags, so that the Liquid tags are visually aligned with the HTML they are generating. + +[source,html] +---- +{% for item in items %} +
+

{{ item.title }}

+

+ {{ item.description }} + {% if item.link %} +


+ Read more + {% endif %} +

+
+{% endfor %} +---- + +[[asciidoc-rendering-style]] +=== AsciiDoc Rendering Style + +Because AsciiDoc does not involve much indentation, Liquid syntax meant to render AsciiDoc output is a little awkward. + +Maintain left-flush, un-indented Liquid tags, but indent the tag internals to match the intended AsciiDoc output. + +[source,asciidoc] +---- +{%- for item in items %} +{{ item.title }};; +{{ item.description }} +{%- if item.link %} +Link:;;; {{ item.link }} +{%- endif %} +{% endfor %} +---- + +Use left-side whitespace control (`{%-`) to avoid extra blank lines in the output, but skip it on typical `{% endfor %}` tags to ensure a blank line between iterations where it matters. + +[[yaml-rendering-style]] +=== YAML Rendering Style + +YAML pre-processing with Liquid can be fairly straightforward. +YAML is a great target for Liquid, even though indentation and whitespace control matter more than for HTML or AsciiDoc. + +Intertwine Liquid on its own indentation scale and it should map to the YAML fairly well. + +[source,yaml] +---- +{%- for item in items %} +- title: {{ item.title }} + description: {{ item.description }} + {%- if item.link %} + link: {{ item.link }} + {%- endif %} +{% endfor %} +---- + + +[[invocation]] +== Means of Invocation + +If you are developing a Ruby gem or app in the DocOps Lab ecosystem, the basic Jekyll/Liquid engine is invoked via the `asciisourcerer` gem. + +[source,ruby] +---- +require 'asciisourcerer' + +Sourcerer::Rendering.render_outputs([ + { + template: 'templates/release-notes.liquid', + data: 'data/release.yml', + out: 'build/docs/release-notes.md', + key: 'release', + attrs: 'README.adoc', + engine: 'liquid' + }, + { + converter: 'MyProject::JsonRenderer', + data: 'data/release.yml', + out: 'build/api/release.json' + } +]) +---- + +Direct invocation of Liquid rendering is also possible, but the above method is recommended for most use cases in DocOps Lab projects, as it provides a consistent user experience in terms of availability and behavior of tags and fliters. + +// end::content[] \ No newline at end of file diff --git a/_docs/reference/schemagraphy-sgyml.adoc b/_docs/reference/schemagraphy-sgyml.adoc new file mode 100644 index 00000000..44b6b8f3 --- /dev/null +++ b/_docs/reference/schemagraphy-sgyml.adoc @@ -0,0 +1,56 @@ +--- +title: SchemaGraphy and SGYML in DocOps Lab Development +docs-group: technical +description: "Using in-house tools to load YAML/SGYML in DocOps Lab projects." +order: 81 +--- +include::../_local_settings.adoc[] += SchemaGraphy and SGYML in DocOps Lab Development + +The SchemaGraphy gem provides enhanced means of loading and working with YAML and the nascent format SGYML (SchemaGraphy YAML-based modeling language). + +The extension of YAML is limited to this day, but the SchemaGraphy gem provides a convenient way to load and work with these files in Ruby. + + +[[loading-yaml-sgyml]] +== Loading YAML/SGYML + +DocOps Lab projects often require loading YAML or SGYML data files for configuration, content, or other purposes. +The `schemagraphy` gem provides a convenient way to load and work with these files in Ruby. + +// tag::loading-yaml[] +There are two types of basic YAML loaders in SchemaGraphy, differing around whether you need to resolve AsciiDoc attributes in the content. + +`SchemaGraphy::Loader.load_yaml_with_tags`:: +This method loads a YAML file while preserving any custom tags (e.g., `!sometag`, `!somenamespace:anothertag`). +Custom tags are attached to the data structure, allowing for later processing based on those tags. ++ +The resulting data structure will have the original string value along with a `__tag__` key that contains the normalized tag name (without `!` or namespace). + +`SchemaGraphy::Loader.load_yaml_with_attributes`:: +This method loads a YAML file and resolves AsciiDoc attribute references like `\{attribute_name}`. +It first loads the YAML with tags and then resolves any attribute references using the provided attributes. +// end::loading-yaml[] + + +[[yaml-tags]] +== De-tagging and Normalization + +When using either loading method, the resulting data structure may include both the original value and a `__tag__` key for any tagged values. +When you want to work with the original value without the tag metadata, you can use the `SchemaGraphy::TagUtils.detag` method to extract just the value. + +This is useful when you want to ignore the tags and work with the raw data. + +To ensure you are working with the original value without the tag metadata, you can use the `SchemaGraphy::TagUtils.detag` method: + +// tag::detagging-example[] +[source,ruby] +---- +require 'schemagraphy' + +yaml_data = SchemaGraphy::Loader.load_yaml_with_tags('path/to/file.yaml') +original_value = SchemaGraphy::TagUtils.detag(yaml_data['some_key']) +puts original_value +---- + +// end::detagging-example[] \ No newline at end of file From d32c2a9b4546a14bfa7e6fc75d54b403bd75ebdd Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Wed, 9 Sep 2026 01:07:44 -0400 Subject: [PATCH 26/30] chore(labdev): Upgrade to published asciisourcerer 0.5.0 Both Gemfiles and gemspec updated, replacing relative dev refs. --- Gemfile | 2 +- Gemfile.lock | 26 ++++++++++-------------- gems/docopslab-dev/Gemfile | 2 +- gems/docopslab-dev/Gemfile.lock | 26 ++++++++++-------------- gems/docopslab-dev/docopslab-dev.gemspec | 2 +- 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/Gemfile b/Gemfile index c411a6a3..bbf213e9 100644 --- a/Gemfile +++ b/Gemfile @@ -12,7 +12,7 @@ gem 'reverse_markdown' gem 'rubyzip', '~> 2.3' # For Vale package building gem 'sass' # DocOps Lab -gem 'asciisourcerer', path: '../asciisourcerer' +gem 'asciisourcerer' gem 'schemagraphy' group :jekyll_plugins do diff --git a/Gemfile.lock b/Gemfile.lock index 2ed7a049..e34d048a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,22 +1,9 @@ -PATH - remote: ../asciisourcerer - specs: - asciisourcerer (0.5.0) - asciidoctor (~> 2.0) - asciidoctor-html5s (~> 0.5) - jekyll (~> 4.4) - jekyll-asciidoc (~> 3.0) - kramdown-asciidoc (~> 2.1) - liquid (~> 4.0) - reverse_markdown (~> 2.1) - yard (~> 0.9) - PATH remote: gems/docopslab-dev specs: docopslab-dev (0.4.0) asciidoctor (~> 2.0) - asciisourcerer (~> 0.4) + asciisourcerer (~> 0.5) brakeman (~> 7.1) bundler-audit (~> 0.9) debride (~> 1.13) @@ -45,6 +32,15 @@ GEM asciidoctor-html5s (0.5.1) asciidoctor (>= 1.5.7, < 3.0) thread_safe (~> 0.3.4) + asciisourcerer (0.5.0) + asciidoctor (~> 2.0) + asciidoctor-html5s (~> 0.5) + jekyll (~> 4.4) + jekyll-asciidoc (~> 3.0) + kramdown-asciidoc (~> 2.1) + liquid (~> 4.0) + reverse_markdown (~> 2.1) + yard (~> 0.9) ast (2.4.3) async (2.32.0) console (~> 1.29) @@ -404,7 +400,7 @@ PLATFORMS x86_64-linux DEPENDENCIES - asciisourcerer! + asciisourcerer colorize (~> 1.1) docopslab-dev! feedjira (~> 3.2) diff --git a/gems/docopslab-dev/Gemfile b/gems/docopslab-dev/Gemfile index 19f22ecf..76803852 100644 --- a/gems/docopslab-dev/Gemfile +++ b/gems/docopslab-dev/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' -gem 'asciisourcerer', path: '../../../asciisourcerer' +gem 'asciisourcerer' gemspec diff --git a/gems/docopslab-dev/Gemfile.lock b/gems/docopslab-dev/Gemfile.lock index 943a26c1..8634e20b 100644 --- a/gems/docopslab-dev/Gemfile.lock +++ b/gems/docopslab-dev/Gemfile.lock @@ -1,22 +1,9 @@ -PATH - remote: ../../../asciisourcerer - specs: - asciisourcerer (0.5.0) - asciidoctor (~> 2.0) - asciidoctor-html5s (~> 0.5) - jekyll (~> 4.4) - jekyll-asciidoc (~> 3.0) - kramdown-asciidoc (~> 2.1) - liquid (~> 4.0) - reverse_markdown (~> 2.1) - yard (~> 0.9) - PATH remote: . specs: docopslab-dev (0.4.0) asciidoctor (~> 2.0) - asciisourcerer (~> 0.4) + asciisourcerer (~> 0.5) brakeman (~> 7.1) bundler-audit (~> 0.9) debride (~> 1.13) @@ -45,6 +32,15 @@ GEM asciidoctor-html5s (0.5.1) asciidoctor (>= 1.5.7, < 3.0) thread_safe (~> 0.3.4) + asciisourcerer (0.5.0) + asciidoctor (~> 2.0) + asciidoctor-html5s (~> 0.5) + jekyll (~> 4.4) + jekyll-asciidoc (~> 3.0) + kramdown-asciidoc (~> 2.1) + liquid (~> 4.0) + reverse_markdown (~> 2.1) + yard (~> 0.9) ast (2.4.3) async (2.32.1) console (~> 1.29) @@ -373,7 +369,7 @@ PLATFORMS x86_64-linux DEPENDENCIES - asciisourcerer! + asciisourcerer docopslab-dev! rspec (~> 3.0) diff --git a/gems/docopslab-dev/docopslab-dev.gemspec b/gems/docopslab-dev/docopslab-dev.gemspec index 7000f060..dcd45639 100644 --- a/gems/docopslab-dev/docopslab-dev.gemspec +++ b/gems/docopslab-dev/docopslab-dev.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'yaml', '~> 0.2' # Code quality and linting - spec.add_dependency 'asciisourcerer', '~> 0.4' + spec.add_dependency 'asciisourcerer', '~> 0.5' spec.add_dependency 'debride', '~> 1.13' spec.add_dependency 'fasterer', '~> 0.11' spec.add_dependency 'flog', '~> 4.8' From 121fdb69672df3bafe661c65c08cefb96b35317b Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Wed, 9 Sep 2026 02:13:20 -0400 Subject: [PATCH 27/30] edit(docs): Fix typos and heading spacing found by lint pass --- README.adoc | 1 + _docs/reference/liquid-templating.adoc | 5 +++-- gems/docopslab-dev/README.adoc | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.adoc b/README.adoc index 35e4b4d2..677381dd 100644 --- a/README.adoc +++ b/README.adoc @@ -601,6 +601,7 @@ Downstream projects running `bundle exec rake labdev:sync:docs` read the local c Use `gemdo:push:library:docs:local` before running `bundle exec rake labdev:sync:docs` in a local downstream checkout such as AsciiSourcerer. This avoids testing against a stale `~/.cache/docopslab/dev/library/current/` copy. + [[troubleshooting]] == Troubleshooting diff --git a/_docs/reference/liquid-templating.adoc b/_docs/reference/liquid-templating.adoc index 7755974a..8cb954e8 100644 --- a/_docs/reference/liquid-templating.adoc +++ b/_docs/reference/liquid-templating.adoc @@ -22,7 +22,7 @@ This means all Jekyll's filters and its special `include` tag are available with Not all custom tags are always available, but the following are generally available in DocOps Lab projects: -.Table of custom tags, their souurce gems, and usage notes. +.Table of custom tags, their source gems, and usage notes. [cols="1,1,4",options="header"] |=== | Tag Name | Source Gem | Usage Notes @@ -63,6 +63,7 @@ See `.agent/docs/topics/liquid-filters-reference.md` for a listing of filters av See the section starting at `// tag::asciisourcerer[]` for custom filters available downstream of AsciiSourcerer. endif::[] + [[liquid-syntax-styles]] == Liquid Syntax Styles @@ -157,6 +158,6 @@ Sourcerer::Rendering.render_outputs([ ]) ---- -Direct invocation of Liquid rendering is also possible, but the above method is recommended for most use cases in DocOps Lab projects, as it provides a consistent user experience in terms of availability and behavior of tags and fliters. +Direct invocation of Liquid rendering is also possible, but the above method is recommended for most use cases in DocOps Lab projects, as it provides a consistent user experience in terms of availability and behavior of tags and filters. // end::content[] \ No newline at end of file diff --git a/gems/docopslab-dev/README.adoc b/gems/docopslab-dev/README.adoc index d52cbdcb..15466c83 100644 --- a/gems/docopslab-dev/README.adoc +++ b/gems/docopslab-dev/README.adoc @@ -546,7 +546,7 @@ Commit convention overrides are mapping-based. Local projects can add or overwrite keys under `rules`, `conventions.types`, and `conventions.scopes`. Set a key to `null` in the local file to drop an inherited convention. -`git-lint` 6.x expects an XDG config path named `git-lint/configuration.yml`. +Version 6.x of `git-lint` expects an XDG config path named `git-lint/configuration.yml`. The `labdev` wrappers bridge `.config/git-lint.yml` into that path at runtime, so use the rake tasks instead of invoking `git-lint` directly. The distributed `commit-msg` hook runs `labdev:lint:commit` against the unsaved commit message. From edac2b686ee078688003136c608db15589b83c35 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Wed, 9 Sep 2026 02:24:14 -0400 Subject: [PATCH 28/30] fix(labdev): Exempt merge commits from git-lint subject conventions CI's git-lint job failed on the "Merge branch 'main' into dev/0.4" commit: its git-generated subject can never match our `[scope]: Subject` pattern, and git-lint itself has no built-in way to skip merge commits. labdev:lint:commits now filters merge commits (by parent count) out of the target list before invoking git-lint, and skips the check entirely if nothing but merges remain. --- gems/docopslab-dev/lib/docopslab/dev/linters.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/gems/docopslab-dev/lib/docopslab/dev/linters.rb b/gems/docopslab-dev/lib/docopslab/dev/linters.rb index 1488b455..beabd7b1 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/linters.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/linters.rb @@ -198,7 +198,12 @@ def run_git_lint context, target=nil, opts_string='' cmd = %w[bundle exec git-lint analyze] if target - cmd += ['--commit', target] + commits = target.split(',').reject { |sha| merge_commit?(sha) } + if commits.empty? + puts 'ℹ️ All targeted commits are merge commits (exempt from subject conventions); nothing to check.' + return true + end + cmd += ['--commit', commits.join(',')] else cmd << '--branch' end @@ -217,6 +222,14 @@ def run_git_lint context, target=nil, opts_string='' success end + def merge_commit? sha + out, status = Open3.capture2('git', 'rev-list', '--no-walk', '--parents', sha) + return false unless status.success? + + # First token is the commit itself; anything beyond one remaining token is a second parent. + out.strip.split.size > 2 + end + def run_git_lint_hook context, message_file puts "🧾 Checking commit message: #{message_file}" context.generate_git_lint_config if context.respond_to?(:generate_git_lint_config) From c7e5d8214cb1e2279ce9c8f4923756af25fe56e5 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Wed, 9 Sep 2026 02:49:59 -0400 Subject: [PATCH 29/30] fix(labdev): Auto-fetch the library before resolving config-packs The labdev:sync:configs task called Library.resolve directly, which only reads whatever's already in the host cache; it never fetched. On a runner with no prior cache (any first-time CI run), resolution silently failed and generate_git_lint_config fell back to git-lint's raw upstream defaults instead of our commit conventions, with no visible error. Other tools' final configs are committed to git directly, so they degrade gracefully; git-lint.yml is generated-only and has no such fallback. The sync_config_files method now calls Library.ensure_available! first (skipped in offline mode), which fetches from the labdev-library branch when the cache is missing, or raises a clear, visible error instead of silently proceeding with wrong defaults. --- gems/docopslab-dev/lib/docopslab/dev/sync_ops.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gems/docopslab-dev/lib/docopslab/dev/sync_ops.rb b/gems/docopslab-dev/lib/docopslab/dev/sync_ops.rb index 1f93ef97..3f0a9e69 100644 --- a/gems/docopslab-dev/lib/docopslab/dev/sync_ops.rb +++ b/gems/docopslab-dev/lib/docopslab/dev/sync_ops.rb @@ -281,6 +281,15 @@ def sync_config_files context, tool_filter: :all, offline: false return false end + unless offline + begin + Library.ensure_available! + rescue StandardError => e + puts "❌ #{e.message}" + return false + end + end + config_packs_root = Library.resolve('config-packs') unless config_packs_root && Dir.exist?(config_packs_root) puts '❌ config-packs not found in library; run `labdev:sync:library` to fetch.' From 0dcf730293c8736a62d198a64c6beebd2e92cde5 Mon Sep 17 00:00:00 2001 From: Brian Dominick Date: Wed, 9 Sep 2026 03:08:56 -0400 Subject: [PATCH 30/30] chore(auto+docs): Remove git-lint checks from CI --- .github/workflows/qa.yml | 40 +--------------------------------- gems/docopslab-dev/README.adoc | 5 +++-- 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index 1e43331b..e79300d2 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -35,10 +35,6 @@ on: description: "Skip HTML-Proofer link checking" type: boolean default: false - skip_git_lint: - description: "Skip git-lint commit message checking" - type: boolean - default: false jobs: config-sync: @@ -96,31 +92,6 @@ jobs: bundle exec rake labdev:lint:ruby fi - git-lint: - runs-on: ubuntu-latest - needs: config-sync - timeout-minutes: 5 - if: ${{ !inputs.skip_git_lint }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: '3.2' - bundler-cache: ${{ inputs.enable_cache }} - - - name: Sync configurations - run: bundle exec rake labdev:sync:configs - - - name: Run git-lint - run: | - COMMIT_SHA="${{ github.event.pull_request.head.sha || github.sha }}" - bundle exec rake "labdev:lint:commits[$COMMIT_SHA]" - vale: runs-on: ubuntu-latest needs: config-sync @@ -189,7 +160,7 @@ jobs: summary: runs-on: ubuntu-latest - needs: [config-sync, rubocop, git-lint, vale, htmlproofer] + needs: [config-sync, rubocop, vale, htmlproofer] if: always() timeout-minutes: 2 steps: @@ -214,15 +185,6 @@ jobs: echo "❌ **RuboCop**: Failed" >> $GITHUB_STEP_SUMMARY fi - # git-lint - if [ "${{ inputs.skip_git_lint }}" = "true" ]; then - echo "⏭️ **git-lint**: Skipped" >> $GITHUB_STEP_SUMMARY - elif [ "${{ needs.git-lint.result }}" = "success" ]; then - echo "✅ **git-lint**: Passed" >> $GITHUB_STEP_SUMMARY - else - echo "❌ **git-lint**: Failed" >> $GITHUB_STEP_SUMMARY - fi - # Vale if [ "${{ inputs.skip_vale }}" = "true" ]; then echo "⏭️ **Vale**: Skipped" >> $GITHUB_STEP_SUMMARY diff --git a/gems/docopslab-dev/README.adoc b/gems/docopslab-dev/README.adoc index 15466c83..ef36914c 100644 --- a/gems/docopslab-dev/README.adoc +++ b/gems/docopslab-dev/README.adoc @@ -528,7 +528,7 @@ For full Vale configuration settings ("`keys`") reference, see the link:https:// [[git-lint]] === git-lint -Git commit message checking for local hooks and CI. +Git commit message checking for local hooks. Convention data:: `.config/.vendor/docopslab/commit-conventions.yml` Project convention overrides:: `.config/commit-conventions.yml` (inherits via `inherit_from`) @@ -550,7 +550,8 @@ Version 6.x of `git-lint` expects an XDG config path named `git-lint/configurati The `labdev` wrappers bridge `.config/git-lint.yml` into that path at runtime, so use the rake tasks instead of invoking `git-lint` directly. The distributed `commit-msg` hook runs `labdev:lint:commit` against the unsaved commit message. -CI should use `labdev:lint:commits` on feature branches or pull request heads. +This is a local, commit-time check by design, not a CI gate: fixing a violation caught after a push means rewriting shared history, which the local hook avoids entirely. +`labdev:lint:commits` remains available for manually linting a branch or arbitrary commit range. [[htmlproofer]] === HTMLProofer