The controller (oldbox) sent a data-model alignment proposal and asked for six
additive changes on this node, the validation protocol, and an honest report.
Six of the seven changes are done; the seventh is declared missing rather than faked.
Stage 0 - reachability verified exactly as documented: health 200, heartbeat 401
(the correct answer - auth is refusing), missing Host 400. Nothing else was sent.
source_unit_key (controller 5.1, the blocking item)
umbraaudio/source_units.py. sha256("uk1" | chapter_source_text_sha256 |
occurrence | text_sha256). Nothing positional in the key: "occurrence"
disambiguates repeated text in document order.
- 18,181/18,181 units keyed across all four projects, 0 collisions
- chapter 1 renumbered +100 -> 0 keys changed (the load-bearing proof)
- backfill 10,015 rows in 0.11s, second run stamps 0 (idempotent)
- alignment phase 3: 200/200 probe units resolved with used_index: false
- tolerates the older projects that have no chapters.chapter_index
Recorded at generation time in chapter_builder, BEFORE the rows are read and
BEFORE the marker is written, so every path that renders audio also produces
identity and the clip can name its source unit.
5.7 recorded: utterance_index is KEPT as a display ordinal and made
non-load-bearing. spec 21 §10.
Blob hashes and effect chain (5.2)
Markers now carry the full sha256 of the delivered bytes and the effects that
produced them. Verified the existing order is already correct - write_marker
runs AFTER apply_effects - and closed the remaining hole: a changed trait list
changed the hash with no recorded cause. sha256 verification is opt-in, since
hashing every file on every validation pass would re-read gigabytes.
Vocabularies published (5.4) - umbraaudio/definitions.py, 19 sets, 126 values,
each value carrying whether it is 'declared' (spec/controller) or 'extracted'
(read out of code), so a diff shows a difference of meaning, not of wording.
Alignment report tool (6.6) - scripts/alignment_report.py --phase all. Emits the
controller's envelope with the per-phase evidence shapes, and obeys the two
rules they set: no evidence means blocked, never pass; a field we cannot produce
is null PLUS a finding, never omitted. Phase 2 is deliberately 'fail' - the
three-way store disagreement is real, and a pass there would be a check that
passed by doing nothing.
Fingerprint discipline (5.5) - the drift detector reported its OWN fingerprint
change when source_unit_key was added rather than hiding it. Node profile
updated with the new fingerprints and an honest note that they moved.
Node report extended - per-project source-unit-key coverage, per-voice-asset
sha256 + object_key, definition_set_version, controller stage reached.
Six gaps found in the proposal (spec 21 §11), including the one they invited plus
five more. The sharpest: "hash at generation" ignores that audio is
post-processed afterwards, and our key is anchored to chapters.source_text - a
second copy of the manuscript - so any change to chapter storage silently
invalidates every key in that chapter.
5.6 deliberately NOT faked: personality has no versioned artifact here, only
unversioned LLM prose in speakers.json. Reported as phase 6b finding B-03
instead, because wrapping unversioned inputs in a version field would produce
artifacts claiming a lineage they do not have.
Answered the controller's two open questions (infra/controller-handoff-2026-10-03.md):
forward-only link for a revealed recurring character, and yes - a new edition may
contradict a frozen state, since this node's correction path IS a regeneration.
Full suite 484 passed / 8 failed - the same 8 pre-existing and unrelated failures.
ruff clean on every file touched. The parallel session's chapter_builder hunk
(helper_url) was filtered out of the commit and left intact in the working tree.
185 lines
7.4 KiB
JSON
185 lines
7.4 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": "NONE (unsigned active_project cookie). Do not expose beyond the LAN.",
|
|
"autostart": "launchd LaunchAgent com.umbraaudio.web (login-scanned, KeepAlive)"
|
|
},
|
|
"tts_local": {
|
|
"url": "http://localhost:8881",
|
|
"engine": "Qwen3-TTS (transformers, MPS)",
|
|
"autostart": "NONE - manual nohup .venv/bin/python scripts/qwen_tts_server.py"
|
|
},
|
|
"tts_relay_listener": {
|
|
"url": "http://127.0.0.1:18881",
|
|
"note": "root LaunchDaemon on this host forwards to a LAN GPU box; has no upstream fallback"
|
|
},
|
|
"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, 484 passing at 2026-10-03 (8 pre-existing/parallel failures)",
|
|
"open_defects_blocking_exposure": [
|
|
"no authentication on any endpoint (spec 20 J.6)",
|
|
"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"
|
|
]
|
|
}
|
|
} |