Pass-2 follow-ups: dead presence check, RefKind derives, stamped world_id, stale imports and comment

Four cuts Soul named on pass 2, none of them behaviour changes.

- runtime.rs: delete the `source_version` presence check in
  `admit_controller_command`. It called `.ok_or_else(...)?` and discarded the
  value, so it validated nothing. The `expected_revision = source_version - 1`
  computation on the approve/activate path is untouched; that one still needs a
  caller-supplied binding.
- world/patch.rs: drop `Serialize`/`Deserialize` from `RefKind`.
  `RefKind::Subject(Option<SubjectKind>)` is an internally tagged newtype over a
  non-map, which serde refuses at runtime, so the derives were unexercised
  weight. `RefKind` is never persisted: it appears only in `Mismatch` (not
  serializable), the resolution index, and the three kind constants. Soul's
  `soul_ref_kind_is_not_a_serializable_shape` cannot compile as a negative
  without the derives it falsifies, so it is deleted; the doc comment on
  `RefKind` now carries the rule it was proving.
- world/mailbox.rs: document that `submit_stamped` stamps `world_id` as well as
  `revision`, and why — the mailbox owns exactly one world, so there is no other
  world a controller runner could mean. The consequence is that
  `WorldKernel::submit`'s `WorldMismatch` check is unreachable on this path;
  `soul_a_stamped_submission_still_fails_closed_on_a_foreign_scope` stays as the
  proof that scope still fails closed.
- world/mod.rs: `ComponentOp` and `RouteDeclaration` are test-only, so their
  import moves behind `#[cfg(test)]`.
  ghostlight-persona-projection: `opportunity_digest` no longer claims to bind
  "world state as one value"; pass 2 made it bind the scope digest.

cargo test -p ghostlight-dungeon --bin ghostlight-dungeon world::
  75 passed (76 minus the deleted RefKind test), 1 ignored.
cargo test -p ghostlight-dungeon: 110 passed + 1, 1 ignored.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-04 18:19:35 +02:00
commit db8eb0b2a7
7 changed files with 23 additions and 35 deletions

View file

@ -5,8 +5,8 @@
//! one process write lease. Ghostlight reports what this process actually is;
//! it never promotes its route or manufactures Ready.
use anyhow::{Context, Result, anyhow, bail, ensure};
use crate::world::{STATE_SCHEMA, state_schema_compatibility_tag};
use anyhow::{Context, Result, anyhow, bail, ensure};
#[cfg(test)]
use cultcache_rs::CacheBackingStore;
use cultcache_rs::{CultCacheEnvelope, DatabaseEntry, SingleFileMessagePackBackingStore};

View file

@ -972,11 +972,6 @@ async fn admit_controller_command(
"human decisions cannot enter through the controller runner".into(),
));
}
invocation
.operation
.route_hint
.source_version
.ok_or_else(|| RuntimeCommandError::Payload("source version is required".into()))?;
let snapshot = current_world(state)
.await
.map_err(|error| RuntimeCommandError::Payload(error.to_string()))?

View file

@ -974,13 +974,7 @@ mod tests {
.commits
.values()
.map(|commit| {
envelope(
COMMIT_ROW,
COMMIT_SCHEMA,
commit.command.id().key(),
commit,
)
.unwrap()
envelope(COMMIT_ROW, COMMIT_SCHEMA, commit.command.id().key(), commit).unwrap()
})
.collect();
let rows_for = |state: &WorldState, row_type: &str| {

View file

@ -194,6 +194,14 @@ impl WorldMailbox {
.await
}
/// Stamps both `world_id` and `expected_revision` from the live kernel. The
/// mailbox owns exactly one world, so there is no other world a caller could
/// have meant, and asking a controller runner to restate an ID it cannot
/// choose would only invent a way to be wrong. The consequence is that
/// `WorldKernel::submit`'s `WorldMismatch` check cannot fire on this path:
/// scope, not world identity, is what fails an opportunity-bearing command
/// here, and `soul_a_stamped_submission_still_fails_closed_on_a_foreign_scope`
/// is the proof that it still fails closed.
async fn submit_stamped(
&self,
command_id: CommandId,

View file

@ -21,7 +21,9 @@ pub(crate) use patch::{
AccessKind, Cost, Declaration, DraftHandle, EntityDeclaration, EntityKind, Mismatch,
PatchAnswer, Position, Ref, SubjectDeclaration, WorldPatch,
};
use patch::{ComponentOp, EdgeRecord, EntityRecord, ResolvedOp, ResolvedPatch, RouteDeclaration};
#[cfg(test)]
use patch::{ComponentOp, RouteDeclaration};
use patch::{EdgeRecord, EntityRecord, ResolvedOp, ResolvedPatch};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

View file

@ -62,8 +62,11 @@ pub(crate) struct Position {
/// expects; resolution refuses a handle that answers with another one. A subject
/// expectation carries `None` where the referring position constrains the
/// namespace but not the kind.
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(tag = "namespace", rename_all = "snake_case")]
///
/// Not serializable: a `RefKind` is never persisted. It lives in `Mismatch`, in
/// the resolution index, and in the three constants below, all of which stay in
/// memory for the length of one reduction.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum RefKind {
Subject(Option<SubjectKind>),
Entity(EntityKind),
@ -2112,18 +2115,6 @@ mod tests {
assert_eq!(candidate, kernel.state);
}
/// Soul falsification: `RefKind` is never persisted, and it could not be.
/// `Subject(Option<SubjectKind>)` under `#[serde(tag = "namespace")]` is an
/// internally tagged newtype variant over a non-map, which serde refuses at
/// runtime. The derives are unexercised weight, and the deviation is safe
/// only because nothing writes a `RefKind`.
#[test]
fn soul_ref_kind_is_not_a_serializable_shape() {
assert!(serde_json::to_value(RefKind::Entity(EntityKind::Place)).is_ok());
assert!(serde_json::to_value(ANY_SUBJECT).is_err());
assert!(serde_json::to_value(RefKind::Subject(Some(SubjectKind::Person))).is_err());
}
/// Soul falsification: the three pass-2 `Mismatch` variants that no landed
/// test names are each reachable from a real patch.
#[test]
@ -2250,12 +2241,9 @@ mod tests {
},
};
let mut candidate = kernel.state.clone();
let error = super::super::apply_effect(
&mut candidate,
&CallerId::Principal(owner()),
&forged,
)
.unwrap_err();
let error =
super::super::apply_effect(&mut candidate, &CallerId::Principal(owner()), &forged)
.unwrap_err();
assert!(matches!(error, KernelError::Invariant(_)));
assert_eq!(candidate, kernel.state);
}

View file

@ -142,7 +142,8 @@ pub struct PersonaTurnBinding {
pub controller_id: String,
/// Digest of the complete canonical decision opportunity supplied to the
/// Projector. It binds scope, controller mode, affordances, revision, and
/// world state as one value instead of inventing a second opportunity ID.
/// the scope digest as one value instead of inventing a second opportunity
/// ID.
pub opportunity_digest: String,
pub world_revision: u64,
pub scope_digest: String,