The daemon declared its own GHOSTLIGHT_RUNTIME_ID, GHOSTLIGHT_DUNGEON_BIND,
and GHOSTLIGHT_DUNGEON_ROOT. Under the managed contract Idunn owns all three:
the runtime id arrives inside the activation bundle, the bind is a candidate
socket Idunn hands down, and the state root is an admitted workload path.
A service that also names them from its own environment is a second opinion
about facts it does not own.
runtime.rs already resolved the runtime id from the admission and kept
GHOSTLIGHT_RUNTIME_ID only as the unmanaged fallback. This is the caller side
catching up: main.rs accepts --state-root and passes it through, and the recipe
declares the state_root argument binding plus the three GAMECULT_IDUNN_*
variables while dropping the three the daemon no longer reads.
The binding is required, not cosmetic. Idunn refuses a stateful workload with
no state_root argument binding and refuses one whose binding disagrees with
the admitted root, so the recipe could not have deployed without it.
The acceptance harness kept its own GHOSTLIGHT_RUNTIME_ID; it is renamed to
GHOSTLIGHT_ACCEPTANCE_RUNTIME_ID so a test-supplied identity cannot be mistaken
for the managed one.
The mesh surface test compared the published record against a freshly built
surface, including updatedAtUtc, which is a live timestamp. It now asserts the
structural fields, parses the timestamp, and compares surface and commands.
cargo test --workspace: 89 passed, 0 failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>