instar 1.3.804 → 1.3.805

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.804",
3
+ "version": "1.3.805",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-07-10T21:31:11.605Z",
5
- "instarVersion": "1.3.804",
4
+ "generatedAt": "2026-07-10T21:41:03.168Z",
5
+ "instarVersion": "1.3.805",
6
6
  "entryCount": 202,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -418,7 +418,7 @@
418
418
  "type": "route-group",
419
419
  "domain": "monitoring",
420
420
  "sourcePath": "src/server/routes.ts",
421
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
421
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
422
422
  "since": "2025-01-01"
423
423
  },
424
424
  "route-group:agents": {
@@ -426,7 +426,7 @@
426
426
  "type": "route-group",
427
427
  "domain": "sessions",
428
428
  "sourcePath": "src/server/routes.ts",
429
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
429
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
430
430
  "since": "2025-01-01"
431
431
  },
432
432
  "route-group:backups": {
@@ -434,7 +434,7 @@
434
434
  "type": "route-group",
435
435
  "domain": "operations",
436
436
  "sourcePath": "src/server/routes.ts",
437
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
437
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
438
438
  "since": "2025-01-01"
439
439
  },
440
440
  "route-group:git": {
@@ -442,7 +442,7 @@
442
442
  "type": "route-group",
443
443
  "domain": "coordination",
444
444
  "sourcePath": "src/server/routes.ts",
445
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
445
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
446
446
  "since": "2025-01-01"
447
447
  },
448
448
  "route-group:memory": {
@@ -450,7 +450,7 @@
450
450
  "type": "route-group",
451
451
  "domain": "memory",
452
452
  "sourcePath": "src/server/routes.ts",
453
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
453
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
454
454
  "since": "2025-01-01"
455
455
  },
456
456
  "route-group:semantic": {
@@ -458,7 +458,7 @@
458
458
  "type": "route-group",
459
459
  "domain": "memory",
460
460
  "sourcePath": "src/server/routes.ts",
461
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
461
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
462
462
  "since": "2025-01-01"
463
463
  },
464
464
  "route-group:status": {
@@ -466,7 +466,7 @@
466
466
  "type": "route-group",
467
467
  "domain": "monitoring",
468
468
  "sourcePath": "src/server/routes.ts",
469
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
469
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
470
470
  "since": "2025-01-01"
471
471
  },
472
472
  "route-group:capabilities": {
@@ -474,7 +474,7 @@
474
474
  "type": "route-group",
475
475
  "domain": "mapping",
476
476
  "sourcePath": "src/server/routes.ts",
477
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
477
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
478
478
  "since": "2025-01-01"
479
479
  },
480
480
  "route-group:project-map": {
@@ -482,7 +482,7 @@
482
482
  "type": "route-group",
483
483
  "domain": "mapping",
484
484
  "sourcePath": "src/server/routes.ts",
485
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
485
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
486
486
  "since": "2025-01-01"
487
487
  },
488
488
  "route-group:coherence": {
@@ -490,7 +490,7 @@
490
490
  "type": "route-group",
491
491
  "domain": "coherence",
492
492
  "sourcePath": "src/server/routes.ts",
493
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
493
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
494
494
  "since": "2025-01-01"
495
495
  },
496
496
  "route-group:topic-bindings": {
@@ -498,7 +498,7 @@
498
498
  "type": "route-group",
499
499
  "domain": "sessions",
500
500
  "sourcePath": "src/server/routes.ts",
501
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
501
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
502
502
  "since": "2025-01-01"
503
503
  },
504
504
  "route-group:context": {
@@ -506,7 +506,7 @@
506
506
  "type": "route-group",
507
507
  "domain": "context",
508
508
  "sourcePath": "src/server/routes.ts",
509
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
509
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
510
510
  "since": "2025-01-01"
511
511
  },
512
512
  "route-group:scope-coherence": {
@@ -514,7 +514,7 @@
514
514
  "type": "route-group",
515
515
  "domain": "coherence",
516
516
  "sourcePath": "src/server/routes.ts",
517
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
517
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
518
518
  "since": "2025-01-01"
519
519
  },
520
520
  "route-group:canonical-state": {
@@ -522,7 +522,7 @@
522
522
  "type": "route-group",
523
523
  "domain": "state",
524
524
  "sourcePath": "src/server/routes.ts",
525
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
525
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
526
526
  "since": "2025-01-01"
527
527
  },
528
528
  "route-group:ci": {
@@ -530,7 +530,7 @@
530
530
  "type": "route-group",
531
531
  "domain": "monitoring",
532
532
  "sourcePath": "src/server/routes.ts",
533
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
533
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
534
534
  "since": "2025-01-01"
535
535
  },
536
536
  "route-group:sessions": {
@@ -538,7 +538,7 @@
538
538
  "type": "route-group",
539
539
  "domain": "sessions",
540
540
  "sourcePath": "src/server/routes.ts",
541
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
541
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
542
542
  "since": "2025-01-01"
543
543
  },
544
544
  "route-group:jobs": {
@@ -546,7 +546,7 @@
546
546
  "type": "route-group",
547
547
  "domain": "scheduling",
548
548
  "sourcePath": "src/server/routes.ts",
549
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
549
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
550
550
  "since": "2025-01-01"
551
551
  },
552
552
  "route-group:skip-ledger": {
@@ -554,7 +554,7 @@
554
554
  "type": "route-group",
555
555
  "domain": "scheduling",
556
556
  "sourcePath": "src/server/routes.ts",
557
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
557
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
558
558
  "since": "2025-01-01"
559
559
  },
560
560
  "route-group:telegram": {
@@ -562,7 +562,7 @@
562
562
  "type": "route-group",
563
563
  "domain": "communication",
564
564
  "sourcePath": "src/server/routes.ts",
565
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
565
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
566
566
  "since": "2025-01-01"
567
567
  },
568
568
  "route-group:attention": {
@@ -570,7 +570,7 @@
570
570
  "type": "route-group",
571
571
  "domain": "communication",
572
572
  "sourcePath": "src/server/routes.ts",
573
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
573
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
574
574
  "since": "2025-01-01"
575
575
  },
576
576
  "route-group:relationships": {
@@ -578,7 +578,7 @@
578
578
  "type": "route-group",
579
579
  "domain": "relationships",
580
580
  "sourcePath": "src/server/routes.ts",
581
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
581
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
582
582
  "since": "2025-01-01"
583
583
  },
584
584
  "route-group:feedback": {
@@ -586,7 +586,7 @@
586
586
  "type": "route-group",
587
587
  "domain": "feedback",
588
588
  "sourcePath": "src/server/routes.ts",
589
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
589
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
590
590
  "since": "2025-01-01"
591
591
  },
592
592
  "route-group:updates": {
@@ -594,7 +594,7 @@
594
594
  "type": "route-group",
595
595
  "domain": "updates",
596
596
  "sourcePath": "src/server/routes.ts",
597
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
597
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
598
598
  "since": "2025-01-01"
599
599
  },
600
600
  "route-group:dispatches": {
@@ -602,7 +602,7 @@
602
602
  "type": "route-group",
603
603
  "domain": "dispatches",
604
604
  "sourcePath": "src/server/routes.ts",
605
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
605
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
606
606
  "since": "2025-01-01"
607
607
  },
608
608
  "route-group:quota": {
@@ -610,7 +610,7 @@
610
610
  "type": "route-group",
611
611
  "domain": "monitoring",
612
612
  "sourcePath": "src/server/routes.ts",
613
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
613
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
614
614
  "since": "2025-01-01"
615
615
  },
616
616
  "route-group:publishing": {
@@ -618,7 +618,7 @@
618
618
  "type": "route-group",
619
619
  "domain": "publishing",
620
620
  "sourcePath": "src/server/routes.ts",
621
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
621
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
622
622
  "since": "2025-01-01"
623
623
  },
624
624
  "route-group:private-views": {
@@ -626,7 +626,7 @@
626
626
  "type": "route-group",
627
627
  "domain": "publishing",
628
628
  "sourcePath": "src/server/routes.ts",
629
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
629
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
630
630
  "since": "2025-01-01"
631
631
  },
632
632
  "route-group:tunnel": {
@@ -634,7 +634,7 @@
634
634
  "type": "route-group",
635
635
  "domain": "networking",
636
636
  "sourcePath": "src/server/routes.ts",
637
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
637
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
638
638
  "since": "2025-01-01"
639
639
  },
640
640
  "route-group:events": {
@@ -642,7 +642,7 @@
642
642
  "type": "route-group",
643
643
  "domain": "networking",
644
644
  "sourcePath": "src/server/routes.ts",
645
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
645
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
646
646
  "since": "2025-01-01"
647
647
  },
648
648
  "route-group:evolution": {
@@ -650,7 +650,7 @@
650
650
  "type": "route-group",
651
651
  "domain": "evolution",
652
652
  "sourcePath": "src/server/routes.ts",
653
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
653
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
654
654
  "since": "2025-01-01"
655
655
  },
656
656
  "route-group:watchdog": {
@@ -658,7 +658,7 @@
658
658
  "type": "route-group",
659
659
  "domain": "monitoring",
660
660
  "sourcePath": "src/server/routes.ts",
661
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
661
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
662
662
  "since": "2025-01-01"
663
663
  },
664
664
  "route-group:topic-memory": {
@@ -666,7 +666,7 @@
666
666
  "type": "route-group",
667
667
  "domain": "memory",
668
668
  "sourcePath": "src/server/routes.ts",
669
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
669
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
670
670
  "since": "2025-01-01"
671
671
  },
672
672
  "route-group:state-sync": {
@@ -674,7 +674,7 @@
674
674
  "type": "route-group",
675
675
  "domain": "coordination",
676
676
  "sourcePath": "src/server/routes.ts",
677
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
677
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
678
678
  "since": "2025-01-01"
679
679
  },
680
680
  "route-group:intent": {
@@ -682,7 +682,7 @@
682
682
  "type": "route-group",
683
683
  "domain": "intent",
684
684
  "sourcePath": "src/server/routes.ts",
685
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
685
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
686
686
  "since": "2025-01-01"
687
687
  },
688
688
  "route-group:triage": {
@@ -690,7 +690,7 @@
690
690
  "type": "route-group",
691
691
  "domain": "safety",
692
692
  "sourcePath": "src/server/routes.ts",
693
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
693
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
694
694
  "since": "2025-01-01"
695
695
  },
696
696
  "route-group:operations": {
@@ -698,7 +698,7 @@
698
698
  "type": "route-group",
699
699
  "domain": "safety",
700
700
  "sourcePath": "src/server/routes.ts",
701
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
701
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
702
702
  "since": "2025-01-01"
703
703
  },
704
704
  "route-group:sentinel": {
@@ -706,7 +706,7 @@
706
706
  "type": "route-group",
707
707
  "domain": "safety",
708
708
  "sourcePath": "src/server/routes.ts",
709
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
709
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
710
710
  "since": "2025-01-01"
711
711
  },
712
712
  "route-group:trust": {
@@ -714,7 +714,7 @@
714
714
  "type": "route-group",
715
715
  "domain": "safety",
716
716
  "sourcePath": "src/server/routes.ts",
717
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
717
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
718
718
  "since": "2025-01-01"
719
719
  },
720
720
  "route-group:monitoring": {
@@ -722,7 +722,7 @@
722
722
  "type": "route-group",
723
723
  "domain": "monitoring",
724
724
  "sourcePath": "src/server/routes.ts",
725
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
725
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
726
726
  "since": "2025-01-01"
727
727
  },
728
728
  "route-group:commitments": {
@@ -730,7 +730,7 @@
730
730
  "type": "route-group",
731
731
  "domain": "commitments",
732
732
  "sourcePath": "src/server/routes.ts",
733
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
733
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
734
734
  "since": "2025-01-01"
735
735
  },
736
736
  "route-group:episodes": {
@@ -738,7 +738,7 @@
738
738
  "type": "route-group",
739
739
  "domain": "memory",
740
740
  "sourcePath": "src/server/routes.ts",
741
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
741
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
742
742
  "since": "2025-01-01"
743
743
  },
744
744
  "route-group:messages": {
@@ -746,7 +746,7 @@
746
746
  "type": "route-group",
747
747
  "domain": "coordination",
748
748
  "sourcePath": "src/server/routes.ts",
749
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
749
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
750
750
  "since": "2025-01-01"
751
751
  },
752
752
  "route-group:system-reviews": {
@@ -754,7 +754,7 @@
754
754
  "type": "route-group",
755
755
  "domain": "monitoring",
756
756
  "sourcePath": "src/server/routes.ts",
757
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
757
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
758
758
  "since": "2025-01-01"
759
759
  },
760
760
  "route-group:machine-mesh": {
@@ -770,7 +770,7 @@
770
770
  "type": "route-group",
771
771
  "domain": "security",
772
772
  "sourcePath": "src/server/routes.ts",
773
- "contentHash": "ebbf2d5b98d390b5b127cda84bc053b0ad9a0b551d59566dee002f4607f2d235",
773
+ "contentHash": "c24f3022b7d216daab5b517b55fae6da6d9ca04471c4fcf47ee18c9d1abb37a9",
774
774
  "since": "2025-01-01"
775
775
  },
776
776
  "cli:init": {
@@ -0,0 +1,41 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Subscriptions "Set up" flow — the five topic-29836 operator-observed defects (screenshot-proven, 2026-07-10), fixed end to end:
9
+
10
+ 1. **D1 — a background refresh never clobbers an open interaction (new Dashboard UX Standard floor F9).** The tab's 30s poll used to rebuild the matrix/panel out from under the operator: the PIN input reverted to "Set up" mid-typing; the code-paste step was swapped for "◷ Signing in…" before the code could be pasted. New shared primitives in `dashboard/subscriptions.js` (`hasOpenInteraction`, `updateCountdowns`): a surface holding an open interaction (an episode marked `data-interaction-open`, a focused text-entry, or a dirty field) is never rebuilt — the poll merges server state around it (live TTL countdowns via `data-ttl-expires`) until the flow reaches a terminal state or the operator backs out (new Back affordance on PIN entry). Applied to the matrix, the pending panel, AND the follow-me Approve card (same defect class). Floor registered in `docs/specs/dashboard-ux-standard.md` (F9) + `docs/STANDARDS-REGISTRY.md`; enforcement: `tests/unit/dashboard-refresh-interaction-hold.test.ts` (with a negative control) + controller integration tests. Migrating the other polling tabs onto the primitives is tracked in the spec <!-- tracked: topic-29836 -->.
11
+ 2. **D2 — the matrix cell carries the COMPLETE flow.** Sign-in link, expected-account notice, code input + Submit, live link-expiry countdown, the two-codes heads-up, and Cancel all render IN the cell — from SERVER pending-login state (`appendCellSignInFlow`), so the step survives reloads and rebuilds and can never exist only in the bottom panel. start-cell responses pass through `expectedEmail`/`ttlExpiresAt`/`notice`/`kind`. The bottom panel stays as a mirror.
12
+ 3. **D3 — wrong-account hazard, both layers.** UI: the cell and the panel state prominently which account the provider's OAuth page MUST show ("the sign-in page must show headley.justin@gmail.com — if it shows a different account, tap “Switch account” first"), rendered from the enrollment record. Structural: the pre-existing fail-closed S7 email gate's held verdict now carries `expected`/`got` through `completeFollowMe` → the submit-code response → plain-language copy naming BOTH accounts ("that code signed in X — this slot needs Y; the account was NOT enrolled"). Oracle-unavailable keeps failing CLOSED, now with honest "couldn't confirm which account that was" copy.
13
+ 4. **D4 — unmistakable terminal states.** Validated → in-cell "✓ All set — <email> is signed in" with a transient `just-verified` highlight that bridges until the pool read shows Active (never a blink back to "Set up"); held/expired/dead-pane → explicit red states with a working Retry; the pending panel keeps durable ✓ Done / ✗ failed / expired cards (never a vanishing line).
14
+ 5. **D5 — the re-sign-in flow made honest (the 2026-07-10 justin-gmail incident).** (a) Record⟂pane liveness: pending-logins responses annotate each local login with `paneAlive` (tri-state, fail-toward-unknown; peers annotate their own in the pool merge); a dead-pane record renders an explicit "Sign-in needs a restart" state (no code input) and code-submit against it answers a machine-readable 409 `code:'pane-dead'` (live-but-not-ready → `pane-not-ready`). (b) Single-attempt discipline at the mint chokepoint: enroll/start returns the existing LIVE attempt on a re-request (one attempt, one URL — parallel-PKCE code crossing is structurally closed) and atomically supersedes a dead-pane zombie (abandon + fresh drive, record and pane replaced together); the start-cell reuse gate honors the same rule. (c) The already-authorized short-circuit ("You're all set up… close this window", no code ever shown): a new `EnrollmentWizard.sweepFollowMeCompletions` on the existing 5-min reissue timer detects the landed credential and completes through the SAME identity gate — making submit-code's "the reissue/complete sweep finishes it" contract true. (d) A validated completion UPSERTS an existing pool account back to active (re-auth previously crashed on `add()`'s duplicate-id refusal and stranded the flow at "finishing sign-in…" forever). (e) Wording floors: accounts by email, machines by nickname (the pool-scope pending merge now tags the SELF machine's nickname; a raw `m_<hex>` id is never shown), and error copy only references affordances that exist on its surface (no more "re-tap Approve" on surfaces with no Approve).
15
+
16
+ All touched routes stay behind the existing `multiMachine.accountFollowMe` dev-gate — fleet installs keep 503ing exactly as before. API changes are additive response fields only; never a credential.
17
+
18
+ ## What to Tell Your User
19
+
20
+ - Setting up an account on a machine from the Subscriptions grid no longer fights you: the page can't wipe your PIN or your pasted code while it refreshes, every step (link, code box, expiry countdown, warnings, Cancel) lives right in the grid cell, and finishing shows an unmistakable green "✓ All set" instead of a quiet text line.
21
+ - Before you open a sign-in link, the cell now tells you exactly which account the sign-in page must show — and if you sign in with the wrong one, it's refused safely with a message naming both accounts. Nothing gets enrolled under the wrong identity.
22
+ - Re-signing-in an account that expired ("Needs sign-in") actually works now: dead sign-in attempts announce themselves and restart cleanly, a second tap can't create two crossing sign-ins, and a sign-in that finishes without showing a code completes on its own within a few minutes.
23
+
24
+ ## Summary of New Capabilities
25
+
26
+ | Capability | How to Use |
27
+ |---|---|
28
+ | Never-clobber refresh (Dashboard UX floor F9) | Automatic on the Subscriptions tab — type/tap freely; the poll merges around you |
29
+ | Complete in-cell sign-in flow | Tap "Set up"/"Sign in"/"Retry" on a grid cell — every step stays in the cell |
30
+ | Expected-account warning + both-accounts mismatch copy | Automatic — rendered from the enrollment record; held refusals name both emails |
31
+ | Pane-liveness on pending logins | `GET /subscription-pool/pending-logins` → each local login carries `paneAlive: true\|false\|null` |
32
+ | Machine-readable submit refusals | 409 `{ code: 'pane-dead' \| 'pane-not-ready' }` on `/follow-me/enroll/:id/submit-code` |
33
+ | Single live attempt per slot | Automatic — enroll/start reuses the live attempt (`reused:true`) or supersedes a dead one |
34
+ | No-code (short-circuit) completion sweep | Automatic — runs on the enrollment reissue timer; identity-gated like every completion |
35
+
36
+ ## Evidence
37
+
38
+ - Tier 1: `tests/unit/dashboard-refresh-interaction-hold.test.ts` (16 — F9 predicate both sides, merge arm, NEGATIVE CONTROL proving a naive rebuild clobbers, wording floors, both-accounts copy); `tests/unit/subscriptions-render.test.ts` (48 — full in-cell flow, broken/expired/just-verified/held-detail states, expected-account notice, dead-pane card, outcome cards, wording floors, XSS-inertness); `tests/unit/enrollment-wizard.test.ts` (36 — held verdict carries expected/got both sides; completion sweep: match→validated+pool-add, mismatch→held+attention+NOT added, not-ready→untouched, non-follow-me skipped, throwing probe safe).
39
+ - Tier 2: `tests/integration/subscriptions-tab.test.ts` (16 — poll mid-PIN/mid-code/untyped-open never clobbers, Back releases, countdown merges on held DOM, validated→ceremony→active+just-verified→done card, held names both accounts and persists, pane-dead→needs-restart, vanished-episode→explicit expired, panel dead-pane not submittable, Approve-card PIN survives); `tests/integration/account-matrix-start-cell-route.test.ts` (7 — flow-detail passthrough, reuse carries same URL, dead-pane superseded not reused); `tests/integration/account-follow-me-submit-code-route.test.ts` (19 — pane-dead/pane-not-ready codes, wording floor, held carries both emails + parked-not-pooled, re-auth upsert); `tests/integration/account-follow-me-enroll-start-route.test.ts` (9 — healthy reuse: one URL/one drive; dead-pane supersede: exactly one live attempt); `tests/integration/subscription-enrollment-routes.test.ts` (paneAlive tri-state + live/dead annotation).
40
+ - Tier 3: `tests/e2e/subscriptions-tab-lifecycle.test.ts` — the production route path + SHIPPED controller: the matrix cell renders the complete flow from the LIVE server (expected email, code=true link, code input, TTL, Cancel), `paneAlive` flows through the real read, and a REAL poll tick against the live server preserves a half-typed code. `tests/e2e/matrix-cell-cancel-alive.test.ts` re-run green (Cancel relay preserved).
41
+ - Regression sweep: full subscription/follow-me/matrix suites + dashboard floor suites green.
@@ -0,0 +1,79 @@
1
+ # Side-Effects Review — Subscriptions "Set up" flow: never-clobber refresh, in-cell complete flow, wrong-account guard surfacing, unmistakable terminal states, record⟂pane liveness (topic 29836 D1–D5)
2
+
3
+ **Version / slug:** `subs-setup-flow-ux`
4
+ **Date:** `2026-07-10`
5
+ **Author:** Echo (instar-dev agent)
6
+ **Second-pass reviewer:** not required (no new blocking authority — see §4; the one identity gate touched is pre-existing, converged, and only has its verdict SURFACED)
7
+
8
+ ## Summary of the change
9
+
10
+ Fixes the five operator-observed (screenshot-proven, topic 29836, 2026-07-10) defects in the dashboard Subscriptions tab's account×machine "Set up" enrollment flow. **D1** — the 30s poll re-rendered the matrix/panel out from under an open interaction (PIN input reverted to a button mid-typing; the code-paste step swapped for "◷ Signing in…"); fixed as a RULE: new F9 primitives `hasOpenInteraction` + `updateCountdowns` in `dashboard/subscriptions.js`, and the controller rebuilds a surface only while no interaction is open (episode marker `data-interaction-open`, focused text-entry, or dirty field), merging countdowns in place otherwise. **D2** — the flow's continuation lived only in the bottom "Pending logins" panel; now the matrix cell carries the COMPLETE flow (sign-in link, expected-account notice, code input, TTL countdown, two-codes notice, Cancel), rendered from SERVER pending-login state via `appendCellSignInFlow` so it also survives reloads and rebuilds. **D3** — the wrong-account hazard: the UI now states which account the OAuth page MUST show (from the enrollment record's `expectedEmail`, passed through start-cell responses), and the pre-existing S7 email-gate's held verdict now carries `expected`/`got` through `EnrollmentWizard.completeFollowMe` → the submit-code response → plain-language copy naming BOTH accounts; oracle-unavailable keeps failing CLOSED with honest "couldn't confirm" copy. **D4** — invisible success: explicit terminal presentations (in-cell "✓ All set" + transient `just-verified` highlight bridging until the pool read catches up; explicit held/expired/broken states; durable done/failed cards in the pending panel). **D5** — the re-sign-in flow: pending logins are annotated with `paneAlive` (record⟂pane reconciliation; tri-state, fail-toward-unknown); a dead-pane record renders an explicit needs-restart state and code-submit against it answers a machine-readable 409 `pane-dead`; enroll/start enforces single-attempt discipline (reuse a live attempt / supersede a dead one atomically — codes can never cross between parallel PKCE attempts); a new `sweepFollowMeCompletions` handles the already-authorized short-circuit (credential lands with no code ever shown) through the SAME identity gate; validated completions UPSERT an existing pool account (re-auth previously crashed on `add()`'s duplicate-id refusal and stranded the flow); wording floors (account by email, machine by nickname — never a raw `m_<hex>` id; errors only reference affordances that exist on their surface). Files: `dashboard/subscriptions.js`, `dashboard/index.html`, `src/server/routes.ts`, `src/core/EnrollmentWizard.ts`, `src/commands/server.ts`, `docs/specs/dashboard-ux-standard.md` (F9), `docs/STANDARDS-REGISTRY.md`, tests in all three tiers.
11
+
12
+ ## Decision-point inventory
13
+
14
+ - `S7 email gate (validateEnrolledAccountEmail / completeFollowMe)` — pass-through — the pre-existing fail-closed identity gate is UNCHANGED in logic; its verdict now carries `expected`/`got` up to the surface. No new accept/reject behavior.
15
+ - `submit-code pane-readiness guard (routes.ts)` — modify (split, not weakened) — the existing fail-closed 409 is split into two machine-readable refusals: `pane-dead` (capture null/empty) vs `pane-not-ready` (live but not at the code prompt). Every input refused before is still refused; nothing new is accepted.
16
+ - `enroll/start single-attempt pre-check (routes.ts)` — add — a re-request while a HEALTHY attempt is live returns THAT attempt (idempotent read, not a block); only a provably-dead-pane attempt is superseded (abandon + fresh drive). Fails toward REUSE when liveness is unverifiable — it can never kill a healthy attempt on a capture hiccup.
17
+ - `start-cell reuse gate (routes.ts)` — modify — reuse additionally requires `paneAlive !== false` (same fail-toward-reuse posture).
18
+ - `sweepFollowMeCompletions (EnrollmentWizard + server.ts timer)` — add — detects a landed credential and drives the EXISTING completeFollowMe gate; it holds no accept authority of its own (a mismatch is held exactly as before).
19
+ - `F9 interaction hold (dashboard/subscriptions.js)` — add — client-side rendering-timing rule only; it gates WHEN the DOM rebuilds, never what the server accepts or what messages flow.
20
+
21
+ ---
22
+
23
+ ## 1. Over-block
24
+
25
+ - `pane-dead`/`pane-not-ready` split: no legitimate input newly rejected — the predicate set is identical to the old combined check; only the refusal is classified. A pane that IS at the code prompt still accepts the code.
26
+ - enroll/start reuse: a re-request that previously minted a duplicate (or 500'd on the store's duplicate-pending refusal after killing the old pane) now gets the live attempt back — strictly fewer refusals.
27
+ - Supersede: only fires when `paneAlive === false` (capture EXPLICITLY says the tmux session is gone). An unverifiable capture (`null`) reuses — no healthy attempt is ever abandoned on uncertainty.
28
+ - F9 hold: a surface with a permanently-dirty field would stop rebuilding — mitigated: terminal paths clear inputs, the PIN stage has an explicit Back, episodes always resolve (validated/held/broken/expired via the reconciler), and countdown merges keep held surfaces honest meanwhile. Worst case is a stale (not wrong) panel until the operator finishes or backs out.
29
+ - No issue identified beyond the above.
30
+
31
+ ## 2. Under-block
32
+
33
+ - `paneAlive` is advisory/observational (tri-state, null on any doubt): a pane that dies BETWEEN the poll and the code submit still reaches the submit-time fail-closed capture check — the authoritative guard is unchanged and still refuses.
34
+ - The completion sweep runs on the 5-min reissue cadence: a short-circuited login can sit "signing in" up to ~5 min before flipping — bounded delay, not a miss (the submit-code 30s poll covers the code path).
35
+ - A zombie whose pane LOOKS alive (a different process reusing the same tmux session name) would be reused; the submit-time positive prompt check (`paste`+`code`, non-shell last line) still refuses typing into it. No issue identified beyond these.
36
+
37
+ ## 3. Level-of-abstraction fit
38
+
39
+ - The identity decision stays where it already lives (the S7 gate in `EnrollmentWizard`/`AccountFollowMeEmailGate`) — the dashboard and routes only surface its verdict. No parallel identity check was added.
40
+ - Pane liveness lives server-side at the routes layer (the only place with tmux access), exposed as data; the client renders it. The client never infers liveness.
41
+ - The F9 rule lives in the shared dashboard module as exported primitives (not inlined per-handler) so other polling tabs can adopt it — the standard names this as the migration path.
42
+ - The completion sweep reuses the existing reissue timer and the existing completion chokepoint rather than adding a second lifecycle owner.
43
+
44
+ ## 4. Signal vs authority compliance
45
+
46
+ Compliant. No new brittle check holds blocking authority: the F9 hold gates rendering timing only; `paneAlive` is a SIGNAL consumed for presentation and for the supersede decision whose failure direction is reuse (non-destructive); the pane-readiness refusal predicate is pre-existing (and was already reviewed as the FD13 fail-closed guard); the S7 email gate — the one true authority here — is untouched in logic. The supersede path's only destructive act (abandoning a pending record) requires an EXPLICIT `false` liveness verdict, and the record it abandons is by construction unusable (no pane to receive its code). Ref: `docs/signal-vs-authority.md`.
47
+
48
+ ## 5. Interactions
49
+
50
+ - The reissue sweep can re-issue an expired login while a cell shows "expired": the next poll renders the fresh in-progress flow from server state — the transient expired presentation yields to live server truth (transient states sit BELOW in-progress in the model's precedence, except held/cant-resolve which are operator-terminal until retap).
51
+ - submit-code's in-flight mutex vs the completion sweep: `complete()` is single-winner (the store's terminal transition); the loser sees not-found and stands down. The upsert makes double-completion idempotent at the pool.
52
+ - The cancel relay (shipped feature) is preserved: the in-cell Cancel uses the same `data-matrix-cancel` handler/route; cancel-while-submitting still gets the existing 409 stand-aside.
53
+ - The matrix "Cancel" e2e (`matrix-cell-cancel-alive`) and the start-cell idempotency tests were re-run green; the reuse gate's new pane condition degrades to the old behavior when no sessionManager exists (tri-state null).
54
+ - Double-fire: `recordSubmitOutcome` is the single chokepoint for both submit surfaces (cell + panel), so an outcome lands once in transients/cards regardless of which surface drove it.
55
+
56
+ ## 6. External surfaces
57
+
58
+ - API responses gain ADDITIVE fields only (`expectedEmail`/`ttlExpiresAt`/`notice`/`kind` on start-cell; `expected`/`got` on held; `email` on validated; `paneAlive` + self `machineNickname` on pending-logins; `code` on the two 409s; `reused` on enroll/start). All are operator-visible account emails / public flow metadata — never credentials. Old dashboards ignore unknown fields; old peers simply lack `paneAlive` (→ tri-state unknown downstream).
59
+ - Timing dependence: pane capture at read time (races documented in §2, all fail safe). No conversation-state dependence.
60
+
61
+ ## 6b. Operator-surface quality
62
+
63
+ This change IS an operator-surface-quality fix (the whole point of the case study). Against the four questions: (1) **leads with the primary action** — every actionable cell leads with its one button (Set up / Sign in / Retry), and the in-flight cell leads with the step instruction + the Sign in link; the PIN stage adds an explicit Back so the operator is never trapped. (2) **Zero raw internals as primary content** — accounts render by EMAIL, machines by NICKNAME (a raw `m_<hex>` id is now suppressed rather than shown — the D5 wording floor, with `friendlyMachine` unit-tested); the only values the operator ever types are their PIN and the provider's own sign-in code (both existing patterns, memory-only, cleared after use); no JSON, fingerprints, or ids are shown or asked for. (3) **Destructive actions de-emphasized** — Cancel is a small secondary control under the flow, guarded by a native confirm; Back is client-side-only and destroys nothing. (4) **Plain language at phone width** — all new copy is jargon-free sentences ("The sign-in page must show X — if it shows a different account, tap 'Switch account' first"; "That code signed in X — this slot needs Y"), the failure/expiry/success states are color+glyph+sentence (not codes), and the flow stays inside one grid cell so the phone never needs the bottom of the page. Error copy only references affordances that exist on the surface it appears on (the "re-tap Approve" ghost-button wording is gone).
64
+
65
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
66
+
67
+ Proxied-on-read, matching the existing WS5.2 design: the pool-scope pending-logins fan-out inherits each peer's OWN `paneAlive`/nickname annotation (each machine answers for its own tmux — liveness is machine-local BY NATURE and never guessed cross-machine); the submit-code/cancel relays are unchanged; a LEGACY peer (pre-this-version) returns rows without `paneAlive` → rendered as the unknown state (submittable, exactly today's behavior) — mixed-version pools degrade to current behavior, never to a fabricated verdict. The enroll/start single-attempt check runs on the TARGET machine (the only place its pane exists). Client transients/episodes are per-dashboard-session by design (another dashboard sees server-truth states). URLs surfaced are the provider's own OAuth URLs — machine-boundary safe.
68
+
69
+ ## 8. Rollback cost
70
+
71
+ Low. No config/schema/migration surface; no state format change (pending-login records unchanged — `paneAlive` is computed at read time, never stored). Revert = revert the PR. The one behavioral write-path change (upsert-on-validated, supersede-dead-pane abandon) only produces states the system already supports (active account; abandoned login). A hot-fix can also disable nothing selectively because nothing ships dark — this is a defect fix to an already-dev-gated feature (`multiMachine.accountFollowMe` gates every touched route; fleet installs keep 503ing exactly as before).
72
+
73
+ ---
74
+
75
+ **Second-pass response:** not required (no new blocking authority; the touched gates keep their exact refusal sets — see Decision-point inventory).
76
+
77
+ ## Follow-up (same PR): no-silent-fallbacks ratchet exemption
78
+
79
+ The D5 pane-liveness guard's `captureOutput` catch (`src/server/routes.ts`, follow-me submit-code route) sets `rawFrame = null` on capture failure, which the ratchet's `hasStateReset` heuristic counted as a new silent fallback (493 > 492 baseline, Unit shard 3/4 red). It is a refusal path, not a degradation — null flows to the explicit `pane-dead` 409 refusal (fails closed; the code is never blind-typed). Marked `@silent-fallback-ok` in place on the same line (no detection-window reshaping of neighboring catch blocks). Side effects: none — comment-only; behavior byte-identical.
@@ -0,0 +1,31 @@
1
+ # Subscriptions "Set up" flow fixes — plain-English overview
2
+
3
+ ## What this actually is
4
+
5
+ The dashboard's Subscriptions tab has a grid — accounts down the side, your machines across the top — where tapping "Set up" on an empty cell signs an account in on that machine: enter your PIN, open a sign-in link, paste back a code. Justin walked this flow on his phone on 2026-07-10 and hit five real problems, each screenshot-proven. This change fixes all five.
6
+
7
+ 1. **The page stopped fighting your fingers.** The tab refreshes itself every 30 seconds, and that refresh used to REBUILD the grid — wiping the PIN box back into a "Set up" button if you didn't type fast enough, and swapping the paste-your-code step for a spinner before you could paste. Now there's a rule (Floor F9 of the Dashboard UX Standard): a refresh may never replace anything you're in the middle of using. An open step, a focused box, or a half-typed field stays exactly where it is; the refresh only updates things AROUND it, like the "link expires in 11m" countdown. The hold releases when the step finishes, fails, expires, or you tap Back.
8
+
9
+ 2. **The whole flow lives in the cell now.** Before, the sign-in link and code box appeared in the cell, but the expiry countdown and the "Claude may ask for TWO codes" heads-up only existed in a panel at the very bottom of the page — Justin thought the flow had stalled until he happened to scroll. Now the cell carries every step end to end; the bottom panel is just a mirror.
10
+
11
+ 3. **The wrong-account trap is guarded.** The sign-in link opens Anthropic's page in whatever account your browser is already logged into — Justin was enrolling headley.justin@gmail.com and the page said "Logged in as justin@sagemindai.io", with no warning anywhere. Now the cell says, right next to the link: "the sign-in page must show headley.justin@gmail.com — if it shows a different account, tap Switch account first." And the server-side identity check (which already existed and already refused mismatches) now tells you exactly what happened in plain words: "that code signed in justin@sagemindai.io — this slot needs headley.justin@gmail.com. The account was NOT enrolled." If the identity can't be confirmed at all, it still refuses — it never enrolls blind.
12
+
13
+ 4. **Success looks like success.** Finishing used to be a quiet grey line of text. Now the cell flips to a green "✓ All set — [account] is signed in", pulses briefly, and the panel keeps an explicit "✓ Done" card. Failures and expired links get equally explicit red states with a working Retry.
14
+
15
+ 5. **The re-sign-in path actually works now.** Justin's "Needs sign-in → Sign in" attempt died three ways at once: the record said "signing in" while the window doing the sign-in was long gone (so the code he pasted had nowhere to go), a second tap started a PARALLEL attempt whose code crossed with the first, and finishing would have crashed anyway because the account already existed in the registry. Now the server checks that the sign-in window is genuinely alive before offering a code box (a dead one shows "Sign-in needs a restart" with a Retry that starts cleanly), a re-tap always returns the ONE live attempt instead of minting a rival, a sign-in that completes without ever showing a code is detected and finished through the same identity check, and re-signing-in an existing account updates it back to active instead of crashing. Wording is fixed too: you see account emails and machine nicknames ("Laptop"), never internal IDs, and no error tells you to tap a button that doesn't exist.
16
+
17
+ ## What already existed
18
+
19
+ The whole enrollment machinery (PIN gate, mandate, sign-in driving, the fail-closed email-identity check, the Cancel button on in-progress cells) already shipped and stays exactly as it was. This change fixes how the flow is PRESENTED and makes the record-keeping honest — the one behavioral addition is the liveness check + single-attempt rule + completion sweep around the existing pieces.
20
+
21
+ ## The safeguards, in plain terms
22
+
23
+ - The identity check that refuses wrong accounts is unchanged — it still refuses, it just explains itself now.
24
+ - Nothing new is auto-accepted anywhere; every new refusal (dead window, not-ready window) refuses things that already failed before, just honestly.
25
+ - If the system can't tell whether a sign-in window is alive, it says "unknown" and behaves exactly like today — it never guesses.
26
+
27
+ ## What you need to decide
28
+
29
+ Nothing — these are defect fixes to an existing flow, on by wherever the account-follow-me feature is already on (your dev agents; dark on the fleet). The one follow-up left open: rolling the new never-clobber refresh rule out to the OTHER dashboard tabs (Mandates, Process Health, Preferences) is tracked in the standard rather than crammed into this fix.
30
+
31
+ _Follow-up (same PR): the pane-liveness check's "couldn't look at the window" handler is deliberately conservative — it treats an unreadable window as dead and refuses the code rather than typing blind. A code-quality checker mistook that refusal for a silent failure; it now carries a note explaining itself. No behavior change._