Keep private assessment outside strategic catch-up
This commit is contained in:
parent
a7a00ab198
commit
e5ef5e22ea
3 changed files with 67 additions and 12 deletions
|
|
@ -4083,13 +4083,14 @@ async fn command(
|
|||
if let Err(error) = validate_player_http_command(&command) {
|
||||
return (StatusCode::UNPROCESSABLE_ENTITY, Json(ErrorBody { error })).into_response();
|
||||
}
|
||||
if let Err(error) = process_due_ticks(
|
||||
&state,
|
||||
&runtime,
|
||||
ghostlight_dungeon::domain::TickSource::ReturnCatchUp,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
if player_command_requires_return_catch_up(&command)
|
||||
&& let Err(error) = process_due_ticks(
|
||||
&state,
|
||||
&runtime,
|
||||
ghostlight_dungeon::domain::TickSource::ReturnCatchUp,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
return (
|
||||
StatusCode::CONFLICT,
|
||||
|
|
@ -4722,6 +4723,13 @@ fn player_http_command_allowed(command: &WorldCommand, player_actor_id: &str) ->
|
|||
}
|
||||
}
|
||||
|
||||
fn player_command_requires_return_catch_up(command: &WorldCommand) -> bool {
|
||||
matches!(
|
||||
command,
|
||||
WorldCommand::Speak { .. } | WorldCommand::Attempt { .. } | WorldCommand::Wait { .. }
|
||||
)
|
||||
}
|
||||
|
||||
fn validate_player_http_command(command: &WorldCommand) -> Result<(), String> {
|
||||
let bounded = |label: &str, value: &str, max: usize| {
|
||||
if value.trim().is_empty() || value.chars().count() > max {
|
||||
|
|
@ -6018,6 +6026,48 @@ mod tests {
|
|||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_fictional_player_commands_require_return_catch_up() {
|
||||
assert!(player_command_requires_return_catch_up(
|
||||
&WorldCommand::Speak {
|
||||
expected_revision: 4,
|
||||
actor_id: "player".into(),
|
||||
text: "Hello.".into(),
|
||||
intended_effect: None,
|
||||
}
|
||||
));
|
||||
assert!(player_command_requires_return_catch_up(
|
||||
&WorldCommand::Attempt {
|
||||
actor_id: "player".into(),
|
||||
assessment_digest: "sha256:assessment".into(),
|
||||
}
|
||||
));
|
||||
assert!(player_command_requires_return_catch_up(
|
||||
&WorldCommand::Wait {
|
||||
expected_revision: 4,
|
||||
minutes: 10,
|
||||
}
|
||||
));
|
||||
assert!(!player_command_requires_return_catch_up(
|
||||
&WorldCommand::Assess {
|
||||
expected_revision: 4,
|
||||
intent: ActionIntent {
|
||||
actor_id: "player".into(),
|
||||
description: "Inspect the seal.".into(),
|
||||
intended_effect: "Learn whether it has authority here.".into(),
|
||||
},
|
||||
proposal: None,
|
||||
}
|
||||
));
|
||||
assert!(!player_command_requires_return_catch_up(
|
||||
&WorldCommand::SetResolutionBudget {
|
||||
expected_revision: 4,
|
||||
expected_resolution_epoch: 2,
|
||||
active_cell_budget: 8,
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn player_http_command_admission_bounds_cost_and_fictional_time() {
|
||||
assert!(
|
||||
|
|
|
|||
|
|
@ -200,8 +200,9 @@ roll's selected band in the same atomic commit as the roll receipt. Narrative
|
|||
stakes describe that transition; they are no longer the transition.
|
||||
|
||||
The scheduler may calculate pending work, but `AdvanceStrategicTick` owns the
|
||||
transition. Return catch-up invokes that same command before the next player
|
||||
action is admitted.
|
||||
transition. Return catch-up invokes that same command before the next fictional
|
||||
player action is admitted. Private assessment and resolution-policy edits do
|
||||
not enter catch-up because neither advances or enters the fiction.
|
||||
|
||||
### Narration projection authority
|
||||
|
||||
|
|
@ -678,7 +679,9 @@ notification that drops the in-flight background wave future, aborting its cell
|
|||
tasks and provider requests before further stages launch. Live requests hold a
|
||||
shared commit gate; scheduler commits require the exclusive side and therefore
|
||||
cannot cross a live request. Return catch-up deliberately uses the live command
|
||||
path and completes required ticks before admitting the player's next action.
|
||||
path and completes required ticks before admitting the player's next fictional
|
||||
action. `Assess` and resolution-policy changes remain outside fictional time and
|
||||
therefore do not launch or wait for strategic simulation.
|
||||
|
||||
### Eve/CultMesh and browser
|
||||
|
||||
|
|
|
|||
|
|
@ -135,13 +135,15 @@ exact campaign revision
|
|||
The model owns no tick mutation. A provider failure or invalid proposal leaves
|
||||
the campaign revision and world time untouched. Background inference checks
|
||||
live-turn pressure before launch and again before commit; return catch-up uses
|
||||
the same command path with player-turn priority.
|
||||
the same command path with player-turn priority for fictional commands.
|
||||
|
||||
A live request also interrupts an in-flight scheduler wave. Dropping that wave
|
||||
aborts its parallel cell tasks before they can launch later Persona stages, and
|
||||
a shared/exclusive commit gate makes scheduler commit impossible while any live
|
||||
request is active. Return catch-up is intentionally exempt because it is part of
|
||||
the live request and must finish before the requested player action.
|
||||
the live request and must finish before the requested fictional action. Private
|
||||
assessment and resolution-policy edits neither advance fiction nor invoke
|
||||
catch-up.
|
||||
|
||||
Resolution-demand focal IDs are salience hints, not partition commands. They
|
||||
cannot create mandatory singleton cells or exceed the configured budget. Cell
|
||||
|
|
|
|||
Loading…
Reference in a new issue