Delete Dungeon's autonomous drivers and owner controls (play agent Cut 1)

Nothing in Dungeon now runs a Persona, an operational agent or an
elaborator on its own. Deleted from runtime.rs: the cover tick driver
(drive_cover_tick, drive_one_tick, run_cover_tick, submit_clock_tick,
CoverSummary, InterruptedCell, cover_panel, the clock-tick constants,
configured_cover_budget, configured_tick_interval), the Active
elaboration sweep (elaborate_world, run_elaboration_sweep,
ELABORATION_SWEEP_INTERVAL, configured_elaboration_ceiling,
AppState.elaboration_permits), and the owner's "Let X act" route
(ControllerActPayload, dispatch_controller, admit_controller_command,
ControllerHttpResult and its builders, controller_error_disposition).
Deleted from eve.rs: CoverPanel, CoverPanelTick, cover_card, the owner's
controller buttons and descriptor, and world.controller.act from
operation_schema.

AppState.controller_quarantined is deleted as well: its only writers were
dispatch_controller and run_cover_tick, so it could never be set again.

controller_permits and configured_controller_concurrency stay for Cut 8;
until then only runtime_readiness reads the pool. ControllerRunner stays
for seeding and custody_probe; world.speak, the story card and
world.advance_time stay until Cut 9.

Tests dropped with the behaviour they pinned: the nine the cut map lists,
plus no_proposal_projection_carries_the_canonical_world_commit, which
tested the deleted controller_submission. Their fixtures go with them;
the world fixture collapses to the Draft-only path its remaining callers
use. removed_legacy_operation_is_denied_before_dispatch now also names
world.controller.act.

Docs: the MVP doc, the ontology and the handoff drop the cover,
tick-interval and elaboration-ceiling variables and the sentences that
described the deleted drivers.

Library untouched. Built and tested on Windows only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-22 01:45:57 +02:00
commit d69e9d43cf
5 changed files with 81 additions and 2265 deletions

View file

@ -1,7 +1,7 @@
//! Eve/CultUI projection for the one live world owner.
use crate::mesh::{COMMAND_BOUNDARY, COMMAND_RESULT_SCHEMA, PROVIDER_ID, SURFACE_ID};
use ghostlight::{ControllerMode, JurisdictionKey, OperatorEvent, WorldPhase, WorldSnapshot};
use ghostlight::{JurisdictionKey, OperatorEvent, WorldPhase, WorldSnapshot};
use anyhow::{Context, bail};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
@ -113,60 +113,6 @@ fn anonymous_surface_at(version: u64) -> Value {
)
}
/// `operator_log` arrives beside the snapshot rather than inside it: the story
/// feed is the human operator's surface, and no subject-facing lane may reach an
/// unscoped event log.
/// The scheduler's compute budget and what the last tick's cover did with it.
/// Rendered read-only, with no command anywhere: an Eve control to change it
/// would make a projection an owner. Change is a restart, like the model names.
#[derive(Clone, Copy, Debug)]
pub(crate) struct CoverPanel {
pub(crate) cells: u16,
pub(crate) constituent_cap: u16,
pub(crate) urgency_slots: u16,
/// `None` until the first tick has run.
pub(crate) last: Option<CoverPanelTick>,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct CoverPanelTick {
pub(crate) tick: u64,
pub(crate) cells: usize,
pub(crate) singletons: usize,
pub(crate) groups: usize,
pub(crate) oversubscribed: bool,
}
fn cover_card(panel: &CoverPanel) -> Value {
let detail = match panel.last {
None => "No tick has run yet.".to_owned(),
Some(last) => format!(
"Tick {}: {} cells, {} in detail, {} coarse.{}",
last.tick,
last.cells,
last.singletons,
last.groups,
if last.oversubscribed {
" The world has outgrown the constituent cap; the cap yielded so every subject stayed covered."
} else {
""
}
),
};
json!({
"id":"world.cover",
"kind":"card",
"props":{
"title":"Attention budget",
"detail":detail,
"cellBudget":panel.cells,
"constituentCap":panel.constituent_cap,
"urgencySlots":panel.urgency_slots
},
"children":[]
})
}
/// What the seed lane has done and what it will answer next. Everything here is
/// derived from `scale_deficit` and `revision`: in Draft every commit is a patch
/// admission, so `revision - 1` is exactly the seed patches committed since
@ -235,11 +181,13 @@ fn jurisdiction_label(world: &WorldSnapshot, jurisdiction: JurisdictionKey) -> S
}
}
/// `operator_log` arrives beside the snapshot rather than inside it: the story
/// feed is the human operator's surface, and no subject-facing lane may reach an
/// unscoped event log.
pub(crate) fn authenticated_surface(
account: &str,
snapshot: Option<&WorldSnapshot>,
operator_log: &[OperatorEvent],
cover: &CoverPanel,
) -> anyhow::Result<Value> {
let version = surface_version(snapshot);
let mut children = vec![json!({
@ -501,53 +449,6 @@ pub(crate) fn authenticated_surface(
"WorldMailbox",
));
}
if world.owner == ghostlight::PrincipalId::new(account) {
children.push(cover_card(cover));
let mut has_controller_command = false;
for (index, opportunity) in world
.opportunities
.iter()
.filter(|opportunity| {
opportunity.controller_mode != ControllerMode::Human
})
.enumerate()
{
let Some(subject) = world
.subjects
.iter()
.find(|subject| subject.id == opportunity.scope.subject_id)
else {
continue;
};
has_controller_command = true;
// The kernel owns the order; the head is simply the
// first row it returned.
let head = world
.opportunities
.first()
.is_some_and(|first| first.scope == opportunity.scope);
children.push(command_button(
&format!("world.controller.act.{index}"),
&format!(
"Let {} act{}",
subject.label,
if head { " (next)" } else { "" }
),
"world.controller.act",
json!({"opportunity":opportunity}),
&[],
));
}
if has_controller_command {
commands.push(command_descriptor(
"world.controller.act",
"ghostlight.world_controller_act.v0",
&["opportunity"],
"ControllerRunner → WorldMailbox",
));
}
}
}
}
}
@ -630,7 +531,6 @@ pub(crate) fn operation_schema(operation: &str) -> Option<&'static str> {
"world.advance_time" => "ghostlight.world_advance_time.v0",
"world.seed" => "ghostlight.world_seed.v1",
"world.speak" => "ghostlight.world_speak.v0",
"world.controller.act" => "ghostlight.world_controller_act.v0",
_ => return None,
})
}
@ -721,15 +621,6 @@ fn short_principal(principal: &str) -> String {
mod tests {
use super::*;
fn fixture_cover() -> CoverPanel {
CoverPanel {
cells: 240,
constituent_cap: 24,
urgency_slots: 36,
last: None,
}
}
fn invocation(operation: &str, schema: &str, payload: Value) -> EveCommandInvocation {
EveCommandInvocation {
schema: "gamecult.eve.command_invocation.v1".into(),
@ -763,7 +654,7 @@ mod tests {
#[test]
fn empty_authenticated_surface_has_create_without_session_zero() {
let surface = authenticated_surface("sha256:owner", None, &[], &fixture_cover()).unwrap();
let surface = authenticated_surface("sha256:owner", None, &[]).unwrap();
let encoded = serde_json::to_string(&surface).unwrap();
assert!(encoded.contains("world.create"));
assert!(encoded.contains("narrative_persona_label"));

File diff suppressed because it is too large Load diff

View file

@ -470,21 +470,12 @@ using unresolved pressure and time since the last opportunity, with subject id
as the final tiebreak. Causal exposure is derivable from `dependencies` and is
not an ordering input. It emits `DecisionOpportunity` values and cannot commit.
`drive_cover_tick` is the single owner of tick cadence, cover derivation, and
the clock: it derives the cover, runs the cells under a bounded concurrency
permit pool with a quarantine flag, and advances the clock after the cells so
every cell in a tick shares one `now` and one tick index. Its budget is
configuration (`GHOSTLIGHT_COVER_CELL_BUDGET`, `GHOSTLIGHT_COVER_CONSTITUENT_CAP`,
`GHOSTLIGHT_COVER_URGENCY_SLOTS`, `GHOSTLIGHT_CONTROLLER_MAX_CONCURRENT`,
`GHOSTLIGHT_TICK_INTERVAL_SECONDS`); changing any is a restart. Concurrency is
two pools, one per lane, each read at open. The simulation pool
(`GHOSTLIGHT_CONTROLLER_MAX_CONCURRENT`, default 4) is a budget: speak turns
and cover cells draw from it and nothing else does. The elaboration pool
(`GHOSTLIGHT_ELABORATION_MAX_CONCURRENT`, default 2, minimum 1) is a ceiling:
the most elaboration sessions that run at once, with no connector capacity
reserved for them. Neither lane acquires the other's pool. Both reach the
connector under one caller id, so under connector capacity pressure the
connector decides which request is refused. The five
Dungeon runs no turn on its own: no wall-clock tick, cover cell, elaboration
sweep or owner-requested controller turn. The clock advances only through the
owner's `world.advance_time`. The controller pool
(`GHOSTLIGHT_CONTROLLER_MAX_CONCURRENT`, default 4) is read at open; nothing
draws from it yet, and runtime readiness reads it. Changing it is a restart.
The five
controller models are configuration too (`GHOSTLIGHT_CONTROLLER_PROJECTOR_MODEL`,
`_PERSONA_MODEL`, `_INTERPRETER_MODEL`, `_OPERATIONAL_MODEL`,
`_ELABORATOR_MODEL`), each with a default when absent.
@ -501,11 +492,7 @@ against a fresh opportunity, with the delta the subject could perceive since
its bound revision (`Overheard` rows plus the typed component diff); the
Persona is never re-run, and the renewed binding carries the one it replaced
(`interrupted_from`) so a second interruption of the same turn ends it rather
than lowering it again. Eve reports an overtaken turn through
`ControllerHttpResult::Interrupted`, which renders in the command result as
`denied` today; the owner sees the interruption itself — subject, both scope
digests, the persona prose and receipt, and the untranslatable gap — in that
result's receipt text, not in the state name.
than lowering it again.
Resolution covers and grouping are compute budgets for projection. They do not
create, merge, fission, or qualify identities. Every active subject is in the

View file

@ -225,10 +225,7 @@ cell is one inference over partitioned per-constituent views returning zero
or one attributed proposal per constituent; declines are submitted first and
each proposal commits or is refused on its own scope digest; a coarse
`NarrativePersona` receives its typed view and does not enter the membrane.
Cell and constituent command ids are sha256-derived. The runtime's
`drive_cover_tick` is the single owner of tick cadence, cover derivation,
the bounded concurrency permit pool, the quarantine flag, and the clock,
which advances after the cells so every cell in a tick shares one `now`.
Cell and constituent command ids are sha256-derived.
`AdmitPatch` has a third author: `CallerId::System(SystemCapability::Consumer
{ consumer })`, minted only by `WorldMailbox::submit_consumer` after the
@ -672,10 +669,7 @@ recording it, each holding one permit of the elaboration ceiling for its whole
step. A sweep first reads the store's in-flight sessions and resumes any that
answer a current demand entry, and the caller runs sweeps one at a time. The
ceiling is the caller's, passed to each sweep; the library holds no
concurrency number. Dungeon keeps two pools: the elaboration pool
(`GHOSTLIGHT_ELABORATION_MAX_CONCURRENT`, a ceiling) and the simulation pool
(`GHOSTLIGHT_CONTROLLER_MAX_CONCURRENT`, a budget) that speak turns and cover
cells draw from, and neither lane acquires the other's. The tool catalog is
concurrency number. The tool catalog is
generated from this document's operation set and declaration kinds, plus
`record_gap` and `submit`. It cannot contain an operation the reducer does not
own, because it is derived from the reducer's vocabulary.

View file

@ -307,9 +307,7 @@ stock lens set, is implemented (`835ea4d..872ba35`; cut map
`docs/architecture/ghostlight-stock-lenses-cut.md`): lens weights are world
data the owner replaces with `SetLensWeights`, each elaborator session records
the lens it drew and that lens's instruction text, and elaboration sessions run
concurrently under Dungeon's elaboration pool
(`GHOSTLIGHT_ELABORATION_MAX_CONCURRENT`, default 2), apart from the simulation
pool.
concurrently under the caller's ceiling.
Also owed on the way, found 2026-09-15 by reading source: