instar 1.3.615 → 1.3.617
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/dashboard/mandates.js +30 -9
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +53 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +23 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/coordination/AccountFollowMeMandateDelivery.d.ts +64 -0
- package/dist/coordination/AccountFollowMeMandateDelivery.d.ts.map +1 -0
- package/dist/coordination/AccountFollowMeMandateDelivery.js +92 -0
- package/dist/coordination/AccountFollowMeMandateDelivery.js.map +1 -0
- package/dist/coordination/DeliveredMandateStore.d.ts +67 -0
- package/dist/coordination/DeliveredMandateStore.d.ts.map +1 -0
- package/dist/coordination/DeliveredMandateStore.js +80 -0
- package/dist/coordination/DeliveredMandateStore.js.map +1 -0
- package/dist/core/AccountFollowMeSpendSlice.d.ts +197 -0
- package/dist/core/AccountFollowMeSpendSlice.d.ts.map +1 -0
- package/dist/core/AccountFollowMeSpendSlice.js +196 -0
- package/dist/core/AccountFollowMeSpendSlice.js.map +1 -0
- package/dist/core/AnthropicSubscriptionRouter.d.ts +28 -0
- package/dist/core/AnthropicSubscriptionRouter.d.ts.map +1 -1
- package/dist/core/AnthropicSubscriptionRouter.js +17 -0
- package/dist/core/AnthropicSubscriptionRouter.js.map +1 -1
- package/dist/core/MeshRpc.d.ts +37 -1
- package/dist/core/MeshRpc.d.ts.map +1 -1
- package/dist/core/MeshRpc.js +30 -0
- package/dist/core/MeshRpc.js.map +1 -1
- package/dist/lifeline/ServerSupervisor.d.ts +12 -0
- package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
- package/dist/lifeline/ServerSupervisor.js +36 -2
- package/dist/lifeline/ServerSupervisor.js.map +1 -1
- package/dist/server/AgentServer.d.ts +27 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +80 -1
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +31 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +93 -3
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +47 -47
- package/upgrades/1.3.616.md +34 -0
- package/upgrades/1.3.617.md +22 -0
- package/upgrades/side-effects/supervisor-sustained-starvation-fix.md +81 -0
- package/upgrades/side-effects/ws52-account-follow-me-r7a-spend-slice.md +70 -0
- package/upgrades/side-effects/ws52-one-dashboard-mandate-delivery.md +74 -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-06-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-06-17T18:44:27.024Z",
|
|
5
|
+
"instarVersion": "1.3.617",
|
|
6
6
|
"entryCount": 201,
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
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": "dadadeca9a58d8cd20b4668255bf9771923adad26cfd4b39428ddccffb0652f7",
|
|
774
774
|
"since": "2025-01-01"
|
|
775
775
|
},
|
|
776
776
|
"cli:init": {
|
|
@@ -1522,7 +1522,7 @@
|
|
|
1522
1522
|
"type": "subsystem",
|
|
1523
1523
|
"domain": "server",
|
|
1524
1524
|
"sourcePath": "src/server/AgentServer.ts",
|
|
1525
|
-
"contentHash": "
|
|
1525
|
+
"contentHash": "2f82d7f142123b9f586c355678de08656a4d3aa857a8d956f8a8bf46fb8a4701",
|
|
1526
1526
|
"since": "2025-01-01"
|
|
1527
1527
|
},
|
|
1528
1528
|
"subsystem:session-manager": {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
WS5.2 R7a — the per-account spend ceiling, lease-sliced and owned by the fenced single-writer, so multiple machines sharing one operator account can never collectively over-spend its quota (bounded by sum-of-leases, never N×ceiling — even under partition and across a lease-holder failover). New module `src/core/AccountFollowMeSpendSlice.ts` (pure/injectable): `SliceIssuer` (holder-side; refuses unless it holds the live fenced lease; delegates the sum-of-leases ceiling to the durable PR1 grant-ledger; epoch-stamps every slice), `SliceRenewalControl` (requester-side rate-cap + exponential backoff + per-(account,machine) coalescing + a P19 breaker that fails a machine closed to its OWN account when the holder is slow/partitioned — so the renewal RPC rate is O(per-account-cap), not O(N)), and `decideAccountUse` (the pure selection-time check — a borrowed account is used ONLY for a live, current-epoch, unexpired, positive-remaining slice; every uncertainty falls back to the machine's own account). A new operator-mandate-gated `slice-renew` mesh verb (its own deny-by-default RBAC case). The `AnthropicSubscriptionRouter` gains an optional consult hook (subscription-path only). Config knobs under `multiMachine.accountFollowMe.spendSlice`. Dark behind `multiMachine.accountFollowMe`.
|
|
9
|
+
|
|
10
|
+
**Scope (honest):** this delivers the spend-ceiling MECHANISM + the consultation; the router currently surfaces the decision to observability. The pool's account selection actually choosing own-vs-borrowed based on it is the enforcement integration that requires the (not-yet-live) borrowed-account-in-pool concept — so the consultation is inert today by construction (nothing is borrowed yet), leaving no reachable over-spend path unguarded. The enforcement wiring lands with that selection layer.
|
|
11
|
+
|
|
12
|
+
WS5.2 — one-dashboard cross-machine mandate delivery. Closes a real UX gap: authorizing an account-follow-me enrollment on another machine used to require a PIN-gated mandate issued on THAT machine's own dashboard (per-machine friction). Now the operator's SINGLE dashboard does it: a new PIN-gated `POST /mandate/issue-for-machine` issues the mandate locally and, for a remote target, packages it with the operator machine's Ed25519 signature (the already-approved ws52 R4a bridge) and delivers it over a new `account-follow-me-mandate-deliver` mesh verb. The target authenticates the mesh sender (existing Ed25519 envelope verification → the registered operator-machine identity), R4a-verifies the mandate's issuance signature against that registered key (a name in the payload is never trusted — Know Your Principal), checks the exact bounds (account-follow-me / this machine / re-mint), and persists it to a new `DeliveredMandateStore`. The enroll-start route then consults the delivered mandate (re-verifying the signature at point-of-use) when the local gate has none — both paths fail-closed. The core MandateGate authorship model is untouched. Dark behind `multiMachine.accountFollowMe`.
|
|
13
|
+
|
|
14
|
+
## What to Tell Your User
|
|
15
|
+
|
|
16
|
+
Nothing to do — this ships off by default. It's the safety cap that lets several of your machines share one subscription account without them collectively blowing past that account's quota: each machine gets a metered slice of the account's allowance from one coordinating machine, and if it can't reach that coordinator it quietly uses its own account instead of overspending the shared one.
|
|
17
|
+
|
|
18
|
+
You now manage everything from your ONE dashboard — no more "open the other machine's dashboard." When you approve a machine to use one of your accounts, you do it once on your single dashboard with your PIN; your approval is cryptographically signed and delivered to that machine, which verifies it really came from you before acting. You never touch the other machine. (Off by default while the broader account-sharing feature is dark.)
|
|
19
|
+
|
|
20
|
+
## Summary of New Capabilities
|
|
21
|
+
|
|
22
|
+
Cross-machine per-account spend ceiling (dark): lease-sliced sub-budgets issued by the fenced single-writer, bounded by sum-of-leases under partition + failover, with an operator-mandate-gated renewal mesh verb and fail-closed-to-own-account on every uncertainty. No user-facing surface is live in this release.
|
|
23
|
+
|
|
24
|
+
One-dashboard cross-machine mandate authorization (dark): issue + deliver an account-follow-me mandate to any of your machines from your single dashboard; the target verifies it via the R4a operator signature and honors it only for its exact bounds. New PIN-gated `POST /mandate/issue-for-machine` + the `account-follow-me-mandate-deliver` mesh verb. No user-facing surface is live until `multiMachine.accountFollowMe` is enabled.
|
|
25
|
+
|
|
26
|
+
## Evidence
|
|
27
|
+
|
|
28
|
+
- 59+ tests across `account-followme-spend-slice` (fenced issuance, non-holder refusal, sum-of-leases/Nth-VM refusal, failover re-derivation with no double-allocation, coalescing, rate-cap+backoff, refusal-vs-failure breaker distinction, breaker open/cooldown/reset, all `decideAccountUse` branches), `MeshRpc` (slice-renew deny-by-default RBAC: unwired/rogue/mandated), and `anthropic-subscription-router` (unwired byte-identical, consult on subscription paths only). `tsc --noEmit` clean. Dark-gate lint golden-map updated for the new config block (24/24).
|
|
29
|
+
- Side-effects review + mandatory independent second-pass security review (concurred on all 7 audit points: sum-of-leases bound, failover no-double-allocation, fail-closed-to-own everywhere, deny-by-default RBAC, O(per-account-cap) control plane, dark-by-default, no fail-open).
|
|
30
|
+
- Spec: `docs/specs/ws52-account-follow-me-security.md` R7a/R7/S5 (converged, approved).
|
|
31
|
+
|
|
32
|
+
- 122 tests across 11 files (delivery store + accept/trust path: valid R4a → stored+honored; bad signature / untrusted key / issuer≠sender / wrong-target / wrong-mechanism / non-follow-me → refused & nothing persisted; mesh-verb RBAC deny-by-default; issue-for-machine PIN-gated + dark→503 + honest 502 on delivery failure + local-target unchanged; enroll-start honoring a delivered+verified mandate vs 403 without one; dashboard routing). `tsc --noEmit` clean.
|
|
33
|
+
- Side-effects review + mandatory independent second-pass security review (concurred on all 8 points: trust anchor = authenticated sender never payload, R4a load-bearing fail-closed, exact-bounds double-gated no-replay, enroll-start additive fail-closed with point-of-use re-verify, PIN-gated issuance / no self-issue, deny-by-default mesh RBAC, dark-by-default, no fail-open).
|
|
34
|
+
- Spec: `docs/specs/ws52-account-follow-me-security.md` R4a/R1/R6a (converged, approved) — wires the spec's R4a cross-machine mandate bridge.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Fixed a server restart loop that could hit an agent on an overloaded machine. The supervisor's CPU-starvation guard — which is supposed to NOT restart an alive-but-slow server while the box is oversubscribed — judged load at a single instant. On a machine whose 1-minute load average oscillates around the starvation threshold, a momentary dip below the line tricked the guard into restarting a server that had been starved the whole time; the restart's heavy boot deepened the overload and the cycle repeated every ~11–15 minutes (2026-06-17 incident, topic 12476). `ServerSupervisor.deferRestartForCpuStarvation()` now judges **sustained** load — it samples the load ratio on each unresponsive health-check tick, keeps the last ~60 seconds of readings, and treats the box as starved if it was starved at any point in that window. A single dip can no longer authorize a restart. The recent-load window is dropped the moment the server recovers, so stale readings can't over-defer a later, unrelated episode. The genuinely-dead-process path (instant restart), the idle-machine-hung path (fast restart), and the ~5-minute hard-cap backstop are all unchanged.
|
|
9
|
+
|
|
10
|
+
## What to Tell Your User
|
|
11
|
+
|
|
12
|
+
If your agent felt laggy or kept "auto-restarting" on a busy machine — replies arriving late, messages queueing, the dashboard briefly 502-ing every ~10–15 minutes — that was this loop. Your agent now rides out short load spikes instead of bouncing itself, so it stays responsive under load. Nothing for you to do; it applies automatically on update. (Separately: if a machine is *chronically* overloaded, the real cure is reducing how much is running on it — this fix stops the self-inflicted restart loop, not the underlying load.)
|
|
13
|
+
|
|
14
|
+
## Summary of New Capabilities
|
|
15
|
+
|
|
16
|
+
No new user-facing capability — this is a reliability fix to the existing server supervisor. The supervisor's CPU-starvation restart guard now uses a sustained (windowed) load signal instead of an instantaneous one.
|
|
17
|
+
|
|
18
|
+
## Evidence
|
|
19
|
+
|
|
20
|
+
- `src/lifeline/ServerSupervisor.ts` — `deferRestartForCpuStarvation()` rewritten to use a windowed-max load signal over `loadSampleWindow` (6 samples ≈ 60s); window cleared on failure-streak reset; deferral log line now reports the sustained ratio.
|
|
21
|
+
- `tests/unit/supervisor-cpu-starvation-defer.test.ts` — updated the test that encoded the old instantaneous behavior (it expected a restart after a single dip — the bug); added cases: a momentary dip does NOT restart a sustainedly-starved server (the 2026-06-17 loop), sustained easing eventually restarts (defer is not permanent), and the window is dropped on streak reset (no stale over-defer).
|
|
22
|
+
- All 4 supervisor-related unit suites green: 40 tests passed (10 in the starvation-defer file). `tsc --noEmit` clean.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Side-Effects Review — Supervisor sustained-CPU-starvation restart guard
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `supervisor-sustained-starvation-fix`
|
|
4
|
+
**Date:** `2026-06-17`
|
|
5
|
+
**Author:** `Echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `(see Phase 5 section below — required: restart/recovery path)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
`ServerSupervisor.deferRestartForCpuStarvation()` decided whether to hold off restarting an alive-but-unresponsive server based on the **instantaneous** CPU load ratio (`loadRatioProvider() > maxLoadRatio`). On an oversubscribed box the 1-minute load average oscillates around the threshold, so a single sample dipping below it authorized a restart of a server that had been CPU-starved the whole time — and the restart's heavy boot deepened the starvation, producing a ~11–15-minute restart loop (2026-06-17 incident, topic 12476). The fix records the load ratio on each unresponsive tick into a small fixed window (`loadSampleWindow = 6`, ~60s) and treats the box as starved if the **windowed max** exceeds the threshold. The window is cleared when the failure streak resets (server recovered), so stale high readings can't over-defer a later episode. Files: `src/lifeline/ServerSupervisor.ts` (the guard + 4 new fields + the deferral log line now reports the sustained ratio), `tests/unit/supervisor-cpu-starvation-defer.test.ts` (updated the test that encoded the old instantaneous behavior; added dip-protection, sustained-easing, and window-reset cases).
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `ServerSupervisor.deferRestartForCpuStarvation()` — **modify** — the starvation SIGNAL feeding the restart authority is changed from instantaneous to a sustained windowed-max reading. No new authority added; the existing restart decision (and its hard cap) is unchanged in structure.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. Over-block
|
|
19
|
+
|
|
20
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
21
|
+
|
|
22
|
+
Mapped to this domain, "over-block" = deferring a restart that genuinely should happen. The change makes deferral *more* likely (it holds off restarting while recent load was high). The bounded risk: a server that is genuinely hung (frozen event loop) AND happens to be on a box that was busy in the last ~60s will wait up to the full window (~60s) longer before the windowed max falls — and in the worst case waits for the ~5-minute hard cap, which is unchanged and still force-restarts. So the maximum added delay before a truly-hung server restarts is bounded by the existing hard cap; the change never *prevents* a restart, only delays it under recent-high-load — which is the intended, safe direction.
|
|
23
|
+
|
|
24
|
+
## 2. Under-block
|
|
25
|
+
|
|
26
|
+
**What failure modes does this still miss?**
|
|
27
|
+
|
|
28
|
+
A box that is chronically starved for longer than the ~5-minute hard cap will still force-restart at the cap, which on a sustainedly-overloaded machine is itself unhelpful (restarting never cures starvation). This fix addresses the dominant observed failure (the sub-cap dip-triggered restarts at "10 checks") but does NOT change the hard-cap behavior. Making the hard cap escalate-instead-of-restart under sustained starvation is a genuine follow-up. <!-- tracked: topic-12476 --> The real cure for chronic starvation is reducing machine load (surfaced to the operator separately).
|
|
29
|
+
|
|
30
|
+
## 3. Level-of-abstraction fit
|
|
31
|
+
|
|
32
|
+
**Is this at the right layer?**
|
|
33
|
+
|
|
34
|
+
Yes. The supervisor is the correct owner of the server-restart decision (process lifecycle). The starvation check is a *detector signal* feeding that authority; this change improves the signal's quality (smoothing out single-sample noise) rather than adding a new authority. It reuses the existing injectable `loadRatioProvider` / `cpuStarvation` primitive — no re-implementation of load reading.
|
|
35
|
+
|
|
36
|
+
## 4. Signal vs authority compliance
|
|
37
|
+
|
|
38
|
+
**Does this hold blocking authority with brittle logic, or produce a signal that feeds a smart gate?** (`docs/signal-vs-authority.md`)
|
|
39
|
+
|
|
40
|
+
Compliant. The brittle part (reading load) is a detector; this change makes that detector *less* brittle by replacing an instantaneous reading with a sustained windowed one. The restart authority (the supervisor's deterministic lifecycle policy — appropriate for this tightly-constrained domain) is unchanged. No new brittle blocker is introduced; the existing one is smoothed.
|
|
41
|
+
|
|
42
|
+
## 5. Interactions
|
|
43
|
+
|
|
44
|
+
**Does it shadow another check, get shadowed, double-fire, race?**
|
|
45
|
+
|
|
46
|
+
- It interacts only with `evaluateUnhealthyServer()`, which calls it once per unresponsive tick. No new timers, no concurrency. The window is single-threaded supervisor state.
|
|
47
|
+
- The hard-cap early-return is preserved and runs *after* the sample is recorded, so the window stays accurate for logging.
|
|
48
|
+
- The window-reset detection keys on `consecutiveFailures` not strictly increasing (it strictly increases within a streak; a new episode restarts at the threshold) — verified against all reset sites; deferral is only consulted at/above threshold, so the `<=` reset test is correct.
|
|
49
|
+
- SleepWakeDetector uses the same `cpuStarvation` ratio but keeps its own state — no shared mutable state touched.
|
|
50
|
+
|
|
51
|
+
## 6. External surfaces
|
|
52
|
+
|
|
53
|
+
**Anything visible to other agents/users/systems? Timing/state dependencies?**
|
|
54
|
+
|
|
55
|
+
Only the supervisor's own log line changes wording (now reports the sustained ratio + window). No API, no message, no cross-agent surface. Behavior is purely local process-lifecycle. The only timing dependency is the existing 10s health-check cadence, which the window is sized against (6 samples ≈ 60s).
|
|
56
|
+
|
|
57
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
58
|
+
|
|
59
|
+
**Machine-local BY DESIGN.** The supervisor watches the server on its OWN machine; CPU starvation and restart decisions are inherently per-machine. There is no cross-machine state to replicate and nothing to proxy on read — each machine's supervisor independently judges its own load. No one-voice/transfer/URL concerns (no user-facing notice, no durable shared state, no generated link).
|
|
60
|
+
|
|
61
|
+
## 8. Rollback cost
|
|
62
|
+
|
|
63
|
+
**If wrong in production, what's the back-out?**
|
|
64
|
+
|
|
65
|
+
Low. Single-file logic change behind the existing injectable provider. Back-out = revert the commit (a hot-fix patch release). No data migration, no state repair — the supervisor holds only in-memory transient state (the load window), which resets on the next boot. Worst-case failure mode of the fix itself (over-deferral) is bounded by the unchanged ~5-minute hard cap.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Phase 5 — Second-pass review (required: restart/recovery path)
|
|
70
|
+
|
|
71
|
+
**Reviewer:** independent reviewer subagent (general-purpose), 2026-06-17.
|
|
72
|
+
|
|
73
|
+
**Verdict: Concur with the review.**
|
|
74
|
+
|
|
75
|
+
Independent audit findings:
|
|
76
|
+
- **Windowing correctness:** the counter strictly increases within a streak because `consecutiveFailures++` precedes every `evaluateUnhealthyServer()` call (both failure callsites), so the `<=` reset-detection correctly distinguishes a continuing streak (no clear) from a new episode (clear); it cannot false-clear mid-streak.
|
|
77
|
+
- **Empty-array / -Infinity:** safe — a sample is pushed unconditionally before every `Math.max(...)`, even right after a reset-to-`[]`.
|
|
78
|
+
- **Safety valves all preserved:** dead process restarts instantly (early return before the guard); hung server on an idle box restarts promptly (window fills with low samples → max < threshold); the ~5-min hard cap still force-restarts.
|
|
79
|
+
- **Signal vs authority:** compliant — improves a detector signal (instantaneous → sustained windowed-max), no new brittle authority.
|
|
80
|
+
- **Direction:** only ever makes restart *less* aggressive; max added delay for a truly-hung-on-recently-busy box is bounded by the unchanged hard cap.
|
|
81
|
+
- **Test quality:** the new tests genuinely fail against the old instantaneous code (the dip-sample and first-eased-tick cases).
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Side-Effects Review — WS5.2 R7a: lease-sliced per-account spend ceiling
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `ws52-account-follow-me-r7a-spend-slice`
|
|
4
|
+
**Date:** `2026-06-17`
|
|
5
|
+
**Author:** `Echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `pending (HIGHEST-risk: cross-machine money/quota ceiling under partition + a new operator-mandate-gated mesh verb)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
WS5.2 R7a. A per-account spend ceiling, lease-sliced, owned by the fenced single-writer (the `FencedLease` holder), so N machines sharing one operator account can never collectively over-spend its quota — bounded by sum-of-leases, never N×ceiling, even under partition and across lease-holder failover. New module `src/core/AccountFollowMeSpendSlice.ts` (pure/injectable): `SliceIssuer` (holder-side — refuses `not-lease-holder`, stamps the current lease epoch, delegates the sum-of-leases ceiling to the existing durable `AccountFollowMeGrantLedger`); `SliceRenewalControl` (requester-side rate-cap + exponential backoff + per-(account,machine) coalescing + P19 breaker → fail-closed-to-own-account on a slow/partitioned holder); `decideAccountUse` (pure selection-time consultation — borrowed account used ONLY for a live, current-epoch, unexpired, remaining>0 slice; every uncertainty → own account). A new operator-mandate-gated `slice-renew` mesh verb in `MeshRpc.ts` (its own deny-by-default `checkCommandRBAC` case via an injected `authorizeSliceRenew` seam). `AnthropicSubscriptionRouter` gains optional `consultSlice`/`onSliceConsult` hooks invoked only on the subscription-pool selection path (no-op when unwired). Config knobs under `multiMachine.accountFollowMe.spendSlice`. Dark behind `multiMachine.accountFollowMe`; default behavior byte-identical when the hook is unwired/off.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `slice-renew` mesh verb RBAC — **add** — deny-by-default; only the operator-mandate-authorized requester (via `authorizeSliceRenew`) may request a slice; absent seam → deny.
|
|
15
|
+
- `SliceIssuer.issueForRenew` — **add** — only the live `FencedLease` holder issues; ceiling enforced by the durable ledger; epoch-stamped.
|
|
16
|
+
- `decideAccountUse` — **add** — selection-time: borrowed vs own account; fail-closed-to-own on every uncertainty.
|
|
17
|
+
- `SliceRenewalControl` — **add** — the control-plane bound (rate-cap/coalesce/breaker) → O(per-account-cap) renewal RPCs.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. Over-block
|
|
22
|
+
|
|
23
|
+
The conservative direction is intentional: when a slice is unknown/stale/expired/exhausted, or the holder is slow/partitioned, a machine "over-blocks" by falling back to its OWN account rather than using the borrowed one. This is the REQUIRED safe direction (never overspend a borrowed money/quota credential). It cannot wrongly deny the machine's own account (fallback is always available). No legitimate over-block of a healthy borrowed slice (a live current-epoch slice with budget is used).
|
|
24
|
+
|
|
25
|
+
## 2. Under-block
|
|
26
|
+
|
|
27
|
+
The ceiling is enforced at slice ISSUANCE (the ledger refuses `outstanding + amount > ceiling`) and at selection (a machine without budget falls back). It does NOT meter per-call token spend WITHIN a slice — a slice is a pre-allocated sub-budget; intra-slice accounting is the slice's granularity (by design — the spec's lease-slice model). A compromised holder could in principle over-issue, but the holder is the fenced single-writer (the same trust root as who-is-awake) and the ledger still caps the durable sum; a non-holder cannot issue at all.
|
|
28
|
+
|
|
29
|
+
## 3. Level-of-abstraction fit
|
|
30
|
+
|
|
31
|
+
Correct layers: issuance authority on the FencedLease holder (the existing single-writer), durable accounting in the existing GrantLedger (reused, not rebuilt), the renewal control-plane as a pure injectable state machine, the consultation as a pure function the router calls. The mesh verb sits in MeshRpc with the other commands. No logic duplicated; the ledger's sum-of-leases + epoch-fencing is reused as-is.
|
|
32
|
+
|
|
33
|
+
## 4. Signal vs authority compliance
|
|
34
|
+
|
|
35
|
+
The authority here (issue a spend slice) is held by the fenced single-writer + gated by the operator mandate (deny-by-default) — appropriate, not a brittle heuristic. The consultation (`decideAccountUse`) is a deterministic pure function (field checks on a slice), fail-closed. The breaker is a signal (consecutive transport failures → open) that only ever makes a machine MORE conservative (fall back to own account). No brittle blocking authority over user actions. Reference `docs/signal-vs-authority.md`: operator-mandate + fenced-single-writer authority, deny-by-default, is correctly-placed.
|
|
36
|
+
|
|
37
|
+
## 5. Interactions
|
|
38
|
+
|
|
39
|
+
Reuses `AccountFollowMeGrantLedger` (PR1) for the durable sum-of-leases bound + epoch-fenced consume — failover re-derivation is "for free" because `SliceIssuer` holds no in-memory slice state (reads the durable store live; a new holder over the same store sees the committed set). The `not-lease-holder` guard prevents a stale ex-holder from issuing during a handoff. The breaker distinguishes grant REFUSALS (`would-exceed-ceiling` — the holder answered; back off but don't trip) from transport FAILURES (trip the breaker) — so a healthy-but-full account doesn't open the breaker. The router hook runs only on the subscription path (never sdk-credit), so it can't perturb SDK routing.
|
|
40
|
+
|
|
41
|
+
## 6. External surfaces
|
|
42
|
+
|
|
43
|
+
One new mesh verb (`slice-renew`), deny-by-default, dark behind the flag. New config block (`spendSlice`, additive, defaulted). No new HTTP route. When `multiMachine.accountFollowMe` is off (or the router hook unwired — the default), behavior is byte-identical: the consult hook is never invoked. Other agents see the new verb only if they're mandated peers in a follow-me pool.
|
|
44
|
+
|
|
45
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
46
|
+
|
|
47
|
+
This IS a multi-machine coherence mechanism by construction. The single source of truth is the fenced-lease holder + the durable grant-ledger; slices are epoch-stamped so a failover voids stale-epoch slices and the new holder re-derives the outstanding set from the durable store before issuing — the sum-of-leases bound holds across the handoff with no double-allocation. Under partition, a VM that can't reach the holder fails closed to its own account (bounded, never unbounded overshoot). The renewal control plane is O(per-account-cap), not O(N), so it can't storm the holder. A single-machine agent is a no-op (no peers, the flag is off).
|
|
48
|
+
|
|
49
|
+
## 8. Rollback cost
|
|
50
|
+
|
|
51
|
+
Low. Entirely dark behind `multiMachine.accountFollowMe` + the unwired router hook → no live effect by default. New module + additive config + one mesh verb. Revert is a single-commit back-out; no migration, no persisted-schema change (the slice records live in the existing grant-ledger store, which already shipped in PR1).
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Scope note (honest, not a hidden deferral)
|
|
56
|
+
|
|
57
|
+
R7a delivers the spend-ceiling MECHANISM end-to-end: the fenced-single-writer slice issuer (ledger-backed sum-of-leases ceiling + epoch-fenced failover re-derivation), the requester-side renewal control plane (rate-cap + coalescing + P19 breaker → O(per-account-cap)), the operator-mandate-gated `slice-renew` mesh verb, the pure `decideAccountUse` consultation (fail-closed-to-own on every uncertainty), AND the router hook that invokes it. The router currently SURFACES the decision to observability (`onSliceConsult`); the pool's account-selection actually CHOOSING own-vs-borrowed based on that decision is the enforcement integration that belongs to — and requires — the borrowed-account-in-pool concept, which is not yet live (enrollment adds accounts as normal local accounts; nothing is marked `isBorrowedAccount` yet). So the consultation is inert today by construction (every account → `own`), meaning there is NO reachable over-spend path that this layer leaves unguarded. This is the correct decomposition for a dark mechanism, not a deferral of reachable behavior; the enforcement wiring lands with the borrowed-account selection layer.
|
|
58
|
+
|
|
59
|
+
## Second-pass review
|
|
60
|
+
|
|
61
|
+
**Concur with the review** (security substance) — the independent reviewer verified all 7 points directly:
|
|
62
|
+
1. **Sum-of-leases bound holds** — `SliceIssuer.issueForRenew` delegates to the ledger's `issue()` (re-reads outstanding from the durable store, refuses `outstanding + amount > ceiling`); `SliceIssuer` holds ZERO in-memory accounting state; the `GrantStore` is synchronous so no interleave past the ceiling (structural invariant: keep the store synchronous). `outstandingFor` conservatively over-counts (safe direction).
|
|
63
|
+
2. **Failover / no double-allocation** — `holdsLease()` blocks a stale ex-holder; the new holder re-derives from the same durable store; epoch-stamped slices are void on stale epoch. Tested.
|
|
64
|
+
3. **Fail-closed-to-own on every uncertainty** — `decideAccountUse` returns `own` for flag-off / not-borrowed / no-slice / stale-epoch / expired / `!(remaining>0)` (0, negative, NaN). `borrowed` only via a live current-epoch unexpired positive-remaining slice.
|
|
65
|
+
4. **slice-renew RBAC deny-by-default** — own `checkCommandRBAC` case; `authorizeSliceRenew?.(...) ?? false` → absent seam OR false → 403; not the any-peer read class.
|
|
66
|
+
5. **Control plane O(per-account-cap)** — coalescing + rate-cap + exponential backoff + P19 breaker on transport `failed` only; grant `refused` (would-exceed-ceiling) backs off but does NOT trip the breaker.
|
|
67
|
+
6. **Dark by default / byte-identical when off** — `consultSlice` no-ops when unwired; subscription-path only, never sdk-credit. (Observe-only in this layer — see Scope note above.)
|
|
68
|
+
7. **No fail-open** — no catch/`||true`/default-allow; every positive outcome follows explicit guards.
|
|
69
|
+
|
|
70
|
+
The reviewer's one CONCERN was the dark-gate lint golden-map line shift from the new `spendSlice` config block — **FIXED**: the 7 shifted entries updated to the attributor's values (924/949/978/1147/1269/1314/1339); `lint-dev-agent-dark-gate.test.ts` now 24/24 green. tsc EXIT=0; the 3 R7a suites 59 passed.
|