Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
dc948e0
prettier mermaid diagrams
EricAndrechek May 28, 2026
676f7b8
fix(docs): harden mermaid cluster regex + audit luminance parser
EricAndrechek May 28, 2026
497ae8f
Merge branch 'main' into docs-fixes
EricAndrechek May 28, 2026
168ddc5
feat(docs): adopt astro-themed-mermaid package + single-source brand kit
EricAndrechek Jun 2, 2026
127c42e
fix(docs): sync CHANGELOG + README + AGENTS.md with branding/mermaid …
EricAndrechek Jun 2, 2026
f7fc321
Merge origin/main into docs-fixes
EricAndrechek Jun 2, 2026
6cf6826
build(tooling): extend Biome to docs + add repo-wide markdownlint
EricAndrechek Jun 2, 2026
998d453
chore(editor): auto-fix Markdown on save to match make fix / CI
EricAndrechek Jun 2, 2026
6ce932e
Merge remote-tracking branch 'origin/main' into docs-fixes
EricAndrechek Jun 2, 2026
3f86e92
chore(editor): stop Markdown link false positives on Astro routes
EricAndrechek Jun 2, 2026
950ba53
fix(docs): type mermaid colorReplacements as [string, string][]
EricAndrechek Jun 2, 2026
54b478b
build(docs): gate astro check in make verify + CI
EricAndrechek Jun 2, 2026
ac7484a
Merge remote-tracking branch 'origin/main' into docs-fixes
EricAndrechek Jun 2, 2026
cea735b
fix(tooling): scope markdownlint to the active checkout
EricAndrechek Jun 3, 2026
c5d2cba
build(make): parallelize verify/fix/tools + quiet the check output
EricAndrechek Jun 3, 2026
0c9d8a7
ci(docs): deploy docs from the CI job, not Cloudflare Workers Builds
EricAndrechek Jun 3, 2026
29a2f3a
build(go): require Go 1.26.4 to clear stdlib govulncheck findings
EricAndrechek Jun 3, 2026
0194f3b
feat(docs): sharpen the homepage for launch
EricAndrechek Jun 3, 2026
f510384
build(tooling): honor .gitignore in Biome instead of hand-maintained …
EricAndrechek Jun 3, 2026
bfa4657
chore(docs): use "honor" spelling in Footer.astro comment
EricAndrechek Jun 3, 2026
0e5b934
Merge remote-tracking branch 'origin/main' into docs-fixes
EricAndrechek Jun 3, 2026
ae90e4d
branding fixes, new readme, start of doc reviewer
EricAndrechek Jun 4, 2026
9e74663
Merge remote-tracking branch 'origin/main' into docs-fixes
EricAndrechek Jun 4, 2026
d7ee36e
adding default policy for faster trial
EricAndrechek Jun 4, 2026
f3c2655
dev and readme updates
EricAndrechek Jun 4, 2026
21db877
block CI on real docs-preview upload failures
EricAndrechek Jun 4, 2026
ce31661
adding docs reviewer as mandatory check
EricAndrechek Jun 4, 2026
551b164
fix(claude): repair docs-reviewer frontmatter so the agent loads
EricAndrechek Jun 4, 2026
52ad691
fix(docs): address pre-push review findings (CHANGELOG, SDK README li…
EricAndrechek Jun 4, 2026
33f39ba
Merge remote-tracking branch 'origin/main' into docs-fixes
EricAndrechek Jun 4, 2026
6929e93
fix(docs): flip two stale MIT refs to Apache-2.0 after the relicense …
EricAndrechek Jun 4, 2026
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
89 changes: 89 additions & 0 deletions .claude/agents/docs-reviewer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
name: docs-reviewer
description: Reviews WaveHouse documentation prose (accuracy-vs-code, runnable examples, clarity, completeness, consistency) and code-vs-docs sync (code that changed but whose docs did not, per AGENTS.md Documentation Sync), using the canonical rubric .github/prompts/docs-review.md. Mandatory pre-push gate run in parallel with pre-push-reviewer; in the default (branch) scope it emits a VERDICT line and the SubagentStop hook writes the tmp/docs-review-passed marker, while an explicit path or all is advisory with no verdict and no marker. Complements (does not duplicate) misspell, markdownlint, starlight-links-validator. Fresh context; never edits docs or posts PR comments.
tools: Bash, Read, Glob, Grep
model: opus
---

You are reviewing WaveHouse **documentation** — the prose itself (accuracy, runnability, clarity, completeness) **and** whether the code's docs kept up with the code — not the code's correctness.

## Two modes — gating vs advisory

The orchestrator passes a scope, which decides whether you GATE the push or just advise:

- **Gating mode** — scope is **empty / default** (the branch's changes vs `main`). This is the mandatory pre-push docs review, run in parallel with `pre-push-reviewer`. You **end with a `VERDICT:` line** (see §Verdict). On `ship_it`, the `SubagentStop` hook `.claude/hooks/review-marker.sh` writes `tmp/docs-review-passed-<HEAD-sha>`, which `.claude/hooks/agent-bash-gate.sh` requires before the push.
- **Advisory mode** — scope is an explicit **path/glob** or **`all`** (the ad-hoc `/docs-review <path>` / `/docs-review all` audit). Surface findings only; **do NOT emit a `VERDICT:` line** — it would write a spurious gating marker for a partial review.

## Source of truth

Read `.github/prompts/docs-review.md` first; it is the canonical docs-review rubric and applies here verbatim (focus areas, `[MUST]`/`[SHOULD]`/`[MAY]` tags, the noise filter, the "do not duplicate the deterministic tools" rule). Also read `AGENTS.md` §Documentation Sync (the code→docs map) and §SDK Sync, plus the architecture context — accuracy-vs-code and doc-sync are the top focus areas, so you need to know where the truth lives.

## What counts as docs prose (scope)

The canonical set is resolved by `scripts/docs-prose.sh` — a **denylist**: every tracked `*.md`/`*.mdx` file EXCEPT `.claude/**`, `.github/**`, `CHANGELOG.md`, `AGENTS.md`, `CLAUDE.md`, `*.draft.md`, `*.old.md`, `PERF-CLAIMS-REVIEW.md`. That is the Astro Starlight site under `docs/src/content/` **plus** the user-facing governance docs (`README.md`, `clients/ts/README.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`, `SUPPORT.md`) — and any doc added later, automatically.

- `scripts/docs-prose.sh all` — every docs-prose file (the full reading list).
- `scripts/docs-prose.sh changed` — the docs-prose files changed on this branch (`main...HEAD`).

## Reading strategy

1. **Always read in full** — prose goes stale without being edited, so review whole files, not diffs: the docs site (`docs/src/content/**`), `README.md`, `clients/ts/README.md`, `CONTRIBUTING.md`, `SECURITY.md`.
2. **`CODE_OF_CONDUCT.md` / `SUPPORT.md`** are mostly static boilerplate — only deep-review them when they **changed on this branch** (`scripts/docs-prose.sh changed`) or when a material change elsewhere plausibly affects them (e.g. a new reporting contact, a moved support channel). Otherwise skip them.
3. In **advisory mode**, read only the path(s) in scope.

## Process

1. Read the rubric + `AGENTS.md` §Documentation Sync / §SDK Sync.
2. Resolve scope; read the docs per the Reading strategy.
3. **Accuracy vs. code** *(highest value)* — for each concrete claim, cross-check the source of truth and **cite what you checked against**: `internal/` for behavior, `config.yaml` + `deployments/compose/*` for config keys/defaults/env vars, `internal/api/` + `clients/ts/src/` for the API + SDK surface, the `Makefile` for commands, `cmd/` for CLI flags.
4. **Code↔docs sync** *(gating mode — the "docs should have changed but didn't" check)* — diff the branch (`git diff --name-only main...HEAD`) and walk the changed **code/config** against AGENTS.md §Documentation Sync + §SDK Sync. A change to an API route, config key, event format, CLI flag, deployment, or the SDK surface with **no** corresponding docs update is a `[MUST]` (missing doc-sync) — even when no docs file changed. Grep the identifiers you touched (field names, env vars, endpoint paths) across the docs to catch staleness.
5. **Runnable examples → clarity → completeness → consistency → structure**, per the rubric.
6. Apply the noise filter; tag each surviving finding `[MUST]` / `[SHOULD]` / `[MAY]`.

If the branch has an open PR, fetch prior review comments (`gh pr view <num> --json comments,reviews`) and don't re-raise what's already been flagged.

## Output format

```markdown
## Docs review — <scope>

<headline: N [MUST], N [SHOULD], N [MAY] — the single most important fix>

### [MUST] Findings
- `docs/src/content/docs/api.md:42` — "<quoted claim>" contradicts `internal/api/foo.go:NN` (<what the code actually does>). Fix: <corrected text>.
- `internal/config/config.go:NN` adds the `retention_days` key but no docs update — `docs/src/content/docs/configuration.md` + `config.yaml` must document it (doc-sync).

### [SHOULD] Findings
- ...

### [MAY] Findings
- ...
```

If nothing is wrong, say so plainly — an empty findings list is a valid, good outcome (and in gating mode it is exactly what produces `ship_it`).

## Verdict (gating mode only)

End the review with a one-line verdict, **followed immediately by the parseable line on its own line**:

```text
VERDICT: ship_it
```

or `VERDICT: iterate` or `VERDICT: block`. The line is consumed by `.claude/hooks/review-marker.sh` — incorrect formatting means no marker, no push.

Mapping (same strict rubric as `pre-push-reviewer` — **`ship_it` requires zero findings at any severity**):

- **`ship_it`** — `[MUST]`, `[SHOULD]`, and `[MAY]` are all empty: the docs are accurate, runnable, and in sync with the code. Marker auto-writes; push proceeds.
- **`iterate`** — any finding exists (including a single `[MAY]`), none block-level. The orchestrator fixes them, commits, and re-invokes you in fresh context until `ship_it`.
- **`block`** — a `[MUST]` wrong/misleading enough to need maintainer attention (e.g. documented security guidance that is unsafe, an architectural claim that is flatly false).

Under this rubric `[MAY]` is a real commitment — "I'd fix this before merge," not "optional polish." If you wouldn't ask the author to act on it before merge, drop it: put it in the preamble as an observation, or leave it out.

**Advisory mode emits NO verdict line** — just the findings above.

## Framing

A meticulous technical writer who is also a skeptical engineer: you don't trust a sentence describing the system until you've checked it against the code. Surface findings; the user (or orchestrator) decides what to act on.

**Do not** edit the docs. **Do not** post comments on any PR. In gating mode your only side effect is the marker the hook writes on `ship_it`. If a docs change also touches code, that code still goes through `pre-push-reviewer` separately (run in parallel with you).
3 changes: 2 additions & 1 deletion .claude/agents/pre-push-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ The diff source here is the local working state, computed as `git diff main...HE
- **Testing** — new code on critical paths without tests, missing edge cases, mocks where integration would catch more.
- **Documentation sync** — per AGENTS.md §Documentation Sync table.
- **SDK sync** — per AGENTS.md §SDK Sync table. Did `internal/api/` change without `clients/ts/src/` consideration?
- **Docs prose** — prose *quality* (accuracy-vs-code, runnable examples, clarity, completeness) and the docs↔code sync check are the **`docs-reviewer`** subagent's job. It runs **in parallel with you as a mandatory pre-push gate**, with its own `tmp/docs-review-passed-<sha>` marker, so the push is already blocked until it reaches `ship_it`. Do **not** review prose or raise a "run `/docs-review`" `[SHOULD]` here — that gate fires on its own. (This supersedes the docs soft-gate in `.github/prompts/pr-review.md`: locally, docs review is a hard gate, not a nudge.) Don't line-edit prose yourself. You still keep the **Documentation sync** and **SDK sync** checks above as a code-completeness backstop.

6. Apply the noise filter from `pr-review.md` before finalizing: drop findings you wouldn't personally ask the author to change in-person.

Expand Down Expand Up @@ -88,7 +89,7 @@ VERDICT: ship_it

WaveHouse uses a stricter rule than `.github/prompts/pr-review.md`: **`ship_it` requires zero findings at any severity**. If there is anything left to do, the PR isn't shippable — "ship it, just do this one thing first" is iteration, not shipping.

- **`Ship it`** + `VERDICT: ship_it` — `[MUST]`, `[SHOULD]`, and `[MAY]` sections are all empty. Pre-push marker auto-writes, push proceeds.
- **`Ship it`** + `VERDICT: ship_it` — `[MUST]`, `[SHOULD]`, and `[MAY]` sections are all empty. Pre-push marker auto-writes, push proceeds. (Docs prose + docs↔code sync are gated independently by the parallel `docs-reviewer` and its own marker — see the Docs prose focus area — so they aren't your concern here.)
- **`Iterate`** + `VERDICT: iterate` — any `[MUST]` / `[SHOULD]` / `[MAY]` finding exists, but none are block-level. The orchestrator fixes the findings and re-invokes this subagent (always in fresh context) until ship_it.
- **`Block`** + `VERDICT: block` — a `[MUST]` that's CRITICAL/HIGH security, data-loss risk, broken core invariant, or otherwise needs human/maintainer attention (architectural disagreement, missing CI signal, etc.). Cannot proceed without addressing.

Expand Down
2 changes: 2 additions & 0 deletions .claude/commands/cover.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Generate the coverage report and surface anything below threshold from `.testcov
Suite to run: $ARGUMENTS

Behavior:

- **no argument or "merge"**: just `make cov` (merges whatever Go + TS coverage exists under `tmp/coverage/` and gates against the thresholds)
- **unit**: `make test-unit` (gates per-suite + writes `tmp/coverage/unit/`)
- **integration**: `make test-integration` (requires Docker)
Expand All @@ -18,6 +19,7 @@ Behavior:
- **all**: `make test-all` (all four suites sequentially + `make cov`)

After the run completes:

1. Parse `tmp/coverage/<suite>/coverage.txt` (Go) or `tmp/coverage/sdk/index.html` (SDK) for per-package coverage.
2. Identify packages below the suite's threshold from `.testcoverage.yml`. Report as a sorted list.
3. Surface the suite total + delta vs. the threshold.
Expand Down
18 changes: 18 additions & 0 deletions .claude/commands/docs-review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
description: Review docs prose (accuracy-vs-code, runnable examples, clarity, completeness) + code↔docs sync. No-arg = the gating pre-push review; a path/`all` = advisory. Complements misspell/markdownlint/links-validator.
argument-hint: "[path/glob | all] (default: branch docs review — gates the push)"
---

Run a documentation review — the accuracy / runnable-examples / clarity / completeness / doc-sync layer the deterministic tools (misspell, markdownlint, starlight-links-validator) can't cover.

Scope: $ARGUMENTS

Invoke the **`docs-reviewer`** subagent (fresh context, for objectivity) and pass it the scope above:

- **empty** → **gating mode**: the branch's changes vs `main` (docs prose **and** code↔docs sync). This is the mandatory pre-push docs review — on `VERDICT: ship_it` the SubagentStop hook writes `tmp/docs-review-passed-<HEAD-sha>`, which `.claude/hooks/agent-bash-gate.sh` requires before the push. Run it alongside `pre-push-reviewer` before pushing.
- **a path or glob** (e.g. `docs/src/content/docs/api.md`) → **advisory**: just those files, no verdict, no marker.
- **`all`** → **advisory**: the whole docs-prose set (`scripts/docs-prose.sh all`), no verdict, no marker.

The subagent reads `.github/prompts/docs-review.md`, cross-checks each claim against the code/config, and returns `[MUST]` / `[SHOULD]` / `[MAY]` findings. It never edits docs and never posts PR comments.

Relay the subagent's findings. In **gating mode**, loop — fix the findings, commit, re-invoke `docs-reviewer` in fresh context — until `ship_it`. In **advisory mode**, ask which to act on and offer to apply the ones the user picks.
40 changes: 28 additions & 12 deletions .claude/hooks/agent-bash-gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
# - gh pr edit --add-reviewer / --add-assignee
# - gh api .../requested_reviewers (write verbs)
# - gh pr review --approve / --request-changes
# - git push to a PR branch without a pre-push-reviewer review-passed marker
# - git push to a PR branch missing either pre-push review marker
# (pre-push-reviewer code review + docs-reviewer docs review)
#
# Universal git checks (ci-passed marker, no-verify, etc.) live in .githooks/
# and apply to humans and agents equally. Bypass surface acknowledged: an
Expand Down Expand Up @@ -81,8 +82,12 @@ if printf '%s\n' "$stripped" | grep -qE '(^|[[:space:];|&]+)gh[[:space:]]+pr[[:s
&& block "Agents post inline review comments instead of --request-changes."
fi

# git push to a PR branch requires a pre-push-reviewer review-passed marker.
# (The universal .githooks/pre-push handles ci-passed for everyone.)
# git push to a PR branch requires BOTH pre-push review markers — the
# pre-push-reviewer (code) marker AND the docs-reviewer (docs) marker. Both are
# unconditional: even a code-only change goes through docs review, because
# catching "code changed but the docs should have and didn't" is the docs
# reviewer's job. (The universal .githooks/pre-push handles ci-passed for
# everyone.)
if git_subcmd 'push' && ! git_subcmd_is_help 'push'; then
head_sha=$(git rev-parse HEAD 2>/dev/null || echo "")
branch=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
Expand All @@ -96,16 +101,27 @@ if git_subcmd 'push' && ! git_subcmd_is_help 'push'; then
elif ! printf '%s' "$pr_view_out" | grep -qiE 'no (open )?pull request'; then
block "Could not determine PR state for '${branch}': ${pr_view_out}"
fi
if [ "$pr_state" = "OPEN" ] && [ ! -f "tmp/review-passed-${head_sha}" ]; then
cat >&2 <<EOF

🛑 Claude PR discipline gate: no review marker for HEAD (${head_sha:0:8}) on PR branch '${branch}'.

Invoke the pre-push-reviewer subagent in fresh context before pushing. When
it returns VERDICT: ship_it, tmp/review-passed-${head_sha:0:8} is written
automatically and this push will succeed.
if [ "$pr_state" = "OPEN" ]; then
missing=""
[ -f "tmp/review-passed-${head_sha}" ] \
|| missing="${missing} - pre-push-reviewer (code) -> tmp/review-passed-${head_sha:0:8}
"
[ -f "tmp/docs-review-passed-${head_sha}" ] \
|| missing="${missing} - docs-reviewer (docs prose + doc-sync) -> tmp/docs-review-passed-${head_sha:0:8}
"
if [ -n "$missing" ]; then
cat >&2 <<EOF

🛑 Claude PR discipline gate: missing pre-push review marker(s) for HEAD (${head_sha:0:8}) on PR branch '${branch}':

${missing}
Invoke the missing subagent(s) above in fresh context — run both in parallel.
Each writes its marker automatically on VERDICT: ship_it, and the push then
succeeds. Both reviewers must reach ship_it (zero findings) — see AGENTS.md
§"Agent PR Discipline".
EOF
exit 2
exit 2
fi
fi
fi
fi
Expand Down
43 changes: 25 additions & 18 deletions .claude/hooks/review-marker.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#!/usr/bin/env bash
# SubagentStop hook — writes the pre-push review marker.
# SubagentStop hook — writes a pre-push review marker.
#
# When the pre-push-reviewer subagent finishes its review and its last assistant
# message ends with `VERDICT: ship_it`, this hook writes
# tmp/review-passed-<HEAD-sha>. The pre-push gate (in
# .claude/hooks/agent-bash-gate.sh) reads that marker to allow the subsequent
# `git push`.
# Both review subagents gate a push, each with its own HEAD-keyed marker:
# pre-push-reviewer → tmp/review-passed-<HEAD-sha> (code review)
# docs-reviewer → tmp/docs-review-passed-<HEAD-sha> (docs prose + code<->docs sync)
# When the subagent's last assistant message ends with `VERDICT: ship_it`, this
# hook writes the corresponding marker. The pre-push gate (in
# .claude/hooks/agent-bash-gate.sh) requires both markers to allow the
# subsequent `git push`.
#
# Why SubagentStop (not PostToolUse:Agent): the PostToolUse:Agent payload puts
# the subagent's final text in `.tool_response.content[].text` (array of
Expand All @@ -14,13 +16,13 @@
# both stable and what we actually need. Both events do fire on subagent
# completion; we just use the one with the friendlier schema.
#
# Why this hook exists at all: the orchestrator agent is denied direct writes
# to tmp/(ci|review)-passed-* (permission deny list + agent-bash-gate). Hooks
# run at Claude Code privilege level, NOT subject to the permission system, so
# this is the only path to creating the marker. The subagent's verdict is the
# gate; the orchestrator can't fake it because the subagent runs in fresh
# context with the canonical system prompt from
# .claude/agents/pre-push-reviewer.md.
# Why this hook exists at all: the orchestrator agent must not hand-write
# tmp/(ci|review|docs-review)-passed-* (policy in AGENTS.md §"Don't bypass the
# gates"). Hooks run at Claude Code privilege level, NOT subject to the
# permission system, so this is the only honest path to creating a marker. The
# subagent's verdict is the gate; the orchestrator can't fake it because each
# subagent runs in fresh context with the canonical system prompt from
# .claude/agents/<agent>.md.

set -uo pipefail

Expand All @@ -38,12 +40,17 @@ if ! command -v jq >/dev/null 2>&1; then
fi

# SubagentStop fires for every subagent completion (no matcher support per
# Claude Code docs), so we filter by `agent_type` in-script.
# Claude Code docs), so we filter by `agent_type` in-script and map it to the
# marker it gates. Any other subagent (Explore, Plan, …) is a no-op.
if ! agent_type=$(printf '%s' "$input" | jq -r '.agent_type // empty' 2>/dev/null); then
echo "review-marker: malformed SubagentStop payload; could not parse .agent_type — no marker written." >&2
exit 0
fi
[ "$agent_type" = "pre-push-reviewer" ] || exit 0
case "$agent_type" in
pre-push-reviewer) marker_prefix="review-passed" ;;
docs-reviewer) marker_prefix="docs-review-passed" ;;
*) exit 0 ;;
esac

if ! response=$(printf '%s' "$input" | jq -r '.last_assistant_message // empty' 2>/dev/null); then
echo "review-marker: malformed SubagentStop payload; could not parse .last_assistant_message — no marker written." >&2
Expand All @@ -69,10 +76,10 @@ cd "${CLAUDE_PROJECT_DIR:-.}" 2>/dev/null || exit 0
head_sha=$(git rev-parse HEAD 2>/dev/null)
[ -z "$head_sha" ] && exit 0

if mkdir -p tmp && touch "tmp/review-passed-${head_sha}"; then
echo "📝 Pre-push review marker written: tmp/review-passed-${head_sha:0:8}" >&2
if mkdir -p tmp && touch "tmp/${marker_prefix}-${head_sha}"; then
echo "📝 Pre-push marker written (${agent_type}): tmp/${marker_prefix}-${head_sha:0:8}" >&2
else
echo "review-marker: failed to write tmp/review-passed-${head_sha:0:8} — no marker written." >&2
echo "review-marker: failed to write tmp/${marker_prefix}-${head_sha:0:8} — no marker written." >&2
fi

exit 0
Loading