Resume stale graphical CCS interviews

This commit is contained in:
hz
2026-08-06 03:34:07 -05:00
parent fa981f68ee
commit 38d62918d4
4 changed files with 34 additions and 4 deletions
+13
View File
@@ -250,6 +250,19 @@ public sealed class CcsCaseTests
Assert.True(CcsStore.CurrentInterviewNode(attempt, definition)!.IsTerminal);
}
[Fact]
public void TopicInterviewRepairsLegacyOpeningCompletionFlag()
{
var definition = ValidImport().Cases[0];
definition.InitialInterview = TopicInterview();
var attempt = new CcsAttempt { InterviewNodeId = "opening", OpeningCompleted = true };
CcsStore.ApplyDialogueChoice(attempt, definition, "chief_concern", DateTime.UtcNow);
Assert.False(attempt.OpeningCompleted);
Assert.Equal("chief_concern", attempt.InterviewNodeId);
}
[Fact]
public void TopicInterviewRejectsPrerequisiteCycles()
{
+12 -3
View File
@@ -436,6 +436,9 @@ public sealed class CcsStore
: [new CcsVitalSnapshot { SimulatedMinute = attempt.SimulatedMinute, Values = new Dictionary<string, string>(attempt.CurrentVitals) }];
CcsInterviewStateResponse? interview = null;
var currentInterviewNode = CurrentInterviewNode(attempt, definition);
var openingCompleted = attempt.OpeningCompleted ?? true;
if (definition.InitialInterview.Topics.Count > 0 && currentInterviewNode?.IsTerminal != true)
openingCompleted = false;
if (currentInterviewNode is not null)
{
interview = new CcsInterviewStateResponse
@@ -443,7 +446,7 @@ public sealed class CcsStore
CurrentNodeId = currentInterviewNode.Id,
CurrentNode = currentInterviewNode,
VisitedChoiceIds = attempt.InterviewChoiceIds,
Completed = attempt.OpeningCompleted ?? false
Completed = openingCompleted
};
}
return new CcsAttemptResponse
@@ -452,7 +455,7 @@ public sealed class CcsStore
CaseTitle = completed ? definition.Title : $"CCS Case {attempt.CaseExternalId}",
Mode = attempt.Mode, State = attempt.State, Revision = attempt.Revision,
SimulatedMinute = attempt.SimulatedMinute, SimulatedStartUtc = definition.InitialChart.SimulatedStartUtc,
OpeningCompleted = attempt.OpeningCompleted ?? true, Location = attempt.Location,
OpeningCompleted = openingCompleted, Location = attempt.Location,
DeadlineUtc = attempt.DeadlineUtc, HardDeadlineUtc = attempt.HardDeadlineUtc,
FinalOrdersDeadlineUtc = attempt.FinalOrdersDeadlineUtc,
CreatedUtc = attempt.CreatedUtc, RealTimeMinutes = definition.RealTimeMinutes,
@@ -482,12 +485,18 @@ public sealed class CcsStore
public static void ApplyDialogueChoice(CcsAttempt attempt, CcsCaseRevision definition, string choiceId, DateTime now)
{
if (attempt.OpeningCompleted == true) throw new InvalidOperationException("The initial interview is already complete.");
if (definition.InitialInterview.Topics.Count > 0)
{
if (attempt.OpeningCompleted == true
&& attempt.InterviewNodeId.Equals("complete", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("The initial interview is already complete.");
// Attempts that passed through the legacy classic opening can have
// this flag set before any graphical topic was completed.
attempt.OpeningCompleted = false;
ApplyTopicChoice(attempt, definition.InitialInterview, choiceId, now);
return;
}
if (attempt.OpeningCompleted == true) throw new InvalidOperationException("The initial interview is already complete.");
var interview = EffectiveInterview(definition);
var node = interview.Nodes.FirstOrDefault(value =>
value.Id.Equals(attempt.InterviewNodeId, StringComparison.OrdinalIgnoreCase))
+2 -1
View File
@@ -102,7 +102,8 @@ func _render_attempt(attempt: Dictionary) -> void:
if node.is_empty():
_show_unsupported_attempt("The graphical interview could not load its current dialogue.")
return
if bool(attempt.get("openingCompleted", false)) and not bool(node.get("isTerminal", false)):
if str(attempt.get("experienceMode", "management")) != "graphical_interview" \
and bool(attempt.get("openingCompleted", false)) and not bool(node.get("isTerminal", false)):
_show_unsupported_attempt("This case has progressed beyond the graphical interview.")
return
_show_doctor_office(node, attempt)
+7
View File
@@ -62,6 +62,13 @@ func _run() -> void:
_assert(not screen.active_scene.is_patient_dialogue_visible(), "the opening should wait for a clinician dialogue selection")
_assert(screen.active_scene.choice_container.get_child_count() == 1, "the scene should render server-provided dialogue choices")
_assert(is_equal_approx(screen.stage_root.scale.x, screen.stage_root.scale.y), "the graphical stage should preserve its aspect ratio")
var stale_graphical_attempt := _sample_attempt(false)
stale_graphical_attempt.experienceMode = "graphical_interview"
stale_graphical_attempt.openingCompleted = true
service.attempt = stale_graphical_attempt
screen._render_state()
await process_frame
_assert(screen.current_view == "doctor_office", "legacy completion flags should not block a graphical interview")
service.attempt = _sample_branching_attempt()
screen._render_state()
await process_frame