Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .claude/rules/docs-publishing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# 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.

## Two jobs: `docs-build` (always) + `docs` (deploy, main only)

- **`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.

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

```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].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

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.
59 changes: 59 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,62 @@ jobs:
fail_ci_if_error: false
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
# 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 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
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: Deploy to GitHub Pages
# `--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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/

4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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*.
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
4 changes: 4 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{%
include-markdown "../README.md"
rewrite-relative-urls=true
%}
59 changes: 59 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
@@ -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/
Comment thread
wjduenow marked this conversation as resolved.

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
17 changes: 14 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +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 `build` for the wheel_smoke marker (`tests/test_wheel_packaging.py`
# shells out `python -m build --wheel`).
# 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",
Expand All @@ -37,6 +47,7 @@ dev = [
"types-PyYAML>=6,<7",
"pytest-cov>=5.0",
"build>=1.2,<2",
{include-group = "docs"},
]

[project.scripts]
Expand Down
Loading
Loading