docs(ci): add CI/CD flow diagrams (#1062)

## Description

Adds a visual CI/CD flow reference for Headroom so contributors can
quickly understand the gated PR, release, Docker, docs deploy, fork
approval, and manual validation paths. Also updates the release
documentation to match the current release-please release flow instead
of the stale push-to-main release model.

## Type of Change

- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [x] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- Added `docs/content/docs/ci-cd-flows.mdx` with Mermaid diagrams and
decision trees for PR review, release publishing, Docker publishing,
docs deploys, fork workflow approval, and manual validation.
- Updated `docs/content/docs/releases.mdx` to describe the current
`release-please` -> GitHub Release -> `release.yml` publishing path.
- Added the CI/CD flow page and existing release page to docs
navigation.
- Added Mermaid to the docs code highlighter language list.
- Restored missing docs helper modules and aligned Fumadocs dependencies
so the docs app can install, generate sources, type-check, and build.

## Testing

- [ ] Unit tests pass (`pytest`)
- [ ] Linting passes (`ruff check .`)
- [ ] Type checking passes (`mypy headroom`)
- [ ] New tests added for new functionality
- [x] Manual testing performed

### Test Output

```text
$ npm ci
> headroom-docs@0.0.0 postinstall
> fumadocs-mdx
[MDX] generated files
added 365 packages, and audited 367 packages

$ npm run types:check
> fumadocs-mdx && next typegen && tsc --noEmit
[MDX] generated files
Generating route types...
✓ Types generated successfully

$ npm run build
> next build
✓ Compiled successfully
Running TypeScript ...
Generating static pages ...
✓ Generating static pages (122/122)

Note: next build completed successfully and emitted two existing Recharts container-size warnings during static generation.

$ git diff --check
# no output

$ act workflow_dispatch -W .github/workflows/docs.yml -n
*DRYRUN* [Deploy Documentation/deploy] 🏁  Job succeeded
```

## Real Behavior Proof

- Environment: Windows local checkout, branch `docs-ci-flow`, Node.js
v22.22.0, `act` 0.2.87.
- Exact command / steps: Ran `npm ci`, `npm run types:check`, and `npm
run build` from `docs/`; ran `git diff --check` and `act
workflow_dispatch -W .github/workflows/docs.yml -n` from the repository
root.
- Observed result: Docs dependencies install, Fumadocs source generation
includes `ci-cd-flows.mdx`, TypeScript passes, Next production build
completes, whitespace check passes, and the docs workflow dry-run
succeeds under `act`.
- Not tested: Full live GitHub Pages deploy and registry/release
publishing, because this PR only changes docs and docs build wiring.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable

## Screenshots (if applicable)

N/A. This is documentation and build wiring; the diagrams are Mermaid
source blocks in the docs page.

## Additional Notes

- No issue is linked because this PR was not opened for a specific
tracked issue.
- `npm run build` still reports two pre-existing Recharts container-size
warnings while completing successfully.
- Python unit/lint/type checks and changelog updates are not applicable
to this docs-only change.
This commit is contained in:
JD Davis 2026-06-17 01:05:15 -05:00 committed by GitHub
parent a99dc61424
commit 8cea290a15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 957 additions and 531 deletions

View file

@ -0,0 +1,242 @@
---
title: CI/CD Flow Diagrams
description: Visual decision trees for pull requests, release publishing, Docker images, docs deploys, and manual validation.
---
## Purpose
This page is the quick visual map for Headroom automation. Use it when opening a PR, reviewing a PR, cutting a release, or deciding which workflow owns a failure.
The short version:
- Pull requests are gated by PR governance, path-filtered CI, targeted e2e workflows, and human review.
- Release publishing is not triggered by every merge to `main`. `release-please` maintains a release PR; merging that release PR creates the tag and GitHub Release that trigger publishing.
- Docker images are built as multi-architecture digests first, then merged into tagged manifests.
- Docs deploy only after docs changes land on `main`.
## PR Flow
```mermaid
flowchart TD
A[Open, edit, synchronize, or mark PR ready] --> B[PR Governance]
B --> B1{Template complete and ready?}
B1 -- no --> B2[Add needs author action label and governance comment]
B1 -- yes --> B3[Add ready for review label when no blocking status exists]
A --> C[Path filters decide workflow surface]
C --> D{Code paths changed?}
D -- yes --> E[CI changes job]
E --> F[lint: ruff, format, mypy]
E --> G[build-wheel: Rust extension wheel]
E --> H[prefetch-model: Hugging Face cache]
G --> I[test shards 1 to 4]
H --> I
G --> J[test-extras, test-agno, dashboard UI]
E --> K[build: release-profile wheel and sdist smoke]
C --> L{E2E paths changed?}
L -- yes --> M[Docker native e2e and platform wrapper checks]
C --> N{Init or wrap paths changed?}
N -- yes --> O[Init E2E, Wrap E2E, native init or wrap smoke tests]
C --> P{Rust paths changed?}
P -- yes --> Q[Rust fmt, clippy, tests, wheel build, audit]
C --> R{Devcontainer paths changed?}
R -- yes --> S[Devcontainer validation and linked worktree smoke]
C --> T{Release-critical paths changed?}
T -- yes --> U[Release dry-run: wheel matrix and smoke-import gates]
C --> V{Workflow files changed?}
V -- yes --> W[Workflow validation with actionlint and act dry-run]
F --> X{All required checks green?}
I --> X
J --> X
K --> X
M --> X
O --> X
Q --> X
S --> X
U --> X
W --> X
B3 --> X
X -- no --> Y[Fix, rebase, or request changes]
X -- yes --> Z[Human review and merge when approved]
```
## PR Decision Tree
```mermaid
flowchart TD
A[Review an open PR] --> B{Draft?}
B -- yes --> C[Do not approve. Comment with remaining readiness steps.]
B -- no --> D{Governance label says needs author action?}
D -- yes --> E[Fix or ask author to complete template and real behavior proof]
D -- no --> F{Merge state dirty or behind?}
F -- dirty --> G[Resolve conflicts before reviewing final code]
F -- behind --> H[Rebase or update branch, then rerun checks]
F -- clean or unknown --> I{Any failing check?}
I -- yes --> J[Read failing logs, classify as stale-main, infra, or code bug]
J --> K{Can maintainer safely fix without changing author intent?}
K -- yes --> L[Patch, test locally, push with lease]
K -- no --> M[Request changes with exact file and line references]
I -- no --> N{Code review complete?}
N -- no --> O[Review diff, tests, docs, and behavior proof]
N -- yes --> P[Approve]
```
## Fork Workflow Approval
GitHub may leave product workflows in `action_required` for first-time or fork contributors. Approve only after the diff is safe enough to execute in CI.
```mermaid
flowchart TD
A[Fork PR has action_required workflows] --> B{Diff is understandable and not suspicious?}
B -- no --> C[Do not approve. Ask for changes or close if unsafe.]
B -- yes --> D{Workflow runs use pull_request with read-scoped token?}
D -- no --> E[Inspect workflow permissions before approving]
D -- yes --> F[Approve queued CI runs]
F --> G[Wait for fresh check results on latest head SHA]
```
## Release Flow
```mermaid
flowchart TD
A[Merge ordinary PR to main] --> B[Release Please on push to main]
B --> C{Commit is releasable?}
C -- docs, ci, chore only --> D[No release PR change]
C -- fix, feat, breaking change --> E[Create or update Release PR]
E --> F[Release PR contains version bump and changelog]
F --> G{Ready to ship?}
G -- no --> H[Keep merging regular PRs; bot updates Release PR]
G -- yes --> I[Merge Release PR]
I --> J[release-please tags vX.Y.Z and publishes GitHub Release]
J --> K[release.yml starts on release: published]
K --> L[detect-version]
L --> M[build: sync versions, verify versions, changelog, npm packs]
M --> N[build-wheels matrix]
N --> O[collect-dist]
O --> P[smoke-import wheels]
P --> Q{Smoke import green?}
Q -- no --> R[Stop before publishing broken wheels]
Q -- yes --> S[publish PyPI]
Q -- yes --> T[publish npm packages]
Q -- yes --> U[publish GitHub Package Registry packages]
Q -- yes --> V[publish Docker images through docker.yml]
S --> W{PyPI published or PYPI_SKIP=true?}
W -- no --> X[Do not update release assets]
W -- yes --> Y[Create or update GitHub Release assets and notes]
T --> Y
U --> Y
V --> Y
```
## Release Decision Tree
```mermaid
flowchart TD
A[Need a release?] --> B{Release PR exists?}
B -- no --> C[Merge at least one releasable conventional commit to main]
B -- yes --> D{Release PR checks green and changelog correct?}
D -- no --> E[Fix source PRs or release config, then let release-please update]
D -- yes --> F{Registry skip variables needed?}
F -- yes --> G[Set PYPI_SKIP, NPM_SKIP, or GH_PACKAGES_SKIP deliberately]
F -- no --> H[Merge Release PR]
G --> H
H --> I[Watch release.yml]
I --> J{Failure before publish?}
J -- yes --> K[Fix and rerun before any package is public]
J -- no --> L{Failure after partial publish?}
L -- yes --> M[Use rerun or skip variables to reach consistent GitHub Release state]
L -- no --> N[Release complete]
```
## Docker Publish Flow
`docker.yml` can run directly on `push`, `workflow_dispatch`, or `release: published`, and it is also called by `release.yml`.
```mermaid
flowchart TD
A[Docker workflow starts] --> B[Matrix: variant x architecture]
B --> C[Build each platform image by digest only]
C --> D[Smoke-test image imports pydantic_core and headroom._core]
D --> E{Smoke test green?}
E -- no --> F[Stop before tag manifest]
E -- yes --> G[Upload digest marker]
G --> H[Per-variant manifest merge]
H --> I[Apply tags to multi-arch manifest]
I --> J{Release event?}
J -- yes --> K[Version tags and latest retag rules]
J -- no --> L[Branch, PR, dev, or manual tags as configured]
```
## Docs Deploy Flow
```mermaid
flowchart TD
A[Docs change in PR] --> B[PR review and normal checks]
B --> C[Merge to main]
C --> D{docs/**, mkdocs.yml, or docs workflow changed?}
D -- no --> E[No docs deploy]
D -- yes --> F[Deploy Documentation workflow]
F --> G[Build docs site]
G --> H[Deploy generated site]
```
## Manual Validation Flow
Use this when editing workflows or release automation.
```mermaid
flowchart TD
A[Edit workflow or release scripts] --> B[Run local workflow validation]
B --> C[actionlint]
B --> D[act dry-run fixtures]
C --> E{Local validation green?}
D --> E
E -- no --> F[Fix before opening or updating PR]
E -- yes --> G[Push PR]
G --> H[workflow-validation job reruns same validation in CI]
```
Recommended local command:
```bash
bash scripts/validate-workflows.sh
```
For release dry-runs:
```bash
act workflow_dispatch -W .github/workflows/release.yml -e .github/act/dry-run.json
```
## Gate Summary
| Flow | Trigger | Main gates | Success condition |
|------|---------|------------|-------------------|
| PR governance | `pull_request_target`, schedule, manual | Template, readiness labels, merge state, check labels | PR has no governance blockers |
| CI | PR, push to `main`, manual | Path filter, lint, mypy, wheel build, model prefetch, test shards, package smoke | Required jobs green or path-skipped |
| Rust | Rust paths, schedule | fmt, clippy, cargo test, wheel build, audit | Rust checks green; nightly parity is allowed to fail in Phase 0 |
| E2E | CLI, install, wrap, Docker, package paths | Docker init/wrap, native init/wrap/install, platform smoke | Relevant lifecycle checks green |
| Release dry-run | PRs touching release-critical paths | Version detection, wheel matrix, smoke imports | Publish path can build before merge |
| Release publish | GitHub Release published by release-please | Version sync, changelog, wheels, smoke import, PyPI gate, npm, GPR, Docker | Public packages and GitHub Release assets are consistent |
| Docs deploy | Push to `main` with docs paths | Docs build | Site deploy completes |
## Workflow Ownership
| Workflow | Owns |
|----------|------|
| `.github/workflows/pr-health.yml` | PR body governance, readiness labels, rebase/conflict/failing-check labels |
| `.github/workflows/ci.yml` | Python lint, type checks, wheel build, test shards, package smoke, workflow validation |
| `.github/workflows/rust.yml` | Rust workspace quality gates and native wheel smoke artifacts |
| `.github/workflows/init-e2e.yml` | Dockerized `headroom init` behavior |
| `.github/workflows/wrap-e2e.yml` | Dockerized `headroom wrap` behavior |
| `.github/workflows/init-native-e2e.yml` | Host-specific `headroom init -g` smoke tests |
| `.github/workflows/install-native-e2e.yml` | Host-specific install CLI smoke tests |
| `.github/workflows/wrap-native-e2e.yml` | Host-specific wrap prepare-only smoke tests |
| `.github/workflows/devcontainers.yml` | Devcontainer startup and linked worktree compatibility |
| `.github/workflows/release-please.yml` | Release PR aggregation from conventional commits |
| `.github/workflows/release.yml` | Release build, wheel smoke-import gates, registry publishing, GitHub Release assets |
| `.github/workflows/docker.yml` | GHCR multi-architecture image builds and manifests |
| `.github/workflows/docs.yml` | Documentation deploy after docs changes merge |

View file

@ -43,6 +43,8 @@
"api-reference",
"---Architecture---",
"architecture",
"ci-cd-flows",
"releases",
"benchmarks",
"limitations",
"---Help---",

View file

@ -1,13 +1,15 @@
---
title: Releases & CI/CD
description: Automated release pipeline with semantic versioning, multi-package publishing, and changelog generation.
description: Automated release pipeline with release-please, semantic versioning, multi-package publishing, and changelog generation.
---
## Overview
Headroom uses a unified GitHub Actions workflow (`.github/workflows/release.yml`) that automatically publishes all three packages, builds version-matched Docker images, generates changelogs from conventional commits, and creates GitHub Releases with package assets attached.
The release workflow also calls `.github/workflows/docker.yml` as a reusable workflow so GHCR images are published in the same release run with the exact same synced version as PyPI, npm, and GitHub release assets.
Headroom uses `release-please` to maintain a release PR from conventional commits on `main`. Merging that release PR creates the release tag and GitHub Release, which triggers `.github/workflows/release.yml` to publish all packages, build version-matched Docker images, and attach release assets.
The release workflow also calls `.github/workflows/docker.yml` as a reusable workflow so GHCR images are published in the same release run with the exact same synced version as PyPI, npm, and GitHub release assets.
For the end-to-end visual flow, see [CI/CD Flow Diagrams](/docs/ci-cd-flows).
## Packages & Registries
@ -24,69 +26,73 @@ The release workflow also calls `.github/workflows/docker.yml` as a reusable wor
## Version Strategy
The workflow uses a **canonical + commit-height** algorithm that eliminates infinite loops:
Release Please calculates the release version from conventional commits and the release manifest. The release workflow still computes and verifies the version it is about to publish:
1. `pyproject.toml` holds the **canonical version** (e.g., `0.5.25`). This is the single source of truth — set manually before merging.
2. On every push to `main`, the workflow counts commits since the last tagged release at the same canonical level.
3. The **git tag** uses `v{canonical}.{height}` format (e.g., `v0.5.25.3` = 3 commits since canonical `0.5.25`).
4. The **npm version** uses 3-part semver — bumped from canonical based on commit type (e.g., `feat:` + canonical `0.5.25` → `0.6.0`).
1. `.release-please-config.json` defines release-please behavior.
2. `.release-please-manifest.json` tracks current package versions.
3. The release PR updates versions and changelog content.
4. Merging the release PR publishes a GitHub Release tagged `vX.Y.Z`.
5. `release.yml` uses that tag as the manual version for the publish run.
### Version Files
- `pyproject.toml` — `[project].version` — **canonical** (set manually before merge)
- `headroom/_version.py` — `__version__` — synced at build time
- `plugins/openclaw/package.json` — `version` — synced at build time
- `sdk/typescript/package.json` — `version` — synced at build time
- `.release-please-config.json` - release-please package configuration
- `.release-please-manifest.json` - release-please version manifest
- `pyproject.toml` - `[project].version`
- `headroom/_version.py` - `__version__`, synced at build time
- `plugins/openclaw/package.json` - `version`, synced at build time
- `sdk/typescript/package.json` - `version`, synced at build time
`release.yml` does not commit back to the repo. Version synchronization happens inside the release build workspace.
**The workflow NEVER commits back to the repo** — this breaks the infinite loop.
## Conventional Commits & Semantic Bumping
The workflow analyzes the **unreleased commits since the previous release tag** and applies the highest required bump level:
| Commit | Bump | Git Tag Example | npm Version |
|--------|------|-----------------|-------------|
| `fix:`, `ci:`, `chore:`, `perf:`, `refactor:` | patch | `v0.5.25.3` | `0.5.26` |
| `feat:` | minor | `v0.6.0.0` | `0.6.0` |
| Any conventional commit with `!` or any commit with `BREAKING CHANGE` in the body | major | `v1.0.0.0` | `1.0.0` |
Commits are linted in CI via `commitlint` using `@commitlint/config-conventional`.
### How the Canonical + Height Algorithm Works
```
Canonical in pyproject.toml: 0.5.25
Last tag at this canonical: v0.5.25.2 (2 commits ago)
HEAD commit: fix: resolve bug
→ bump = patch
→ height = 3 (2 prior + 1 new)
→ git tag: v0.5.25.3
→ npm version: 0.5.26 (canonical 0.5.25 + 1 patch bump)
```
After npm publishes `0.5.26`, update `pyproject.toml` to `0.5.26` (manually, before next release).
## Conventional Commits & Semantic Bumping
Release Please analyzes unreleased conventional commits and applies the highest required bump level:
| Commit | Bump |
|--------|------|
| `fix:` | patch |
| `feat:` | minor |
| Any conventional commit with `!` or any commit with `BREAKING CHANGE` in the body | major |
| `docs:`, `ci:`, `chore:`, `refactor:` | no release note by default unless configured |
Commits are linted in CI via `commitlint` using `@commitlint/config-conventional`.
The release PR is the place where version and changelog changes are reviewed before publishing.
## Release Workflow
The `release.yml` workflow runs on every push to `main` and consists of five jobs:
The `release.yml` workflow runs when a GitHub Release is published, which normally happens when the release-please PR is merged. It also supports manual `workflow_dispatch` and PR dry-runs for release-critical workflow/package changes.
```
detect-version → build → publish-pypi
→ publish-npm
→ publish-github-packages → create-release
```
detect-version → build → build-wheels → collect-dist → smoke-import-wheels
↘ publish-pypi
↘ publish-npm
↘ publish-github-packages
↘ publish-docker
→ create-release
```
The workflow never commits back to the repo.
**Note:** The workflow never commits back to the repo. `pyproject.toml` is updated manually before merging.
### detect-version
Resolves the release version from the trigger. On `release: published`, it uses the published tag (`vX.Y.Z`) as the manual version for the run. On `workflow_dispatch`, it uses the optional `version` input when provided. PR dry-runs compute a version without publishing.
### detect-version
Reads canonical version from `pyproject.toml`, counts commits since the previous release tag, chooses the highest bump level across that unreleased commit range, and computes both the git tag version (`v{canonical}.{height}`) and npm version (3-part semver).
### build
1. Syncs version across TypeScript package files via `scripts/version-sync.py --version {npm_version}`
2. Generates changelog via `scripts/changelog-gen.py`
3. Builds the Python package (`python -m build`)
4. Uploads artifacts (dist + changelog)
### build
1. Syncs version across package files via `scripts/version-sync.py --version {npm_version}`
2. Verifies package versions with `scripts/verify-versions.py`
3. Generates the changelog artifact
4. Builds npm release packages for the TypeScript SDK and OpenClaw plugin
5. Uploads release asset artifacts for downstream publish jobs
### build-wheels
Builds the Python wheel matrix for Linux x86_64, Linux arm64, and Apple Silicon macOS, plus one source distribution. Linux wheels are audited for glibc symbol compatibility.
### collect-dist
Collects the wheel matrix, source distribution, and npm tarballs into the canonical artifacts used by publishing and GitHub Release asset upload.
### smoke-import-wheels
Installs the built wheels into representative customer environments and imports `headroom._core`. This blocks publishing if a wheel builds successfully but cannot import on its promised platform floor.
### publish-pypi
Downloads the Python dist artifact and publishes to PyPI via `pypa/gh-action-pypi-publish@release/v1` (trusted publisher).
@ -107,8 +113,8 @@ Uploads the built Python distributions and both npm tarballs to the GitHub Relea
### publish-docker
Calls the reusable Docker workflow to publish GHCR images with the same semantic version and synced package metadata as the rest of the release.
### create-release
Creates or updates the GitHub Release in the current repo, uploads the built Python distributions and npm tarballs as release assets, and publishes the generated changelog as release notes. The job still runs after the build succeeds even if one of the external registry publishes fails, so GitHub-hosted artifacts remain available on `main`.
### create-release
Creates or updates the GitHub Release in the current repo and uploads the built Python distributions and npm tarballs as release assets. PyPI publish is a hard gate unless `PYPI_SKIP=true`, so release notes and assets do not advertise a version that failed to publish to PyPI.
## Configuration
@ -158,39 +164,45 @@ python scripts/verify-versions.py
That gate fails on cross-package version drift. The sdist build is also checked
for a top-level `LICENSE` file before any publish job can consume it.
## Workflow Triggers
The workflow runs on push to `main`, but ignores pushes that only touch docs, CI config, or scripts:
```yaml
on:
push:
branches: [main]
paths-ignore:
- "docs/**"
- ".github/workflows/ci.yml"
- ".github/workflows/publish.yml"
- "scripts/**"
- ...
```
For manual control, use `workflow_dispatch`:
```yaml
on:
workflow_dispatch:
inputs:
version:
description: "Manual version override (e.g. 0.6.0)"
required: false
dry_run:
description: "Skip publish"
## Workflow Triggers
Release Please runs on pushes to `main` and maintains the release PR:
```yaml
on:
push:
branches: [main]
```
The publish workflow runs when a GitHub Release is published, on PR dry-runs for release-critical paths, and by manual dispatch:
```yaml
on:
release:
types: [published]
pull_request:
paths:
- ".github/workflows/release.yml"
- ".github/workflows/docker.yml"
- "crates/headroom-py/**"
- "pyproject.toml"
- "scripts/verify-versions.py"
- "scripts/version-sync.py"
- "Cargo.toml"
- "Cargo.lock"
workflow_dispatch:
inputs:
version:
description: "Manual version override"
required: false
dry_run:
description: "Skip publish"
type: boolean
default: false
```
- **No version input:** auto-detected from commits (normal flow)
- **`dry_run: true`:** runs all jobs except publish steps — useful for testing
- **Normal release:** merge the release-please PR; the bot publishes a GitHub Release, which triggers `release.yml`.
- **PR dry-run:** release-critical PRs build and smoke-import wheels before merge, but do not publish.
- **Manual dispatch:** use `version` to override the release version and `dry_run: true` to skip publish steps.
## Local Testing with `act`
@ -212,13 +224,21 @@ act workflow_dispatch -W .github/workflows/release.yml -e .github/act/dry-run.js
This runs the full workflow end-to-end with `dry_run=true`, skipping all publish steps.
### Test a Specific Commit Type
```bash
act push -W .github/workflows/release.yml -e .github/act/push-feat.json
```
The `push-feat.json` event file simulates a `feat:` commit on `main`.
### Simulate Release Please
```bash
act push -W .github/workflows/release-please.yml -e .github/act/push-feat.json
```
The `push-feat.json` event file simulates a `feat:` commit on `main` so the release-please workflow can be validated locally.
### Simulate a Published Release
```bash
act release -W .github/workflows/release.yml -e .github/act/release-published.json -n
```
The `release-published.json` event file simulates the event emitted when the release-please PR is merged.
### Validate the Release and Docker Workflows
@ -241,16 +261,18 @@ cp .env.act.example .env.act
| File | Purpose |
|------|---------|
| `.github/workflows/release.yml` | Main release pipeline |
| `.github/workflows/ci.yml` | CI — lint, test, commitlint |
| `.github/workflows/publish.yml` | Manual-only PyPI fallback (superseded by `release.yml`) |
| `.github/workflows/release.yml` | Main release pipeline |
| `.github/workflows/release-please.yml` | Release PR aggregation from conventional commits |
| `.github/workflows/ci.yml` | CI — lint, test, commitlint |
| `.github/workflows/publish.yml` | Manual-only PyPI fallback (superseded by `release.yml`) |
| `.commitlintrc.json` | Conventional commit rules |
| `scripts/version-sync.py` | Sync version across all packages |
| `scripts/changelog-gen.py` | Generate changelog from git log |
| `scripts/verify-versions.py` | Pre-release version alignment check |
| `.github/act/dry-run.json` | `act` event file for dry-run testing |
| `.github/act/push-feat.json` | `act` event file for feat commit testing |
| `.github/act/docker-version.json` | `act` event file for Docker workflow validation |
| `.github/act/dry-run.json` | `act` event file for dry-run testing |
| `.github/act/push-feat.json` | `act` event file for feat commit testing |
| `.github/act/release-published.json` | `act` event file for release publish simulation |
| `.github/act/docker-version.json` | `act` event file for Docker workflow validation |
| `scripts/validate-workflows.sh` | Shared `actionlint` + `act -n` workflow validation script |
| `.actrc` | Default `act` flags (Ubuntu runner, reuse, quiet) |
| `.actrc.local.example` | Local `act` override template |
@ -264,14 +286,12 @@ cp .env.act.example .env.act
The PyPI publish uses trusted publisher OIDC — no secret required, only the `pypi` GitHub Environment must be configured with your PyPI project.
## First Release Note
TypeScript packages (`headroom-ai` SDK and `headroom-openclaw`) start at version `0.1.0`, while Python `headroom-ai` is at `0.5.25`. Before the first automated release:
1. Update `pyproject.toml` to set the desired canonical version (e.g., `0.6.0`)
2. Use `workflow_dispatch` with a manual `version` input to set the target version explicitly
3. The workflow will align all three packages to the same version on first release
### Release Cadence with Canonical + Height
After each release, update `pyproject.toml` to match the published version before merging the next feature branch. This keeps the canonical current and ensures each release gets a unique git tag.
## Release Cadence
Day to day:
1. Merge regular PRs to `main`.
2. Release Please updates the open release PR when releasable conventional commits land.
3. Review the release PR changelog and version bump.
4. Merge the release PR when ready to ship.
5. Watch `release.yml` publish PyPI, npm, GitHub Packages, Docker, and GitHub Release assets.

6
docs/lib/cn.ts Normal file
View file

@ -0,0 +1,6 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

10
docs/lib/layout.shared.ts Normal file
View file

@ -0,0 +1,10 @@
import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export function baseOptions(): BaseLayoutProps {
return {
nav: {
title: 'Headroom',
},
githubUrl: 'https://github.com/chopratejas/headroom',
};
}

8
docs/lib/shared.ts Normal file
View file

@ -0,0 +1,8 @@
export const docsRoute = '/docs';
export const docsContentRoute = '/llms.mdx/docs';
export const gitConfig = {
user: 'chopratejas',
repo: 'headroom',
branch: 'main',
};

35
docs/lib/source.ts Normal file
View file

@ -0,0 +1,35 @@
import { loader } from 'fumadocs-core/source';
import { docs } from '../.source/server';
export const source = loader({
baseUrl: '/docs',
source: docs.toFumadocsSource(),
});
type Page = ReturnType<typeof source.getPages>[number];
export function getPageMarkdownUrl(page: Page) {
const segments = page.slugs.length > 0 ? [...page.slugs, 'content.md'] : ['index', 'content.md'];
return {
segments,
url: `/llms.mdx/docs/${segments.join('/')}`,
};
}
export function getPageImage(page: Page) {
const segments = page.slugs.length > 0 ? [...page.slugs, 'image.png'] : ['index', 'image.png'];
return {
segments,
url: `/og/docs/${segments.join('/')}`,
};
}
export async function getLLMText(page: Page) {
try {
return await page.data.getText('processed');
} catch {
return `# ${page.data.title}\n\n${page.data.description ?? ''}`;
}
}

46
docs/lib/telemetry.ts Normal file
View file

@ -0,0 +1,46 @@
export interface CommunityStats {
total_tokens_saved: number;
total_cost_saved: number;
total_requests: number;
unique_instances: number;
}
const fallbackStats: CommunityStats = {
total_tokens_saved: 0,
total_cost_saved: 0,
total_requests: 0,
unique_instances: 0,
};
export function fmtNum(value: number) {
return new Intl.NumberFormat('en-US', {
notation: value >= 10000 ? 'compact' : 'standard',
maximumFractionDigits: 1,
}).format(value);
}
export function fmtUsd(value: number) {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
notation: value >= 10000 ? 'compact' : 'standard',
maximumFractionDigits: value >= 1000 ? 1 : 2,
}).format(value);
}
export async function fetchCommunityStats(): Promise<CommunityStats> {
const endpoint = process.env.NEXT_PUBLIC_COMMUNITY_STATS_URL;
if (!endpoint) return fallbackStats;
try {
const response = await fetch(endpoint, { next: { revalidate: 300 } });
if (!response.ok) return fallbackStats;
return {
...fallbackStats,
...(await response.json()),
};
} catch {
return fallbackStats;
}
}

886
docs/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -10,12 +10,15 @@
"postinstall": "fumadocs-mdx"
},
"dependencies": {
"@radix-ui/react-slot": "1.3.0",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"dotted-map": "^3.1.0",
"fumadocs-core": "16.7.10",
"fumadocs-mdx": "14.3.2",
"fumadocs-core": "16.10.3",
"fumadocs-mdx": "15.0.12",
"fumadocs-twoslash": "^3.1.3",
"fumadocs-typescript": "^4.0.3",
"fumadocs-ui": "16.7.10",
"fumadocs-ui": "16.10.3",
"headroom-ai": "file:../sdk/typescript",
"lucide-react": "^1.7.0",
"next": "16.2.6",

View file

@ -38,7 +38,7 @@ export default defineConfig({
},
}),
],
langs: ['js', 'jsx', 'ts', 'tsx', 'python', 'bash', 'json', 'yaml', 'toml', 'css'],
langs: ['js', 'jsx', 'ts', 'tsx', 'python', 'bash', 'json', 'yaml', 'toml', 'css', 'mermaid'],
},
},
});