#![allow(clippy::too_many_arguments)]
use meerkat_machine_dsl::machine;
use super::OptionValueExt;
#[derive(
Debug,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Default,
serde::Serialize,
serde::Deserialize,
)]
pub struct SessionId(pub String);
#[derive(
Debug,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Default,
serde::Serialize,
serde::Deserialize,
)]
pub struct RecoveryCandidateId(pub String);
impl<T: Into<String>> From<T> for SessionId {
fn from(value: T) -> Self {
Self(value.into())
}
}
impl SessionId {
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum SessionFirstTurnPhase {
#[default]
Inactive,
Pending,
Consumed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum SessionInitialPromptStageDecision {
#[default]
Clear,
Store,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RealtimeTranscriptRoleKind {
#[default]
User,
Assistant,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RealtimeTranscriptLaneKind {
#[default]
Display,
Spoken,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RealtimeTranscriptStopReasonKind {
Cancelled,
ToolUse,
#[default]
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RealtimeTranscriptMaterializeDecision {
#[default]
Wait,
MarkSkipped,
MaterializeUser,
MaterializeAssistant,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RealtimeUserContentIdentityDisposition {
#[default]
RejectInvalidIdentity,
RejectUnmaterializedPredecessor,
RejectConflict,
AlreadyCommitted,
CommitNew,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RealtimeUserContentBlobStageDisposition {
#[default]
RejectOccupied,
StageNew,
ReuseExact,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RealtimeUserContentBlobRecoveryDisposition {
#[default]
NoPending,
RetryExact,
CommitVerifiedBeforeCurrent,
ClearInvalidBeforeCurrent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RealtimeUserContentBlobFinalizeDisposition {
#[default]
RejectMismatch,
NoPending,
ClearCommitted,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum ObservedSessionTailKind {
#[default]
Empty,
System,
SystemNotice,
User,
BlockAssistant,
ToolResults,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum PendingContinuationDisposition {
RunPending,
#[default]
NoPendingBoundary,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum PendingContinuationPublicTerminal {
#[default]
NoPendingBoundary,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum ResumeOverrideRejection {
#[default]
ProviderRequiresModel,
BuildOnlyAfterFirstTurn,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum ResumeProviderSelection {
#[default]
RecomputeFromModel,
UseOverride,
UseStored,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum ResumeSelfHostedSelection {
#[default]
Clear,
UseOverride,
Retain,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum LiveSessionAuthorityKind {
#[default]
LiveAuthoritative,
DurableAuthoritative,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum LiveSessionAuthorityReason {
#[default]
StoredArchived,
LiveUncommittedTranscript,
StoredTranscriptRevisionDiverged,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RunIdCardinality {
#[default]
NoRunId,
SingleRunId,
MultipleRunIds,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum DurableTailStopReason {
#[default]
Absent,
EndTurn,
ToolUse,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum DurableTailRecoveryClass {
CompletedCandidate,
InterruptedRepairableCandidate,
#[default]
Ambiguous,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum DurableHeadRelation {
#[default]
AbsentOrExact,
RuntimeSnapshotAhead,
VerifiedStrictDescendant,
Diverged,
Unverifiable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum SessionDocumentLifecycle {
#[default]
Active,
Archived,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum SessionArchiveDisposition {
#[default]
Archive,
AlreadyArchived,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum SessionArchiveRuntimeObservation {
#[default]
Absent,
RetirementRequired,
QuiescentTerminal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RuntimeCheckpointProjectionDisposition {
#[default]
IgnoreArchived,
Project,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum SessionDocumentLifecycleMerge {
#[default]
CarryArchived,
CarryAuthority,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum TranscriptEditKind {
#[default]
Fork,
Rewrite,
}
machine! {
machine SessionDocumentMachine {
version: 1,
rust: "self" / "catalog::dsl::session_document",
state {
lifecycle_phase: SessionDocumentPhase,
session_first_turn_phase: Map<SessionId, Enum<SessionFirstTurnPhase>>,
session_pending_initial_prompt_present: Map<SessionId, bool>,
session_pending_tool_results_count: Map<SessionId, u64>,
session_lifecycle_terminal: Map<SessionId, Enum<SessionDocumentLifecycle>>,
}
init(Ready) {
session_first_turn_phase = EmptyMap,
session_pending_initial_prompt_present = EmptyMap,
session_pending_tool_results_count = EmptyMap,
session_lifecycle_terminal = EmptyMap,
}
terminal []
phase SessionDocumentPhase {
Ready,
}
input SessionDocumentInput {
MarkSessionInitialTurnPending { session_id: SessionId },
StartSessionInitialTurn { session_id: SessionId },
StageSessionInitialPrompt { session_id: SessionId, prompt_has_content: bool },
StageSessionToolResults { session_id: SessionId, result_count: u64 },
ConsumeSessionDeferredInputs { session_id: SessionId },
RestoreSessionConsumedInputs {
session_id: SessionId,
restore_first_turn_pending: bool,
pending_initial_prompt_present: bool,
pending_tool_result_message_count: u64,
},
RecoverSessionFirstTurnPhase {
session_id: SessionId,
phase: Enum<SessionFirstTurnPhase>,
pending_initial_prompt_present: bool,
pending_tool_result_message_count: u64,
},
ResolveSessionFirstTurnOverridesAllowed { session_id: SessionId },
ResolveRealtimeItemObserved {
role: Enum<RealtimeTranscriptRoleKind>,
response_discarded: bool,
},
ResolveRealtimeItemSkipped,
ResolveRealtimeUserTranscriptFinal {
text_present: bool,
segment_empty: bool,
segment_matches: bool,
},
ResolveRealtimeUserContentFinal {
content_present: bool,
segment_empty: bool,
segment_matches: bool,
},
ResolveRealtimeUserContentIdentity {
identity_fields_valid: bool,
key_tombstoned: bool,
predecessor_materialized: bool,
existing_identity_present: bool,
existing_payload_matches: bool,
target_item_id_available: bool,
reducer_commit_proof_required: bool,
reducer_commit_proof_present: bool,
},
ResolveRealtimeUserContentBlobStage {
pending_present: bool,
pending_matches_request: bool,
},
ResolveRealtimeUserContentBlobRecovery {
pending_present: bool,
request_matches_pending: bool,
pending_blob_valid: bool,
},
ResolveRealtimeUserContentBlobFinalize {
pending_present: bool,
pending_matches_committed: bool,
},
ResolveRealtimeAssistantDelta {
response_id_valid: bool,
response_discarded: bool,
delta_id_present: bool,
delta_id_seen: bool,
item_has_text: bool,
current_lane: Enum<RealtimeTranscriptLaneKind>,
requested_lane: Enum<RealtimeTranscriptLaneKind>,
response_completed: bool,
text_after_write_present: bool,
},
ResolveRealtimeAssistantTextReplacement {
response_id_valid: bool,
response_discarded: bool,
item_materialized: bool,
item_has_text: bool,
current_lane: Enum<RealtimeTranscriptLaneKind>,
requested_lane: Enum<RealtimeTranscriptLaneKind>,
response_completed: bool,
text_after_replace_present: bool,
},
ResolveRealtimeAssistantTurnCompleted {
response_id_valid: bool,
response_discarded: bool,
stop_reason: Enum<RealtimeTranscriptStopReasonKind>,
},
ResolveRealtimeAssistantTurnInterrupted {
response_id_valid: bool,
},
ResolveRealtimeMaterializeCandidate {
item_materialized: bool,
predecessor_materialized: bool,
item_skipped: bool,
item_ready: bool,
item_text_present: bool,
role: Enum<RealtimeTranscriptRoleKind>,
response_id_present: bool,
completion_present: bool,
completion_usage_consumed: bool,
},
RestoreRealtimeTranscriptState {
item_count: u64,
first_seen_count: u64,
first_seen_unique_count: u64,
every_item_has_order_entry: bool,
every_order_entry_has_item: bool,
all_materialized_predecessor_references_exist: bool,
no_self_predecessor_references: bool,
causal_graph_acyclic: bool,
all_materialized_items_have_materialized_ancestry: bool,
all_identity_fields_valid: bool,
all_user_content_identity_keys_match: bool,
all_user_content_identity_fields_valid: bool,
all_user_content_identity_item_ids_unique: bool,
all_user_content_identities_reference_materialized_user_items: bool,
all_user_content_tombstones_valid: bool,
user_content_identities_and_tombstones_disjoint: bool,
pending_user_content_blob_fields_valid: bool,
pending_user_content_blob_uncommitted: bool,
all_delta_ids_valid: bool,
all_completion_response_ids_valid: bool,
all_discarded_response_ids_valid: bool,
all_materialized_items_were_ready_or_skipped: bool,
all_assistant_items_have_response_unless_skipped: bool,
all_ready_assistant_items_have_completion_or_are_skipped: bool,
all_materialized_assistant_completions_consumed: bool,
all_completed_assistant_text_items_are_ready_or_materialized_or_skipped: bool,
all_discarded_assistant_items_are_skipped_or_materialized: bool,
},
AuthorizeSessionMetadataPersist {
schema_version: u64,
model_present: bool,
},
AuthorizeSessionBuildStatePersist {
mob_tool_authority_context_present: bool,
mob_tool_authority_context_generated: bool,
},
RestoreSessionBuildState,
ResolvePendingContinuation {
session_tail: Enum<ObservedSessionTailKind>,
staged_tool_result_count: u64,
},
AuthorizeSessionResumeOverrides {
provider_override_present: bool,
model_override_present: bool,
self_hosted_server_override_present: bool,
has_build_only_overrides: bool,
first_turn_phase: Enum<SessionFirstTurnPhase>,
},
ClassifyLiveSessionAuthority {
stored_transcript_diverged: bool,
live_has_uncommitted_transcript: bool,
stored_is_archived: bool,
},
RecoverSessionFromStore {
session_id: SessionId,
has_metadata: bool,
has_build_state: bool,
runtime_projection_quarantined: bool,
},
ResolveRuntimeCheckpointProjection { session_id: SessionId },
ResolveSessionDocumentLifecycleMerge {
session_id: SessionId,
authority_archived: bool,
candidate_archived: bool,
},
ClassifyDurableTail {
session_id: SessionId,
candidate_id: RecoveryCandidateId,
relation: Enum<DurableHeadRelation>,
run_id_cardinality: Enum<RunIdCardinality>,
terminal_stop_reason: Enum<DurableTailStopReason>,
dangling_tool_use_count: u64,
orphan_tool_result_count: u64,
messages_after_terminal: bool,
},
ApplyPendingToolResults {
session_id: SessionId,
result_count: u64,
},
TranscriptEdit {
session_id: SessionId,
fork_or_rewrite_directive: Enum<TranscriptEditKind>,
},
RecoverSessionLifecycleTerminal {
session_id: SessionId,
terminal: Enum<SessionDocumentLifecycle>,
},
ReviveArchivedSessionDocument {
session_id: SessionId,
},
ArchiveSessionDocument {
session_id: SessionId,
runtime_backed: bool,
durable_document_present: bool,
runtime_observation: Enum<SessionArchiveRuntimeObservation>,
},
}
effect SessionDocumentEffect {
SessionFirstTurnPhaseResolved {
phase: Enum<SessionFirstTurnPhase>,
was_pending: bool,
},
SessionFirstTurnOverridesResolved { allowed: bool },
SessionInitialPromptStageResolved { decision: Enum<SessionInitialPromptStageDecision> },
SessionToolResultsStageResolved { accepted_count: u64 },
SessionConsumedInputsRestoreResolved {
restore_first_turn_pending: bool,
restore_initial_prompt: bool,
restore_tool_results: bool,
},
SessionFirstTurnPhaseRecovered,
RealtimeTranscriptEventResolved {
observe_item: bool,
observe_skipped: bool,
write_user_segment: bool,
append_assistant_segment: bool,
replace_assistant_segment: bool,
promote_lane: bool,
mark_item_ready: bool,
record_delta_id: bool,
remove_completion: bool,
record_completion: bool,
discard_response: bool,
discard_response_by_lane: bool,
mark_response_ready: bool,
materialize_ready_items: bool,
},
RealtimeMaterializeCandidateResolved {
decision: Enum<RealtimeTranscriptMaterializeDecision>,
consume_usage: bool,
},
RealtimeUserContentIdentityResolved {
disposition: Enum<RealtimeUserContentIdentityDisposition>,
},
RealtimeUserContentBlobStageResolved {
disposition: Enum<RealtimeUserContentBlobStageDisposition>,
},
RealtimeUserContentBlobRecoveryResolved {
disposition: Enum<RealtimeUserContentBlobRecoveryDisposition>,
},
RealtimeUserContentBlobFinalizeResolved {
disposition: Enum<RealtimeUserContentBlobFinalizeDisposition>,
},
RealtimeTranscriptSnapshotRestoreAuthorized,
SessionMetadataPersistAuthorized,
SessionBuildStatePersistAuthorized,
SessionBuildStateRestoreAuthorized,
PendingContinuationResolved { disposition: Enum<PendingContinuationDisposition> },
PendingContinuationPublicTerminalResolved { terminal: Enum<PendingContinuationPublicTerminal> },
SessionResumeOverridesAuthorized {
provider_selection: Enum<ResumeProviderSelection>,
self_hosted_selection: Enum<ResumeSelfHostedSelection>,
provider_overridden: bool,
},
SessionResumeOverridesRejected { reason: Enum<ResumeOverrideRejection> },
LiveSessionAuthorityClassified {
authority: Enum<LiveSessionAuthorityKind>,
reason: Enum<LiveSessionAuthorityReason>,
},
SessionStoreRecoverySourceResolved { recoverable: bool },
RuntimeCheckpointProjectionResolved {
disposition: Enum<RuntimeCheckpointProjectionDisposition>,
},
SessionDocumentLifecycleMergeResolved {
merge: Enum<SessionDocumentLifecycleMerge>,
},
DurableTailClassified {
candidate_id: RecoveryCandidateId,
class: Enum<DurableTailRecoveryClass>,
},
SessionToolResultsApplied {
session_id: SessionId,
applied_count: u64,
},
TranscriptRewriteCommitted {
kind: Enum<TranscriptEditKind>,
success: bool,
},
SessionLifecycleTerminalRecovered,
SessionRevivalResolved,
SessionArchiveResolved {
disposition: Enum<SessionArchiveDisposition>,
write_document: bool,
retire_runtime: bool,
},
}
helper phase_allows_initial_turn_overrides(phase: Enum<SessionFirstTurnPhase>) -> bool {
phase == SessionFirstTurnPhase::Pending
}
helper should_store_initial_prompt(
phase: Enum<SessionFirstTurnPhase>,
prompt_has_content: bool
) -> bool {
phase == SessionFirstTurnPhase::Pending && prompt_has_content
}
helper realtime_delta_is_duplicate(delta_id_present: bool, delta_id_seen: bool) -> bool {
delta_id_present && delta_id_seen
}
helper realtime_lane_accepts(
item_has_text: bool,
current_lane: Enum<RealtimeTranscriptLaneKind>,
requested_lane: Enum<RealtimeTranscriptLaneKind>
) -> bool {
current_lane == requested_lane || item_has_text == false
}
helper realtime_should_mark_ready_after_write(
response_completed: bool,
text_after_write_present: bool
) -> bool {
response_completed && text_after_write_present
}
helper realtime_stop_reason_discards(
stop_reason: Enum<RealtimeTranscriptStopReasonKind>
) -> bool {
stop_reason == RealtimeTranscriptStopReasonKind::Cancelled
}
helper realtime_stop_reason_removes_completion(
stop_reason: Enum<RealtimeTranscriptStopReasonKind>
) -> bool {
stop_reason == RealtimeTranscriptStopReasonKind::ToolUse
}
helper realtime_stop_reason_records_completion(
stop_reason: Enum<RealtimeTranscriptStopReasonKind>
) -> bool {
stop_reason == RealtimeTranscriptStopReasonKind::Other
}
helper tail_has_pending_boundary(session_tail: Enum<ObservedSessionTailKind>) -> bool {
session_tail == ObservedSessionTailKind::User
|| session_tail == ObservedSessionTailKind::ToolResults
}
helper has_effective_pending_boundary(
session_tail: Enum<ObservedSessionTailKind>,
staged_tool_result_count: u64
) -> bool {
tail_has_pending_boundary(session_tail) || staged_tool_result_count > 0
}
helper resume_reject_provider_requires_model(
provider_override_present: bool,
model_override_present: bool
) -> bool {
provider_override_present && model_override_present == false
}
helper resume_reject_build_only_after_first_turn(
has_build_only_overrides: bool,
first_turn_phase: Enum<SessionFirstTurnPhase>
) -> bool {
has_build_only_overrides
&& phase_allows_initial_turn_overrides(first_turn_phase) == false
}
helper resume_overrides_admissible(
provider_override_present: bool,
model_override_present: bool,
has_build_only_overrides: bool,
first_turn_phase: Enum<SessionFirstTurnPhase>
) -> bool {
resume_reject_provider_requires_model(
provider_override_present,
model_override_present
) == false
&& resume_reject_build_only_after_first_turn(
has_build_only_overrides,
first_turn_phase
) == false
}
helper resume_provider_recompute_from_model(
model_override_present: bool,
provider_override_present: bool
) -> bool {
model_override_present && provider_override_present == false
}
helper store_projection_can_recover_authority(
has_metadata: bool,
has_build_state: bool,
runtime_projection_quarantined: bool
) -> bool {
has_metadata || has_build_state || runtime_projection_quarantined
}
helper archive_should_retire_runtime(
runtime_backed: bool,
durable_document_present: bool,
runtime_observation: Enum<SessionArchiveRuntimeObservation>
) -> bool {
runtime_backed
&& runtime_observation
!= SessionArchiveRuntimeObservation::QuiescentTerminal
&& (durable_document_present
|| runtime_observation
== SessionArchiveRuntimeObservation::RetirementRequired)
}
disposition SessionFirstTurnPhaseResolved => local seam NoOwnerRealization,
disposition SessionFirstTurnOverridesResolved => local seam NoOwnerRealization,
disposition SessionInitialPromptStageResolved => local seam NoOwnerRealization,
disposition SessionToolResultsStageResolved => local seam NoOwnerRealization,
disposition SessionConsumedInputsRestoreResolved => local seam NoOwnerRealization,
disposition SessionFirstTurnPhaseRecovered => local seam NoOwnerRealization,
disposition RealtimeTranscriptEventResolved => local seam NoOwnerRealization,
disposition RealtimeMaterializeCandidateResolved => local seam NoOwnerRealization,
disposition RealtimeUserContentIdentityResolved => local seam NoOwnerRealization,
disposition RealtimeUserContentBlobStageResolved => local seam NoOwnerRealization,
disposition RealtimeUserContentBlobRecoveryResolved => local seam NoOwnerRealization,
disposition RealtimeUserContentBlobFinalizeResolved => local seam NoOwnerRealization,
disposition RealtimeTranscriptSnapshotRestoreAuthorized => local seam NoOwnerRealization,
disposition SessionMetadataPersistAuthorized => local seam NoOwnerRealization,
disposition SessionBuildStatePersistAuthorized => local seam NoOwnerRealization,
disposition SessionBuildStateRestoreAuthorized => local seam NoOwnerRealization,
disposition PendingContinuationResolved => local seam NoOwnerRealization,
disposition PendingContinuationPublicTerminalResolved => local seam NoOwnerRealization,
disposition SessionResumeOverridesAuthorized => local seam NoOwnerRealization,
disposition SessionResumeOverridesRejected => local seam NoOwnerRealization,
disposition LiveSessionAuthorityClassified => local seam NoOwnerRealization,
disposition SessionStoreRecoverySourceResolved => local seam NoOwnerRealization,
disposition RuntimeCheckpointProjectionResolved => local seam NoOwnerRealization,
disposition SessionDocumentLifecycleMergeResolved => local seam NoOwnerRealization,
disposition DurableTailClassified => local seam NoOwnerRealization,
disposition SessionToolResultsApplied => local seam NoOwnerRealization,
disposition TranscriptRewriteCommitted => local seam NoOwnerRealization,
disposition SessionLifecycleTerminalRecovered => local seam NoOwnerRealization,
disposition SessionRevivalResolved => local seam NoOwnerRealization,
disposition SessionArchiveResolved => local seam NoOwnerRealization,
transition MarkSessionInitialTurnPendingInactiveOrPending {
on input MarkSessionInitialTurnPending { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& (self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Inactive
|| self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Pending)
}
update {
self.session_first_turn_phase.insert(session_id, SessionFirstTurnPhase::Pending);
}
to Ready
emit SessionFirstTurnPhaseResolved {
phase: SessionFirstTurnPhase::Pending,
was_pending: false
}
}
transition MarkSessionInitialTurnPendingConsumed {
on input MarkSessionInitialTurnPending { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Consumed
}
update {}
to Ready
emit SessionFirstTurnPhaseResolved {
phase: SessionFirstTurnPhase::Consumed,
was_pending: false
}
}
transition StartSessionInitialTurnPending {
on input StartSessionInitialTurn { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Pending
}
update {
self.session_first_turn_phase.insert(session_id, SessionFirstTurnPhase::Consumed);
}
to Ready
emit SessionFirstTurnPhaseResolved {
phase: SessionFirstTurnPhase::Consumed,
was_pending: true
}
}
transition StartSessionInitialTurnInactive {
on input StartSessionInitialTurn { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Inactive
}
update {}
to Ready
emit SessionFirstTurnPhaseResolved {
phase: SessionFirstTurnPhase::Inactive,
was_pending: false
}
}
transition StartSessionInitialTurnConsumed {
on input StartSessionInitialTurn { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Consumed
}
update {}
to Ready
emit SessionFirstTurnPhaseResolved {
phase: SessionFirstTurnPhase::Consumed,
was_pending: false
}
}
transition ResolveSessionFirstTurnOverridesAllowed {
on input ResolveSessionFirstTurnOverridesAllowed { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& phase_allows_initial_turn_overrides(
self.session_first_turn_phase.get_cloned(session_id).get("value")
)
}
update {}
to Ready
emit SessionFirstTurnOverridesResolved { allowed: true }
}
transition ResolveSessionFirstTurnOverridesDenied {
on input ResolveSessionFirstTurnOverridesAllowed { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& phase_allows_initial_turn_overrides(
self.session_first_turn_phase.get_cloned(session_id).get("value")
) == false
}
update {}
to Ready
emit SessionFirstTurnOverridesResolved { allowed: false }
}
transition StageSessionInitialPromptStore {
on input StageSessionInitialPrompt { session_id, prompt_has_content }
guard {
self.lifecycle_phase == Phase::Ready
&& should_store_initial_prompt(
self.session_first_turn_phase.get_cloned(session_id).get("value"),
prompt_has_content
)
}
update {
self.session_pending_initial_prompt_present.insert(session_id, true);
}
to Ready
emit SessionInitialPromptStageResolved {
decision: SessionInitialPromptStageDecision::Store
}
}
transition StageSessionInitialPromptClear {
on input StageSessionInitialPrompt { session_id, prompt_has_content }
guard {
self.lifecycle_phase == Phase::Ready
&& should_store_initial_prompt(
self.session_first_turn_phase.get_cloned(session_id).get("value"),
prompt_has_content
) == false
}
update {
self.session_pending_initial_prompt_present.insert(session_id, false);
}
to Ready
emit SessionInitialPromptStageResolved {
decision: SessionInitialPromptStageDecision::Clear
}
}
transition StageSessionToolResults {
on input StageSessionToolResults { session_id, result_count }
guard {
self.lifecycle_phase == Phase::Ready
&& (self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Inactive
|| self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Pending
|| self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Consumed)
}
update {
self.session_pending_tool_results_count.insert(session_id, result_count);
}
to Ready
emit SessionToolResultsStageResolved { accepted_count: result_count }
}
transition ConsumeSessionDeferredInputsPending {
on input ConsumeSessionDeferredInputs { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Pending
}
update {
self.session_first_turn_phase.insert(session_id, SessionFirstTurnPhase::Consumed);
self.session_pending_initial_prompt_present.insert(session_id, false);
self.session_pending_tool_results_count.insert(session_id, 0);
}
to Ready
emit SessionFirstTurnPhaseResolved {
phase: SessionFirstTurnPhase::Consumed,
was_pending: true
}
}
transition ConsumeSessionDeferredInputsInactive {
on input ConsumeSessionDeferredInputs { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Inactive
}
update {
self.session_pending_initial_prompt_present.insert(session_id, false);
self.session_pending_tool_results_count.insert(session_id, 0);
}
to Ready
emit SessionFirstTurnPhaseResolved {
phase: SessionFirstTurnPhase::Inactive,
was_pending: false
}
}
transition ConsumeSessionDeferredInputsConsumed {
on input ConsumeSessionDeferredInputs { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_first_turn_phase.get_cloned(session_id).get("value")
== SessionFirstTurnPhase::Consumed
}
update {
self.session_pending_initial_prompt_present.insert(session_id, false);
self.session_pending_tool_results_count.insert(session_id, 0);
}
to Ready
emit SessionFirstTurnPhaseResolved {
phase: SessionFirstTurnPhase::Consumed,
was_pending: false
}
}
transition RestoreSessionConsumedInputs {
on input RestoreSessionConsumedInputs {
session_id,
restore_first_turn_pending,
pending_initial_prompt_present,
pending_tool_result_message_count
}
guard { self.lifecycle_phase == Phase::Ready && restore_first_turn_pending }
update {
self.session_first_turn_phase.insert(session_id, SessionFirstTurnPhase::Pending);
self.session_pending_initial_prompt_present
.insert(session_id, pending_initial_prompt_present);
self.session_pending_tool_results_count
.insert(session_id, pending_tool_result_message_count);
}
to Ready
emit SessionConsumedInputsRestoreResolved {
restore_first_turn_pending: restore_first_turn_pending,
restore_initial_prompt: pending_initial_prompt_present,
restore_tool_results: pending_tool_result_message_count > 0
}
}
transition RestoreSessionConsumedInputsNoPhaseRollback {
on input RestoreSessionConsumedInputs {
session_id,
restore_first_turn_pending,
pending_initial_prompt_present,
pending_tool_result_message_count
}
guard { self.lifecycle_phase == Phase::Ready && restore_first_turn_pending == false }
update {
self.session_pending_initial_prompt_present
.insert(session_id, pending_initial_prompt_present);
self.session_pending_tool_results_count
.insert(session_id, pending_tool_result_message_count);
}
to Ready
emit SessionConsumedInputsRestoreResolved {
restore_first_turn_pending: restore_first_turn_pending,
restore_initial_prompt: pending_initial_prompt_present,
restore_tool_results: pending_tool_result_message_count > 0
}
}
transition RecoverSessionFirstTurnPhase {
on input RecoverSessionFirstTurnPhase {
session_id,
phase,
pending_initial_prompt_present,
pending_tool_result_message_count
}
guard {
self.lifecycle_phase == Phase::Ready
&& (phase == SessionFirstTurnPhase::Inactive
|| phase == SessionFirstTurnPhase::Pending
|| phase == SessionFirstTurnPhase::Consumed)
}
update {
self.session_first_turn_phase.insert(session_id, phase);
self.session_pending_initial_prompt_present
.insert(session_id, pending_initial_prompt_present);
self.session_pending_tool_results_count
.insert(session_id, pending_tool_result_message_count);
}
to Ready
emit SessionFirstTurnPhaseRecovered
}
transition ResolveRealtimeItemObservedDiscardedAssistant {
on input ResolveRealtimeItemObserved { role, response_discarded }
guard {
self.lifecycle_phase == Phase::Ready
&& role == RealtimeTranscriptRoleKind::Assistant
&& response_discarded
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: true,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeItemObservedPresent {
on input ResolveRealtimeItemObserved { role, response_discarded }
guard {
self.lifecycle_phase == Phase::Ready
&& (role != RealtimeTranscriptRoleKind::Assistant || response_discarded == false)
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeItemSkipped {
on input ResolveRealtimeItemSkipped
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: true,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeUserTranscriptFinalEmpty {
on input ResolveRealtimeUserTranscriptFinal { text_present, segment_empty, segment_matches }
guard {
self.lifecycle_phase == Phase::Ready
&& text_present == false
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: true,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeUserTranscriptFinalStore {
on input ResolveRealtimeUserTranscriptFinal { text_present, segment_empty, segment_matches }
guard {
self.lifecycle_phase == Phase::Ready
&& text_present
&& segment_empty
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: true,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: true,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeUserTranscriptFinalReplayOrConflict {
on input ResolveRealtimeUserTranscriptFinal { text_present, segment_empty, segment_matches }
guard {
self.lifecycle_phase == Phase::Ready
&& text_present
&& segment_empty == false
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: true,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeUserContentIdentityInvalid {
on input ResolveRealtimeUserContentIdentity { identity_fields_valid, key_tombstoned, predecessor_materialized, existing_identity_present, existing_payload_matches, target_item_id_available, reducer_commit_proof_required, reducer_commit_proof_present }
guard {
self.lifecycle_phase == Phase::Ready
&& (identity_fields_valid == false
|| (key_tombstoned == false
&& predecessor_materialized
&& existing_identity_present == false
&& target_item_id_available
&& reducer_commit_proof_required
&& reducer_commit_proof_present == false))
}
update {}
to Ready
emit RealtimeUserContentIdentityResolved {
disposition: RealtimeUserContentIdentityDisposition::RejectInvalidIdentity
}
}
transition ResolveRealtimeUserContentIdentityUnmaterializedPredecessor {
on input ResolveRealtimeUserContentIdentity { identity_fields_valid, key_tombstoned, predecessor_materialized, existing_identity_present, existing_payload_matches, target_item_id_available, reducer_commit_proof_required, reducer_commit_proof_present }
guard {
self.lifecycle_phase == Phase::Ready
&& identity_fields_valid
&& key_tombstoned == false
&& predecessor_materialized == false
}
update {}
to Ready
emit RealtimeUserContentIdentityResolved {
disposition: RealtimeUserContentIdentityDisposition::RejectUnmaterializedPredecessor
}
}
transition ResolveRealtimeUserContentIdentityConflict {
on input ResolveRealtimeUserContentIdentity { identity_fields_valid, key_tombstoned, predecessor_materialized, existing_identity_present, existing_payload_matches, target_item_id_available, reducer_commit_proof_required, reducer_commit_proof_present }
guard {
self.lifecycle_phase == Phase::Ready
&& identity_fields_valid
&& (key_tombstoned
|| (predecessor_materialized
&& ((existing_identity_present && existing_payload_matches == false)
|| (existing_identity_present == false && target_item_id_available == false))))
}
update {}
to Ready
emit RealtimeUserContentIdentityResolved {
disposition: RealtimeUserContentIdentityDisposition::RejectConflict
}
}
transition ResolveRealtimeUserContentIdentityReplay {
on input ResolveRealtimeUserContentIdentity { identity_fields_valid, key_tombstoned, predecessor_materialized, existing_identity_present, existing_payload_matches, target_item_id_available, reducer_commit_proof_required, reducer_commit_proof_present }
guard {
self.lifecycle_phase == Phase::Ready
&& identity_fields_valid
&& key_tombstoned == false
&& predecessor_materialized
&& existing_identity_present
&& existing_payload_matches
}
update {}
to Ready
emit RealtimeUserContentIdentityResolved {
disposition: RealtimeUserContentIdentityDisposition::AlreadyCommitted
}
}
transition ResolveRealtimeUserContentIdentityCommitNew {
on input ResolveRealtimeUserContentIdentity { identity_fields_valid, key_tombstoned, predecessor_materialized, existing_identity_present, existing_payload_matches, target_item_id_available, reducer_commit_proof_required, reducer_commit_proof_present }
guard {
self.lifecycle_phase == Phase::Ready
&& identity_fields_valid
&& key_tombstoned == false
&& predecessor_materialized
&& existing_identity_present == false
&& target_item_id_available
&& (reducer_commit_proof_required == false || reducer_commit_proof_present)
}
update {}
to Ready
emit RealtimeUserContentIdentityResolved {
disposition: RealtimeUserContentIdentityDisposition::CommitNew
}
}
transition ResolveRealtimeUserContentBlobStageNew {
on input ResolveRealtimeUserContentBlobStage { pending_present, pending_matches_request }
guard {
self.lifecycle_phase == Phase::Ready
&& pending_present == false
}
update {}
to Ready
emit RealtimeUserContentBlobStageResolved {
disposition: RealtimeUserContentBlobStageDisposition::StageNew
}
}
transition ResolveRealtimeUserContentBlobStageReuseExact {
on input ResolveRealtimeUserContentBlobStage { pending_present, pending_matches_request }
guard {
self.lifecycle_phase == Phase::Ready
&& pending_present
&& pending_matches_request
}
update {}
to Ready
emit RealtimeUserContentBlobStageResolved {
disposition: RealtimeUserContentBlobStageDisposition::ReuseExact
}
}
transition ResolveRealtimeUserContentBlobStageRejectOccupied {
on input ResolveRealtimeUserContentBlobStage { pending_present, pending_matches_request }
guard {
self.lifecycle_phase == Phase::Ready
&& pending_present
&& pending_matches_request == false
}
update {}
to Ready
emit RealtimeUserContentBlobStageResolved {
disposition: RealtimeUserContentBlobStageDisposition::RejectOccupied
}
}
transition ResolveRealtimeUserContentBlobRecoveryNone {
on input ResolveRealtimeUserContentBlobRecovery { pending_present, request_matches_pending, pending_blob_valid }
guard {
self.lifecycle_phase == Phase::Ready
&& pending_present == false
}
update {}
to Ready
emit RealtimeUserContentBlobRecoveryResolved {
disposition: RealtimeUserContentBlobRecoveryDisposition::NoPending
}
}
transition ResolveRealtimeUserContentBlobRecoveryExact {
on input ResolveRealtimeUserContentBlobRecovery { pending_present, request_matches_pending, pending_blob_valid }
guard {
self.lifecycle_phase == Phase::Ready
&& pending_present
&& request_matches_pending
}
update {}
to Ready
emit RealtimeUserContentBlobRecoveryResolved {
disposition: RealtimeUserContentBlobRecoveryDisposition::RetryExact
}
}
transition ResolveRealtimeUserContentBlobRecoveryCommitVerified {
on input ResolveRealtimeUserContentBlobRecovery { pending_present, request_matches_pending, pending_blob_valid }
guard {
self.lifecycle_phase == Phase::Ready
&& pending_present
&& request_matches_pending == false
&& pending_blob_valid
}
update {}
to Ready
emit RealtimeUserContentBlobRecoveryResolved {
disposition: RealtimeUserContentBlobRecoveryDisposition::CommitVerifiedBeforeCurrent
}
}
transition ResolveRealtimeUserContentBlobRecoveryClearInvalid {
on input ResolveRealtimeUserContentBlobRecovery { pending_present, request_matches_pending, pending_blob_valid }
guard {
self.lifecycle_phase == Phase::Ready
&& pending_present
&& request_matches_pending == false
&& pending_blob_valid == false
}
update {}
to Ready
emit RealtimeUserContentBlobRecoveryResolved {
disposition: RealtimeUserContentBlobRecoveryDisposition::ClearInvalidBeforeCurrent
}
}
transition ResolveRealtimeUserContentBlobFinalizeNone {
on input ResolveRealtimeUserContentBlobFinalize { pending_present, pending_matches_committed }
guard {
self.lifecycle_phase == Phase::Ready
&& pending_present == false
}
update {}
to Ready
emit RealtimeUserContentBlobFinalizeResolved {
disposition: RealtimeUserContentBlobFinalizeDisposition::NoPending
}
}
transition ResolveRealtimeUserContentBlobFinalizeClearCommitted {
on input ResolveRealtimeUserContentBlobFinalize { pending_present, pending_matches_committed }
guard {
self.lifecycle_phase == Phase::Ready
&& pending_present
&& pending_matches_committed
}
update {}
to Ready
emit RealtimeUserContentBlobFinalizeResolved {
disposition: RealtimeUserContentBlobFinalizeDisposition::ClearCommitted
}
}
transition ResolveRealtimeUserContentBlobFinalizeRejectMismatch {
on input ResolveRealtimeUserContentBlobFinalize { pending_present, pending_matches_committed }
guard {
self.lifecycle_phase == Phase::Ready
&& pending_present
&& pending_matches_committed == false
}
update {}
to Ready
emit RealtimeUserContentBlobFinalizeResolved {
disposition: RealtimeUserContentBlobFinalizeDisposition::RejectMismatch
}
}
transition ResolveRealtimeUserContentFinalEmpty {
on input ResolveRealtimeUserContentFinal { content_present, segment_empty, segment_matches }
guard {
self.lifecycle_phase == Phase::Ready
&& content_present == false
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: true,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeUserContentFinalStore {
on input ResolveRealtimeUserContentFinal { content_present, segment_empty, segment_matches }
guard {
self.lifecycle_phase == Phase::Ready
&& content_present
&& segment_empty
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: true,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: true,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeUserContentFinalReplayOrConflict {
on input ResolveRealtimeUserContentFinal { content_present, segment_empty, segment_matches }
guard {
self.lifecycle_phase == Phase::Ready
&& content_present
&& segment_empty == false
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: true,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeAssistantDeltaInvalidOrDuplicate {
on input ResolveRealtimeAssistantDelta {
response_id_valid,
response_discarded,
delta_id_present,
delta_id_seen,
item_has_text,
current_lane,
requested_lane,
response_completed,
text_after_write_present
}
guard {
self.lifecycle_phase == Phase::Ready
&& (response_id_valid == false
|| realtime_delta_is_duplicate(delta_id_present, delta_id_seen))
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: false
}
}
transition ResolveRealtimeAssistantDeltaDiscarded {
on input ResolveRealtimeAssistantDelta {
response_id_valid,
response_discarded,
delta_id_present,
delta_id_seen,
item_has_text,
current_lane,
requested_lane,
response_completed,
text_after_write_present
}
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid
&& response_discarded
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: true,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeAssistantDeltaLaneConflict {
on input ResolveRealtimeAssistantDelta {
response_id_valid,
response_discarded,
delta_id_present,
delta_id_seen,
item_has_text,
current_lane,
requested_lane,
response_completed,
text_after_write_present
}
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid
&& response_discarded == false
&& realtime_delta_is_duplicate(delta_id_present, delta_id_seen) == false
&& realtime_lane_accepts(item_has_text, current_lane, requested_lane) == false
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: delta_id_present,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeAssistantDeltaAccepted {
on input ResolveRealtimeAssistantDelta {
response_id_valid,
response_discarded,
delta_id_present,
delta_id_seen,
item_has_text,
current_lane,
requested_lane,
response_completed,
text_after_write_present
}
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid
&& response_discarded == false
&& realtime_delta_is_duplicate(delta_id_present, delta_id_seen) == false
&& realtime_lane_accepts(item_has_text, current_lane, requested_lane)
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: true,
replace_assistant_segment: false,
promote_lane: true,
mark_item_ready: realtime_should_mark_ready_after_write(response_completed, text_after_write_present),
record_delta_id: delta_id_present,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeAssistantReplacementInvalid {
on input ResolveRealtimeAssistantTextReplacement {
response_id_valid,
response_discarded,
item_materialized,
item_has_text,
current_lane,
requested_lane,
response_completed,
text_after_replace_present
}
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid == false
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: false
}
}
transition ResolveRealtimeAssistantReplacementDiscarded {
on input ResolveRealtimeAssistantTextReplacement {
response_id_valid,
response_discarded,
item_materialized,
item_has_text,
current_lane,
requested_lane,
response_completed,
text_after_replace_present
}
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid
&& response_discarded
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: true,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeAssistantReplacementLocked {
on input ResolveRealtimeAssistantTextReplacement {
response_id_valid,
response_discarded,
item_materialized,
item_has_text,
current_lane,
requested_lane,
response_completed,
text_after_replace_present
}
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid
&& response_discarded == false
&& item_materialized
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeAssistantReplacementLaneConflict {
on input ResolveRealtimeAssistantTextReplacement {
response_id_valid,
response_discarded,
item_materialized,
item_has_text,
current_lane,
requested_lane,
response_completed,
text_after_replace_present
}
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid
&& response_discarded == false
&& item_materialized == false
&& realtime_lane_accepts(item_has_text, current_lane, requested_lane) == false
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeAssistantReplacementAccepted {
on input ResolveRealtimeAssistantTextReplacement {
response_id_valid,
response_discarded,
item_materialized,
item_has_text,
current_lane,
requested_lane,
response_completed,
text_after_replace_present
}
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid
&& response_discarded == false
&& item_materialized == false
&& realtime_lane_accepts(item_has_text, current_lane, requested_lane)
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: true,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: true,
promote_lane: true,
mark_item_ready: realtime_should_mark_ready_after_write(response_completed, text_after_replace_present),
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeAssistantTurnCompletedInvalid {
on input ResolveRealtimeAssistantTurnCompleted { response_id_valid, response_discarded, stop_reason }
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid == false
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: false
}
}
transition ResolveRealtimeAssistantTurnCompletedDiscard {
on input ResolveRealtimeAssistantTurnCompleted { response_id_valid, response_discarded, stop_reason }
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid
&& (response_discarded || realtime_stop_reason_discards(stop_reason))
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: true,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeAssistantTurnCompletedToolUse {
on input ResolveRealtimeAssistantTurnCompleted { response_id_valid, response_discarded, stop_reason }
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid
&& response_discarded == false
&& realtime_stop_reason_removes_completion(stop_reason)
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: true,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: true
}
}
transition ResolveRealtimeAssistantTurnCompletedRecord {
on input ResolveRealtimeAssistantTurnCompleted { response_id_valid, response_discarded, stop_reason }
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid
&& response_discarded == false
&& realtime_stop_reason_records_completion(stop_reason)
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: true,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: true,
materialize_ready_items: true
}
}
transition ResolveRealtimeAssistantTurnInterruptedInvalid {
on input ResolveRealtimeAssistantTurnInterrupted { response_id_valid }
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid == false
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: false,
discard_response: false,
discard_response_by_lane: false,
mark_response_ready: false,
materialize_ready_items: false
}
}
transition ResolveRealtimeAssistantTurnInterruptedValid {
on input ResolveRealtimeAssistantTurnInterrupted { response_id_valid }
guard {
self.lifecycle_phase == Phase::Ready
&& response_id_valid
}
update {}
to Ready
emit RealtimeTranscriptEventResolved {
observe_item: false,
observe_skipped: false,
write_user_segment: false,
append_assistant_segment: false,
replace_assistant_segment: false,
promote_lane: false,
mark_item_ready: false,
record_delta_id: false,
remove_completion: false,
record_completion: true,
discard_response: false,
discard_response_by_lane: true,
mark_response_ready: true,
materialize_ready_items: true
}
}
transition ResolveRealtimeMaterializeAlreadyDone {
on input ResolveRealtimeMaterializeCandidate {
item_materialized,
predecessor_materialized,
item_skipped,
item_ready,
item_text_present,
role,
response_id_present,
completion_present,
completion_usage_consumed
}
guard {
self.lifecycle_phase == Phase::Ready
&& item_materialized
}
update {}
to Ready
emit RealtimeMaterializeCandidateResolved {
decision: RealtimeTranscriptMaterializeDecision::Wait,
consume_usage: false
}
}
transition ResolveRealtimeMaterializeWaitForPredecessor {
on input ResolveRealtimeMaterializeCandidate {
item_materialized,
predecessor_materialized,
item_skipped,
item_ready,
item_text_present,
role,
response_id_present,
completion_present,
completion_usage_consumed
}
guard {
self.lifecycle_phase == Phase::Ready
&& item_materialized == false
&& predecessor_materialized == false
}
update {}
to Ready
emit RealtimeMaterializeCandidateResolved {
decision: RealtimeTranscriptMaterializeDecision::Wait,
consume_usage: false
}
}
transition ResolveRealtimeMaterializeSkipped {
on input ResolveRealtimeMaterializeCandidate {
item_materialized,
predecessor_materialized,
item_skipped,
item_ready,
item_text_present,
role,
response_id_present,
completion_present,
completion_usage_consumed
}
guard {
self.lifecycle_phase == Phase::Ready
&& item_materialized == false
&& predecessor_materialized
&& item_skipped
}
update {}
to Ready
emit RealtimeMaterializeCandidateResolved {
decision: RealtimeTranscriptMaterializeDecision::MarkSkipped,
consume_usage: false
}
}
transition ResolveRealtimeMaterializeWaitForReadyText {
on input ResolveRealtimeMaterializeCandidate {
item_materialized,
predecessor_materialized,
item_skipped,
item_ready,
item_text_present,
role,
response_id_present,
completion_present,
completion_usage_consumed
}
guard {
self.lifecycle_phase == Phase::Ready
&& item_materialized == false
&& predecessor_materialized
&& item_skipped == false
&& (item_ready == false || item_text_present == false)
}
update {}
to Ready
emit RealtimeMaterializeCandidateResolved {
decision: RealtimeTranscriptMaterializeDecision::Wait,
consume_usage: false
}
}
transition ResolveRealtimeMaterializeUser {
on input ResolveRealtimeMaterializeCandidate {
item_materialized,
predecessor_materialized,
item_skipped,
item_ready,
item_text_present,
role,
response_id_present,
completion_present,
completion_usage_consumed
}
guard {
self.lifecycle_phase == Phase::Ready
&& item_materialized == false
&& predecessor_materialized
&& item_skipped == false
&& item_ready
&& item_text_present
&& role == RealtimeTranscriptRoleKind::User
}
update {}
to Ready
emit RealtimeMaterializeCandidateResolved {
decision: RealtimeTranscriptMaterializeDecision::MaterializeUser,
consume_usage: false
}
}
transition ResolveRealtimeMaterializeAssistant {
on input ResolveRealtimeMaterializeCandidate {
item_materialized,
predecessor_materialized,
item_skipped,
item_ready,
item_text_present,
role,
response_id_present,
completion_present,
completion_usage_consumed
}
guard {
self.lifecycle_phase == Phase::Ready
&& item_materialized == false
&& predecessor_materialized
&& item_skipped == false
&& item_ready
&& item_text_present
&& role == RealtimeTranscriptRoleKind::Assistant
&& response_id_present
&& completion_present
}
update {}
to Ready
emit RealtimeMaterializeCandidateResolved {
decision: RealtimeTranscriptMaterializeDecision::MaterializeAssistant,
consume_usage: completion_usage_consumed == false
}
}
transition ResolveRealtimeMaterializeAssistantMissingCompletion {
on input ResolveRealtimeMaterializeCandidate {
item_materialized,
predecessor_materialized,
item_skipped,
item_ready,
item_text_present,
role,
response_id_present,
completion_present,
completion_usage_consumed
}
guard {
self.lifecycle_phase == Phase::Ready
&& item_materialized == false
&& predecessor_materialized
&& item_skipped == false
&& item_ready
&& item_text_present
&& role == RealtimeTranscriptRoleKind::Assistant
&& (response_id_present == false || completion_present == false)
}
update {}
to Ready
emit RealtimeMaterializeCandidateResolved {
decision: RealtimeTranscriptMaterializeDecision::Wait,
consume_usage: false
}
}
transition AuthorizeRestoreRealtimeTranscriptState {
on input RestoreRealtimeTranscriptState {
item_count,
first_seen_count,
first_seen_unique_count,
every_item_has_order_entry,
every_order_entry_has_item,
all_materialized_predecessor_references_exist,
no_self_predecessor_references,
causal_graph_acyclic,
all_materialized_items_have_materialized_ancestry,
all_identity_fields_valid,
all_user_content_identity_keys_match,
all_user_content_identity_fields_valid,
all_user_content_identity_item_ids_unique,
all_user_content_identities_reference_materialized_user_items,
all_user_content_tombstones_valid,
user_content_identities_and_tombstones_disjoint,
pending_user_content_blob_fields_valid,
pending_user_content_blob_uncommitted,
all_delta_ids_valid,
all_completion_response_ids_valid,
all_discarded_response_ids_valid,
all_materialized_items_were_ready_or_skipped,
all_assistant_items_have_response_unless_skipped,
all_ready_assistant_items_have_completion_or_are_skipped,
all_materialized_assistant_completions_consumed,
all_completed_assistant_text_items_are_ready_or_materialized_or_skipped,
all_discarded_assistant_items_are_skipped_or_materialized
}
guard {
self.lifecycle_phase == Phase::Ready
&& item_count == first_seen_count
&& first_seen_count == first_seen_unique_count
&& every_item_has_order_entry
&& every_order_entry_has_item
&& all_materialized_predecessor_references_exist
&& no_self_predecessor_references
&& causal_graph_acyclic
&& all_materialized_items_have_materialized_ancestry
&& all_identity_fields_valid
&& all_user_content_identity_keys_match
&& all_user_content_identity_fields_valid
&& all_user_content_identity_item_ids_unique
&& all_user_content_identities_reference_materialized_user_items
&& all_user_content_tombstones_valid
&& user_content_identities_and_tombstones_disjoint
&& pending_user_content_blob_fields_valid
&& pending_user_content_blob_uncommitted
&& all_delta_ids_valid
&& all_completion_response_ids_valid
&& all_discarded_response_ids_valid
&& all_materialized_items_were_ready_or_skipped
&& all_assistant_items_have_response_unless_skipped
&& all_ready_assistant_items_have_completion_or_are_skipped
&& all_materialized_assistant_completions_consumed
&& all_completed_assistant_text_items_are_ready_or_materialized_or_skipped
&& all_discarded_assistant_items_are_skipped_or_materialized
}
update {}
to Ready
emit RealtimeTranscriptSnapshotRestoreAuthorized
}
transition AuthorizeSessionMetadataPersist {
on input AuthorizeSessionMetadataPersist {
schema_version,
model_present,
}
guard {
self.lifecycle_phase == Phase::Ready
&& schema_version > 0
&& model_present == true
}
update {}
to Ready
emit SessionMetadataPersistAuthorized
}
transition AuthorizeSessionBuildStatePersist {
on input AuthorizeSessionBuildStatePersist {
mob_tool_authority_context_present,
mob_tool_authority_context_generated,
}
guard {
self.lifecycle_phase == Phase::Ready
&& (
mob_tool_authority_context_present == false
|| mob_tool_authority_context_generated == true
)
}
update {}
to Ready
emit SessionBuildStatePersistAuthorized
}
transition RestoreSessionBuildState {
on input RestoreSessionBuildState
guard { self.lifecycle_phase == Phase::Ready }
update {}
to Ready
emit SessionBuildStateRestoreAuthorized
}
transition ResolvePendingContinuationWithBoundary {
on input ResolvePendingContinuation {
session_tail,
staged_tool_result_count
}
guard {
self.lifecycle_phase == Phase::Ready
&& has_effective_pending_boundary(session_tail, staged_tool_result_count)
}
update {}
to Ready
emit PendingContinuationResolved {
disposition: PendingContinuationDisposition::RunPending
}
}
transition ResolvePendingContinuationWithoutBoundary {
on input ResolvePendingContinuation {
session_tail,
staged_tool_result_count
}
guard {
self.lifecycle_phase == Phase::Ready
&& has_effective_pending_boundary(session_tail, staged_tool_result_count) == false
}
update {}
to Ready
emit PendingContinuationResolved {
disposition: PendingContinuationDisposition::NoPendingBoundary
}
emit PendingContinuationPublicTerminalResolved {
terminal: PendingContinuationPublicTerminal::NoPendingBoundary
}
}
transition AuthorizeSessionResumeOverridesRejectProviderRequiresModel {
on input AuthorizeSessionResumeOverrides {
provider_override_present,
model_override_present,
self_hosted_server_override_present,
has_build_only_overrides,
first_turn_phase
}
guard {
self.lifecycle_phase == Phase::Ready
&& resume_reject_provider_requires_model(
provider_override_present,
model_override_present
)
}
update {}
to Ready
emit SessionResumeOverridesRejected {
reason: ResumeOverrideRejection::ProviderRequiresModel
}
}
transition AuthorizeSessionResumeOverridesRejectBuildOnlyAfterFirstTurn {
on input AuthorizeSessionResumeOverrides {
provider_override_present,
model_override_present,
self_hosted_server_override_present,
has_build_only_overrides,
first_turn_phase
}
guard {
self.lifecycle_phase == Phase::Ready
&& resume_reject_provider_requires_model(
provider_override_present,
model_override_present
) == false
&& resume_reject_build_only_after_first_turn(
has_build_only_overrides,
first_turn_phase
)
}
update {}
to Ready
emit SessionResumeOverridesRejected {
reason: ResumeOverrideRejection::BuildOnlyAfterFirstTurn
}
}
transition AuthorizeSessionResumeOverridesAcceptRecomputeProvider {
on input AuthorizeSessionResumeOverrides {
provider_override_present,
model_override_present,
self_hosted_server_override_present,
has_build_only_overrides,
first_turn_phase
}
guard {
self.lifecycle_phase == Phase::Ready
&& resume_overrides_admissible(
provider_override_present,
model_override_present,
has_build_only_overrides,
first_turn_phase
)
&& resume_provider_recompute_from_model(
model_override_present,
provider_override_present
)
&& self_hosted_server_override_present == false
}
update {}
to Ready
emit SessionResumeOverridesAuthorized {
provider_selection: ResumeProviderSelection::RecomputeFromModel,
self_hosted_selection: ResumeSelfHostedSelection::Clear,
provider_overridden: true
}
}
transition AuthorizeSessionResumeOverridesAcceptRecomputeProviderWithSelfHostedOverride {
on input AuthorizeSessionResumeOverrides {
provider_override_present,
model_override_present,
self_hosted_server_override_present,
has_build_only_overrides,
first_turn_phase
}
guard {
self.lifecycle_phase == Phase::Ready
&& resume_overrides_admissible(
provider_override_present,
model_override_present,
has_build_only_overrides,
first_turn_phase
)
&& resume_provider_recompute_from_model(
model_override_present,
provider_override_present
)
&& self_hosted_server_override_present
}
update {}
to Ready
emit SessionResumeOverridesAuthorized {
provider_selection: ResumeProviderSelection::RecomputeFromModel,
self_hosted_selection: ResumeSelfHostedSelection::UseOverride,
provider_overridden: true
}
}
transition AuthorizeSessionResumeOverridesAcceptUseOverride {
on input AuthorizeSessionResumeOverrides {
provider_override_present,
model_override_present,
self_hosted_server_override_present,
has_build_only_overrides,
first_turn_phase
}
guard {
self.lifecycle_phase == Phase::Ready
&& resume_overrides_admissible(
provider_override_present,
model_override_present,
has_build_only_overrides,
first_turn_phase
)
&& resume_provider_recompute_from_model(
model_override_present,
provider_override_present
) == false
&& provider_override_present
&& self_hosted_server_override_present == false
}
update {}
to Ready
emit SessionResumeOverridesAuthorized {
provider_selection: ResumeProviderSelection::UseOverride,
self_hosted_selection: ResumeSelfHostedSelection::Clear,
provider_overridden: true
}
}
transition AuthorizeSessionResumeOverridesAcceptUseOverrideWithSelfHostedOverride {
on input AuthorizeSessionResumeOverrides {
provider_override_present,
model_override_present,
self_hosted_server_override_present,
has_build_only_overrides,
first_turn_phase
}
guard {
self.lifecycle_phase == Phase::Ready
&& resume_overrides_admissible(
provider_override_present,
model_override_present,
has_build_only_overrides,
first_turn_phase
)
&& resume_provider_recompute_from_model(
model_override_present,
provider_override_present
) == false
&& provider_override_present
&& self_hosted_server_override_present
}
update {}
to Ready
emit SessionResumeOverridesAuthorized {
provider_selection: ResumeProviderSelection::UseOverride,
self_hosted_selection: ResumeSelfHostedSelection::UseOverride,
provider_overridden: true
}
}
transition AuthorizeSessionResumeOverridesAcceptRetainStored {
on input AuthorizeSessionResumeOverrides {
provider_override_present,
model_override_present,
self_hosted_server_override_present,
has_build_only_overrides,
first_turn_phase
}
guard {
self.lifecycle_phase == Phase::Ready
&& resume_overrides_admissible(
provider_override_present,
model_override_present,
has_build_only_overrides,
first_turn_phase
)
&& resume_provider_recompute_from_model(
model_override_present,
provider_override_present
) == false
&& provider_override_present == false
&& self_hosted_server_override_present == false
}
update {}
to Ready
emit SessionResumeOverridesAuthorized {
provider_selection: ResumeProviderSelection::UseStored,
self_hosted_selection: ResumeSelfHostedSelection::Retain,
provider_overridden: false
}
}
transition AuthorizeSessionResumeOverridesAcceptRetainStoredWithSelfHostedOverride {
on input AuthorizeSessionResumeOverrides {
provider_override_present,
model_override_present,
self_hosted_server_override_present,
has_build_only_overrides,
first_turn_phase
}
guard {
self.lifecycle_phase == Phase::Ready
&& resume_overrides_admissible(
provider_override_present,
model_override_present,
has_build_only_overrides,
first_turn_phase
)
&& resume_provider_recompute_from_model(
model_override_present,
provider_override_present
) == false
&& provider_override_present == false
&& self_hosted_server_override_present
}
update {}
to Ready
emit SessionResumeOverridesAuthorized {
provider_selection: ResumeProviderSelection::UseStored,
self_hosted_selection: ResumeSelfHostedSelection::UseOverride,
provider_overridden: false
}
}
transition ClassifyLiveSessionAuthorityLive {
on input ClassifyLiveSessionAuthority {
stored_transcript_diverged,
live_has_uncommitted_transcript,
stored_is_archived
}
guard {
self.lifecycle_phase == Phase::Ready
&& stored_transcript_diverged == false
&& live_has_uncommitted_transcript == false
&& stored_is_archived == false
}
update {}
to Ready
emit LiveSessionAuthorityClassified {
authority: LiveSessionAuthorityKind::LiveAuthoritative,
reason: LiveSessionAuthorityReason::StoredArchived
}
}
transition ClassifyLiveSessionAuthorityDurableArchived {
on input ClassifyLiveSessionAuthority {
stored_transcript_diverged,
live_has_uncommitted_transcript,
stored_is_archived
}
guard {
self.lifecycle_phase == Phase::Ready
&& stored_is_archived == true
}
update {}
to Ready
emit LiveSessionAuthorityClassified {
authority: LiveSessionAuthorityKind::DurableAuthoritative,
reason: LiveSessionAuthorityReason::StoredArchived
}
}
transition ClassifyLiveSessionAuthorityDurableUncommitted {
on input ClassifyLiveSessionAuthority {
stored_transcript_diverged,
live_has_uncommitted_transcript,
stored_is_archived
}
guard {
self.lifecycle_phase == Phase::Ready
&& stored_is_archived == false
&& live_has_uncommitted_transcript == true
}
update {}
to Ready
emit LiveSessionAuthorityClassified {
authority: LiveSessionAuthorityKind::DurableAuthoritative,
reason: LiveSessionAuthorityReason::LiveUncommittedTranscript
}
}
transition ClassifyLiveSessionAuthorityDurableRevision {
on input ClassifyLiveSessionAuthority {
stored_transcript_diverged,
live_has_uncommitted_transcript,
stored_is_archived
}
guard {
self.lifecycle_phase == Phase::Ready
&& stored_is_archived == false
&& live_has_uncommitted_transcript == false
&& stored_transcript_diverged == true
}
update {}
to Ready
emit LiveSessionAuthorityClassified {
authority: LiveSessionAuthorityKind::DurableAuthoritative,
reason: LiveSessionAuthorityReason::StoredTranscriptRevisionDiverged
}
}
transition RecoverSessionFromStoreAuthorized {
on input RecoverSessionFromStore {
session_id,
has_metadata,
has_build_state,
runtime_projection_quarantined
}
guard {
self.lifecycle_phase == Phase::Ready
&& store_projection_can_recover_authority(
has_metadata,
has_build_state,
runtime_projection_quarantined
)
}
update {}
to Ready
emit SessionStoreRecoverySourceResolved { recoverable: true }
}
transition RecoverSessionFromStoreUnrecoverable {
on input RecoverSessionFromStore {
session_id,
has_metadata,
has_build_state,
runtime_projection_quarantined
}
guard {
self.lifecycle_phase == Phase::Ready
&& store_projection_can_recover_authority(
has_metadata,
has_build_state,
runtime_projection_quarantined
) == false
}
update {}
to Ready
emit SessionStoreRecoverySourceResolved { recoverable: false }
}
transition ClassifyDurableTailCompleted {
on input ClassifyDurableTail {
session_id,
candidate_id,
relation,
run_id_cardinality,
terminal_stop_reason,
dangling_tool_use_count,
orphan_tool_result_count,
messages_after_terminal
}
guard {
self.lifecycle_phase == Phase::Ready
&& relation == DurableHeadRelation::VerifiedStrictDescendant
&& run_id_cardinality == RunIdCardinality::SingleRunId
&& terminal_stop_reason == DurableTailStopReason::EndTurn
&& dangling_tool_use_count == 0
&& orphan_tool_result_count == 0
&& messages_after_terminal == false
}
update {}
to Ready
emit DurableTailClassified {
candidate_id: candidate_id,
class: DurableTailRecoveryClass::CompletedCandidate
}
}
transition ClassifyDurableTailRepairable {
on input ClassifyDurableTail {
session_id,
candidate_id,
relation,
run_id_cardinality,
terminal_stop_reason,
dangling_tool_use_count,
orphan_tool_result_count,
messages_after_terminal
}
guard {
self.lifecycle_phase == Phase::Ready
&& relation == DurableHeadRelation::VerifiedStrictDescendant
&& run_id_cardinality == RunIdCardinality::SingleRunId
&& dangling_tool_use_count == 0
&& orphan_tool_result_count == 0
&& messages_after_terminal == false
&& (terminal_stop_reason == DurableTailStopReason::ToolUse
|| terminal_stop_reason == DurableTailStopReason::Absent)
}
update {}
to Ready
emit DurableTailClassified {
candidate_id: candidate_id,
class: DurableTailRecoveryClass::InterruptedRepairableCandidate
}
}
transition ClassifyDurableTailAmbiguous {
on input ClassifyDurableTail {
session_id,
candidate_id,
relation,
run_id_cardinality,
terminal_stop_reason,
dangling_tool_use_count,
orphan_tool_result_count,
messages_after_terminal
}
guard {
self.lifecycle_phase == Phase::Ready
&& (relation != DurableHeadRelation::VerifiedStrictDescendant
|| run_id_cardinality != RunIdCardinality::SingleRunId
|| orphan_tool_result_count != 0
|| messages_after_terminal == true
|| terminal_stop_reason == DurableTailStopReason::Other
|| dangling_tool_use_count != 0)
}
update {}
to Ready
emit DurableTailClassified {
candidate_id: candidate_id,
class: DurableTailRecoveryClass::Ambiguous
}
}
transition ResolveRuntimeCheckpointProjectionActive {
on input ResolveRuntimeCheckpointProjection { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_lifecycle_terminal.get_cloned(session_id).get("value")
== SessionDocumentLifecycle::Active
}
update {}
to Ready
emit RuntimeCheckpointProjectionResolved {
disposition: RuntimeCheckpointProjectionDisposition::Project
}
}
transition ResolveRuntimeCheckpointProjectionArchived {
on input ResolveRuntimeCheckpointProjection { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_lifecycle_terminal.get_cloned(session_id).get("value")
== SessionDocumentLifecycle::Archived
}
update {}
to Ready
emit RuntimeCheckpointProjectionResolved {
disposition: RuntimeCheckpointProjectionDisposition::IgnoreArchived
}
}
transition ResolveSessionDocumentLifecycleMergeArchivedAbsorbing {
on input ResolveSessionDocumentLifecycleMerge {
session_id,
authority_archived,
candidate_archived
}
guard {
self.lifecycle_phase == Phase::Ready
&& (authority_archived == true || candidate_archived == true)
}
update {}
to Ready
emit SessionDocumentLifecycleMergeResolved {
merge: SessionDocumentLifecycleMerge::CarryArchived
}
}
transition ResolveSessionDocumentLifecycleMergeAuthority {
on input ResolveSessionDocumentLifecycleMerge {
session_id,
authority_archived,
candidate_archived
}
guard {
self.lifecycle_phase == Phase::Ready
&& authority_archived == false
&& candidate_archived == false
}
update {}
to Ready
emit SessionDocumentLifecycleMergeResolved {
merge: SessionDocumentLifecycleMerge::CarryAuthority
}
}
transition ApplyPendingToolResults {
on input ApplyPendingToolResults {
session_id,
result_count
}
guard { self.lifecycle_phase == Phase::Ready }
update {}
to Ready
emit SessionToolResultsApplied {
session_id: session_id,
applied_count: result_count
}
}
transition TranscriptEditFork {
on input TranscriptEdit {
session_id,
fork_or_rewrite_directive
}
guard {
self.lifecycle_phase == Phase::Ready
&& fork_or_rewrite_directive == TranscriptEditKind::Fork
}
update {}
to Ready
emit TranscriptRewriteCommitted {
kind: TranscriptEditKind::Fork,
success: true
}
}
transition TranscriptEditRewrite {
on input TranscriptEdit {
session_id,
fork_or_rewrite_directive
}
guard {
self.lifecycle_phase == Phase::Ready
&& fork_or_rewrite_directive == TranscriptEditKind::Rewrite
}
update {}
to Ready
emit TranscriptRewriteCommitted {
kind: TranscriptEditKind::Rewrite,
success: true
}
}
transition RecoverSessionLifecycleTerminal {
on input RecoverSessionLifecycleTerminal { session_id, terminal }
guard {
self.lifecycle_phase == Phase::Ready
&& (terminal == SessionDocumentLifecycle::Active
|| terminal == SessionDocumentLifecycle::Archived)
}
update {
self.session_lifecycle_terminal.insert(session_id, terminal);
}
to Ready
emit SessionLifecycleTerminalRecovered
}
transition ReviveArchivedSessionDocument {
on input ReviveArchivedSessionDocument { session_id }
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_lifecycle_terminal.get_cloned(session_id).get("value")
== SessionDocumentLifecycle::Archived
}
update {
self.session_lifecycle_terminal
.insert(session_id, SessionDocumentLifecycle::Active);
}
to Ready
emit SessionRevivalResolved
}
transition ArchiveSessionDocumentActive {
on input ArchiveSessionDocument {
session_id,
runtime_backed,
durable_document_present,
runtime_observation
}
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_lifecycle_terminal.get_cloned(session_id).get("value")
== SessionDocumentLifecycle::Active
}
update {
self.session_lifecycle_terminal
.insert(session_id, SessionDocumentLifecycle::Archived);
}
to Ready
emit SessionArchiveResolved {
disposition: SessionArchiveDisposition::Archive,
write_document: durable_document_present,
retire_runtime: archive_should_retire_runtime(
runtime_backed,
durable_document_present,
runtime_observation)
}
}
transition ArchiveSessionDocumentAlreadyArchived {
on input ArchiveSessionDocument {
session_id,
runtime_backed,
durable_document_present,
runtime_observation
}
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_lifecycle_terminal.get_cloned(session_id).get("value")
== SessionDocumentLifecycle::Archived
}
guard "runtime_quiescent" {
runtime_observation != SessionArchiveRuntimeObservation::RetirementRequired
}
update {}
to Ready
emit SessionArchiveResolved {
disposition: SessionArchiveDisposition::AlreadyArchived,
write_document: false,
retire_runtime: false
}
}
transition ArchiveSessionDocumentCompleteRetire {
on input ArchiveSessionDocument {
session_id,
runtime_backed,
durable_document_present,
runtime_observation
}
guard {
self.lifecycle_phase == Phase::Ready
&& self.session_lifecycle_terminal.get_cloned(session_id).get("value")
== SessionDocumentLifecycle::Archived
}
guard "runtime_residue" {
runtime_observation == SessionArchiveRuntimeObservation::RetirementRequired
}
update {}
to Ready
emit SessionArchiveResolved {
disposition: SessionArchiveDisposition::Archive,
write_document: false,
retire_runtime: true
}
}
}
}