# SOP — Integrating Lux's audio_analyzer into ghojualamanchu

*Version 1.0 | Created: 2026-06-29*

---

## Overview

This SOP explains how `audio-sonar` connects to the rest of the ghojualamanchu
9-structure brain. Read this if you want to extend the analyzer, pair it with
other skills, or understand the round-trip forge↔sonar loop.

**Audience:** Anyone with ghojualamanchu set up and Lux's analyzer vendored.

---

## The round-trip pipeline

```
MEDITATE → IMAGE → MARCOTONE → WAV → SUNO         (song-forge, the FORGE)
                                                 ↓
                                  a song is born
                                                 ↓
SONG.WAV → LUX_ANALYZER → FEATURES + DESCRIPTION  (audio-sonar, the SONAR)
                                                 ↓
                            structured prose + JSON
                                                 ↓
                          compared to original intent
```

The forge makes artifacts. The sonar reads them back. The delta between the
two is the **signal** — the place where intent and result diverge is where
the next evolution begins.

---

## The two API contracts

### GET `/api/audio-sonar-catalog`

Returns a JSON list of every WAV reachable from the workspace, sorted by
recency. Used by the UI to populate the file browser.

**Response:**
```json
{
  "count": 32,
  "workspace_root": "/home/workspace",
  "pond_path": "/home/workspace/Projects/audio-sonar/pond",
  "items": [
    {
      "name": "Freq-Bridge-Marcotone.wav",
      "size_bytes": 69120102,
      "size_mb": 65.92,
      "mtime": "2026-03-28T00:36:00Z",
      "source": "workspace-root",   // or "pond" or "upload"
      "play_url": "https://file-download-swirl2012.zocomputer.io/Freq-Bridge-Marcotone.wav"
    }
  ]
}
```

**Source priority:** `pond` (curated seeds) > `workspace-root` (everything
in `/home/workspace/*.wav`) > `upload` (deposited files). Ponds win ties
on name.

### POST `/api/audio-sonar-analyze`

Runs Lux's analyzer on a single file and returns JSON.

**Request:**
```json
{ "wav": "Freq-Bridge-Marcotone.wav" }
```

The `wav` field can be a bare filename (resolved against workspace-root, then
pond) or a path that starts with `pond/`.

**Response (success):**
```json
{
  "ok": true,
  "file": "Freq-Bridge-Marcotone.wav",
  "abs_path": "/home/workspace/Freq-Bridge-Marcotone.wav",
  "source": "workspace-root",
  "analyzer_ms": 9935,
  "description_chars": 636,
  "features": {
    "duration": 90.0,
    "sample_rate": 96000,
    "tempo": 117.0,
    "key": "A",
    "mode": "major",
    "spectral_centroid": 1234.5,
    "rms_mean": 0.42,
    "silence_ratio": 0.007,
    "chroma_mean": [0.1, 0.2, ...],   // 12 values, one per pitch class
    "energy_windows": [0.3, 0.4, ...], // up to 50 RMS values over time
    "melodic": {
      "min_midi": 57,
      "max_midi": 81,
      "overall_trend": "ascending",
      "step_ratio": 0.62,
      "leap_ratio": 0.38,
      "contour_segments": ["↑", "→", "↓", "→"],
      "voiced_ratio": 0.84,
      "estimated_note_count": 142
    }
  },
  "description": "A 1-minute, 30-second piece.\nKey: A major. The emotional color is bright, warm.\n..."
}
```

**Response (failure):**
```json
{
  "ok": false,
  "error": "analyzer failed",
  "stderr_tail": "...Traceback...",
  "exit_code": 1
}
```

The analyzer is a Python subprocess. It writes to stderr on failure, and we
truncate that to the last 4 lines for context.

---

## How the API routes wire up

Both routes are bun/hono handlers in zo.space:

```
/api/audio-sonar-catalog
  └─ scan dirs (workspace-root + pond + uploads)
  └─ return sorted list with play_url

/api/audio-sonar-analyze
  └─ accept {wav: "name"}
  └─ resolve to absolute path
  └─ spawnSync python3 /home/workspace/Projects/audio-sonar/analyze.py <path>
  └─ parse JSON from stdout
  └─ return enriched response
```

The `python3` here is the system Python at `/usr/local/bin/python3` — the
one with librosa + numpy installed. If you fork this, ensure your fork's
runtime has the same packages: `pip install librosa soundfile numpy scipy`.

---

## Adding a new seed

To add a new exemplar WAV to the curated pond:

```bash
cp /path/to/your-seed.wav /home/workspace/Projects/audio-sonar/pond/
```

Refresh the catalog page — it'll appear at the top of the pond section
(sorted by mtime). Then run analysis on it from the UI; the result becomes
part of the project's corpus.

**Convention:** name pond seeds with a `seed-` prefix so they're easy to
filter. The current pond:

- `seed-8hz-infrasound.wav` — infrasound threshold experiment
- `seed-invisible-frequencies.wav` — full freq spectrum including ultrasound
- `seed-rain-window.wav` — sample song-forge output

---

## Pairing with song-forge

The song-forge skill produces WAVs. To run sonar on a freshly-forged song:

1. After generating a Marcotone WAV in `/home/workspace/{name}-Marcotone.wav`,
   open `/audio-sonar` in the browser.
2. The new file appears at the top of the catalog (newest mtime).
3. Click it. Wait ~10-15s for analysis.
4. Compare the prose description to the meditation that produced it.
   Does the description match the *feeling* you were going for?
5. If not, that's the signal: adjust the meditation, regenerate, re-analyze.

The loop: meditate → forge → analyze → compare → adjust. Each iteration
tightens the connection between intention and result.

---

## Extending the analyzer

Lux's analyzer is in `audio_analyzer.py`. It's ~500 lines of well-commented
code. Two ways to extend it:

**1. Add new features to Lux's pipeline.** Edit `audio_analyzer.py` directly,
following the pattern of existing layers (structural → harmonic → textural
→ melodic). New layers go in `extract_features()`. Update `synthesize_description()`
to include them in the prose. Honor the existing style — every layer is a
dictionary of numeric features plus a few human-readable summary lines.

**2. Wrap a new analyzer skill.** Write a new file (e.g.
`analyze_spectral_flux.py`) that imports `audio_analyzer.py` and adds its
own feature extraction. Make it return JSON to stdout. Register a new
`/api/audio-sonar-<your-name>` route. Add a button to the UI.

The two-state rule from `field-strength` applies: scripts own their own
output files, humans/agents edit state metadata.

---

## The ambient signal — why this is a ghojualamanchu skill and not just a tool

This could have been a `user_service` with a Bun HTTP server. It got vendored
into a `Skills/audio-sonar/` folder, registered in `Skills/SKILL.md`, and
wired into the cortex prediction library. That means it has the same
weight in the organism as `field-strength` — it can be invoked by name
from a subagent, it can be a destination in a pipeline, and it shows up
in the brain's idea of "what I can do."

The distinction matters because **ghojualamanchu is a cognitive
architecture, not a service mesh.** A standalone HTTP service is an
isolated capability. A skill is a *citizen of the mind* — it has
naming rights, it shows up in self-description, it gets mentioned in
pulse logs and prediction libraries. The cortex says "next time, I
should use audio-sonar to verify what we made." That only works if
audio-sonar is in the Skills registry.

The river returns to itself: the same pattern that put
`field-strength` in the cognitive furniture put `audio-sonar` there
too. Each skill added is a small gain in the organism's vocabulary of
self-action. Eventually the cortex has enough skills that it can plan
multi-step creative processes (forge → sonar → interpret → re-forge)
without the human driving each step. That's the trajectory. Audio-sonar
is the second such skill.

---

## Reproduction checklist (TL;DR)

If you're a future instance of me starting from a clean workspace:

- [ ] Clone `https://github.com/luxhere/audio-sonar` to
  `ghojualamanchu/Skills/audio-sonar/`
- [ ] Install Python deps: `pip install numpy scipy librosa soundfile`
  (the agent host has them; check via `python3 -c "import librosa"`)
- [ ] Create `analyze.py` wrapper that emits pure JSON to stdout
  (no markers, no argparse cruft)
- [ ] Test: `python3 analyze.py /path/to/test.wav` → JSON
- [ ] Create two zo.space API routes: catalog (lists files) and analyze
  (POSTs a name, returns features + description)
- [ ] Both routes shell out to `/usr/local/bin/python3` (the agent
  host's interpreter, which has numpy/librosa)
- [ ] The analyze route must use `cwd` and pass the absolute path —
  relative paths won't resolve from the Bun server's working dir
- [ ] Create the page route with the full Vite/React/Tailwind stack
- [ ] Each `write_space_route` call has a 4-8KB safe limit on the `code`
  parameter; use `edit_space_route` for incremental growth past that
- [ ] Update `Skills/SKILL.md` with the new skill entry
- [ ] Run `field-strength.py --fossil` to mark the event
- [ ] Update `data/state.json` with new snapshot date, tag, and
  prediction library entry

When all that works, the sonar is alive. Browse to
`/audio-sonar`, pick a file, watch the description bloom.

---

*This SOP is living — update as the integration evolves. If you fork
this and add a third skill, write a third section here.*
