commit 1cad6c43041ff4e8aa812694aace18eee55baa75
parent 381be8f9a8108c46bd95b6ee032bcda6277190b9
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 28 Sep 2026 11:56:56 -0400
umtool: SONG_DATA and VIDEO_ROOT default from the home directory
song/paths.mjs: SONG_DIR ?? ~/.local/share/archilyzer/song, through
realpathSync so SONG_SCRATCH (dirname(SONG_DATA)) keeps pointing at the
real directory when that path is a symlink -- the supported way to keep
the data where it is. A path that does not exist is used as given.
spec.mjs and video-dir.mjs: VIDEO_ROOT ?? ~/reports/quartering-uh-song/
videos. The fixture comments and docs/cli.md say the new defaults.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
6 files changed, 52 insertions(+), 15 deletions(-)
diff --git a/umtool/docs/cli.md b/umtool/docs/cli.md
@@ -37,7 +37,16 @@ projects answering to one name is reported, never resolved by picking one.
## Environment
`REPORTS_DIR`, `SONG_REPORTS_DIR`, `SONG_DIR`, `CHANNELS_DIR`, `UMTOOL_INDEX_DIR`
-— which is how it is tested against the e2e fixture.
+— which is how it is tested against the e2e fixture. The path defaults
+(`lib/paths.mjs`, `song/paths.mjs`):
+
+| Variable | Default |
+|---|---|
+| `SONG_DIR` | `~/.local/share/archilyzer/song`, through its realpath — a symlink there is the supported way to keep the data where it is |
+| `SONG_REPORTS_DIR` | `~/reports/quartering-uh-song` |
+| `REPORTS_DIR` | `~/reports` (the parent of `SONG_REPORTS_DIR` when that is set) |
+| `CHANNELS_DIR` | `$TRANSCRIPTS_DIR/channels`, else the checkout's `transcripts/channels` (found by walking up from the cwd to `pnpm-workspace.yaml`) |
+| `VIDEO_ROOT` (`song/spec.mjs`, `song/video-dir.mjs`) | `~/reports/quartering-uh-song/videos` |
## `check` is the one to run before every build
diff --git a/umtool/e2e/fixtures/make-fixture.mjs b/umtool/e2e/fixtures/make-fixture.mjs
@@ -26,9 +26,9 @@ import { songCapabilities } from "./song-capabilities.mjs";
const CODE = path.resolve(path.dirname(new URL(import.meta.url).pathname), "..", "..", "song");
const dest = path.resolve(process.argv[2] ?? path.join(process.cwd(), ".e2e-song"));
-// A readdir that answers "nothing" instead of throwing. SONG_DATA's default is
-// the job temp dir the corpus was mined into, which on most machines no longer
-// exists — see the capabilities block below.
+// A readdir that answers "nothing" instead of throwing. SONG_DATA's default
+// (`~/.local/share/archilyzer/song`) does not exist on most machines — see the
+// capabilities block below.
const listDir = (p) => {
try {
return readdirSync(p);
@@ -99,8 +99,8 @@ writeFileSync(path.join(dest, "code", "um-manifest.json"), JSON.stringify({ vers
// -- a few candidates, from videos whose audio is actually present ------------
//
// TOLERANT OF A MISSING SONG_DATA, and that is the whole point of the
-// capabilities file below. `SONG_DIR`'s default is the job temp dir the corpus
-// was mined into, which on most machines no longer exists — so this readdir
+// capabilities file below. `SONG_DIR`'s default does not exist on most
+// machines — so this readdir
// used to throw and take the entire suite down before the test server started,
// including the two dozen specs that have nothing to do with the song project.
const cands = listDir(path.join(SONG_DATA, "cand2")).filter((f) => f.endsWith(".json"));
diff --git a/umtool/e2e/fixtures/song-capabilities.mjs b/umtool/e2e/fixtures/song-capabilities.mjs
@@ -5,8 +5,9 @@ import path from "node:path";
// readers.
//
// The 39 GB (`wav48/`, `asr/`, `media/`) is re-derivable from the archive and
-// deliberately not in the repo. `SONG_DIR`'s default is the job temp dir the
-// corpus was mined into, which on most machines no longer exists, so
+// deliberately not in the repo. `SONG_DIR`'s default
+// (`~/.local/share/archilyzer/song`, song/paths.mjs) is empty or absent on
+// most machines, so
// `make-fixture.mjs` builds an empty fixture and every spec that judges a clip
// used to fail — LOUDLY, as a red suite, over a machine that never had the data
// rather than over anything a change broke. Red that means "you are on a
diff --git a/umtool/song/paths.mjs b/umtool/song/paths.mjs
@@ -12,9 +12,32 @@
// SONG_DATA the bulk data, wherever it currently sits
//
// Point SONG_DIR at a copy to run the toolchain somewhere else. The default is
-// the job temp dir the corpus was mined into, which is where it still is.
+// ~/.local/share/archilyzer/song, and the data does NOT have to move there: a
+// symlink is the supported way to keep it where it is,
+//
+// mkdir -p ~/.local/share/archilyzer
+// ln -s <where the song data is> ~/.local/share/archilyzer/song
+//
+// SONG_DATA is that path's REALPATH, so lib/paths.mjs's SONG_SCRATCH
+// (dirname(SONG_DATA), where render-poly.mjs writes its scratch and logs) keeps
+// pointing at the real directory the data sits in, not at ~/.local/share. A
+// path that does not exist has no realpath; it is used as given, so a machine
+// with no song data still gets a SONG_DATA -- one every reader finds empty
+// (e2e/fixtures/song-capabilities.mjs says which specs that skips).
+import { realpathSync } from "node:fs";
+import os from "node:os";
import path from "node:path";
-export const SONG_DATA = path.resolve(
- process.env.SONG_DIR ?? "/home/user/.claude/jobs/efbe67a7/tmp/song",
+const requested = path.resolve(
+ process.env.SONG_DIR ?? path.join(os.homedir(), ".local", "share", "archilyzer", "song"),
);
+
+const realOrAsGiven = (p) => {
+ try {
+ return realpathSync(p);
+ } catch {
+ return p;
+ }
+};
+
+export const SONG_DATA = realOrAsGiven(requested);
diff --git a/umtool/song/spec.mjs b/umtool/song/spec.mjs
@@ -24,11 +24,13 @@
// keys are PRESERVED: this file is hand-edited, and silently dropping something
// somebody typed is worse than ignoring it.
//
-// VIDEO_ROOT overrides where the song directories live.
+// VIDEO_ROOT overrides where the song directories live (default
+// ~/reports/quartering-uh-song/videos).
import { readFileSync, writeFileSync, existsSync, readdirSync, statSync, renameSync, mkdirSync } from "node:fs";
+import os from "node:os";
import path from "node:path";
-const ROOT = process.env.VIDEO_ROOT ?? "/home/user/reports/quartering-uh-song/videos";
+const ROOT = process.env.VIDEO_ROOT ?? path.join(os.homedir(), "reports", "quartering-uh-song", "videos");
const argv = process.argv.slice(2);
const file = (song) => path.join(ROOT, song, "spec.json");
diff --git a/umtool/song/video-dir.mjs b/umtool/song/video-dir.mjs
@@ -45,12 +45,14 @@
// Recording is ADDITIVE: writing `wide.mp4` leaves every other entry alone, so a
// builder that makes one cut at a time does not erase the other three.
//
-// VIDEO_ROOT overrides where the song directories live.
+// VIDEO_ROOT overrides where the song directories live (default
+// ~/reports/quartering-uh-song/videos).
import { readFileSync, writeFileSync, mkdirSync, existsSync, statSync, readdirSync, renameSync } from "node:fs";
import { execFileSync } from "node:child_process";
+import os from "node:os";
import path from "node:path";
-const ROOT = process.env.VIDEO_ROOT ?? "/home/user/reports/quartering-uh-song/videos";
+const ROOT = process.env.VIDEO_ROOT ?? path.join(os.homedir(), "reports", "quartering-uh-song", "videos");
const argv = process.argv.slice(2);
const take = (flag) => {