aion-server 0.31.0

Aion workflow server library: HTTP, gRPC, WebSocket, and worker endpoints. Run it with the `aion` binary from the aion-cli crate.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
//! The lifecycle decisions, measured against a real store.
//!
//! # Why no process is spawned
//!
//! Every decision pinned here is made BEFORE a harness would be started: whether
//! a dormant session can be reopened, whether an append that fails takes the
//! broadcast with it, whether a command the agent never advertised is refused,
//! which session is the caller's current one. Spawning an agent to measure them
//! would put a process boundary between the assertion and the thing it is about.
//!
//! Where a decision can only be told apart by what happens NEXT — the resumable
//! arm — the discriminator is the failure's own variant: a session that must not
//! be reopened is refused as [`AssistantSessionError::Ended`] and never reaches
//! a spawn, while a resumable one reaches one and fails as
//! [`AssistantSessionError::HarnessFailed`] against a binary that is not an ACP
//! agent. Two different variants is the proof that two different branches ran.

use std::sync::Arc;

use aion_core::{
    AssistantCommand, AssistantCommandInvocation, AssistantSessionEvent, AssistantSessionState,
};
use aion_store::assistant::AssistantSessionStore;

use super::error::AssistantSessionError;
use super::fixture::{
    OPERATOR, RefusingTranscriptStore, open_session, opened, registry, registry_over, went_dormant,
};
use super::lifecycle::{CLOSED_RESUMABLE, DELETED, ENDED_IS_TERMINAL, RESUME_REFUSED};

type TestResult = Result<(), String>;

/// The bound every "must not hang" assertion runs under.
///
/// Generous relative to the work (all of it is in-memory) and short relative to
/// a hang: a refusal that took this long would be a refusal nobody waits for.
const BOUNDED_WAIT: std::time::Duration = std::time::Duration::from_secs(10);

/// Run `future` under [`BOUNDED_WAIT`], failing by name if it does not finish.
///
/// The requirement is "a typed refusal WITHIN a bounded wait", and a refusal
/// that arrives is only half of it — a hang and a very slow answer look
/// identical to anyone watching, so the bound is asserted rather than assumed.
async fn within_bound<T>(what: &str, future: impl Future<Output = T>) -> Result<T, String> {
    tokio::time::timeout(BOUNDED_WAIT, future)
        .await
        .map_err(|_elapsed| format!("{what} did not answer within {BOUNDED_WAIT:?}; it hung"))
}

/// Row (a), the refusing arm: a dormant session whose agent never advertised
/// `loadSession` refuses the next turn, by a TYPED variant, within a bounded
/// wait — and never spawns a process that would then hang on a `session/load`
/// the agent cannot answer.
#[tokio::test]
async fn a_dormant_session_whose_agent_cannot_reload_refuses_the_next_turn() -> TestResult {
    let (sessions, _store) = registry();
    let summary = open_session(&sessions).await?;
    opened(&sessions, summary.session_id, false).await?;
    went_dormant(&sessions, summary.session_id).await?;

    let refusal = within_bound(
        "a turn on an unresumable dormant session",
        sessions.turn(
            OPERATOR,
            summary.session_id,
            "carry on".to_owned(),
            None,
            None,
        ),
    )
    .await?;
    match refusal {
        Err(AssistantSessionError::Ended { reason, .. }) => {
            assert_eq!(
                reason, RESUME_REFUSED,
                "the refusal must name the capability the agent did not advertise"
            );
            assert!(
                reason.contains("loadSession"),
                "the reason names the capability: {reason}"
            );
        }
        other => {
            return Err(format!(
                "an unresumable dormant session must refuse as Ended, got {other:?}"
            ));
        }
    }

    // And the refusal is WRITTEN BACK: the session is now settled ended by an
    // appended record, so the next reader projects the decision rather than
    // recomputing it — and the panel can say why instead of offering a box that
    // would refuse again.
    let (state, reason) = sessions
        .state_of_session(summary.session_id)
        .await
        .map_err(|error| error.to_string())?;
    assert_eq!(state, AssistantSessionState::Ended);
    assert_eq!(reason.as_deref(), Some(RESUME_REFUSED));
    Ok(())
}

/// Row (a), the resuming arm, and the density claim.
///
/// A dormant session whose agent DID advertise `loadSession` takes the other
/// branch: it reaches a spawn, and the spawn asks the agent to load the prior
/// conversation. The fixture's stub agent refuses every `session/load` by name,
/// so the spawn fails — as `HarnessFailed`, which is a different variant from
/// the refusing arm above and is therefore the proof that a different branch
/// ran; and it fails in milliseconds on any box under any load, which is what
/// keeps [`BOUNDED_WAIT`] an assertion about this code rather than about the
/// host. It is NOT settled ended, because nothing decided the conversation was
/// over.
///
/// The transcript's indices continue DENSE across the whole episode: the resume
/// attempt appends where the transcript left off rather than restarting, which
/// is what makes a client's `?after=` cursor survive a dormancy.
#[tokio::test]
async fn a_dormant_session_whose_agent_can_reload_reaches_a_resume_and_keeps_its_indices()
-> TestResult {
    let (sessions, store) = registry();
    let summary = open_session(&sessions).await?;
    opened(&sessions, summary.session_id, true).await?;
    went_dormant(&sessions, summary.session_id).await?;
    let before = store
        .assistant_transcript_head(&summary.session_id)
        .await
        .map_err(|error| error.to_string())?;

    let outcome = within_bound(
        "a turn on a resumable dormant session",
        sessions.turn(
            OPERATOR,
            summary.session_id,
            "carry on".to_owned(),
            None,
            None,
        ),
    )
    .await?;
    match outcome {
        Err(AssistantSessionError::HarnessFailed { .. }) => {}
        Err(AssistantSessionError::Ended { reason, .. }) => {
            return Err(format!(
                "a resumable session must NOT be settled ended before it is tried: {reason}"
            ));
        }
        other => {
            return Err(format!(
                "the fixture's command is not an ACP agent, so the spawn must fail by name, got \
                 {other:?}"
            ));
        }
    }

    // Nothing settled it: a failed spawn is a failed attempt, not the end of a
    // conversation the agent still holds.
    let (state, _reason) = sessions
        .state_of_session(summary.session_id)
        .await
        .map_err(|error| error.to_string())?;
    assert_ne!(
        state,
        AssistantSessionState::Ended,
        "a spawn that failed must not end a conversation the agent can still reload"
    );

    // Dense, and CONTINUING: every index from zero, with nothing restarted.
    let frames = store
        .assistant_transcript(&summary.session_id, None)
        .await
        .map_err(|error| error.to_string())?;
    let indices: Vec<u64> = frames.iter().map(|frame| frame.index).collect();
    let expected: Vec<u64> = (0..indices.len() as u64).collect();
    assert_eq!(
        indices, expected,
        "the transcript's indices must stay dense from zero across a resume"
    );
    assert!(
        indices.len() as u64 >= before,
        "the resume must append at the head, never behind it"
    );
    Ok(())
}

/// Putting a conversation away is not ending it — when the agent can reload
/// it.
///
/// Two arms on the same delete, told apart by the one fact every resume
/// decision turns on. The agent that advertised `loadSession` leaves a session
/// that is CLOSED: settled by name, out of the operator's current slot, and
/// still reaching a spawn on the next turn (the fixture's binary is not an ACP
/// agent, so that spawn fails as `HarnessFailed` — the variant that proves the
/// resume arm ran, not the refusing one). The agent that did not leaves a
/// session that is ENDED, refused before any spawn.
#[tokio::test]
async fn closing_a_reloadable_session_keeps_it_reopenable_but_never_current() -> TestResult {
    let (sessions, _store) = registry();

    // The reloadable arm.
    let reloadable = open_session(&sessions).await?;
    opened(&sessions, reloadable.session_id, true).await?;
    went_dormant(&sessions, reloadable.session_id).await?;
    let current = sessions
        .current(OPERATOR)
        .await
        .map_err(|error| error.to_string())?
        .map(|summary| summary.session_id);
    assert_eq!(
        current,
        Some(reloadable.session_id),
        "before it is put away, the dormant session is the operator's current one"
    );
    sessions
        .delete(OPERATOR, reloadable.session_id)
        .await
        .map_err(|error| error.to_string())?;
    let (state, reason) = sessions
        .state_of_session(reloadable.session_id)
        .await
        .map_err(|error| error.to_string())?;
    assert_eq!(
        state,
        AssistantSessionState::Closed,
        "a reloadable session the caller shut is closed, not ended"
    );
    assert_eq!(reason.as_deref(), Some(CLOSED_RESUMABLE));
    let current = sessions
        .current(OPERATOR)
        .await
        .map_err(|error| error.to_string())?;
    assert!(
        current.is_none(),
        "a closed session is never the current one, however new it is: {current:?}"
    );
    let outcome = within_bound(
        "a turn on a closed session",
        sessions.turn(
            OPERATOR,
            reloadable.session_id,
            "let us pick this back up".to_owned(),
            None,
            None,
        ),
    )
    .await?;
    match outcome {
        Err(AssistantSessionError::HarnessFailed { .. }) => {}
        other => {
            return Err(format!(
                "a turn on a closed session must reach a resume (and fail by name against a \
                 binary that is not an agent), got {other:?}"
            ));
        }
    }

    // The arm that cannot reload.
    let unreloadable = open_session(&sessions).await?;
    opened(&sessions, unreloadable.session_id, false).await?;
    went_dormant(&sessions, unreloadable.session_id).await?;
    sessions
        .delete(OPERATOR, unreloadable.session_id)
        .await
        .map_err(|error| error.to_string())?;
    let (state, reason) = sessions
        .state_of_session(unreloadable.session_id)
        .await
        .map_err(|error| error.to_string())?;
    assert_eq!(
        state,
        AssistantSessionState::Ended,
        "a session whose agent cannot reload it is ended when the caller shuts it"
    );
    assert_eq!(reason.as_deref(), Some(DELETED));
    match within_bound(
        "a turn on an ended session",
        sessions.turn(
            OPERATOR,
            unreloadable.session_id,
            "carry on".to_owned(),
            None,
            None,
        ),
    )
    .await?
    {
        Err(AssistantSessionError::Ended { .. }) => Ok(()),
        other => Err(format!(
            "a turn on an ended session is refused before any spawn, got {other:?}"
        )),
    }
}

/// Ended is terminal through the OTHER door: a session that was created and
/// never opened, then deleted, has no prior conversation to resume — so the
/// spawn path, not the resume path, would serve its next turn. Before this
/// cell it did: the turn spawned a fresh agent under the deleted id and the
/// transcript read `live` again (a real transcript, 2026-08-31, a console
/// deleting a session moments before its first turn arrived). The turn is
/// refused by name, nothing spawns, and no settling record can leave `ended`.
#[tokio::test]
async fn a_turn_on_a_never_opened_session_the_caller_deleted_is_refused_and_spawns_nothing()
-> TestResult {
    let (sessions, store) = registry();
    let created = open_session(&sessions).await?;
    sessions
        .delete(OPERATOR, created.session_id)
        .await
        .map_err(|error| error.to_string())?;
    let (state, reason) = sessions
        .state_of_session(created.session_id)
        .await
        .map_err(|error| error.to_string())?;
    assert_eq!(state, AssistantSessionState::Ended);
    assert_eq!(reason.as_deref(), Some(DELETED));
    let frames_before = store
        .assistant_transcript(&created.session_id, None)
        .await
        .map_err(|error| error.to_string())?
        .len();

    match within_bound(
        "a turn on a never-opened, deleted session",
        sessions.turn(
            OPERATOR,
            created.session_id,
            "hello?".to_owned(),
            None,
            None,
        ),
    )
    .await?
    {
        Err(AssistantSessionError::Ended { reason, .. }) => {
            assert_eq!(reason, ENDED_IS_TERMINAL);
        }
        other => {
            return Err(format!(
                "a turn on an ended session is refused by name before any spawn, got {other:?}"
            ));
        }
    }
    // The refusal is the turn's own failure frame — one row, so a reloaded
    // conversation shows the question with its reason — and nothing else: no
    // opening, because nothing spawned.
    let rows_after = store
        .assistant_transcript(&created.session_id, None)
        .await
        .map_err(|error| error.to_string())?
        .len();
    assert_eq!(
        rows_after,
        frames_before + 1,
        "a refused turn on an ended session records exactly its own failure"
    );
    let frames_after = sessions
        .transcript_from(created.session_id, None)
        .await
        .map_err(|error| error.to_string())?;
    match frames_after.last().map(|frame| &frame.event) {
        Some(AssistantSessionEvent::TurnFailed { message, .. }) => {
            assert!(
                message.contains(ENDED_IS_TERMINAL),
                "the failure frame names the reason: {message}"
            );
        }
        other => {
            return Err(format!(
                "the last frame is the refused turn's failure, got {other:?}"
            ));
        }
    }
    assert!(
        !frames_after
            .iter()
            .any(|frame| matches!(frame.event, AssistantSessionEvent::SessionOpened { .. })),
        "a refused turn on an ended session spawns nothing: {frames_after:?}"
    );

    // The other direction: a settling record that would leave `ended` is refused.
    match sessions
        .settle(created.session_id, AssistantSessionState::Live, "resurrect")
        .await
    {
        Err(AssistantSessionError::Ended { reason, .. }) => assert_eq!(reason, DELETED),
        other => {
            return Err(format!(
                "settling out of ended must be refused, got {other:?}"
            ));
        }
    }
    let (state, _) = sessions
        .state_of_session(created.session_id)
        .await
        .map_err(|error| error.to_string())?;
    assert_eq!(state, AssistantSessionState::Ended, "still ended");
    Ok(())
}

/// T1: append-before-broadcast. A store that refuses the append fails the turn
/// and broadcasts NOTHING.
///
/// The subscriber is taken first and is the instrument: a frame the client saw
/// and the store never took would make the live stream and the record disagree
/// in the one direction no later read can repair.
#[tokio::test]
async fn a_store_that_refuses_the_append_fails_the_turn_and_broadcasts_nothing() -> TestResult {
    let store = Arc::new(RefusingTranscriptStore::new());
    let sessions = registry_over(Arc::clone(&store) as Arc<dyn AssistantSessionStore>);
    // The record write still works on this store, so a session exists to turn
    // on — and `create` itself fails at its first APPEND, which is the same
    // discipline seen one call earlier.
    let created = open_session(&sessions).await;
    match created {
        Err(message) => {
            assert!(
                message.contains(RefusingTranscriptStore::REFUSAL),
                "the refusal must reach the caller verbatim: {message}"
            );
        }
        Ok(summary) => {
            return Err(format!(
                "a store that refuses every append must fail the opening it cannot record, got \
                 {summary:?}"
            ));
        }
    }

    // The record was written before the refused append, so a session id exists
    // to subscribe to and drive. This is the arm the requirement names.
    let listing = store
        .list_assistant_sessions()
        .await
        .map_err(|error| error.to_string())?;
    let record = listing
        .sessions
        .first()
        .ok_or_else(|| {
            "the create wrote its record before the append it could not make".to_owned()
        })?
        .clone();

    let mut watcher = sessions.recorder(record.session_id).subscribe();
    let refusal = within_bound(
        "a turn on a store that refuses every append",
        sessions.turn(OPERATOR, record.session_id, "hello".to_owned(), None, None),
    )
    .await?;
    assert!(
        refusal.is_err(),
        "a turn whose frames cannot be recorded must fail rather than stream unrecorded frames"
    );
    assert!(
        matches!(
            watcher.try_recv(),
            Err(tokio::sync::broadcast::error::TryRecvError::Empty)
        ),
        "NOTHING may be broadcast when the append that would have justified it failed"
    );
    Ok(())
}

/// The positive control for the cell above: on a store that ACCEPTS the append,
/// the same subscriber does receive frames. Without this, a broadcast channel
/// that never delivered anything would pass the pin above for the wrong reason.
#[tokio::test]
async fn a_store_that_accepts_the_append_does_broadcast_what_it_took() -> TestResult {
    let (sessions, _store) = registry();
    let summary = open_session(&sessions).await?;
    let mut watcher = sessions.recorder(summary.session_id).subscribe();
    sessions
        .recorder(summary.session_id)
        .record(AssistantSessionEvent::Delta {
            turn_id: "t-1".to_owned(),
            text: "hello".to_owned(),
        })
        .await
        .map_err(|error| error.to_string())?;
    let frame = watcher
        .try_recv()
        .map_err(|error| format!("a committed frame must reach a subscriber: {error}"))?;
    assert!(matches!(frame.event, AssistantSessionEvent::Delta { .. }));
    Ok(())
}

/// C4: the REQUEST frame is appended at acceptance, before anything the harness
/// produces for that turn — so a reloaded conversation is questions and answers
/// rather than answers alone.
///
/// Measured against the fixture's stub agent, which starts and answers, so the
/// ordering is a real one between a request and the frames an agent produced.
/// (A turn refused at the spawn records only its failure — O5's second half,
/// pinned below — because no turn frame may precede the opening.)
#[tokio::test]
async fn the_operators_request_reaches_the_transcript_before_any_harness_frame() -> TestResult {
    let (sessions, store) = registry();
    let summary = open_session(&sessions).await?;
    // The spawn will fail, which is fine: the request is recorded by the turn
    // path, and nothing the harness would say can precede it.
    drop(
        within_bound(
            "a turn whose harness cannot start",
            sessions.turn(
                OPERATOR,
                summary.session_id,
                "fix the check".to_owned(),
                None,
                None,
            ),
        )
        .await?,
    );
    let frames = store
        .assistant_transcript(&summary.session_id, None)
        .await
        .map_err(|error| error.to_string())?;
    let decoded: Vec<AssistantSessionEvent> = frames
        .iter()
        .map(|frame| serde_json::from_slice(frame.payload.bytes()))
        .collect::<Result<_, _>>()
        .map_err(|error| error.to_string())?;
    assert!(
        decoded.iter().any(|event| matches!(
            event,
            AssistantSessionEvent::Request { text, .. } if text == "fix the check"
        )),
        "the operator's own words must be on the transcript: {decoded:?}"
    );
    Ok(())
}

/// T4's refusal arm: a command the harness never advertised is refused by name,
/// and the refusal states what the harness DOES advertise.
#[tokio::test]
async fn a_command_the_harness_never_advertised_is_refused_naming_what_it_does_offer() -> TestResult
{
    let (sessions, _store) = registry();
    let summary = open_session(&sessions).await?;
    sessions
        .recorder(summary.session_id)
        .record(AssistantSessionEvent::AvailableCommands {
            commands: vec![AssistantCommand {
                name: "compact".to_owned(),
                description: "compact the conversation".to_owned(),
                input_hint: None,
            }],
        })
        .await
        .map_err(|error| error.to_string())?;

    let refusal = within_bound(
        "a turn invoking an unadvertised command",
        sessions.turn(
            OPERATOR,
            summary.session_id,
            String::new(),
            None,
            Some(AssistantCommandInvocation {
                name: "rewrite-everything".to_owned(),
                input: None,
            }),
        ),
    )
    .await?;
    match refusal {
        Err(AssistantSessionError::UnknownCommand {
            requested,
            advertised,
            ..
        }) => {
            assert_eq!(requested, "rewrite-everything");
            assert!(
                advertised.contains("compact"),
                "the refusal must say what IS offered: {advertised}"
            );
        }
        other => {
            return Err(format!(
                "an unadvertised command must be refused by name, got {other:?}"
            ));
        }
    }
    Ok(())
}

/// The positive control: the ADVERTISED command is not refused here. It reaches
/// the spawn and fails there, which is a different variant — so the refusal
/// above is about the command and not about commands in general.
#[tokio::test]
async fn an_advertised_command_passes_the_check_and_reaches_the_harness() -> TestResult {
    let (sessions, store) = registry();
    let summary = open_session(&sessions).await?;
    sessions
        .recorder(summary.session_id)
        .record(AssistantSessionEvent::AvailableCommands {
            commands: vec![AssistantCommand {
                name: "compact".to_owned(),
                description: "compact the conversation".to_owned(),
                input_hint: Some("what to keep".to_owned()),
            }],
        })
        .await
        .map_err(|error| error.to_string())?;

    let outcome = within_bound(
        "a turn invoking an advertised command",
        sessions.turn(
            OPERATOR,
            summary.session_id,
            String::new(),
            None,
            Some(AssistantCommandInvocation {
                name: "compact".to_owned(),
                input: Some("keep the plan".to_owned()),
            }),
        ),
    )
    .await?;
    assert!(
        !matches!(outcome, Err(AssistantSessionError::UnknownCommand { .. })),
        "an advertised command must not be refused: {outcome:?}"
    );

    // And the DELIVERY form is on the record: `/compact keep the plan`, which is
    // exactly what the ACP schema's `unstructured` input describes — the command
    // name followed by everything typed after it.
    let frames = store
        .assistant_transcript(&summary.session_id, None)
        .await
        .map_err(|error| error.to_string())?;
    let decoded: Vec<AssistantSessionEvent> = frames
        .iter()
        .map(|frame| serde_json::from_slice(frame.payload.bytes()))
        .collect::<Result<_, _>>()
        .map_err(|error| error.to_string())?;
    assert!(
        decoded.iter().any(|event| matches!(
            event,
            AssistantSessionEvent::Request { text, command: Some(command), .. }
                if text == "/compact keep the plan" && command.name == "compact"
        )),
        "the composed line AND the structured invocation must both be recorded: {decoded:?}"
    );
    Ok(())
}

/// The listing's command cache is exactly what the transcript projects. The
/// record holds a copy so a listing need not read whole conversations; this is
/// what stops that copy becoming a second, drifting answer.
#[tokio::test]
async fn the_records_command_cache_is_what_the_transcript_projects() -> TestResult {
    let (sessions, store) = registry();
    let summary = open_session(&sessions).await?;
    let advertised = vec![
        AssistantCommand {
            name: "compact".to_owned(),
            description: "compact the conversation".to_owned(),
            input_hint: None,
        },
        AssistantCommand {
            name: "plan".to_owned(),
            description: "draft a plan".to_owned(),
            input_hint: Some("what to plan".to_owned()),
        },
    ];
    sessions
        .recorder(summary.session_id)
        .record(AssistantSessionEvent::AvailableCommands {
            commands: advertised.clone(),
        })
        .await
        .map_err(|error| error.to_string())?;

    let projected = sessions
        .projection(summary.session_id)
        .await
        .map_err(|error| error.to_string())?
        .commands;
    let cached = store
        .get_assistant_session(&summary.session_id)
        .await
        .map_err(|error| error.to_string())?
        .ok_or_else(|| "the session record is present".to_owned())?
        .commands;
    assert_eq!(projected, advertised);
    assert_eq!(
        cached, projected,
        "the record's cache and the transcript's projection are ONE answer"
    );
    Ok(())
}

/// T5, both arms: `current` is the caller's newest non-ended session, and an
/// absence when they hold none.
#[tokio::test]
async fn the_current_session_is_the_newest_continuable_one_or_an_absence() -> TestResult {
    let (sessions, _store) = registry();
    assert_eq!(
        sessions
            .current(OPERATOR)
            .await
            .map_err(|error| error.to_string())?,
        None,
        "a caller who has started nothing holds no current session"
    );

    let first = open_session(&sessions).await?;
    let second = open_session(&sessions).await?;
    let current = sessions
        .current(OPERATOR)
        .await
        .map_err(|error| error.to_string())?
        .ok_or_else(|| "a caller with two open sessions holds a current one".to_owned())?;
    assert_eq!(
        current.session_id, second.session_id,
        "the NEWEST continuable session is the current one"
    );

    // Ending the newest falls back to the one before it, rather than to nothing:
    // a closed conversation does not close the operator's thread.
    sessions
        .delete(OPERATOR, second.session_id)
        .await
        .map_err(|error| error.to_string())?;
    let after = sessions
        .current(OPERATOR)
        .await
        .map_err(|error| error.to_string())?
        .ok_or_else(|| "the earlier session is still continuable".to_owned())?;
    assert_eq!(after.session_id, first.session_id);

    // And another caller's sessions are not this caller's: `current` is
    // caller-scoped like every other read here.
    assert_eq!(
        sessions
            .current("somebody-else")
            .await
            .map_err(|error| error.to_string())?,
        None,
        "one caller's conversation must never be another's current session"
    );
    Ok(())
}

/// O5, the first half: NOTHING pre-warms.
///
/// Creating a session records a conversation and starts no process — there is no
/// opening on its transcript and no live session in the registry — so the first
/// message is what spawns the harness. A server that warmed a child at create
/// time would be running an agent on the operator's box for a conversation
/// nobody had started.
#[tokio::test]
async fn creating_a_session_starts_nothing_and_records_no_opening() -> TestResult {
    let (sessions, _store) = registry();
    let summary = open_session(&sessions).await?;
    assert_eq!(
        summary.state,
        AssistantSessionState::Dormant,
        "a conversation nobody has spoken in has no process"
    );
    assert!(
        !sessions.is_live(summary.session_id).await,
        "no harness process may exist before the first message"
    );
    let frames = sessions
        .transcript_from(summary.session_id, None)
        .await
        .map_err(|error| error.to_string())?;
    assert!(
        !frames
            .iter()
            .any(|frame| matches!(frame.event, AssistantSessionEvent::SessionOpened { .. })),
        "an opening on the transcript of a session nobody has spoken in means a process was \
         started for it: {frames:?}"
    );
    Ok(())
}

/// O5, the second half: no turn frame can precede the opening.
///
/// The first turn goes to the SPAWN before it records anything of its own, so a
/// turn that never reached an agent leaves no `request` and no `turn_started` on
/// the transcript — only its own typed failure. That ordering is what makes
/// "the `initialize` event is recorded before any turn event" true by
/// construction rather than by luck: there is no path on which a turn frame is
/// written first.
///
/// Measured against a session whose harness the build no longer ships, because
/// that refusal is met at exactly the same point a missing binary is — inside
/// `ensure_live`, before any frame — and needs no process to reach.
#[tokio::test]
async fn a_turn_that_never_reached_an_agent_records_its_failure_and_no_turn_frames() -> TestResult {
    let (sessions, store) = registry();
    let summary = open_session(&sessions).await?;
    // The record names a harness this build does not carry — a session opened
    // by an older build, which is the honest shape of "the spawn cannot be
    // planned".
    let mut record = store
        .get_assistant_session(&summary.session_id)
        .await
        .map_err(|error| error.to_string())?
        .ok_or_else(|| "the opened session has a record".to_owned())?;
    record.harness = "a-harness-this-build-does-not-ship".to_owned();
    store
        .put_assistant_session(record)
        .await
        .map_err(|error| error.to_string())?;

    let refusal = within_bound(
        "a turn on a session whose harness cannot be planned",
        sessions.turn(OPERATOR, summary.session_id, "hello".to_owned(), None, None),
    )
    .await?;
    match refusal {
        Err(AssistantSessionError::UnknownHarness { requested, .. }) => {
            assert_eq!(requested, "a-harness-this-build-does-not-ship");
        }
        other => {
            return Err(format!(
                "a turn that cannot plan a spawn must refuse by name, got {other:?}"
            ));
        }
    }

    let frames = sessions
        .transcript_from(summary.session_id, None)
        .await
        .map_err(|error| error.to_string())?;
    assert!(
        !frames.iter().any(|frame| matches!(
            frame.event,
            AssistantSessionEvent::Request { .. } | AssistantSessionEvent::TurnStarted { .. }
        )),
        "a turn that never reached an agent must not have written a request or a start: {frames:?}"
    );
    // But it must not be silent either: the operator is watching the socket, so
    // the refusal is on the transcript with its typed code.
    let failed = frames
        .iter()
        .find_map(|frame| match &frame.event {
            AssistantSessionEvent::TurnFailed { code, message, .. } => {
                Some((code.clone(), message.clone()))
            }
            _ => None,
        })
        .ok_or_else(|| format!("the failure must reach the transcript: {frames:?}"))?;
    assert_eq!(failed.0, "unknown_harness");
    assert!(
        failed.1.contains("a-harness-this-build-does-not-ship"),
        "the recorded failure names what could not be run: {}",
        failed.1
    );
    Ok(())
}

/// O1: `sessions_disabled_reason` is for a fault the product can NAME.
///
/// A stock server serves the assistant, so the only thing that can take the
/// surface down is the store the conversation would live in — and when that
/// happens the reason carries the store's own error, not a sentence about a
/// configuration section that no longer exists.
#[tokio::test]
async fn a_store_the_boot_sweep_cannot_read_takes_the_surface_down_by_name() -> TestResult {
    let (sessions, _store) = registry();
    assert!(
        sessions.availability().is_available(),
        "a stock server serves the assistant before anything has gone wrong"
    );
    let refusing = registry_over(Arc::new(RefusingTranscriptStore::new()));
    // The boot sweep is where the store is first exercised end to end, so it is
    // where the answer comes from. A sweep that could not settle its orphans is
    // a server that cannot record a conversation.
    let settled = open_session(&refusing).await;
    assert!(
        settled.is_err(),
        "this instrument's store refuses every append"
    );
    refusing.report_store_fault(&AssistantSessionError::Store(
        aion_store::StoreError::Backend(RefusingTranscriptStore::REFUSAL.to_owned()),
    ));
    let reason = refusing
        .availability()
        .reason()
        .ok_or_else(|| "a server with an unusable store must say so".to_owned())?
        .to_owned();
    assert!(
        reason.contains(RefusingTranscriptStore::REFUSAL),
        "the reason carries the store's own error: {reason}"
    );
    assert!(
        !reason.contains("[assistant]"),
        "`not configured` is never the reason any more: {reason}"
    );
    // And a create on it is refused with that same sentence, so the panel and
    // the refusal say one thing.
    match refusing.create(OPERATOR, None, None, None).await {
        Err(AssistantSessionError::NotCommissioned { reason: refused }) => {
            assert_eq!(refused, reason);
            Ok(())
        }
        other => Err(format!(
            "a server that cannot record a conversation must refuse to open one, got {other:?}"
        )),
    }
}

/// The record's token digest follows the secret most recently handed to a live
/// child, and nothing that runs later in the same turn may restore an older
/// one.
///
/// This is the defect's own interleave, without a process. A turn used to read
/// its copy of the record, the spawn stored the digest of the secret it handed
/// the child, and then `touch` — taking the caller's pre-spawn COPY by value —
/// put that copy back whole, restoring the pre-spawn digest. The child held the
/// new secret, the store held the old digest, and every MCP call the agent made
/// was refused as `WrongToken` from that write on. The pin drives the same
/// interleave through the store: read-copy, spawn-write, bookkeeping — and
/// asserts the spawn's digest survives the bookkeeping, which also proves the
/// bookkeeping ran against what the store held NOW rather than a copy carried
/// across an await.
#[tokio::test]
async fn the_turn_bookkeeping_never_restores_a_pre_spawn_token_digest() -> TestResult {
    let (sessions, store) = registry();
    let summary = open_session(&sessions).await?;
    let session_id = summary.session_id;

    // The spawn's write: the digest of the token the child was actually handed.
    // (`open_session` mints no bearer, so the pre-spawn copy a stale writer
    // would restore is `None` — the exact value the live defect restored.)
    let handed = "the-digest-of-the-secret-the-child-holds".to_owned();
    let mut spawned = store
        .get_assistant_session(&session_id)
        .await
        .map_err(|error| error.to_string())?
        .ok_or("the opened session's record is missing before the spawn write")?;
    spawned.mcp_token_digest = Some(handed.clone());
    store
        .put_assistant_session(spawned)
        .await
        .map_err(|error| error.to_string())?;

    // The turn's bookkeeping, handed only the session id.
    sessions
        .touch(session_id, Some("first words of the conversation"))
        .await
        .map_err(|error| error.to_string())?;

    let held = store
        .get_assistant_session(&session_id)
        .await
        .map_err(|error| error.to_string())?
        .ok_or("the session's record is missing after the bookkeeping write")?;
    if held.mcp_token_digest.as_deref() != Some(handed.as_str()) {
        return Err(format!(
            "the bookkeeping write restored a pre-spawn token digest: the child holds a secret \
             for {handed:?} and the store now says {:?}",
            held.mcp_token_digest
        ));
    }
    if held.turns != 1 {
        return Err(format!(
            "the bookkeeping did not run: expected 1 recorded turn, found {}",
            held.turns
        ));
    }
    if held.title.as_deref() != Some("first words of the conversation") {
        return Err(format!(
            "the first turn's words did not become the title: found {:?}",
            held.title
        ));
    }
    Ok(())
}