The UmbraTrans controller asked (commit 70c23fd) for machine-readable metadata about the audio system, in a specified layout, with a validator that ships with the request so the sender can check its own work. That request is answered here. scripts/build_controller_bundle.py emits that bundle: one real book (Deal Hunter), 8,884 records, no media, no credentials, no hostnames. It passes the controller's own validator, exit 0. Three things in this system will break a mapping that does not know them, and the bundle's real purpose is to say so precisely rather than to look tidy: * A chapter has THREE numbers. The database row id is what filenames use, chapter_index is reading order, and the distribution track number is a third thing again. Only the row id survives an edit. Front and back matter are numbered with roman numerals in the MP3 filenames. * The narrator is written three ways, and the most common way is nothing at all. 1,803 utterances have a NULL speaker_name, meaning narrator; 614 say "narrator" and 15 say "Narrator". A join that skips nulls loses most of the narration silently. Utterances now carry speaker_name_raw, the resolved name, and speaker_resolution, and the two spellings are not merged - that is a decision for whoever owns speaker identity, not something an export should do quietly. * A .wav file is not always a WAV. 30 of the 91 voice reference files are MPEG. Every render reports the container measured from the bytes, with extension_matches_container, so a reader cannot be misled by the name. A validator proves the bundle is internally consistent; it cannot prove the bundle is true. scripts/audit_controller_bundle.py re-measures every hash, byte size, duration and count against the project on disk - 66,011 claims, all holding. It caught two real defects in the first draft of this bundle, including one of my own: produced_at was stamped with the build time, which is a claim about the audio that nothing supported. The validator was also run against seven deliberately broken bundles to confirm it can fail. A validator nobody has seen fail is not evidence of anything; those cases are now tests. scripts/audio_controller_connect.py runs the T01-T10 suite: 4 pass, 1 fails, 5 are blocked. T04 fails honestly - there is no ingest endpoint, and nothing was pushed. T05-T08 are blocked because the controller's own plan sequences ingest after the metadata arrives; a proposed contract is included rather than an invented endpoint. T02 is blocked: no node key or service token is configured here, and an operator has to supply one. Two corrections to the node profile, which the controller reads: it claimed the web UI has no authentication (a session gate was added 2026-10-09) and that the controller host is offline (it is reachable and serving umbratrans-web 0.1.0). Full suite 751 passed, 8 failed - the same pre-existing pronunciation and accent failures, unchanged. ruff and mypy clean on everything added.
185 lines
7.9 KiB
JSON
185 lines
7.9 KiB
JSON
{
|
|
"_comment": [
|
|
"Umbra node profile - WHAT THIS MACHINE IS, declared by this machine.",
|
|
"Produced under spec 21 (specs/21-umbra-system-nodes-tasks-and-changes.md).",
|
|
"Read-only fact check: .venv/bin/python scripts/umbra_node_report.py --check",
|
|
"This file contains NO secrets. Only ids, names, labels and counts.",
|
|
"'status: draft' means the controller has not yet ratified this node."
|
|
],
|
|
|
|
"node_contract_version": 1,
|
|
"status": "draft",
|
|
|
|
"identity": {
|
|
"node_id": "umbraaudio-m4",
|
|
"display_name": "UmbraTools - Mac mini M4 (UmbraAudio)",
|
|
"organization": "umbra",
|
|
"role": "worker",
|
|
"products": ["umbraaudio"],
|
|
"primary_product": "umbraaudio",
|
|
"timezone": "America/Los_Angeles",
|
|
"operator": "single human operator (B.D. Murphy)"
|
|
},
|
|
|
|
"host": {
|
|
"hostname": "mini.bdmurphy.me",
|
|
"os": "macOS 26.6.2 (arm64)",
|
|
"lan_ipv4": ["192.168.123.12", "192.168.123.154"],
|
|
"storage_root": "/Volumes/1TB_APL_Storage",
|
|
"home_relocated": "/Volumes/Dock/worstauthor",
|
|
"python": "3.12.13"
|
|
},
|
|
|
|
"services": {
|
|
"web_ui": {
|
|
"bind": "0.0.0.0:8420",
|
|
"stack": "FastAPI + HTMX",
|
|
"public_name": "umbraaudio.bdmurphy.me",
|
|
"exposure": "Caddy reverse proxy on ANOTHER LAN machine -> this port",
|
|
"auth": "HTTP Basic session auth (umbraaudio/auth.py, PBKDF2-HMAC-SHA256) enforced by middleware in umbraaudio/web/__init__.py, fail-closed. added 2026-10-09.",
|
|
"autostart": "launchd LaunchAgent com.umbraaudio.web at /Library/LaunchAgents (system-wide, KeepAlive)"
|
|
},
|
|
"tts_local": {
|
|
"url": "http://localhost:8881",
|
|
"engine": "Qwen3-TTS (transformers, MPS)",
|
|
"autostart": "launchd LaunchAgent com.umbraaudio.qwentts (KeepAlive). LM Studio is MANUAL by design: an LLM model and the TTS model cannot be co-resident in 16 GB."
|
|
},
|
|
"tts_relay_listener": {
|
|
"status": "RETIRED 2026-10-08. The GPU box 192.168.123.14 was taken offline and the root relay removed; port 18881 is closed. Local :8881 is the only TTS backend.",
|
|
"note": "Do not re-add qwen_tts_helper_url or :18881 speculatively; re-add a TTS backend only when the controller exposes one."
|
|
},
|
|
"llm_local": {
|
|
"url": "http://localhost:1234/v1",
|
|
"engine": "LM Studio, google/gemma-4-e4b",
|
|
"note": "MUST NOT be co-resident with local Qwen3-TTS (16 GB unified memory)"
|
|
}
|
|
},
|
|
|
|
"capabilities": [
|
|
"audio.parse_manuscript",
|
|
"audio.segment_utterances",
|
|
"analysis.character_discovery",
|
|
"analysis.speaker_attribution",
|
|
"analysis.speaker_clarity",
|
|
"analysis.name_token_scan",
|
|
"character.personality_analysis",
|
|
"character.consistency_check",
|
|
"voice.capture_character",
|
|
"voice.reference_generation",
|
|
"voice.attribute_measurement",
|
|
"audio.render_utterances",
|
|
"audio.stitch_chapters",
|
|
"audio.normalize_loudness",
|
|
"export.m4b",
|
|
"export.mp3_package",
|
|
"review.speaker_QA"
|
|
],
|
|
"capability_status": {
|
|
"_note": "Declared per controller §2.3 as a vocabulary even with no scheduler behind it. Status is honest about what is real today.",
|
|
"character.personality_analysis": "declared_not_implemented - what exists is unversioned LLM prose in speakers.json, not a versioned artifact (alignment phase 6b finding B-03)",
|
|
"character.consistency_check": "declared_not_implemented",
|
|
"voice.capture_character": "partial - vmatrix capture exists but is name-keyed, locale-blind, and absent for the active project (manifest.md §5.4)"
|
|
},
|
|
|
|
"resource_pools": {
|
|
"cpu": {"capacity": "bounded; shares the same 16 GB as TTS", "concurrency": 1},
|
|
"llm": {"capacity": "1 x gemma-4-e4b (MPS, ~21 tok/s)", "concurrency": 1},
|
|
"tts_mps": {"capacity": "1 x Qwen3-TTS-12Hz-1.7B", "concurrency": 1, "note": "one orphan generation can stall the model lock for ~20 min"},
|
|
"gpu_cuda": {"capacity": 0, "note": "no local NVIDIA GPU; GPU work is delegated to 192.168.123.14"},
|
|
"ffmpeg": {"capacity": 1, "note": "launchd PATH must be extended or export fails"}
|
|
},
|
|
|
|
"constraints": [
|
|
"macOS Local Network TCC blocks launchd-spawned processes from all LAN IPs (EHOSTUNREACH); loopback is exempt",
|
|
"external-volume TCC: only .venv/bin/python may read/write /Volumes/1TB_APL_Storage under launchd; a shell cannot even open a script there",
|
|
"Qwen3-TTS and LM Studio must never be co-resident",
|
|
"PROJECT_BASE is hard-coded in umbraaudio/config.py and is not configurable",
|
|
"four divergent DDL authorities in this repo (see manifest.md 1.3)"
|
|
],
|
|
|
|
"projects": [
|
|
"DealHunterShip_4.0e_whole-to-audio",
|
|
"Last Dwarf",
|
|
"Treasure Island",
|
|
"Vocals"
|
|
],
|
|
|
|
"project_lifecycle": {
|
|
"DealHunterShip_4.0e_whole-to-audio": "released",
|
|
"Last Dwarf": "active",
|
|
"Treasure Island": "experimental",
|
|
"Vocals": "showcase"
|
|
},
|
|
|
|
"schema_fingerprints": ["399285355e2d456e", "b7c9ae76bc6615bd"],
|
|
"_schema_fingerprints_note": [
|
|
"Two distinct fingerprints = real per-project schema drift, deliberately NOT normalised.",
|
|
"399285355e2d456e: current shape (has name_tokens, the 5 clarity columns, and source_unit_key) - Last Dwarf, DealHunter.",
|
|
"b7c9ae76bc6615bd: older shape (no name_tokens, missing 9 utterance columns) - Treasure Island, Vocals.",
|
|
"History: 0078569a0fa6bc70 / 367d5df9e546dbba before 2026-10-03. The change is the",
|
|
"additive source_unit_key column; the drift detector reported it rather than hiding it.",
|
|
"Produced by scripts/umbra_node_report.py; re-run --check after any migration."
|
|
],
|
|
|
|
"authoritative_data_summary": {
|
|
"in_sql": ["project", "chapters", "utterances", "name_tokens"],
|
|
"in_json": [
|
|
"speakers.json",
|
|
"voice_profiles.json",
|
|
"voice_status.json",
|
|
"voice_engines.json",
|
|
"phonetic_names.json",
|
|
"m4b_meta.json"
|
|
],
|
|
"on_filesystem_only": ["voices/*_ref.wav", "voice_prompts/*.pt (vmatrix)", "chapter_audio/**/*.wav", "*.wav.txt markers"],
|
|
"never_import_as_authoritative": ["pipeline_status.json", "*_build_status.json", "manifests/*.json", "discovery_pass*.json"]
|
|
},
|
|
|
|
"facts_for_the_controller": {
|
|
"empty_sql_tables": [
|
|
"characters",
|
|
"generated_audio",
|
|
"pronunciation_guide",
|
|
"quality_reports",
|
|
"review_decisions",
|
|
"exception_reviews",
|
|
"voice_review_history"
|
|
],
|
|
"has_stable_utterance_id": true,
|
|
"source_unit_key": {
|
|
"status": "implemented and backfilled 2026-10-03",
|
|
"derivation": "sha256('uk1' | chapter_source_text_sha256 | occurrence | text_sha256)",
|
|
"project_agnostic_by_content": true,
|
|
"coverage": "10015/10015 Last Dwarf, 4319/4319 DealHunter, 3811/3811 Treasure Island, 36/36 Vocals",
|
|
"collisions": 0,
|
|
"renumbering_proven_safe": "chapter 1 renumbered +100 -> 0 keys changed",
|
|
"resolvable_without_index": "alignment phase 3: 200/200 units resolved, 0 used utterance_index"
|
|
},
|
|
"has_stable_speaker_fk": false,
|
|
"has_user_accounts": false,
|
|
"has_shared_postgres_client": false,
|
|
"has_heartbeat_or_lease": false,
|
|
"controller_stage_reached": "0 verified 2026-10-03 (health 200, heartbeat 401, no-Host 400 - all as documented)",
|
|
"test_suite": "pytest, 751 passing at 2026-10-14 (8 pre-existing failures: 2 test_pronunciation helper_url mock, 6 test_voice_profile_manager accent)",
|
|
"open_defects_blocking_exposure": [
|
|
"no authentication on any endpoint (spec 20 J.6) -- RESOLVED 2026-10-09, auth gate added in umbraaudio/web/__init__.py",
|
|
"download path traversal via str.startswith (routes.py ~5049)",
|
|
"upload filename not sanitised with Path(...).name",
|
|
"no CSRF, no security headers, no TrustedHost"
|
|
]
|
|
},
|
|
|
|
"contracts": {
|
|
"informational_only": {
|
|
"min_node_contract_commit": "4775e3b",
|
|
"note": "git HEAD moves constantly; this field is documentation, never drift-checked"
|
|
},
|
|
"specs": [
|
|
"specs/21-umbra-system-nodes-tasks-and-changes.md",
|
|
"specs/20-enterprise-database-integration.md",
|
|
"specs/19-enterprise-control-plane.md",
|
|
"specs/18-UmbraTrans.md",
|
|
"manifest.md"
|
|
]
|
|
}
|
|
} |