Skip to content
Open
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
53 changes: 53 additions & 0 deletions .claude/commands/rhiza_book.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
description: Build the documentation book into a local _book folder and open it in the default browser
allowed-tools: Bash, Read
---

Build the MkDocs/zensical documentation book locally and open it in the user's
standard web browser. Follow the command-execution policy: always prefer
`make <target>`; never invoke `.venv/bin/...` directly.

This command depends on the **book** bundle (it expects a root `mkdocs.yml` and a
`make book` target from `.rhiza/make.d/book.mk`). If `mkdocs.yml` is missing,
stop and tell the user the `book` bundle is not set up here rather than guessing.

Do the following, in order:

1. **Build the book.** Run `make book`. This regenerates the `_book/` output
folder via zensical (it also runs the `_book-reports` / `_book-notebooks`
prerequisites). Run it in the foreground so build errors are visible. If it
fails, show the relevant output, diagnose the root cause, and stop — do not
try to open a stale or partial book.

2. **Confirm the output.** Verify `_book/index.html` exists after the build. If
it does not, report what `make book` produced instead and stop.

3. **Serve it locally (background).** Static zensical sites need to be served
over HTTP — search and navigation break under `file://`. Start a local server
for the built folder **in the background** so it does not block the session:

```
(cd _book && uv run python -m http.server 8000)
```
Comment on lines +27 to +31

Use port 8000 by default (this matches `make serve`). If 8000 is already in
use, pick the next free port (8001, 8002, …) and use that URL throughout.
Do **not** use `make serve` here — it rebuilds the book a second time and
blocks the terminal; the book is already built from step 1.

4. **Open the default browser.** Open the served URL with the platform's
standard opener:
- macOS: `open http://localhost:8000`
- Linux: `xdg-open http://localhost:8000`
- Windows: `start http://localhost:8000`

Detect the platform and use the right one.

5. **Report.** Tell the user the book was built at `_book/`, the URL it is being
served at, and how to stop the background server (e.g. the background task /
PID, or "kill the `http.server` process on port 8000"). Note that `_book/` is
a generated, gitignored artifact.

If `$ARGUMENTS` is non-empty, treat it as an override hint — e.g. a custom port
(`/rhiza_book 9000`) or a request to only build without serving/opening (`/rhiza_book
build-only`) — and adjust accordingly.
80 changes: 80 additions & 0 deletions .claude/commands/rhiza_quality.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
description: Run the Rhiza code-quality gate and score the repo (lint, types, docs, deps, security, tests)
---
Comment on lines +1 to +3

Assess the quality of this repo against Rhiza standards. Follow the
command-execution policy: always prefer `make <target>`; never invoke
`.venv/bin/...` directly. Run the gates in order — cheapest checks first so fast
failures surface before the slow test suite — and collect results:

1. `make fmt` — pre-commit hooks + linting (ruff format/check, markdownlint, bandit, actionlint, …)
2. `make typecheck` — static type checking (`ty`, and `mypy --strict` if configured) over `src/`
3. `make docs-coverage` — docstring coverage (interrogate) over `src/`
4. `make deptry` — unused/missing/misplaced dependency analysis
5. `make security` — pip-audit + bandit scans
6. `make validate` — validate project structure against the Rhiza template (`.rhiza/template.yml`)
7. `make test` — full test suite **with** its coverage gate (slowest, run last)

Guidelines:

- Run all gates even after an early failure, so the full picture is visible
rather than stopping at the first red.
- If something fails, show the relevant output, diagnose the root cause, and
propose (or apply, if clearly correct, low-risk, **and** the fix is in a
locally-owned file per the scoping rule below) a fix.
- If `$ARGUMENTS` is non-empty, scope the assessment to that path or topic
instead of the whole repo.
- End with a concise PASS/FAIL summary per gate.

**Coverage expectation.** `make test` enforces a coverage gate
(`COVERAGE_FAIL_UNDER`, default 90%; many projects raise it to 100%). Treat
anything below the configured threshold on locally-owned `src/` as a gap to
flag, not an acceptable baseline. When scoring the test-coverage subcategory,
the configured threshold is the bar for a 10; report uncovered lines
(`file:line`) and the test that would close each.

**`make validate`.** A failure means this repo has drifted from the Rhiza
template (a synced file edited locally, or a missing/extra file). That is
in-scope: fix it by re-syncing from Rhiza or by adjusting `.rhiza/template.yml`,
not by editing the synced artifact in place.

Then report:

- A pass/fail summary per step.
- Failures grouped by file, with the specific rule/error and line.
- A prioritized list of what to fix first (blocking errors before style nits).

Then analyse the repo and give marks on a scale of 1 to 10 for all relevant
subcategories. Pick the subcategories that fit what you actually observe — e.g.
linting/style, type safety, test pass rate, test coverage & depth, code
structure & readability, documentation, dependency & security hygiene, CI/tooling
health. For each: the score, a one-line justification grounded in evidence from
the checks above (and a quick look at the code where needed), and what would
raise it. Close with an overall score and the single highest-leverage
improvement.

**Scope the scorecard to locally-owned items — not what the mother repo (Rhiza)
owns.** This project syncs its dev infrastructure from `jebel-quant/rhiza`; see
`CLAUDE.md` for the authoritative split and the `files:` block of
`.rhiza/template.lock` for the machine-generated list of synced files. Score
only what this repo actually controls — `src/`, `tests/`, `pyproject.toml`,
Comment on lines +56 to +60
`README.md`, project-specific docs, `.rhiza/template.yml`, and any
locally-hardened config. Do **not** let Rhiza-managed files (the
`.github/workflows/*`, `Makefile`, `.pre-commit-config.yaml`, `pytest.ini`,
`ruff.toml`, the typecheck/mutation/fuzzing targets, etc.) drive the marks — a
gap there is fixed upstream in Rhiza, not here. If a relevant signal is
Rhiza-owned, note it as "upstream/out-of-scope" rather than scoring it against
this repo.

Then, from the scorecard above, identify **actionable issues to improve the
score** — one per subcategory scoring below 10 (skip any that are maxed). For
each, give: a concrete title, the subcategory and current→target score it moves,
the specific file(s)/lines or config to change, and a crisp acceptance criterion
("done when…"). Keep them in-scope (locally-owned, per the scoping rule above) —
flag anything Rhiza-owned as upstream rather than listing it as a local action.
Order them by leverage (biggest score gain for least effort first). This is a
list of recommendations only — do not create GitHub issues or change code unless
I explicitly ask.

If everything passes, say so plainly — but still produce the 1–10 subcategory
marks. Do not fix anything unless I ask — this command only assesses.
152 changes: 152 additions & 0 deletions .claude/commands/rhiza_update.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
---
description: Update the pinned Rhiza version in .rhiza/template.yml, sync, resolve conflicts, and verify
---
Comment on lines +1 to +3

Update this repo's Rhiza template to a newer release: bump the pin in
`.rhiza/template.yml`, run the sync, resolve every conflict, verify the quality
gates, and open a PR. Follow the command-execution policy: always prefer
`make <target>`; never invoke `.venv/bin/...` directly.

`$ARGUMENTS` may name a target version (e.g. `v0.19.1`). If empty, target the
**latest** release of the upstream template repo.

## 1. Determine current and target versions

- Read `.rhiza/template.yml`: the `repository:` field is the upstream template
repo (usually `jebel-quant/rhiza`) and `ref:` is the currently pinned version.
- Resolve the target version:
- If `$ARGUMENTS` names a version, use it (verify the tag exists:
`gh api repos/<repository>/git/ref/tags/<ref>`).
- Otherwise get the latest release:
`gh release view --repo <repository> --json tagName,publishedAt`.
Comment on lines +17 to +21
- If `ref:` already equals the target, report "already up to date" and stop.
- Briefly summarize what's between the two versions when it's cheap to do so
(`gh release view`/release notes), so the reviewer knows what's landing.

### The Rhiza CLI pin (`.rhiza/.rhiza-version`) — ask before changing it

`.rhiza/.rhiza-version` separately pins the **Rhiza CLI** — the `rhiza` package on
PyPI that `make sync` runs as `uvx "rhiza==<version>"`. It is independent of the
template `ref:` above, and there is no `$ARGUMENTS` override for it: always target
the **latest** published version.

- Read the current pin from `.rhiza/.rhiza-version`.
- Resolve the latest published version on PyPI:
`curl -s https://pypi.org/pypi/rhiza/json` and read `.info.version`.
- If the pin already equals the latest, there is nothing to do — leave it.
- If a newer version is available, **ask the user whether to update the CLI** to
that latest version (state current → latest). Only bump `.rhiza/.rhiza-version`
if they agree; if they decline, leave the file untouched and proceed with the
template sync alone.

## 2. Bump the pin(s) and commit (the tree must be clean to sync)

- `make sync` refuses to run on a dirty tree, so the bump lands first.
- Branch off the default branch (don't work on `main`/`master` directly):
`git checkout -b sync/rhiza-<target>`.
- Edit `ref:` in `.rhiza/template.yml` to the target version. If the user agreed
to a CLI bump above, also set `.rhiza/.rhiza-version` to the latest PyPI version
in the same step, so `make sync` runs with the chosen CLI.
- Commit the pin change(s) (e.g. `Chore: bump rhiza template ref <old> → <target>`,
noting the CLI bump in the message when one was made).

## 3. Sync

- Run `make sync` (it invokes `rhiza sync`). Expect it to either complete
cleanly or report conflicts. It writes the refreshed `.rhiza/template.lock`.
- If it completes with no conflicts, skip to step 5.

## 4. Resolve every conflict

The sync is a 3-way merge. Two kinds of leftovers can appear — handle both, and
finish with **zero** `*.rej` files and **zero** conflict markers
(`<<<<<<<` / `=======` / `>>>>>>>`) anywhere tracked
(`git grep -lE '^(<<<<<<<|=======|>>>>>>>)'`).

**`*.rej` files (rejected hunks).** The 3-way merge often *already applied* a
hunk and still drops a duplicate `.rej`. For each, verify whether the change is
already present in the file (the added `+` lines exist; no conflict markers
remain). If it is, the `.rej` is spurious — delete it. If a hunk genuinely did
not apply, apply it by hand, then delete the `.rej`.

**Conflict-marked files.** Resolve by the ownership rule (see `CLAUDE.md` and the
`files:` block of `.rhiza/template.lock` for the authoritative managed-file list):

Comment on lines +72 to +74
- **Rhiza-managed files** (the `.github/workflows/*`, `Makefile`,
`.pre-commit-config.yaml`, `pytest.ini`, the `.rhiza/` engine, etc.): take the
**incoming/upstream** side — these are owned by the template and should match
it (`git checkout --theirs -- <file>` then `git add`).
- **Locally-owned or locally-hardened files** (notably `ruff.toml`, plus
`pyproject.toml`, `README.md`, `src/`, your `tests/`): **merge by hand** —
keep the local intent (e.g. stricter lint rules) while folding in genuine
upstream additions, and make the result internally coherent (dedupe, drop
comments that now contradict the config).

Validate every touched workflow/YAML still parses before moving on.

## 5. Verify the gates and fix fallout

A version bump can tighten the gates (new lint rules, `mypy --strict`, expanded
docs-coverage scope, etc.) and surface pre-existing issues. Run them and get
them green:

1. `make fmt` — pre-commit + lint
2. `make typecheck`
3. `make docs-coverage`
4. `make deptry`
5. `make security`
6. `make test`

**Scope your fixes.** Fix issues only in **locally-owned** files (`src/`,
`tests/`, `pyproject.toml`, locally-hardened config). If a gate fails because of
a **Rhiza-managed** file, that is an upstream problem: fix it in
`jebel-quant/rhiza` and bump again — do **not** edit the synced artifact in
place. Call out any such upstream-owned failure explicitly rather than papering
over it locally.

### Configure the CI variables/secrets the synced workflows need (fuzzing & mutation)

If this sync added or updated the fuzzing/mutation workflows
(`.github/workflows/rhiza_fuzzing.yml`, `.github/workflows/rhiza_mutation.yml`),
make sure the configuration they read is present. These are **GitHub Actions
repository variables and secrets** — *not* local env vars or files — set under
**Settings → Secrets and variables → Actions** (or via `gh`), and documented in
`.github/CONFIG.md`. Skip this step entirely if neither workflow is present.

Comment on lines +111 to +115
Inspect what is already set (presence only — secret values are never readable):

- `gh variable list`
- `gh secret list`

**Mutation** (`rhiza_mutation.yml`) reads:

- `MUTATION_ENABLED` (variable) — must be `true` for the mutation gate/badge to run.
- `GH_PAT` (secret) — git auth for installing private dependencies.
- `UV_EXTRA_INDEX_URL` (secret) — extra package index URL (with credentials) for private deps.

**Fuzzing** (`rhiza_fuzzing.yml`) needs no user configuration — it uses the
automatic `GITHUB_TOKEN`, so do not prompt for any fuzzing secret.

For each of the above that is **missing**, **ask the user** whether to set it and
for its value; set only the ones they provide:

- variables: `gh variable set MUTATION_ENABLED --body true`
- secrets: `gh secret set GH_PAT` (let `gh` read the value from stdin — never
echo a secret value into the transcript or a commit).

Leave anything the user declines unset, and note it in the PR body so the reviewer
knows the corresponding workflow step will be skipped or fail until it is configured.

## 6. Commit, push, open a PR

- Commit the resolution and any in-scope fixes with clear messages (one logical
change per commit: the conflict resolution, then each gate fix).
- Push the branch and open a PR (`gh pr create`) titled for the bump, e.g.
`Chore: sync Rhiza template <old> → <target>`. In the body, summarize how each
conflict was resolved, list any gate fallout you fixed, and flag anything that
needs an **upstream** fix in Rhiza.
- Report a concise per-gate PASS/FAIL summary. If the workflow files changed,
note that pushing them needs a token with the `workflow` scope.

Do not merge the PR. Stop after it is open and summarize what landed and what
(if anything) is blocked on an upstream Rhiza change.
Loading