From 28a204b8868619430ccce65c225ab3437e6bd3c6 Mon Sep 17 00:00:00 2001 From: wjduenow Date: Tue, 19 May 2026 12:00:22 -0700 Subject: [PATCH 1/2] docs: publish MkDocs Material site to GitHub Pages on push to main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors clauditor's docs-publishing setup. The published site at https://wjduenow.github.io/SignalForge/ redeploys on every push to main; the gh-pages branch is generated, not authored. - `mkdocs.yml`: Material theme with light/dark toggle, search + include-markdown plugins, mermaid via pymdownx.superfences, exclude_docs: research/ (internal analysis stays off the site), edit_uri: edit/dev/docs/ (doc edits land on dev like every other PR; main is the release line). Nav: Home + CLI Reference + Pipeline Stages (manifest/warehouse/safety/draft/prune/grade/diff) + Audits & Sidecars + e2e + coverage. - `docs/index.md`: 4-line include-markdown stub pulling in the root README. Mirrors clauditor verbatim — README stays the canonical authored home doc; site home auto-syncs on every build. - `.github/workflows/ci.yml`: new `docs:` job gated on `github.ref == 'refs/heads/main' && github.event_name == 'push'` with job-scoped `permissions: contents: write` (workflow default stays `contents: read`). Uses pinned `astral-sh/setup-uv@v8.1.0` with `python-version: "3.11"` (matches publish.yml floor); runs `uv run mkdocs gh-deploy --force --no-history`. Deliberately does NOT set `persist-credentials: false` on the checkout step — `gh-deploy` needs the persisted GITHUB_TOKEN to push to gh-pages. - `pyproject.toml`: add `mkdocs-material>=9.0` and `mkdocs-include-markdown-plugin>=6.0` to `[dependency-groups].dev` only. Pip contributors don't need to build docs; this stays a uv-only delta (alongside `build` for wheel_smoke). - `.gitignore`: ignore `site/` (mkdocs build output). - `README.md`: docs badge top-of-file. - `CLAUDE.md` / `CONTRIBUTING.md`: one-liner pointing at the published site and the deploy contract. - `.claude/rules/docs-publishing.md` (new): full deploy contract — trigger rule, include-markdown pattern, edit_uri choice, persist-credentials exception, exclude_docs convention, local build recipe, when to update mkdocs.yml vs the docs themselves, first-time GH Pages setup the maintainer does once. Local validation: `uv run mkdocs build` succeeds (warnings on repo-internal links to plans/super/ and .claude/rules/ are expected and matched in clauditor — those aren't part of the published site). `uv run ruff check .` / `ruff format --check .` clean. 1823/1823 tests pass on 3.13 (6 pre-existing #96 failures unrelated). Post-merge maintainer step (once): Settings → Pages → source = "Deploy from a branch", branch = gh-pages, folder = / (root). The first push to main after this merges lands the gh-pages branch; toggle Pages on right after. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/rules/docs-publishing.md | 65 +++++++++ .github/workflows/ci.yml | 35 +++++ .gitignore | 3 + CLAUDE.md | 4 + CONTRIBUTING.md | 2 + README.md | 2 +- docs/index.md | 4 + mkdocs.yml | 59 +++++++++ pyproject.toml | 10 +- uv.lock | 220 +++++++++++++++++++++++++++++++ 10 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 .claude/rules/docs-publishing.md create mode 100644 docs/index.md create mode 100644 mkdocs.yml diff --git a/.claude/rules/docs-publishing.md b/.claude/rules/docs-publishing.md new file mode 100644 index 00000000..4c98a85f --- /dev/null +++ b/.claude/rules/docs-publishing.md @@ -0,0 +1,65 @@ +# Docs publishing (MkDocs Material → GitHub Pages) + +The published documentation site at https://wjduenow.github.io/SignalForge/ is built by MkDocs Material on every push to `main` and pushed to the `gh-pages` branch. The setup mirrors clauditor's docs-publishing pattern. + +## Trigger: push to `main` only + +The `docs:` job in `.github/workflows/ci.yml` is gated by `if: github.ref == 'refs/heads/main' && github.event_name == 'push'`. Dev pushes do NOT redeploy — the published site reflects the released line, not every in-flight iteration. The dev → main release merge IS the publish event. + +PRs against `main` do not trigger the docs job either; only the post-merge push does. This is deliberate: a PR's mkdocs-build only matters when it lands. + +## `docs/index.md` is a 4-line include-markdown stub + +```markdown +{% + include-markdown "../README.md" + rewrite-relative-urls=true +%} +``` + +The root `README.md` stays the canonical authored "home" doc. The site's home page is auto-synced on every build via the `mkdocs-include-markdown-plugin`. Don't author a separate `docs/index.md` body — drift between the README and the site is exactly what this stub exists to prevent. The `rewrite-relative-urls=true` flag fixes relative links so they keep working when the README content renders under `/index.html` instead of repo-root. + +When the README adds a new section, the site picks it up next push to main. No mkdocs.yml edit needed unless you want the section in the top-nav. + +## `exclude_docs: research/` keeps internal analysis off the published site + +`docs/research/` contains internal analysis (dbt-pain-deep-dive, dbt-research-index, etc.) used to drive product direction — not user-facing. The `exclude_docs:` block in `mkdocs.yml` keeps them out of the build. Adding a new research doc requires no mkdocs.yml change; adding a new user-facing doc DOES require a `nav:` entry. + +## `edit_uri: edit/dev/docs/` — edits land on dev, not main + +The "Edit this page" link on each rendered doc page targets the `dev` branch, not `main`. Doc edits land on dev (where every other PR work happens) and reach the published site after the next dev → main release. This keeps the doc-edit workflow aligned with the code-edit workflow; the alternative (`edit/main/docs/`) would bypass dev review. + +## `persist-credentials` is the inverse of the lint-test/publish job + +`actions/checkout` in the docs job deliberately does NOT set `persist-credentials: false`. `mkdocs gh-deploy` issues a real `git push` to the `gh-pages` branch; it relies on the persisted GITHUB_TOKEN in the runner's git config. The token's scope is bounded by `permissions.contents: write` at the job level (the workflow default stays `contents: read`), so this is the principle of least privilege at the *job* level even though the *step* persists credentials. The runner's post-job cleanup clears the token; this is the recommended pattern for the rare workflow that legitimately needs to push to a branch. + +## Build the site locally + +```bash +uv sync --dev +uv run mkdocs serve # localhost:8000 with hot reload +uv run mkdocs build # writes site/ — same recipe CI uses +``` + +Do NOT use `--strict` locally. The ops docs link to `plans/super/*.md`, `.claude/rules/*.md`, and other repo-internal paths that are deliberately not part of the published site; `--strict` rejects them as broken links. The non-strict build still emits the warnings to stdout — useful as a sanity check that the doc set is internally consistent, but not a CI gate. + +## When to update mkdocs.yml vs the docs themselves + +- **New ops doc under `docs/`** (e.g. `docs/foo-ops.md`) → add a `nav:` entry under "Pipeline Stages" or a new section. +- **New research doc under `docs/research/`** → no mkdocs.yml change (covered by `exclude_docs:`). +- **Renaming an existing ops doc** → update `mkdocs.yml` `nav:` in the same commit; otherwise the nav link 404s. +- **Theme tweaks** → adjust `theme:` block. Keep palette toggle (DEC: accessibility floor). +- **New plugin** → add to `[dependency-groups].dev` in `pyproject.toml` (uv-only — pip contributors don't need to build docs) AND `plugins:` in mkdocs.yml. + +## First-time setup the maintainer does once + +After the first deploy lands a `gh-pages` branch on the repo, enable GitHub Pages: + +1. **Settings → Pages → Build and deployment**: source = "Deploy from a branch", branch = `gh-pages`, folder = `/ (root)`. +2. The first publish takes ~1 minute to propagate. After that, every push to `main` triggers a redeploy within ~30 seconds of CI completion. + +The deploy is idempotent — `--force` on `mkdocs gh-deploy` overwrites the prior gh-pages commit (the site is generated, not authored). `--no-history` keeps the gh-pages branch shallow so the repo stays small. + +## Reference + +`mkdocs.yml` — current site config. `.github/workflows/ci.yml` § `docs:` — the deploy job. `docs/index.md` — the include-markdown stub. clauditor's docs-publishing setup — the precedent this mirrors. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e86b28a1..aa818505 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,3 +65,38 @@ jobs: fail_ci_if_error: false env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + docs: + # Publish the MkDocs site to GitHub Pages. Triggers only on push to + # main — `main` is the released line; the published site reflects + # the last release, not every dev iteration. See + # `.claude/rules/docs-publishing.md` for the full deploy contract. + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + # Only the docs job needs write access (to push the built site to + # the `gh-pages` branch). The workflow default `contents: read` + # covers every other job. + permissions: + contents: write + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + # `mkdocs gh-deploy` issues a real `git push` to the `gh-pages` + # branch — it relies on the persisted GITHUB_TOKEN in the + # runner's git config. Do NOT set `persist-credentials: false` + # here; doing so breaks the deploy. The token is post-job-cleaned + # by the runner; the scope is bounded by `permissions.contents: + # write` above. + - name: Set up uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + with: + # Same floor as publish.yml — keeps the doc-build interpreter + # deterministic across runs. + python-version: "3.11" + - name: Sync dev dependencies + run: uv sync --dev + - name: Deploy to GitHub Pages + # `--force` overwrites the prior gh-pages commit (the site is + # generated, not authored). `--no-history` keeps the gh-pages + # branch shallow so the repo stays small. + run: uv run mkdocs gh-deploy --force --no-history diff --git a/.gitignore b/.gitignore index a74f133e..61699b22 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,6 @@ work-*/ # Windows NTFS Alternate Data Streams (created when files cross from Windows -> WSL) *:Zone.Identifier +# MkDocs build output (CI publishes to gh-pages branch; local builds are throwaway) +site/ + diff --git a/CLAUDE.md b/CLAUDE.md index 8bd57961..6763c9a2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -74,6 +74,10 @@ uv sync --dev && uv run ruff check . && uv run ruff format --check . && uv run p The repo is uv-managed (see `.claude/rules/python-build.md`). `pip install -e ".[dev]"` still works for contributors without uv (the `[project.optional-dependencies].dev` extra is kept in sync with `[dependency-groups].dev`), but uv is the default; `uv.lock` is committed. +## Documentation site + +Published at https://wjduenow.github.io/SignalForge/ — MkDocs Material build, redeploy on every push to `main` via the `docs:` job in `.github/workflows/ci.yml`. The site's home page is the root `README.md` rendered via `mkdocs-include-markdown-plugin`; the per-stage ops docs under `docs/*-ops.md` ship as the nav. Internal research under `docs/research/` is excluded. See `.claude/rules/docs-publishing.md` for the full deploy contract. + ## What SignalForge is A CLI that drafts dbt `schema.yml`, tests, and docs with an LLM, then **prunes** the candidates against real warehouse data so only signal-bearing artifacts ship. The differentiator vs. dbt Copilot / dbt-codegen / DinoAI / datapilot is the prune step — competitors generate; SignalForge generates *and grades*. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 76a25713..3b04a18f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,6 +30,8 @@ uv run ruff check . && uv run ruff format --check . && uv run pyright && uv run **Coverage:** see [`docs/codecov-ops.md`](docs/codecov-ops.md) for Codecov setup, badge interpretation, and threshold bumps. +**Docs:** the [published docs site](https://wjduenow.github.io/SignalForge/) is built by MkDocs Material on every push to `main`. Edits to `docs/*.md` and `README.md` land on `dev` like any other PR; the published site picks them up on the next `dev → main` merge. Local preview with `uv run mkdocs serve`. See [`.claude/rules/docs-publishing.md`](.claude/rules/docs-publishing.md) for the full deploy contract. + ## Pre-release coverage audit The default `pytest` run — and therefore the coverage badge — measures only the diff --git a/README.md b/README.md index 3b19c48d..e3fc456d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![codecov](https://codecov.io/gh/wjduenow/SignalForge/branch/dev/graph/badge.svg)](https://codecov.io/gh/wjduenow/SignalForge) +[![codecov](https://codecov.io/gh/wjduenow/SignalForge/branch/dev/graph/badge.svg)](https://codecov.io/gh/wjduenow/SignalForge) [![docs](https://img.shields.io/badge/docs-signalforge-blue?logo=materialformkdocs)](https://wjduenow.github.io/SignalForge/) # SignalForge diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..a7d74ae3 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,4 @@ +{% + include-markdown "../README.md" + rewrite-relative-urls=true +%} diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..f08c4a42 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,59 @@ +site_name: SignalForge +site_description: Draft dbt schema.yml, tests, and docs with an LLM, then prune candidates against real warehouse data so only signal-bearing artifacts ship. +site_url: https://wjduenow.github.io/SignalForge/ +repo_url: https://github.com/wjduenow/SignalForge +repo_name: wjduenow/SignalForge +# Edits target dev — main is the release line; doc PRs land on dev like every +# other change. The published site reflects main (the docs job publishes on +# push to main only), so an edit-now-publish-later workflow follows the +# regular dev → main release cadence. +edit_uri: edit/dev/docs/ + +theme: + name: material + palette: + - scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to light mode + features: + - navigation.tabs + - navigation.top + - search.highlight + - content.code.copy + +# Research docs are internal analysis (dbt-pain-deep-dive, dbt-research-index, +# etc.) — not user-facing. Excluded from the published site. +exclude_docs: | + research/ + +plugins: + - search + - include-markdown + +markdown_extensions: + - md_in_html + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + +nav: + - Home: index.md + - CLI Reference: cli-ops.md + - Pipeline Stages: + - Manifest Loader: manifest-loader-ops.md + - Warehouse Adapter: warehouse-adapter-ops.md + - Safety Layer: safety-ops.md + - LLM Drafter: draft-ops.md + - Prune Engine: prune-ops.md + - Quality Grader: grade-ops.md + - Diff Renderer: diff-ops.md + - Audits & Sidecars: audits.md + - End-to-End Smoke Test: e2e-smoke-test.md + - Coverage Setup: codecov-ops.md diff --git a/pyproject.toml b/pyproject.toml index 3b68d06c..992833b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,8 +27,12 @@ dev = ["ruff", "pyright==1.1.409", "pytest", "dbt-core>=1.8,<2", "types-PyYAML>= [dependency-groups] # uv-native dev group (PEP 735). Mirrors `[project.optional-dependencies].dev` -# plus `build` for the wheel_smoke marker (`tests/test_wheel_packaging.py` -# shells out `python -m build --wheel`). +# plus a few uv-only extras: +# - `build` powers the wheel_smoke marker (`tests/test_wheel_packaging.py`). +# - `mkdocs-material` + `mkdocs-include-markdown-plugin` build the GitHub +# Pages site (`.github/workflows/ci.yml` docs job). +# The mkdocs deps are deliberately uv-only — the docs job runs under uv; +# pip contributors don't need them. dev = [ "ruff", "pyright==1.1.409", @@ -37,6 +41,8 @@ dev = [ "types-PyYAML>=6,<7", "pytest-cov>=5.0", "build>=1.2,<2", + "mkdocs-material>=9.0", + "mkdocs-include-markdown-plugin>=6.0", ] [project.scripts] diff --git a/uv.lock b/uv.lock index f47263e7..d5947f6c 100644 --- a/uv.lock +++ b/uv.lock @@ -84,6 +84,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, ] +[[package]] +name = "backrefs" +version = "7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/a7dd63622beef68cc0d3c3c36d472e143dd95443d5ebf14cd1a5b4dfbf11/backrefs-7.0.tar.gz", hash = "sha256:4989bb9e1e99eb23647c7160ed51fb21d0b41b5d200f2d3017da41e023097e82", size = 7012453, upload-time = "2026-04-28T16:28:04.215Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/39/39a31d7eae729ea14ed10c3ccef79371197177b9355a86cb3525709e8502/backrefs-7.0-py310-none-any.whl", hash = "sha256:b57cd227ea556b0aed3dc9b8da4628db4eabc0402c6d7fcfc69283a93955f7e9", size = 380824, upload-time = "2026-04-28T16:27:55.647Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl", hash = "sha256:a0fa7360c63509e9e077e174ef4e6d3c21c8db94189b9d957289ae6d794b9475", size = 392626, upload-time = "2026-04-28T16:27:57.42Z" }, + { url = "https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl", hash = "sha256:ca42ce6a49ace3d75684dfa9937f3373902a63284ecb385ce36d15e5dcb41c12", size = 398537, upload-time = "2026-04-28T16:27:58.913Z" }, + { url = "https://files.pythonhosted.org/packages/00/bb/90ba423612b6aa0adccc6b1874bcd4a9b44b660c0c16f346611e00f64ac3/backrefs-7.0-py313-none-any.whl", hash = "sha256:f2c52955d631b9e1ac4cd56209f0a3a946d592b98e7790e77699339ae01c102a", size = 400491, upload-time = "2026-04-28T16:28:00.928Z" }, + { url = "https://files.pythonhosted.org/packages/3e/5c/fb93d3092640a24dfb7bd7727a24016d7c01774ca013e60efd3f683c8002/backrefs-7.0-py314-none-any.whl", hash = "sha256:a6448b28180e3ca01134c9cf09dcebafad8531072e09903c5451748a05f24bc9", size = 412349, upload-time = "2026-04-28T16:28:02.412Z" }, +] + +[[package]] +name = "bracex" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/9a/fec38644694abfaaeca2798b58e276a8e61de49e2e37494ace423395febc/bracex-2.6.tar.gz", hash = "sha256:98f1347cd77e22ee8d967a30ad4e310b233f7754dbf31ff3fceb76145ba47dc7", size = 26642, upload-time = "2025-06-22T19:12:31.254Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/2a/9186535ce58db529927f6cf5990a849aa9e052eea3e2cfefe20b9e1802da/bracex-2.6-py3-none-any.whl", hash = "sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952", size = 11508, upload-time = "2025-06-22T19:12:29.781Z" }, +] + [[package]] name = "build" version = "1.5.0" @@ -619,6 +641,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a7/5f/ed01f9a3cdffbd5a008556fc7b2a08ddb1cc6ace7effa7340604b1d16699/docstring_parser-0.18.0-py3-none-any.whl", hash = "sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b", size = 22484, upload-time = "2026-04-14T04:09:18.638Z" }, ] +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, +] + [[package]] name = "google-api-core" version = "2.30.3" @@ -1018,6 +1052,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1a/d4/c4dcb02ed11f8884e169b3350fc40aa4c08edf8bed77a8f0f267542e6452/leather-0.4.1-py3-none-any.whl", hash = "sha256:ec61cba1ca3ccb96ed90e38b116fc58757d97d352171006b3288c47ce3fbd183", size = 30340, upload-time = "2025-12-15T19:01:40.823Z" }, ] +[[package]] +name = "markdown" +version = "3.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, +] + [[package]] name = "markupsafe" version = "3.0.3" @@ -1109,6 +1152,97 @@ msgpack = [ { name = "msgpack" }, ] +[[package]] +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mergedeep" }, + { name = "platformdirs" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047, upload-time = "2026-03-10T02:46:33.632Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555, upload-time = "2026-03-10T02:46:32.256Z" }, +] + +[[package]] +name = "mkdocs-include-markdown-plugin" +version = "7.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mkdocs" }, + { name = "wcmatch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/2b/c788fc5c39ccba342eb9067be637327faebbc0e47da41ea79dc2526e693a/mkdocs_include_markdown_plugin-7.3.0.tar.gz", hash = "sha256:2800126746452e31c2e321bbd43c8190b356e0de353e20cbc16a34a3c3d6796c", size = 25527, upload-time = "2026-05-15T18:16:13.946Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/84/c0109c5991b89cbf028b8597f73fa7bd6a6b092407be2369a354b52737ab/mkdocs_include_markdown_plugin-7.3.0-py3-none-any.whl", hash = "sha256:5b5c99b5d3c9b9ce0114a9e60353bbafb6be53a26c2d3b74ec6b767a7a8e55ca", size = 29675, upload-time = "2026-05-15T18:16:12.654Z" }, +] + +[[package]] +name = "mkdocs-material" +version = "9.7.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "backrefs" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/29/6d2bcf41ae40802c4beda2432396fff97b8456fb496371d1bc7aad6512ec/mkdocs_material-9.7.6.tar.gz", hash = "sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69", size = 4097959, upload-time = "2026-03-19T15:41:58.161Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl", hash = "sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba", size = 9305470, upload-time = "2026-03-19T15:41:55.217Z" }, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, +] + [[package]] name = "more-itertools" version = "10.8.0" @@ -1207,6 +1341,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] +[[package]] +name = "paginate" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, +] + [[package]] name = "parsedatetime" version = "2.6" @@ -1225,6 +1368,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, ] +[[package]] +name = "platformdirs" +version = "4.9.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, +] + [[package]] name = "pluggy" version = "1.6.0" @@ -1417,6 +1569,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, ] +[[package]] +name = "pymdown-extensions" +version = "10.21.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/26/d1015444da4d952a1ca487a236b522eb979766f0295a0bd0c5fc089989a9/pymdown_extensions-10.21.3.tar.gz", hash = "sha256:72cfcf55f07aea0d4af2c4f11dd4e52466ddfb1bb819673146398e0bd3a77354", size = 854140, upload-time = "2026-05-13T12:57:32.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/85/545a951eecc270fcd688288c600017e2050a1aacb56c711d208586d3e470/pymdown_extensions-10.21.3-py3-none-any.whl", hash = "sha256:d7a5d08014fc571e80ca21dd6f854e31f94c489800350564d55d15b3c41e76b6", size = 269002, upload-time = "2026-05-13T12:57:30.296Z" }, +] + [[package]] name = "pyproject-hooks" version = "1.2.0" @@ -1566,6 +1731,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, +] + [[package]] name = "referencing" version = "0.37.0" @@ -1752,6 +1929,8 @@ dev = [ dev = [ { name = "build" }, { name = "dbt-core" }, + { name = "mkdocs-include-markdown-plugin" }, + { name = "mkdocs-material" }, { name = "pyright" }, { name = "pytest" }, { name = "pytest-cov" }, @@ -1778,6 +1957,8 @@ provides-extras = ["dev"] dev = [ { name = "build", specifier = ">=1.2,<2" }, { name = "dbt-core", specifier = ">=1.8,<2" }, + { name = "mkdocs-include-markdown-plugin", specifier = ">=6.0" }, + { name = "mkdocs-material", specifier = ">=9.0" }, { name = "pyright", specifier = "==1.1.409" }, { name = "pytest" }, { name = "pytest-cov", specifier = ">=5.0" }, @@ -1936,6 +2117,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, ] +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393, upload-time = "2024-11-01T14:06:31.756Z" }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392, upload-time = "2024-11-01T14:06:32.99Z" }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019, upload-time = "2024-11-01T14:06:34.963Z" }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +] + +[[package]] +name = "wcmatch" +version = "10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bracex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/3e/c0bdc27cf06f4e47680bd5803a07cb3dfd17de84cde92dd217dcb9e05253/wcmatch-10.1.tar.gz", hash = "sha256:f11f94208c8c8484a16f4f48638a85d771d9513f4ab3f37595978801cb9465af", size = 117421, upload-time = "2025-06-22T19:14:02.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/d8/0d1d2e9d3fabcf5d6840362adcf05f8cf3cd06a73358140c3a97189238ae/wcmatch-10.1-py3-none-any.whl", hash = "sha256:5848ace7dbb0476e5e55ab63c6bbd529745089343427caa5537f230cc01beb8a", size = 39854, upload-time = "2025-06-22T19:14:00.978Z" }, +] + [[package]] name = "zipp" version = "3.23.1" From 7edd8286ac777a76e2f57bc9f38c6c7ee09a780c Mon Sep 17 00:00:00 2001 From: wjduenow Date: Tue, 19 May 2026 16:06:10 -0700 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20address=20PR=20#97=20review=20?= =?UTF-8?q?=E2=80=94=20split=20docs=20dep=20group=20+=20PR-time=20build=20?= =?UTF-8?q?gate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two Copilot suggestions, both real wins: - Extract `[dependency-groups].docs` (mkdocs-material + mkdocs-include-markdown-plugin only). Both CI docs jobs now `uv run --only-group docs ...` so they pull just MkDocs + plugins, not the heavy dev set (dbt-core, pyright, pytest). `dev` includes `docs` via `{include-group = "docs"}` so `uv sync --dev` still gives contributors everything. [thread: ci.yml deploy pulled --dev] - Add a read-only `docs-build` job that runs on every PR + push (`uv run --only-group docs mkdocs build`, no write perms). Catches a broken mkdocs.yml / plugin config / include-markdown syntax error at PR time instead of silently merging and only failing the post-merge gh-pages deploy. [thread: no pre-merge docs build signal] The third thread (Home page "Edit this page" link points at the docs/index.md include stub rather than README.md) is an accepted limitation of the include-markdown pattern — Material has no per-page edit-button hide without a template override, and clauditor's identical setup accepts it. The stub a contributor lands on literally documents where the real content lives. Documented in the review summary; no code change. `.claude/rules/docs-publishing.md` updated: two-job structure, the docs dependency-group split, and the "new plugin goes in docs not dev" convention. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/rules/docs-publishing.md | 13 +++++++---- .github/workflows/ci.yml | 40 +++++++++++++++++++++++++------- pyproject.toml | 23 +++++++++++------- uv.lock | 8 +++++++ 4 files changed, 63 insertions(+), 21 deletions(-) diff --git a/.claude/rules/docs-publishing.md b/.claude/rules/docs-publishing.md index 4c98a85f..b82edc17 100644 --- a/.claude/rules/docs-publishing.md +++ b/.claude/rules/docs-publishing.md @@ -2,11 +2,16 @@ The published documentation site at https://wjduenow.github.io/SignalForge/ is built by MkDocs Material on every push to `main` and pushed to the `gh-pages` branch. The setup mirrors clauditor's docs-publishing pattern. -## Trigger: push to `main` only +## Two jobs: `docs-build` (always) + `docs` (deploy, main only) -The `docs:` job in `.github/workflows/ci.yml` is gated by `if: github.ref == 'refs/heads/main' && github.event_name == 'push'`. Dev pushes do NOT redeploy — the published site reflects the released line, not every in-flight iteration. The dev → main release merge IS the publish event. +- **`docs-build`** runs on every PR and push — a read-only "does the site still build?" gate. It runs `uv run --only-group docs mkdocs build` with NO write permissions. A broken `mkdocs.yml`, plugin config, or include-markdown syntax error fails the PR here instead of silently merging and only breaking the post-merge deploy. +- **`docs`** (deploy) is gated by `if: github.ref == 'refs/heads/main' && github.event_name == 'push'`. Dev pushes do NOT redeploy — the published site reflects the released line, not every in-flight iteration. The dev → main release merge IS the publish event. PRs against `main` don't trigger the deploy job either; only the post-merge push does. -PRs against `main` do not trigger the docs job either; only the post-merge push does. This is deliberate: a PR's mkdocs-build only matters when it lands. +The split matters: without the always-on `docs-build` gate, the deploy job (main-only) is the *first* place a doc-config regression surfaces, which is too late. Keep both. + +## `docs` dependency group is separate from `dev` + +`[dependency-groups].docs` carries ONLY `mkdocs-material` + `mkdocs-include-markdown-plugin`. Both CI docs jobs `uv run --only-group docs ...` so they pull just MkDocs + plugins — not the heavy `dev` set (`dbt-core`, `pyright`, etc.). The `dev` group includes `docs` via `{include-group = "docs"}`, so a single `uv sync --dev` still gives a contributor everything (docs preview included). When adding a new docs-only tool, put it in the `docs` group; when adding a dev tool that isn't needed to build docs, put it in `dev` directly. ## `docs/index.md` is a 4-line include-markdown stub @@ -49,7 +54,7 @@ Do NOT use `--strict` locally. The ops docs link to `plans/super/*.md`, `.claude - **New research doc under `docs/research/`** → no mkdocs.yml change (covered by `exclude_docs:`). - **Renaming an existing ops doc** → update `mkdocs.yml` `nav:` in the same commit; otherwise the nav link 404s. - **Theme tweaks** → adjust `theme:` block. Keep palette toggle (DEC: accessibility floor). -- **New plugin** → add to `[dependency-groups].dev` in `pyproject.toml` (uv-only — pip contributors don't need to build docs) AND `plugins:` in mkdocs.yml. +- **New plugin** → add to `[dependency-groups].docs` in `pyproject.toml` (NOT `dev` — the `docs` group is what the CI jobs sync; `dev` picks it up via `include-group`) AND `plugins:` in mkdocs.yml. ## First-time setup the maintainer does once diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa818505..d155e163 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,6 +66,31 @@ jobs: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + docs-build: + # Read-only "does the site still build?" gate. Runs on every PR and + # push (same triggers as lint-test) so a broken mkdocs.yml / plugin + # config / include-markdown syntax error fails the PR instead of + # silently merging and only breaking the post-merge deploy. No write + # permissions — this never touches gh-pages. + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + # Read-only build; never pushes. Least-privilege. + persist-credentials: false + - name: Set up uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + with: + python-version: "3.11" + - name: Build the docs site + # `--only-group docs` installs JUST MkDocs + plugins, not the + # heavy dev set. Non-strict build: it returns non-zero on real + # config/syntax/plugin errors (the cases this gate exists to + # catch) but tolerates the expected warnings on repo-internal + # links to plans/super/ and .claude/rules/ that aren't published. + run: uv run --only-group docs mkdocs build + docs: # Publish the MkDocs site to GitHub Pages. Triggers only on push to # main — `main` is the released line; the published site reflects @@ -73,8 +98,8 @@ jobs: # `.claude/rules/docs-publishing.md` for the full deploy contract. runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' && github.event_name == 'push' - # Only the docs job needs write access (to push the built site to - # the `gh-pages` branch). The workflow default `contents: read` + # Only the docs deploy job needs write access (to push the built site + # to the `gh-pages` branch). The workflow default `contents: read` # covers every other job. permissions: contents: write @@ -93,10 +118,9 @@ jobs: # Same floor as publish.yml — keeps the doc-build interpreter # deterministic across runs. python-version: "3.11" - - name: Sync dev dependencies - run: uv sync --dev - name: Deploy to GitHub Pages - # `--force` overwrites the prior gh-pages commit (the site is - # generated, not authored). `--no-history` keeps the gh-pages - # branch shallow so the repo stays small. - run: uv run mkdocs gh-deploy --force --no-history + # `--only-group docs` keeps the deploy env minimal (no dbt-core / + # pyright). `--force` overwrites the prior gh-pages commit (the + # site is generated, not authored). `--no-history` keeps the + # gh-pages branch shallow so the repo stays small. + run: uv run --only-group docs mkdocs gh-deploy --force --no-history diff --git a/pyproject.toml b/pyproject.toml index 992833b2..b171270e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,13 +26,19 @@ dependencies = [ dev = ["ruff", "pyright==1.1.409", "pytest", "dbt-core>=1.8,<2", "types-PyYAML>=6,<7", "pytest-cov>=5.0"] [dependency-groups] -# uv-native dev group (PEP 735). Mirrors `[project.optional-dependencies].dev` -# plus a few uv-only extras: -# - `build` powers the wheel_smoke marker (`tests/test_wheel_packaging.py`). -# - `mkdocs-material` + `mkdocs-include-markdown-plugin` build the GitHub -# Pages site (`.github/workflows/ci.yml` docs job). -# The mkdocs deps are deliberately uv-only — the docs job runs under uv; -# pip contributors don't need them. +# uv-native dependency groups (PEP 735). +# +# `docs` is a standalone group so the GitHub Pages build/deploy jobs +# (`.github/workflows/ci.yml`) can `uv sync --only-group docs` and pull in +# JUST MkDocs + plugins — not the heavy `dev` set (dbt-core, pyright, etc.). +# The mkdocs deps are uv-only; pip contributors don't build docs. +docs = [ + "mkdocs-material>=9.0", + "mkdocs-include-markdown-plugin>=6.0", +] +# `dev` mirrors `[project.optional-dependencies].dev` plus `build` (powers the +# wheel_smoke marker in `tests/test_wheel_packaging.py`) and the `docs` group +# (so a single `uv sync --dev` gives a contributor everything, docs included). dev = [ "ruff", "pyright==1.1.409", @@ -41,8 +47,7 @@ dev = [ "types-PyYAML>=6,<7", "pytest-cov>=5.0", "build>=1.2,<2", - "mkdocs-material>=9.0", - "mkdocs-include-markdown-plugin>=6.0", + {include-group = "docs"}, ] [project.scripts] diff --git a/uv.lock b/uv.lock index d5947f6c..f1a7002b 100644 --- a/uv.lock +++ b/uv.lock @@ -1937,6 +1937,10 @@ dev = [ { name = "ruff" }, { name = "types-pyyaml" }, ] +docs = [ + { name = "mkdocs-include-markdown-plugin" }, + { name = "mkdocs-material" }, +] [package.metadata] requires-dist = [ @@ -1965,6 +1969,10 @@ dev = [ { name = "ruff" }, { name = "types-pyyaml", specifier = ">=6,<7" }, ] +docs = [ + { name = "mkdocs-include-markdown-plugin", specifier = ">=6.0" }, + { name = "mkdocs-material", specifier = ">=9.0" }, +] [[package]] name = "six"