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
31 changes: 31 additions & 0 deletions .github/workflows/docs-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Docs PR Build

on:
pull_request:
paths:
- 'docs/**'
- 'zensical.toml'
- 'pyproject.toml'
- 'uv.lock'
- '.github/workflows/docs-pr.yml'

permissions:
contents: read

concurrency:
group: docs-pr-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
# setup-uv publishes no moving major tag -- v9 does not resolve.
- uses: astral-sh/setup-uv@v9.0.0
- run: uv python install 3.13
- run: uv sync --locked --group docs
- run: uv run --locked zensical build --strict
15 changes: 9 additions & 6 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ on:
branches: [main]
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'zensical.toml'
- 'pyproject.toml'
- 'uv.lock'
- '.github/workflows/docs.yml'
workflow_dispatch:

Expand All @@ -23,13 +24,14 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: astral-sh/setup-uv@v7
# setup-uv publishes no moving major tag -- v9 does not resolve.
- uses: astral-sh/setup-uv@v9.0.0
- run: uv python install 3.13
- run: uv sync --group docs
- run: uv run mkdocs build
- run: uv sync --locked --group docs
- run: uv run --locked zensical build --strict
- uses: actions/upload-pages-artifact@v5
with:
path: site-build
Expand All @@ -41,5 +43,6 @@ jobs:
runs-on: ubuntu-latest
needs: build
steps:
- id: deployment
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
/build/ghostty-src/
/site-build/

# Zensical differential-build cache (root only -- the anchor keeps this
# from also swallowing a nested .cache/ elsewhere in the tree)
/.cache/

# Rust workspace
/target/
**/*.rs.bk
Expand Down
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,43 @@ builds the DMG + `.deb`s and publishes to the apt repo. Bump
`[workspace.package].version` in `Cargo.toml` to match before tagging (the
release workflow asserts they agree).

## Unreleased

### Changed

- **Docs site migrated from Material for MkDocs to
[Zensical](https://zensical.org)**, the successor from the same team.
Material entered maintenance mode in November 2025 and now warns on every
build that MkDocs 2.0 will remove the plugin and theming systems with no
migration path. `mkdocs.yml` is replaced by a native `zensical.toml`;
documentation content is unchanged aside from a handful of links that
`--strict` now validates (out-of-tree paths became GitHub blob URLs; one
stale heading anchor was corrected).

The look now comes from the shared
[stridelabs-docs-theme](https://github.com/charliek/stridelabs-docs-theme)
package rather than per-repo config, so restyling every site is a version
bump instead of an edit in each repo. Fonts are self-hosted by the theme, so
the site no longer requests anything from `fonts.googleapis.com` or
`fonts.gstatic.com`.

Working notes previously withheld via MkDocs `exclude_docs` moved to
`discovery/` at the repo root — Zensical has no equivalent, and leaving
them under `docs/` would have published them.

Verified against the pre-migration build: identical 27-page set and all
374 heading anchors preserved, so existing deep links still resolve.
`docs/appcast.xml` still copies through to the published site. Page
`<title>` now derives from the page `<h1>` rather than the nav label, which
is the one intentional difference.

### Added

- **`Docs PR Build` workflow.** Docs previously built only on push to `main`,
and without `--strict`, so a broken link or heading anchor could land on
`main` and be caught at deploy time or not at all. Both workflows now build
`--strict` and watch `uv.lock`.

## v0.0.17 — 2026-07-31

Agents move out of the palette and into the sidebar. Every running agent now
Expand Down
38 changes: 38 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,44 @@ wrapper small.
and before the single-instance lock, so it never touches a running
instance.

## Docs

**Not part of `make check` or the `ci-success` gate.** The docs site has
its own toolchain (uv/Python) and its own CI workflows; the Rust/Swift
gates do not cover it. Run it for commits touching `docs/`,
`zensical.toml`, `pyproject.toml`, `uv.lock`, or either docs workflow —
both workflows trigger on those shared inputs (and each additionally on
its own file), because a dependency or lockfile change can break the
build just as easily as a content change:

```bash
make docs # uv run --locked zensical build --strict
make docs-serve # preview on http://127.0.0.1:7070
```

The site is [Zensical](https://zensical.org) (not MkDocs — migrated 2026-08),
configured in `zensical.toml`, built into `site-build/`. `--strict` fails
on broken links and anchors and is what both CI workflows run, so run it
locally before pushing docs changes. Note `zensical serve --strict` is
unsupported; verify strictness via `build`.

The look comes from the shared
[stridelabs-docs-theme](https://github.com/charliek/stridelabs-docs-theme)
package, pinned by tag in `pyproject.toml`. Palette, fonts and feature
toggles live there, not here — do not add `theme.palette`, `theme.features`,
or a `[project.theme.font]` table to `zensical.toml`. The last is the
sharp edge: it re-enables Zensical's Google Fonts `<link>` on every page
while the theme's self-hosted faces keep loading anyway.

Working notes in `discovery/` live outside `docs/` on purpose. Zensical
has no `exclude_docs` equivalent; files under `docs_dir` are published.

Two gotchas worth knowing: Zensical **silently ignores unknown config
keys** even under `--strict`, so a green build does not prove a config
edit did what you meant; and the `pymdownx.emoji` callables live in the
`zensical.extensions.emoji` namespace — the Material for MkDocs
`material.extensions.emoji` namespace aborts the build.

## Build

- libghostty-vt is pinned to a specific Ghostty commit in
Expand Down
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -259,11 +259,11 @@ check: fmt-check clippy themes-check test ## Pre-push gate: fmt-check + clippy
# ---- docs -------------------------------------------------------------

.PHONY: docs docs-serve
docs: ## Build the mkdocs site into site-build/
uv sync --group docs && uv run mkdocs build
docs: ## Build the docs site into site-build/ (same as CI)
uv sync --locked --group docs && uv run --locked zensical build --strict

docs-serve: ## Serve the docs locally (mkdocs serve)
uv sync --group docs && uv run mkdocs serve
docs-serve: ## Serve the docs locally with live reload
uv sync --locked --group docs && uv run --locked zensical serve

# ---- clean ------------------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ cd mac && swift build # or: ./mac/scripts/bundle.sh release →

## Documentation

The full site lives under `docs/` and builds with `mkdocs-material` (`make docs-serve` → http://127.0.0.1:7070):
The full site lives under `docs/` and builds with [Zensical](https://zensical.org) (`make docs-serve` → http://127.0.0.1:7070):

- [Installation](docs/getting-started/installation.md) — toolchain + build
- [First Run](docs/getting-started/first-run.md) — launch behavior + where state lives
Expand Down
2 changes: 1 addition & 1 deletion RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ Inspect or edit at https://github.com/charliek/roost/rules.
## The appcast lives where

The Sparkle appcast is at `docs/appcast.xml`, served by GitHub Pages from
`https://charliek.github.io/roost/appcast.xml` via `docs.yml`'s mkdocs
`https://charliek.github.io/roost/appcast.xml` via `docs.yml`'s Zensical
deploy. The `appcast` job mutates that file in place, commits it as the
release-bot, and pushes to main; `docs.yml` redeploys Pages shortly after.

Expand Down
6 changes: 3 additions & 3 deletions docs/discovery/README.md → discovery/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Discovery notes

Working notes, not product commitments. `mkdocs.yml` lists this
folder under `exclude_docs`, so it is not in the nav and is not
built into the public site.
Working notes, not product commitments. These files live outside
`docs/` so they are not built into the public site — Zensical has
no equivalent of MkDocs `exclude_docs`.

| Note | What it is |
|---|---|
Expand Down
File renamed without changes.
File renamed without changes.
8 changes: 4 additions & 4 deletions docs/development/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ Mac tests are under `mac/Tests/RoostTests/`; they cover the workspace state mach

## Documentation site

Markdown sources live in `docs/`. mkdocs-material builds them through `uv`:
Markdown sources live in `docs/`. [Zensical](https://zensical.org) builds them through `uv`:

```bash
make docs # static site under site-build/
make docs # static site under site-build/ (`zensical build --strict`)
make docs-serve # live-reload server at http://127.0.0.1:7070
```

`uv sync --group docs` runs automatically; no global Python install needed beyond the `uv` binary.
`uv sync --locked --group docs` runs automatically; no global Python install needed beyond the `uv` binary. `zensical serve --strict` is unsupported — verify with `make docs`.

The voice for new docs is set in `mkdocs.yml`: professional + direct (no marketing), tables for option lists, code blocks with language hints, admonitions only for important notes/warnings, copy-pasteable examples, one topic per page.
The voice for new docs is set in `zensical.toml`: professional + direct (no marketing), tables for option lists, code blocks with language hints, admonitions only for important notes/warnings, copy-pasteable examples, one topic per page.

## Bumping the pinned Ghostty SHA

Expand Down
10 changes: 5 additions & 5 deletions docs/development/test-automation.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Test automation & scripting architecture (plan)

Status: **largely implemented** (2026-05). The functional E2E harness
(pytest) is built and headless in CI as [`tools/roosttest/`](../../tools/roosttest/README.md);
(pytest) is built and headless in CI as [`tools/roosttest/`](https://github.com/charliek/roost/blob/main/tools/roosttest/README.md);
the tooling is reorganized into three layers (see
[`tools/README.md`](../../tools/README.md)); and `tab.dump`, `wait`, and
[`tools/README.md`](https://github.com/charliek/roost/blob/main/tools/README.md)); and `tab.dump`, `wait`, and
the `palette.*` ops shipped. The plan below is kept as the design
rationale — the "Gap" entries it lists are mostly closed now. The Lua
scripting layer (§ below) remains the open piece. The north star is
Expand Down Expand Up @@ -119,7 +119,7 @@ across both implementations?*
| IPC surface | `roost-ipc`: tab/project CRUD, set-state, notify, focus, send, resize, reorder, screenshot, claude-hook, identify — **now also `tab.dump` (content), `palette.*` (UI-action), and `roostctl wait`**. | Copy/paste + live `events.subscribe` still unimplemented. |
| Event stream | UIs consume an **in-process** event bus. `events.subscribe` over the wire is **stubbed not-implemented on both UIs** (`mac/Sources/Roost/IPCHandlerImpl.swift`, `crates/roost-linux/src/ipc.rs`). | External clients can't wait on events yet (the pytest harness condition-waits via polling instead). |
| Render state | `roost-vt` `RenderState.walk(|cell| …)` yields `Cell { text: String /*grapheme*/, fg, bg }` + cursor; mirrored 1:1 in `mac/Sources/Roost/RenderState.swift`. Both UIs walk it to draw. | **Now exposed over IPC as text via `tab.dump`** (viewport only; scrollback is a follow-up). |
| Tooling | Three layers (see [`tools/README.md`](../../tools/README.md)): `tools/roosttest/` (pytest, IPC, **in CI**), `tools/screenshot/` (bash + roostctl + pngtool, visual), `tools/input/linux/` (uinput/clipboard, real input). | Real-input (Layer 3) + visual (Layer 2) are local-only; a Mac CGEvent injector is still to come. |
| Tooling | Three layers (see [`tools/README.md`](https://github.com/charliek/roost/blob/main/tools/README.md)): `tools/roosttest/` (pytest, IPC, **in CI**), `tools/screenshot/` (bash + roostctl + pngtool, visual), `tools/input/linux/` (uinput/clipboard, real input). | Real-input (Layer 3) + visual (Layer 2) are local-only; a Mac CGEvent injector is still to come. |

This unified design is now realized: the pytest harness (`tools/roosttest/`)
is Tier 1, and the screenshot + input harnesses are reorganized by layer.
Expand Down Expand Up @@ -281,7 +281,7 @@ compat (optional fields). Document each new op in
### 5.6 Hermetic runs, harness flags & the skip policy *(2026-05)*

The pytest harness is parameterized + configured by these knobs (full
operational detail in [`tools/roosttest/README.md`](../../tools/roosttest/README.md)):
operational detail in [`tools/roosttest/README.md`](https://github.com/charliek/roost/blob/main/tools/roosttest/README.md)):

**Make targets.** `make e2e` / `e2e-gtk` / `e2e-mac` are the quick local
runs (reuse a running UI if present). `make e2e-gtk-ci` / `e2e-mac-ci`
Expand Down Expand Up @@ -396,7 +396,7 @@ network in the exposed `roost` table.

**Decision: pytest drives the tests; Lua is a scoped user-scripting
surface, not the test mechanism** (see
[vision.md DL-12](vision.md#dl-12-pytest-drives-the-tests-lua-is-a-user-scripting-surface)).
[vision.md DL-12](vision.md#dl-12-pytest-drives-the-tests-lua-is-a-user-scripting-surface-2026-05-26)).
The analysis that led there is kept below; the key insight that made it
low-stakes is that E2E robustness lives in the *affordances*, not the
runner.
Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@ done (above) and [`nfpm`](https://nfpm.goreleaser.com) on `PATH`:
./linux/scripts/build-deb.sh 0.0.1-dev
```

See [`linux/README.md`](../../linux/README.md) and
[`packaging/nfpm.yaml`](../../packaging/nfpm.yaml) for what the
See [`linux/README.md`](https://github.com/charliek/roost/blob/main/linux/README.md) and
[`packaging/nfpm.yaml`](https://github.com/charliek/roost/blob/main/packaging/nfpm.yaml) for what the
package contains.

## CLI on PATH
Expand Down
Loading