---
name: spiral-calibrate
description: Run a calibration check on Spiralyst Lab — play deterministic test tones via the local API, capture the audio analysis, and verify pitch detection and gain response. Invoke when the user says /spiral-calibrate or asks to verify Spiralyst Lab's audio analysis is accurate.
---

# Spiralyst Lab — calibration check

Spiralyst Lab can play deterministic test tones into the live audio path and report
the analysis result. The agent runs the loop: play tone → capture snapshot → grade
detected pitch and level against expected values. Audible by design — a quiet room
or loopback gives the cleanest numbers.

Read `$BASE` and `$AUTH` from `~/.spiralyst-lab/api-port.json` as in `/spiral-observe`,
and send the bearer token on every request.

## Preconditions

- Spiralyst Lab is running (check with `pgrep -f "Spiralyst Lab"`).
- macOS Screen-Recording permission is granted (the system-audio tap requires it).
- The app's audio source is `system` so the tap captures the tones the app plays.

## What to do when invoked

### Pitch sweep

For each frequency in a sweep (e.g. 110, 220, 440, 880, 1760 Hz):

```
curl -s -H "$AUTH" -XPOST "$BASE/api/audio/tone" \
  -H 'Content-Type: application/json' \
  -d '{"freq": 440, "durationMs": 1500, "gain": 0.5}'

sleep 0.5

curl -s -H "$AUTH" "$BASE/api/audio/snapshot"   # → reads pitchHz

curl -s -H "$AUTH" -XPOST "$BASE/api/audio/tone/stop"
```

Convert detected to cents error vs. expected:
`cents = 1200 * log2(detected / expected)`. ≤ ±5 cents = excellent; ≤ ±15 cents =
acceptable; anything wider points at a real issue.

### Gain sweep

Repeat with one frequency at varying `gain` (0.1, 0.25, 0.5, 0.75, 1.0). Confirm
`rms` in the snapshot rises monotonically.

### Beat-timing check

Stream the high-rate frame data and compute onset times from `flux` peaks:

```
curl -s -H "$AUTH" "$BASE/api/audio/frames?since=0" | jq .
```

Frames arrive at ~94 Hz, plenty resolved for tempo timing at any musical BPM.

### Render check

For each fractal type the user wants verified:

1. `POST /api/state/apply` with `{type: "<name>"}` patched in.
2. `POST /api/export/png` then `GET /api/export/png` (the PNG path).
3. Read the PNG. Confirm it's not all-black — a successful render produces visible
   geometry on the dark background.

## Report

Print a table per check: expected, detected, error / pass-fail. Tie it together with
a one-line summary: pitch accuracy within tolerance, gain response monotonic, beat
timing usable, every fractal rendered without a black-screen failure.

All of this is implementable from the public HTTP endpoints — no helper scripts on
the user's machine required.
