Commit graph

275 commits

Author SHA1 Message Date
Meta Vens
1073ae67de Point the pins at real upstreams, and name what still blocks the build
odin-core was a path dependency on `../odin-core`, a sibling that does not exist
in this repository — a leftover from when these crates lived inside Odin. It now
resolves from Odin's upstream at 3a6368f. CultLib moves from c13b6ba to 96138ff,
which is where the media contract, wire envelope and channel delivery live.

That surfaces a diamond rather than fixing one. odin-core pins CultLib at
c13b6ba; this crate now needs 96138ff; Cargo resolves two cultcache-rs copies
and the DatabaseEntry from one is not the DatabaseEntry from the other:

  git+...CultLib.git?rev=96138ff#cultcache-rs@0.1.0
  git+...CultLib.git?rev=c13b6ba#cultcache-rs@0.1.0

Muninn cannot resolve this alone. It uses 23 symbols from odin-core, and while
16 are its own records that belong here — capture stream, command boundary, HID
controller state, the five Move records, OBS catalog, Quest access, telemetry
surface, transport profile — the remaining 7 are genuinely Odin's: provider
discovery, Eve advertisement and surface records, Idunn daemon health. Moving
the 16 shrinks the coupling; it does not remove it.

The sequence that unblocks a clean build is therefore:

  1. CultLib claude/cultmesh-media-stream-contract merges to main
  2. Odin bumps its CultLib pin to that main
  3. this builds standalone

Steps 1 and 2 are not this repository's to take. Until then a local
[patch."https://github.com/GameCult/CultLib.git"] unifying both consumers on one
CultLib checkout builds fine, and is deliberately not committed — it points at
working trees that exist on one machine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 02:28:04 +02:00
Meta Vens
d2689af772 Speak the shared media contract instead of a private copy
Muninn's media records now come from CultLib rather than odin-core, and its
envelope, record keys, validators and chunk key are gone in favour of the shared
ones. 608 lines removed against 149 added, nearly all of it duplication that
existed because there was nowhere else to put it.

What went, and why it was there:

The four *WirePayload tuple structs restated each record's entire field list
positionally, kept in step by hand, for the sole purpose of applying
serde_bytes to the payload — a workaround for the DatabaseEntry derive ignoring
serde attributes. The records carry `bytes` now, so the derive emits bin itself.
CultLib has a test comparing both encodings byte for byte before the deletion.

encode/decode_media_wire_record, the four record-key builders, the four record
validators, VideoChunkKey and the feedback-key normaliser were all contract
living inside one producer. A consumer needed every one of them and had to
reimplement or approximate them, which is precisely how the C++ receiver drifted.

Muninn keeps what is genuinely its own: it names itself as producer, once, in
MUNINN_MEDIA_PRODUCER, and passes that through MediaWireProvenance. The envelope
no longer hardcodes "muninn-media:" and "muninn.media" into documents any other
producer would also have had to emit.

One test changed meaning rather than being deleted. A hex fixture captured from
the deployed C++ OBS bridge carries muninn.media_receiver_feedback.v1, which the
rename retires. It is now
`the_previous_generation_bridge_is_deliberately_no_longer_understood` and asserts
the refusal, naming what it refused. That break is real and accepted: the bridge
is being replaced by Ratatoskr, and accepting both schema names would preserve
the ownership confusion the rename exists to remove. If that test starts passing,
someone has put the old id back.

Requires CultLib 96138ff on claude/cultmesh-media-stream-contract. The pin in
Cargo.toml still points at c13b6ba and must move before this builds clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 02:24:17 +02:00
Meta Vens
2f13c6fed5 Record Ratatoskr, and what still blocks it
The receiving end now has a repo. The wire envelope does not: MuninnMediaWireRecord
and encode_media_wire_record are still here, both ends need them, and until they
reach CultLib beside the gamecult.media_* records Ratatoskr cannot decode a real
stream.

Also corrects this session's claim that no audio decoder existed. There is one --
an ffmpeg child with a hardcoded -f aac. Opus is that becoming codec-driven, and
it measures 16.8x against PCM.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 01:42:57 +02:00
Meta Vens
97e7b35865 Record what the impairment run measured
The map argued congestion collapse: a reliable channel retransmitting under
loss until it starved itself. The measurement refutes the mechanism and keeps
the conclusion. Reliable loses 31% of a clean 12 Mbps offer with no loss and no
congestion present, and barely moves as loss rises.

Also corrects the claim that June tuning was fitted against the VPS path. The
July acceptance ledger records those runs as direct LAN with no WireGuard route;
the hairpin arrived with the relocation, afterwards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 01:08:59 +02:00
Meta Vens
24d51015f8 Ask for the unreliable video lane the test always wanted
Video now opts into an unreliable media channel and audio states Reliable
explicitly, rather than both inheriting a hardcoded default.

`rudp_media_transport_options_follow_low_latency_profile` has asserted
Unreliable since it was written and has never passed. It was not aspirational:
CultNet hardcoded the media channel Reliable in both the profile builder and the
send path, so Muninn had no way to express the intent. Muninn signalled it the
only way available, by passing no reliable expiry — meaning "unreliable, so
expiry is moot" — and got reliable delivery instead.

Measured on Raven through `cultnet-impair`, 848-byte packets at 12 Mbps, which
is Muninn's real shape:

    profile    mode          delivered
    clean      unreliable      100.00%
    clean      reliable         69.10%
    loss 1%    unreliable       98.95%
    loss 1%    reliable         72.00%
    loss 3%    unreliable       96.73%
    loss 3%    reliable         73.30%

The reliable channel loses 31% of a clean 12 Mbps offer, and its delivery barely
moves as loss rises, so loss was never the binding constraint — it cannot carry
this rate at all. The unreliable channel holds every packet on a clean link and
then degrades by roughly the loss injected, which is what a media lane should
do. Reverse traffic differs by three orders of magnitude: 43 bytes of
acknowledgement against ~135,000.

An earlier draft of this message argued the defect was retransmission amplifying
load under loss. The measurement does not support that mechanism. The conclusion
survives it; the reasoning did not.

Audio passes Reliable explicitly and does not change. That pairing is still
wrong — 3 Mbps of uncompressed PCM does not belong on a reliable channel — but
that is the Opus change, and inheriting the right value silently is what
produced this bug.

Requires CultLib's `claude/media-channel-delivery`. The dependency pin still
points at c13b6ba and must be moved before this builds from a clean checkout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 01:08:07 +02:00
Meta Vens
8dfb9083ec Cut the AAC path that PCM replaced
`Switch Muninn RUDP audio to PCM packets` (2a4f08d, 2026-06-23) moved audio onto
raw PCM and left the ADTS machinery compiled in behind it. Nothing has called it
since: AudioAdtsStreamSendConfig, AudioAdtsStreamSendState and its impl,
CompleteAdtsFrames and complete_adts_frames formed a closed cluster reachable
only from its own five tests.

295 lines, verified unreachable by the compiler with tests compiled, and with
the platform trap checked — none of it sits behind a cfg, unlike the bluetoothctl
helpers that only look dead because their #[cfg(unix)] callers vanish on Windows.

Deliberately narrower than the dead-code warnings suggest. The rest of what the
compiler calls dead in media_packetizer.rs is the receiver half: frame assembly,
chunk expiry, receiver feedback. That is not abandoned work. It is the only
stand-in the sender's feedback path has for the C++ receiver in Mimir — the
tests for MuninnRudpReceiverFeedbackStats and RecentVideoChunkRepairCache build
their fixtures with build_receiver_feedback — and it is the reference shape that
the hand-rolled C++ implementation drifted away from. Deleting it would blind
the path we are debugging and discard the seed of the Rust receiver that
replaces the drift.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-10 00:28:03 +02:00
Meta Vens
7975634a1f Give the extracted Muninn a root of its own
Muninn's three crates carried 268 commits inside Odin, from "Add lazy Muninn
telemetry organ" on 2026-06-08 to the Idunn extraction merge on 2026-09-05.
That history is the evidence trail for everything the rebuild has to answer,
so it moves intact rather than being flattened into an initial commit.

Adds only what a standalone repository needs: a workspace manifest, a README
stating the authority Muninn actually holds, and the working teardown map.

Nothing is cut here and nothing builds yet. muninn-daemon still depends on
odin-core by path for twenty record types, which is one of the inversions
being removed. Deleting the old-generation deployment scripts is a separate
intent and waits on the Idunn recipe that replaces them, because three of
them are still the Restart actuators for live Idunn target records.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 23:04:41 +02:00
Meta Vens
1cb0a2f68f Merge the Idunn extraction: Odin no longer owns deployment authority
Idunn lived as a crate inside the repository of a service it manages, which
inverted the authority it exists to hold. It is now GameCult/Idunn with its
full 136-commit history; this merge removes the old body from Odin.

Cut: crates/idunn-daemon and vendor/cultmesh-rs. Odin keeps its own Idunn
projection (crates/odin-core/src/idunn.rs), its RUDP client, and its recipe
under deployment/idunn -- those are Odin's view of a managed relationship,
not Idunn's authority.

Conflicts resolved:
- crates/idunn-daemon/src/main.rs, scripts/linux/idunn-yggdrasil: deleted,
  per the extraction. Both live in GameCult/Idunn.
- docs/architecture.md: took the branch. Main still described Gjallar as
  Nightwing-resident under E:\Projects\Gjallar; it runs on Yggdrasil from
  /srv/gjallar/current, confirmed against the host.
- src/odin/documents.cjs: union. All three hunks were pure branch-side
  additions of the ghostlight.schema_catalog type; markers stripped rather
  than taking --theirs, to preserve auto-merged HEAD changes elsewhere in
  the file. node --check passes.

Verified: odin-core 22/22 on Windows; odin-daemon type-checks clean for
x86_64-unknown-linux-gnu, which is its real target -- it is unconditionally
Unix (systemd socket activation via File::from_raw_fd) and does not build
on Windows by design.

main is tagged pre-idunn-extraction-main: the last tree containing the
swarm keepalive supervision and the July Bifrost Persona feedback target,
neither of which exists on the branch or in GameCult/Idunn. See the merge
notes for why that matters.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 22:33:39 +02:00
Meta Vens
a3bb36d306 Cut idunn-daemon and the vendored cultmesh fork out of Odin
Idunn now lives at https://github.com/GameCult/Idunn with its full 136-commit
history. Odin no longer contains the deployment authority that manages it.

Also removes vendor/cultmesh-rs, the private fork that had diverged from
CultLib in both directions. Its unique capability -- the RUDP document server
and the message_id correlation rework -- was reconciled into CultLib first, so
this deletes a duplicate rather than a capability. The four crates that
consumed it by path (muninn-daemon, odin-core, odin-daemon, sleipnir-daemon)
now consume CultLib at c13b6ba like everything else.

What deliberately stays in Odin: crates/odin-core/src/idunn.rs, which is Odin's
own projection of Idunn state for aggregation and is Odin's to own;
src/odin/idunn-rudp.cjs, its client for talking to Idunn; and
deployment/idunn/recipe.toml, which despite the path is Odin's own deployment
recipe expressed in Idunn's format -- it builds odin-daemon.

odin-core: 22 passed, 0 failed, unchanged across the cut. odin-daemon still
fails to compile on Windows for the pre-existing Unix-only std::os::fd reason.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 18:09:11 +02:00
Meta Vens
9872a81643 Sever idunn-daemon's last import from odin-core
IdunnDaemonHealthTrustBindingRecord and its schema constant moved to
cultnet-rs (GameCult/CultLib@c13b6ba). odin-core now imports them from
cultnet_rs instead of defining them, and idunn-daemon/provisioning.rs
pulls all six provider-health symbols from cultnet_rs directly, merged
into its existing cultnet_rs use block. Dropped the odin-core path
dependency from idunn-daemon/Cargo.toml — nothing in the crate reaches
odin_core:: anymore. Bumped the CultLib git rev across all six
consuming manifests to the new commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 17:43:24 +02:00
Meta Vens
bfc9adf0ea Bump CultLib to ae0ac0d and give odin-core its missing rmp-serde dev-dep
Moves all six CultLib pins from cbde940 to ae0ac0d, which carries the
reconciled cultmesh-rs. The delta is three commits and near-nothing in the
packages Odin consumes: a license line, two lines in cultnet-rs replication,
and one additive pub fn.

The dev-dependency is a real pre-existing defect, not fallout from the bump.
odin-core's tests use rmp_serde in crates/odin-core/src/idunn.rs but never
declared it, so the lib-test target has never compiled. Verified pre-existing:
9 errors at the old pin, 8 at the new. With the dep declared, odin-core tests
compile and 22 pass -- including idunn_records_round_trip_through_cultmesh,
which matters for the extraction this branch is preparing.

Note for anyone checking this on Windows: odin-daemon does not build here and
that is expected, not a regression. It imports std::os::fd and from_raw_fd,
which are Unix-only. Identical failure at both pins.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 17:35:54 +02:00
Meta Vens
0535ca0a86 Apply rustfmt
The drift was 94 hunks across 4 files, not 94 files as I described it earlier.
muninn-daemon carries almost all of it; sleipnir-daemon needed four lines.

Checked rather than assumed. Ignoring whitespace, the only remaining deltas are
trailing commas rustfmt adds when it wraps an argument list, and braces removed
from single-expression closure bodies. Both are semantics-preserving.
sleipnir-daemon's change is whitespace and nothing else.

Everything except the Unix-only odin-daemon builds; muninn and sleipnir pass 162
tests, with the same pre-existing media-channel failure.

vendor/cultmesh-rs is its own workspace so `cargo fmt` does not reach it, but it
reports no drift of its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 15:28:32 +02:00
Meta Vens
c08ef0b44b Consume CultCache and CultNet from CultLib instead of forking them
The CultNet submodule and the in-tree CultCache copy are gone. Both now come
from CultLib, and vendor/cultmesh-rs is the only fork left -- it too consumes
CultLib, so exactly one version of each crate spans the graph. That mattered:
mixing sources would have put two cultnet-rs crates in one build, and the types
would not have unified.

The in-flight CultMesh RUDP document server comes with it because it is a
prerequisite, not a coincidence. The previously committed CultMesh calls
assume_connected(), a CultNet method Odin's fork carried and CultLib does not,
so the old code cannot build against CultLib at all. The newer code no longer
calls it. Verified by reverting the file and watching it fail.

Three API differences needed adapting: CultLib's transport options carry a
reconnect_policy the fork lacked (7 sites), its create_ack builds the packet
straight from ack state and so cannot fail, and its transport profile owns each
channel's reliable expiry.

Everything but odin-daemon builds; odin-daemon is Unix-only and does not compile
on this machine either way. muninn and sleipnir pass 162 tests. The one failure,
rudp_media_transport_options_follow_low_latency_profile, expects an Unreliable
media channel while every CultNet lineage including both revisions Odin had
pinned declares it Reliable, so it failed before this change too.

The in-flight idunn-daemon work is deliberately left uncommitted; the committed
tree was verified to build without it. Also left alone: the untracked
odin-daemon crate and deployment recipe, and the root manifest, whose now-dead
vendor excludes are inert. That recipe still requires the vendor/cultnet-rs
gitlink and will refuse admission until that line is dropped -- the fix is in
the working tree, waiting on that directory being tracked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 14:54:58 +02:00
Meta Vens
3ae8e361d6 Rebase Idunn actuator scripts off the projects root
The previous commit fixed the target catalog and the operator alarm, but the
actuators the catalog invokes carried the same E: paths. Idunn would have
resolved every target and then failed on the first restart or deploy it ran,
for exactly the reason the alarm failed.

Rebased 41 references across 27 scripts:

- .cmd bodies invoke their sibling .ps1 through %~dp0 instead of an absolute
  drive path, and restart-weksa.cmd reaches its sibling repo the same way.
- .ps1 parameter defaults and assignments derive from $PSScriptRoot: one level
  up for paths inside Odin, two for sibling repos under the projects root.

Left deliberately alone: $RemoteViliRoot and $StateRoot in restart-vili.ps1.
Both are consumed inside the EncodedCommand block that restart-vili ssh's to
raven, so they describe raven's filesystem, not starfire's. Whether raven also
moved off E: is an operator question, not something to infer from here.

Verified: all 46 scripts under scripts/ parse clean, and the derivation
resolves to F:\Projects\Odin and F:\Projects on this host.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 01:05:40 +02:00
Meta Vens
eabc46ad2d Read CultMath shaders from CultLib 2026-08-26 17:57:51 +02:00
Meta Vens
b204c0176f Read CultMath shaders from CultLib 2026-08-26 17:57:36 +02:00
Meta Vens
0620a9d163 Allow disabling Move hue transitions 2026-07-14 19:40:09 +01:00
Meta Vens
8e76475b4c Expose Move hue transition percentage 2026-07-14 19:29:15 +01:00
Meta Vens
e65969d29c Reject implausible Move optical observations 2026-07-14 01:06:57 +01:00
Meta Vens
a1491c7c0f Decouple Move discovery from light cadence 2026-07-13 17:18:10 +01:00
Meta Vens
7f030eb2a1 Advertise Nightwing commands over WireGuard 2026-07-13 05:34:11 +01:00
Meta Vens
5ff31a362c Consume Eve Move hue command envelopes 2026-07-13 05:30:10 +01:00
Meta Vens
1de7eccb67 Smooth Move hue transitions at 25 ms cadence 2026-07-13 05:20:20 +01:00
Meta Vens
379fe5f38c Clock Move evidence at configured camera cadence 2026-07-13 02:28:38 +01:00
Meta Vens
ad68c18133 Publish Move evidence only from fresh optical input 2026-07-13 02:24:30 +01:00
Meta Vens
854316db1b Give Move evidence its own aggregation loop 2026-07-13 02:21:13 +01:00
Meta Vens
8d29a1366f Service Muninn health transport between reports 2026-07-13 02:14:25 +01:00
Meta Vens
4eb3113aa2 Keep Muninn health on one RUDP session 2026-07-13 02:10:04 +01:00
Meta Vens
50c85e755f Allow WireGuard latency in Muninn health publication 2026-07-13 02:02:33 +01:00
Meta Vens
65b02b090a Keep remote Move providers off unconsumed local rings 2026-07-13 01:51:47 +01:00
Meta Vens
d4780756c2 Publish Move evidence transport cadence health 2026-07-13 01:46:03 +01:00
Meta Vens
be66675ed9 Stream latest Move evidence without stale queueing 2026-07-13 01:37:31 +01:00
Meta Vens
d898230792 Keep remote Move evidence independent of local ring capacity 2026-07-13 01:31:32 +01:00
Meta Vens
8ec2b61b4b Tune Move tracker exposure per camera 2026-07-13 01:23:11 +01:00
Meta Vens
4f0266d430 Measure expected Move colors in tracker frames 2026-07-13 01:18:07 +01:00
Meta Vens
cd57e07352 Terminate private Move workers with their Muninn generation 2026-07-13 01:14:47 +01:00
Meta Vens
22894e883e Keep latest private Move tracker state 2026-07-13 01:09:29 +01:00
Meta Vens
f1ca7a5d1c Expose private Move tracker stream failures 2026-07-13 01:06:10 +01:00
Meta Vens
82622b7946 Pipe Move hue state into private tracker workers 2026-07-13 01:01:35 +01:00
Meta Vens
8ad2518343 Isolate PS Move camera trackers behind Muninn workers 2026-07-13 00:55:23 +01:00
Meta Vens
914131676f Expose blocked Move tracker initialization 2026-07-13 00:44:16 +01:00
Meta Vens
fa5f901e79 Keep Move tracker feature boundary quiet 2026-07-13 00:36:29 +01:00
Meta Vens
deaea120c5 Publish per-camera Move tracker health cleanly 2026-07-13 00:36:02 +01:00
Meta Vens
013762c852 Revert "Publish per-camera Move tracker health"
This reverts commit be9ba36113.
2026-07-13 00:33:30 +01:00
Meta Vens
be9ba36113 Publish per-camera Move tracker health 2026-07-13 00:33:16 +01:00
Meta Vens
14b2b3734f Collapse Nightwing Move cameras into one Muninn provider 2026-07-13 00:22:23 +01:00
Meta Vens
77686c6fc9 Run Nightwing cameras as isolated Muninn observers 2026-07-12 23:56:39 +01:00
Meta Vens
1ee07c4c01 Trace Muninn provider command handshakes 2026-07-12 23:22:43 +01:00
Meta Vens
60b1f4a2bd Prove Muninn provider command ingress over RUDP 2026-07-12 23:19:50 +01:00
Meta Vens
4933ce118a Use provider-advertised command connection IDs 2026-07-12 23:15:03 +01:00