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))