instar 1.3.741 → 1.3.742
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/dist/messaging/OutboundContentDedup.d.ts +34 -1
- package/dist/messaging/OutboundContentDedup.d.ts.map +1 -1
- package/dist/messaging/OutboundContentDedup.js +75 -1
- package/dist/messaging/OutboundContentDedup.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +18 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +46 -46
- package/upgrades/1.3.742.md +64 -0
- package/upgrades/side-effects/double-send-inflight-reservation.eli16.md +33 -0
- package/upgrades/side-effects/double-send-inflight-reservation.md +100 -0
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-07-03T23:
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-07-03T23:34:41.792Z",
|
|
5
|
+
"instarVersion": "1.3.742",
|
|
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": "
|
|
421
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
429
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
437
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
445
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
453
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
461
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
469
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
477
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
485
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
493
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
501
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
509
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
517
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
525
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
533
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
541
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
549
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
557
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
565
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
573
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
581
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
589
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
597
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
605
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
613
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
621
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
629
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
637
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
645
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
653
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
661
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
669
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
677
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
685
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
693
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
701
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
709
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
717
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
725
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
733
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
741
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
749
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
757
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
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": "
|
|
773
|
+
"contentHash": "ad3532799dcd634e9c480f2f31923dcaa1b22c4d13070e4caa54725c9c1bb7d3",
|
|
774
774
|
"since": "2025-01-01"
|
|
775
775
|
},
|
|
776
776
|
"cli:init": {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Closes a check-then-send race in the existing exact-match outbound duplicate guard
|
|
9
|
+
(`OutboundContentDedup`) at the `/telegram/reply` chokepoint — one mechanism behind the
|
|
10
|
+
user-reported double-sends (RCA topics 30823/30837).
|
|
11
|
+
|
|
12
|
+
The guard recorded a sent fingerprint only AFTER a successful send. Under a server stall a send
|
|
13
|
+
can be in flight for tens of seconds, and a second identical request arriving in that window
|
|
14
|
+
passed the pre-check (nothing recorded yet) and sent a duplicate. The fix adds an atomic
|
|
15
|
+
**reserve → confirm/release** lifecycle to the SAME exact-match guard:
|
|
16
|
+
|
|
17
|
+
- `tryReserve(topicId, text)` — synchronously re-checks the sent-window AND claims an in-flight
|
|
18
|
+
reservation in one step; returns suppress when the text was already sent within the window OR
|
|
19
|
+
is currently in flight. Auto-expires after `reserveTtlMs` (default 3min) so a leaked claim can
|
|
20
|
+
never permanently suppress.
|
|
21
|
+
- `releaseReservation(topicId, text)` — clears the claim when the send FAILS, so the legitimate
|
|
22
|
+
retry is not suppressed.
|
|
23
|
+
- `record()` clears the claim on success (the longer sent-window takes over).
|
|
24
|
+
|
|
25
|
+
Route wiring: the reservation is taken AFTER the tone gate (a held/blocked message never
|
|
26
|
+
reserves — no leak) and immediately before the send; `releaseReservation` runs in the
|
|
27
|
+
send-failure catch. `allowDuplicate` bypasses it; brief acks (below the length floor) are never
|
|
28
|
+
reserved.
|
|
29
|
+
|
|
30
|
+
Deliberately scoped: this is the **exact-duplicate** half. The **reworded** near-duplicate is
|
|
31
|
+
NOT hard-blocked here — that requires a similarity threshold, and a brittle similarity detector
|
|
32
|
+
must not hold blocking authority (`docs/signal-vs-authority.md`). The reworded/systemic case is
|
|
33
|
+
routed to the RCA shared-primitives spec (topic 30837), not botched into a brittle blocker here.
|
|
34
|
+
|
|
35
|
+
## What to Tell Your User
|
|
36
|
+
|
|
37
|
+
If you ever saw the agent send you the same message twice, this fixes one cause of it. When the
|
|
38
|
+
server was briefly slow, a message could start sending, and a second identical copy could slip
|
|
39
|
+
out before the first was marked "done." Now the agent claims a message the instant it starts
|
|
40
|
+
sending, so an identical copy can't sneak through that gap — while a failed send still lets its
|
|
41
|
+
retry go through, and short replies like "got it" are never affected. Note this covers *exact*
|
|
42
|
+
duplicates; the case where the same point comes back slightly *reworded* is a harder problem
|
|
43
|
+
being handled separately.
|
|
44
|
+
|
|
45
|
+
## Summary of New Capabilities
|
|
46
|
+
|
|
47
|
+
None — this is a reliability fix to existing duplicate-suppression behavior. The reply endpoint
|
|
48
|
+
keeps the same shape; an in-flight duplicate is suppressed the same way an already-sent one is.
|
|
49
|
+
|
|
50
|
+
## Evidence
|
|
51
|
+
|
|
52
|
+
- `npx vitest run tests/unit/OutboundContentDedup.test.ts` → **18 tests** (reserve claims;
|
|
53
|
+
concurrent identical suppressed before record; window catch after record; release-on-failure
|
|
54
|
+
retry; TTL auto-expiry; brief-ack exemption; per-topic isolation; disabled no-op).
|
|
55
|
+
- `npx vitest run tests/unit/outbound-content-dedup-route.test.ts` → **8 tests** incl. a route
|
|
56
|
+
test that literally holds one send IN FLIGHT and proves the identical second is suppressed, and
|
|
57
|
+
that a FAILED send releases the reservation so its retry reaches Telegram.
|
|
58
|
+
- `npx vitest run tests/unit/OutboundContentDedup.test.ts tests/unit/outbound-content-dedup-route.test.ts tests/unit/outbound-dedup-durable.test.ts tests/unit/relayContentDedup.test.ts`
|
|
59
|
+
→ **41 tests, 0 failures**.
|
|
60
|
+
- `npx tsc --noEmit -p tsconfig.json` → **0 errors**.
|
|
61
|
+
- Independent second-pass reviewer (delivery-path audit): **Concur** — no over-block path drops a
|
|
62
|
+
legitimate message; reservation taken strictly after the tone-gate early-return; failure
|
|
63
|
+
release + TTL expiry + allowDuplicate bypass all verified line-by-line.
|
|
64
|
+
- Side-effects review: `upgrades/side-effects/double-send-inflight-reservation.md`.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Double-send in-flight reservation — Plain-English Overview
|
|
2
|
+
|
|
3
|
+
> The one-line version: when the server was slow, the same message could go out twice because the "have I already sent this?" check happened before the send was marked done — this reserves the message the instant it starts sending, so a second identical copy can't slip through the gap.
|
|
4
|
+
|
|
5
|
+
## The problem in one breath
|
|
6
|
+
|
|
7
|
+
Sometimes users saw the same message from the agent twice. One cause: the agent has a guard that says "if I already sent this exact text to this chat recently, don't send it again." But that guard only wrote down "sent!" AFTER the send finished. When the server was stalled, a send could take 30+ seconds — and if a second identical send started during that gap, the guard hadn't written anything down yet, so both went out.
|
|
8
|
+
|
|
9
|
+
## What already exists
|
|
10
|
+
|
|
11
|
+
- **The exact-duplicate guard** — already blocks the identical message from being sent twice, and even remembers across restarts (a small durable store). It works fine *once the first send has finished and been recorded*. The gap was only the in-between window.
|
|
12
|
+
- **A separate near-duplicate detector** — notices when two messages are *similar but reworded*. Crucially, this one is only a *hint* fed to the smart safety reviewer; it is deliberately NOT allowed to block on its own, because "80% similar" can mean "a bug repeated itself" OR "the user asked me to say it again," and a dumb similarity score can't tell those apart.
|
|
13
|
+
|
|
14
|
+
## What this adds
|
|
15
|
+
|
|
16
|
+
A "reserve" step. The instant the agent decides to send a message, it now *claims* that exact text as in-flight — before the send starts. If a second identical send shows up while the first is still going, it sees the claim and steps aside. When the first send finishes, the claim is upgraded to the normal "already sent" record. If the first send *fails*, the claim is released immediately so the genuine retry can go through. And if something crashes and a claim is never resolved, it auto-expires after a few minutes so it can never permanently block that text.
|
|
17
|
+
|
|
18
|
+
## The new pieces
|
|
19
|
+
|
|
20
|
+
- **`tryReserve`** — one atomic step that both checks "already sent or already in flight?" and, if not, claims it. Returns "go ahead" or "stop, it's a duplicate."
|
|
21
|
+
- **`releaseReservation`** — undoes the claim when a send fails, so retries aren't punished.
|
|
22
|
+
- **Auto-expiry** — a claim that's never resolved disappears on its own after ~3 minutes.
|
|
23
|
+
|
|
24
|
+
## The safeguards
|
|
25
|
+
|
|
26
|
+
- It only ever suppresses an **exact** duplicate (same text, same chat) — never a reworded message, never a short "got it" ack, and never anything the caller marked "allow duplicate."
|
|
27
|
+
- It is placed AFTER the smart safety reviewer, so a message that reviewer holds for later never gets stuck behind a stale claim.
|
|
28
|
+
- It changes nothing about the *reworded* double-send. That's a harder problem that needs a proper design, because catching "similar" messages with a blunt threshold would start blocking legitimate repeats — exactly the mistake instar's "signal vs authority" rule exists to prevent. That work is tracked separately.
|
|
29
|
+
- Everything is in memory only — nothing new is written to disk, so rolling this back is a clean one-step revert.
|
|
30
|
+
|
|
31
|
+
## What you need to decide
|
|
32
|
+
|
|
33
|
+
This is the principle-compliant half of the double-send fix: it closes the exact-duplicate race safely, with full test coverage (unit tests for the reserve/release/expiry lifecycle, plus a route test that literally holds one send in flight and proves the second is suppressed and that a failed send still lets its retry through). The one thing to be aware of: it does NOT stop *reworded* duplicates — that half is intentionally deferred to a proper spec, because doing it naively would violate a core instar principle. So this ships as a real, safe improvement, with the harder half honestly flagged rather than botched.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Side-Effects Review — Double-send in-flight reservation (close the exact-duplicate send race)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `double-send-inflight-reservation`
|
|
4
|
+
**Date:** `2026-07-03`
|
|
5
|
+
**Author:** `Echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `reviewer subagent (see appended concurrence)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Closes a check-then-send race in the existing EXACT-match outbound duplicate guard (`OutboundContentDedup`) at the `/telegram/reply` chokepoint. The guard recorded a fingerprint only AFTER a successful send, leaving a window: under a server stall a send can be in flight for tens of seconds, and a second identical request arriving in that window passed the pre-check (nothing recorded yet) and sent a duplicate — one mechanism behind the user-reported double-sends (RCA topics 30823/30837).
|
|
11
|
+
|
|
12
|
+
The fix adds an atomic **reserve → confirm/release** lifecycle to the SAME exact-match guard:
|
|
13
|
+
|
|
14
|
+
- `OutboundContentDedup.tryReserve(topicId, text)` — synchronously re-checks the sent-window AND claims an in-flight reservation in one step; returns `false` (suppress) if the text was already sent within the window OR is currently reserved (in flight). Reservations auto-expire after `reserveTtlMs` (default 3min) so a leaked claim can never permanently suppress a fingerprint.
|
|
15
|
+
- `OutboundContentDedup.releaseReservation(topicId, text)` — clears the reservation when the send FAILS, so the legitimate retry is not suppressed.
|
|
16
|
+
- `record()` (unchanged call site) now also clears the reservation on success (the longer sent-window takes over).
|
|
17
|
+
|
|
18
|
+
Route wiring (`src/server/routes.ts`, `/telegram/reply`): the cheap `isDuplicate` pre-check before the tone gate is unchanged (early exit for already-sent duplicates); a new `tryReserve` gate is taken AFTER the tone gate, immediately before the send, and `releaseReservation` is called in the send-failure catch. `allowDuplicate` bypasses the reservation entirely.
|
|
19
|
+
|
|
20
|
+
Files: `src/messaging/OutboundContentDedup.ts`, `src/server/routes.ts`, plus unit + route tests.
|
|
21
|
+
|
|
22
|
+
## Decision-point inventory
|
|
23
|
+
|
|
24
|
+
- `OutboundContentDedup` (exact-match duplicate guard) — modify — adds an in-flight reservation phase to an EXISTING deterministic block. It is NOT a new decision point; it makes the existing one race-safe.
|
|
25
|
+
- `/telegram/reply` send path — modify — the reservation gate is taken before the send and released on failure.
|
|
26
|
+
|
|
27
|
+
The reservation is placed AFTER the tone gate, so a held/blocked message never reserves (no interaction with the tone gate's own retry/hold path).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 1. Over-block
|
|
32
|
+
|
|
33
|
+
**What legitimate inputs does this reject that it shouldn't?** Only an EXACT (whitespace-normalized) duplicate of a message currently in flight to the SAME topic. Concretely: if the agent deliberately sends the byte-identical long message twice to the same topic within ~3 minutes while the first is still sending, the second is suppressed. That is the intended behavior and the existing guard already suppresses the same text once recorded — this only extends it to the in-flight window. Legitimate repeats are protected three ways: (a) brief acks (< `minLength`, default 40) are never reserved or suppressed; (b) `allowDuplicate: true` bypasses it; (c) a DIFFERENT (even slightly reworded) message has a different fingerprint and is never touched. The reworded double-send is explicitly NOT addressed here (see §2) precisely to avoid the brittle-similarity over-block the signal-vs-authority principle warns against.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. Under-block
|
|
38
|
+
|
|
39
|
+
**What failure modes does this still miss?** (a) The **reworded** near-duplicate — a resend with changed wording has a different fingerprint and is NOT caught. This is deliberate: catching it requires a similarity threshold, which is a brittle detector that must NOT hold blocking authority (`docs/signal-vs-authority.md` uses this exact example). That case stays a signal to the tone-gate authority and is routed to a proper spec, not this quick-win. (b) A **cross-process/cross-machine** in-flight race (a durable-relay retry from a different process while the first is in flight) — the in-memory reservation is per-process; once the first send records, the existing durable sent-window store catches the retry, but the sub-second cross-process in-flight window is not closed here. Both are named, not silently deferred: the reworded/systemic case is the RCA's shared-primitives spec work (topic 30837); the cross-process reservation is a follow-up tracked below.
|
|
40
|
+
|
|
41
|
+
<!-- tracked: topic-30823 — reworded near-dup + cross-process in-flight reservation are follow-ups; the reworded case requires the signal-vs-authority-compliant design in the sibling RCA's shared-primitives spec (topic 30837), NOT a brittle hard block here. -->
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 3. Level-of-abstraction fit
|
|
46
|
+
|
|
47
|
+
Correct layer. The reservation lives INSIDE `OutboundContentDedup` (the class that owns the exact-match guard), so it composes with the existing sent-window logic and durable store rather than being bolted onto the route. The route only sequences reserve → send → confirm/release. This is a deterministic guard becoming race-safe — not a new authority and not a re-implementation of one.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 4. Signal vs authority compliance
|
|
52
|
+
|
|
53
|
+
**Compliant — and deliberately so.** The reservation strengthens an EXISTING deterministic, EXACT-match block (fingerprint equality) by closing a TOCTOU race. It introduces NO brittle/similarity logic and NO new blocking authority. The reworded near-dup — the case that WOULD require a brittle similarity detector — is explicitly kept OUT (it stays a signal to the tone-gate authority), directly honoring `docs/signal-vs-authority.md`'s canonical warning that a similarity score must not hold blocking authority. This change is the principle-compliant half of the double-send fix.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 5. Interactions
|
|
58
|
+
|
|
59
|
+
- **Tone gate:** the reservation is taken AFTER the tone gate, so a held/blocked message never reserves — no leak on the tone-gate hold/retry path.
|
|
60
|
+
- **Durable sent-window store:** `tryReserve` calls `isDuplicate`, which already consults the durable store; the reservation is additive (in-memory in-flight only) and never conflicts with the recorded sent-window.
|
|
61
|
+
- **`record` on success / `releaseReservation` on failure / TTL expiry** form a complete lifecycle — every reserved fingerprint is resolved (success clears, failure clears) or auto-expires; no path leaks a permanent suppression.
|
|
62
|
+
- **`allowDuplicate`** short-circuits before `tryReserve` (no reserve, no block), preserving the escape hatch.
|
|
63
|
+
- No double-fire with the delivery-id LRU or the byte-identical pre-check — they suppress different windows and all return the same `suppressedDuplicate: true` shape.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 6. External surfaces
|
|
68
|
+
|
|
69
|
+
- **`/telegram/reply`** — same response shape. A suppressed in-flight duplicate returns `{ ok: true, suppressedDuplicate: true }` (identical to the existing byte-identical suppression). No new error surface; a genuine send failure still returns 500 (and now releases the reservation).
|
|
70
|
+
- Behavior depends on timing (in-flight window) by nature — but the outcome is deterministic given fingerprint equality; the TTL bounds any timing dependence.
|
|
71
|
+
- No change visible to other agents/users beyond fewer duplicate messages.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
76
|
+
|
|
77
|
+
**Machine-local (in-flight state) BY DESIGN, with a named cross-machine follow-up.** The in-flight reservation is per-process in-memory — correct, because the send it guards executes in THIS process. The already-shipped durable `OutboundDedupStore` handles the cross-restart / cross-process SENT-window (once a send records). The remaining sub-second cross-process in-flight window (a durable-relay retry on another machine racing this process's in-flight send) is NOT closed here and is tracked (§2). On a single-machine agent there is no cross-process race and the fix is complete. No user-facing URL/topic-transfer concerns.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 8. Rollback cost
|
|
82
|
+
|
|
83
|
+
Low. Two files; straight revert restores the prior record-after-success behavior with no data migration (reservations are in-memory only — nothing persisted). The reservation is bypassable at runtime via `allowDuplicate` per-call, and the whole guard is disable-able via `outboundContentDedup.enabled: false` in config. If the reservation were ever suspected of wrongly suppressing, `reserveTtlMs` can be lowered (or the guard disabled) without a code change.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Second-pass review (independent reviewer subagent)
|
|
88
|
+
|
|
89
|
+
**Concur with the review.** Line-by-line audit of the delivery path confirmed no over-block:
|
|
90
|
+
|
|
91
|
+
- `tryReserve` is taken strictly AFTER the tone-gate early-return, so a held/blocked message NEVER reserves — the leak-that-would-suppress-the-retry path does not exist.
|
|
92
|
+
- No `await`/`return` sits between `tryReserve` and the awaited `sendToTopic`; the reserve→send is effectively atomic and any throw lands in the catch which calls `releaseReservation`. Failed send → reservation released → retry passes (confirmed by route test).
|
|
93
|
+
- A leaked reservation (crash before resolve) is freed by `reserveTtlMs` (3min) via the expiry check + `pruneReserved`.
|
|
94
|
+
- `!allowDuplicate &&` guards both the pre-check and `tryReserve`; below-`minLength` acks are never reserved.
|
|
95
|
+
- `tryReserve`/`releaseReservation`/`record`/`pruneReserved` have no await between check and claim (single-threaded atomic), correct TTL math, per-topic isolation, bounded map growth.
|
|
96
|
+
- Signal-vs-authority: only the exact fingerprint-equality guard gained the in-flight phase; no similarity logic gains blocking authority (reworded case deliberately excluded).
|
|
97
|
+
|
|
98
|
+
**Pre-existing, out-of-scope observations (NOT introduced by this change, NOT blocking):** (1) `record()` fires after any non-throwing `sendToTopic` regardless of whether `sendResult.messageId` indicates real delivery — a relay that "succeeds" without delivering could record+suppress a later legitimate resend; this predates the reservation work. (2) `reserveTtlMs` (3min) correctly stays above the outbound send budget (tone-gate rate-limit wait ≤120s).
|
|
99
|
+
|
|
100
|
+
<!-- tracked: topic-30823 — pre-existing observation: record()-after-non-throwing-send ignores messageId; a non-delivering "success" could suppress a later resend. Predates this change; folds into the RCA shared-primitives spec (topic 30837) alongside the reworded/cross-process work. -->
|