From 8cd4734fbb71ee637230f766134b0d9a676a9495 Mon Sep 17 00:00:00 2001 From: Alisue Date: Sun, 11 Oct 2026 13:55:26 +0900 Subject: [PATCH 1/4] chore: run the quality gate with just and drop flake-utils The quality gate was a set of npm scripts that CI and the guides each listed on their own. `just ci` runs the build, the link check and a nixfmt check, and CI runs the same recipe. The flake lists its systems with nixpkgs.lib.genAttrs instead of flake-utils and is formatted with nixfmt. The dev shell names Node 24, since the scripts rely on Node's TypeScript type stripping, and provides just and nixfmt. The repository ignores its own .envrc, .direnv, result and worktrees instead of relying on a global ignore, and .gitattributes keeps checkouts LF on every OS. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N3xvJ1TaozjYSE6eoyo9mq --- .envrc.sample | 1 + .gitattributes | 3 +++ .gitignore | 4 ++++ README.md | 13 ++++++++---- flake.lock | 36 +-------------------------------- flake.nix | 55 ++++++++++++++++++++++++++++++++++++-------------- justfile | 42 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 100 insertions(+), 54 deletions(-) create mode 100644 .gitattributes create mode 100644 justfile diff --git a/.envrc.sample b/.envrc.sample index 3550a30..11af9c3 100644 --- a/.envrc.sample +++ b/.envrc.sample @@ -1 +1,2 @@ +# Copy to .envrc and run `direnv allow` to enter the Nix dev shell automatically. use flake diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ca5e98b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Check text out with LF on every OS, so a Windows checkout builds and tests +# the same bytes as the others. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore index ac6c48f..cdbbc2d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ .playwright-mcp dist/ node_modules/ +/.envrc +.direnv/ +result +/.claude/worktrees/ diff --git a/README.md b/README.md index e207fa4..be3641c 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,14 @@ scenario-based testing framework shipped as a single binary. git clone https://github.com/probitas-test/probitas-test.github.io.git cd probitas-test.github.io +# Enter the development shell (Node.js, Pagefind, just) +nix develop + # Install dependencies -npm ci +just install # Start development server -npm run dev +just dev ``` ## Commands @@ -36,6 +39,7 @@ npm run dev | `npm run preview` | Preview built site | | `npm test` | Check internal links in the built site | | `npm run generate-api` | Regenerate API documentation JSON | +| `just ci` | Everything CI checks (Nix format, build, link check) | ## Project Structure @@ -71,7 +75,8 @@ npm run dev ### Prerequisites -- [Node.js](https://nodejs.org/) (LTS) +- [Nix](https://nixos.org/) with flakes enabled; `nix develop` provides Node.js, + Pagefind and [just](https://just.systems/). Without Nix, install Node.js 24. ### Regenerating API Documentation @@ -105,7 +110,7 @@ The `npm run build` command performs the following steps: This site is deployed to **GitHub Pages** via GitHub Actions. 1. Push to `main` branch -2. GitHub Actions runs `npm run build` +2. GitHub Actions runs `just ci`, which builds the site and checks its links 3. Static files in `dist/` are deployed to GitHub Pages Live site: https://probitas-test.github.io/ diff --git a/flake.lock b/flake.lock index 5bad000..2af9c5a 100644 --- a/flake.lock +++ b/flake.lock @@ -18,24 +18,6 @@ "type": "github" } }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1766747458, @@ -70,7 +52,7 @@ }, "probitas-flake": { "inputs": { - "flake-utils": "flake-utils_2", + "flake-utils": "flake-utils", "nixpkgs": "nixpkgs_2" }, "locked": { @@ -89,7 +71,6 @@ }, "root": { "inputs": { - "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "probitas-flake": "probitas-flake" } @@ -108,21 +89,6 @@ "repo": "default", "type": "github" } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 8a2b994..ca379df 100644 --- a/flake.nix +++ b/flake.nix @@ -1,26 +1,51 @@ { - description = "Development environment for probitas-test/documents"; + description = "Development environment for the Probitas documentation site"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; probitas-flake.url = "github:probitas-test/probitas"; - flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { nixpkgs, probitas-flake, flake-utils, ... }: - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = import nixpkgs { + outputs = + { nixpkgs, probitas-flake, ... }: + let + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); + pkgsFor = + system: + import nixpkgs { inherit system; overlays = [ probitas-flake.overlays.default ]; }; - in { - devShells.default = pkgs.mkShell { - packages = with pkgs; [ - nodejs - pagefind - probitas - ]; - }; - }); + in + { + devShells = forAllSystems ( + system: + let + pkgs = pkgsFor system; + in + { + default = pkgs.mkShell { + packages = [ + # The scripts and tests run as TypeScript through Node's type + # stripping, so the major version is named instead of following + # whatever nixpkgs' default `nodejs` is. + pkgs.nodejs_24 + pkgs.pagefind + # Runs the scenarios under probitas/ against the site. + pkgs.probitas + pkgs.just + pkgs.nixfmt + ]; + }; + } + ); + + formatter = forAllSystems (system: nixpkgs.legacyPackages.${system}.nixfmt); + }; } diff --git a/justfile b/justfile new file mode 100644 index 0000000..a122654 --- /dev/null +++ b/justfile @@ -0,0 +1,42 @@ +# Run `nix develop` (or use direnv) first so every tool is on PATH. + +set shell := ["bash", "-euo", "pipefail", "-c"] + +# List the recipes. +default: + @just --list + +# Install the npm dependencies exactly as package-lock.json pins them. +install: + npm ci + +# Start the dev server with HMR. +dev: + npm run dev + +# Build the static site into dist/ (client, SSG, Pagefind index). +build: + npm run build + +# Preview the built site. +preview: + npm run preview + +# Check the internal links of the built site. +test: + npm test + +# Regenerate data/api/ from the published npm packages. +generate-api: + npm run generate-api + +# Format the Nix files. +fmt: + git ls-files -z '*.nix' | xargs -0 nixfmt + +# Check the Nix files' formatting without changing them. +fmt-check: + git ls-files -z '*.nix' | xargs -0 nixfmt --check + +# Everything CI runs. +ci: fmt-check install build test From ebeac8e5ea2ade533f88720ad2c6e3a9b397beb0 Mon Sep 17 00:00:00 2001 From: Alisue Date: Sun, 11 Oct 2026 13:55:26 +0900 Subject: [PATCH 2/4] docs: keep the agent guide in AGENTS.md A CLAUDE.md anywhere in the tree makes Claude Code ignore AGENTS.md, and other agents read only AGENTS.md, so the guide lived where only one tool looked. Its content moves to a root AGENTS.md with the layer table, the quality gate (`just ci`), the layout, and the commit convention in use. The architecture rule no longer calls the repository "documents". Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N3xvJ1TaozjYSE6eoyo9mq --- .claude/CLAUDE.md | 38 --------------- .claude/rules/architecture.md | 4 +- AGENTS.md | 90 +++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 39 deletions(-) delete mode 100644 .claude/CLAUDE.md create mode 100644 AGENTS.md diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md deleted file mode 100644 index 3de6d44..0000000 --- a/.claude/CLAUDE.md +++ /dev/null @@ -1,38 +0,0 @@ -# Probitas Documentation Site - -Documentation for Probitas - a scenario-based testing framework shipped as a -single `probitas` binary (Bun runtime embedded). - -## Quick Reference - -- **Runtime**: Node.js + HonoX + Vite SSG -- **API Generation**: TypeDoc JSON from the npm packages -- **Deploy**: GitHub Pages - -## Commands - -```bash -npm run dev # Dev server with HMR -npm run build # Build static site (client + SSG + Pagefind) -npm run preview # Preview production build -npm test # Check internal links in the built site -npm run generate-api # Regenerate data/api/ from the npm packages -``` - -## Pre-Completion Verification - -BEFORE reporting task completion, run and ensure zero errors: - -```bash -npm run build -``` - -## Reference Probitas Packages - -When referencing Probitas API: - -- Scenarios import from `@probitas-test/probitas` (npm); the JSR mirror is - `@probitas/probitas` -- `data/api/{package}.json` holds the generated TypeDoc JSON of each package -- Check `../probitas/` for the user-facing package and CLI source -- Check `../probitas-packages/` for framework and client package source diff --git a/.claude/rules/architecture.md b/.claude/rules/architecture.md index 547a0e6..df96af9 100644 --- a/.claude/rules/architecture.md +++ b/.claude/rules/architecture.md @@ -7,7 +7,7 @@ paths: "app/**/*.{ts,tsx}, scripts/**/*.ts, vite.config.ts" ## Directory Structure ``` -documents/ +probitas-test.github.io/ ├── app/ # Application source │ ├── server.ts # HonoX entry point │ ├── client.ts # Client-side JavaScript (bundled by Vite) @@ -53,6 +53,8 @@ documents/ ├── scripts/ # Build and generation scripts │ ├── post-build.js # Pagefind indexing & llms.txt.html→llms.txt rename │ └── generate-api-docs.ts # Generate API docs from the npm packages +├── flake.nix # Nix development shell +├── justfile # Task recipes (`just ci` is the quality gate) ├── package.json # npm dependencies ├── vite.config.ts # Vite + HonoX SSG configuration └── tsconfig.json # TypeScript configuration diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8ca3e73 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,90 @@ +# probitas-test.github.io + +Documentation site for [Probitas](https://github.com/probitas-test/probitas) - a +scenario-based testing framework shipped as a single binary. + +- **Runtime**: Node.js + HonoX + Vite SSG +- **API reference**: TypeDoc JSON generated from the npm packages +- **Deploy**: GitHub Pages, from `main` + +## How it is built + +- **Test first.** Red → green → refactor. Write the failing test before the + code that makes it pass. +- **Each layer holds one thing.** Put a fact in the one layer that owns it, + and nowhere else. + + | Layer | Holds | + | --- | --- | + | Tests | the details that pin the behaviour down: rules, boundaries, tables | + | Code | how it is done | + | Commits | why a change was made | + | Comments | why it is not done another way | + +- **Dependencies are current.** Before adding or bumping one, look up its + latest release in the registry. Do not copy a version from memory or from + another repository. + +Site structure, routes and the build pipeline are described in +[`.claude/rules/architecture.md`](.claude/rules/architecture.md); writing +guidelines for the pages under `docs/` in +[`.claude/rules/content.md`](.claude/rules/content.md). + +## Commands + +Run `nix develop` (or use direnv with `.envrc.sample`) first so every tool is +on PATH, then: + +```bash +just install # npm ci +just dev # Dev server with HMR +just build # Build static site (client + SSG + Pagefind) +just preview # Preview production build +just test # Check internal links in the built site +just generate-api # Regenerate data/api/ from the npm packages +``` + +## Quality gate + +Run `just ci` before reporting a change as done. CI runs the same recipe. + +## Reference Probitas packages + +When referencing the Probitas API: + +- Scenarios import from `@probitas-test/probitas` (npm); the JSR mirror is + `@probitas/probitas` +- `data/api/{package}.json` holds the generated TypeDoc JSON of each package +- Check `../probitas/` for the user-facing package and CLI source +- Check `../probitas-packages/` for framework and client package source + +## Layout + +| Path | What lives there | +| --- | --- | +| `app/` | HonoX routes, templates, components and libraries | +| `data/` | page configuration, home page examples, generated API JSON | +| `docs/` | Markdown source of the documentation pages (site content) | +| `public/` | static assets copied into the site | +| `scripts/` | post-build step and the API reference generator | +| `tests/` | link check of the built site | +| `probitas/` | Probitas scenarios checking a served site (`DOCS_BASE_URL`) | +| `.github/` | CI and the GitHub Pages deployment | + +## Language + +- README, docs and commit messages: English +- Code comments, log and error messages: English + +## Commits + +[Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/), as +described in [`.gitmessage`](.gitmessage) (install it with +`git config commit.template .gitmessage`): + +``` +[optional scope]: +``` + +- Types: `feat`, `fix`, `deps`, `refactor`, `test`, `docs`, `style`, `chore`. +- Scopes name the area touched, such as `api`, `ci`, `scripts`, `search`. From 616c1fb127b9f45cb5fc75157be66a762809be81 Mon Sep 17 00:00:00 2001 From: Alisue Date: Sun, 11 Oct 2026 13:55:26 +0900 Subject: [PATCH 3/4] ci: split the gate from the Pages deploy and harden the workflow Pull request builds ran with the Pages write and OIDC permissions and queued behind deploys in the single "pages" group. The gate is now its own job: it loads the dev shell and runs `just ci`, and a pull request cancels its superseded runs. Only the deploy job gets pages and id-token write, and it keeps the non-cancelling "pages" group. The workflow reads the repository by default, the checkout no longer keeps credentials, and the actions are on their latest majors. upload-pages-artifact v4 stopped including dotfiles, so the build's .vite directory is included explicitly to keep the deployed site the same. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N3xvJ1TaozjYSE6eoyo9mq --- .github/workflows/deploy.yml | 64 ++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 26af321..bff692a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,49 +7,63 @@ on: pull_request: workflow_dispatch: -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read - pages: write - id-token: write - -# Allow only one concurrent deployment -concurrency: - group: "pages" - cancel-in-progress: false jobs: - build: + ci: runs-on: ubuntu-latest + # A pull request only needs its latest commit checked. A run on main waits + # for the one before it instead of cancelling it, so each one finishes. + concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 + - uses: actions/checkout@v7 with: - node-version: lts/* - cache: npm - - - run: npm ci - - - run: npm run build - - - run: npm test - - # Upload artifact for GitHub Pages + persist-credentials: false + - uses: cachix/install-nix-action@v31 + with: + extra_nix_config: | + experimental-features = nix-command flakes + # The store the development shell comes from. Only an exact match is + # restored, and only main saves, so pull requests cannot evict the + # caches each other start from. + - uses: nix-community/cache-nix-action@v7 + with: + primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('flake.nix', 'flake.lock') }} + save: ${{ github.ref == 'refs/heads/main' }} + # Load the development shell (flake.nix) into the job. + - uses: nicknovitski/nix-develop@v1 + # The same gate as local development (justfile). + - run: just ci + env: + CI: "1" - name: Upload artifact if: github.ref == 'refs/heads/main' && github.event_name == 'push' - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: path: ./dist + # Keep dist/.vite in the artifact as earlier versions of this action + # did; v4 started dropping dotfiles by default. + include-hidden-files: true deploy: if: github.ref == 'refs/heads/main' && github.event_name == 'push' - needs: build + needs: ci runs-on: ubuntu-latest + # Deployments queue up and never cancel each other. + concurrency: + group: pages + cancel-in-progress: false + permissions: + contents: read + pages: write + id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 From 22e4e8ba51a528c5265d733b07052d4c6aa7dc24 Mon Sep 17 00:00:00 2001 From: Alisue Date: Sun, 11 Oct 2026 13:55:26 +0900 Subject: [PATCH 4/4] fix: mark the site package private The manifest was still named @probitas/docs, a scope the project does not publish under, and without "private" a mistaken `npm publish` would try to release the site. It is now named after the repository and private. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N3xvJ1TaozjYSE6eoyo9mq --- package-lock.json | 4 ++-- package.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c2c5a7..de0dbcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "@probitas/docs", + "name": "probitas-test.github.io", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@probitas/docs", + "name": "probitas-test.github.io", "version": "0.1.0", "dependencies": { "hono": "^4.7.0", diff --git a/package.json b/package.json index 544209b..63ab472 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { - "name": "@probitas/docs", + "name": "probitas-test.github.io", + "private": true, "version": "0.1.0", "type": "module", "scripts": {