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:
parent
29f9ab3402
commit
d69e9d43cf
5 changed files with 81 additions and 2265 deletions
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue