instar 1.3.546 → 1.3.548
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/index.html +12 -1
- package/dashboard/mandates.js +88 -0
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +6 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/AuthorizationRequestStore.d.ts +138 -0
- package/dist/core/AuthorizationRequestStore.d.ts.map +1 -0
- package/dist/core/AuthorizationRequestStore.js +307 -0
- package/dist/core/AuthorizationRequestStore.js.map +1 -0
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +6 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +10 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +6 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +3 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +34 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +16 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +18 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +204 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/instar-dev-precommit.js +24 -1
- package/scripts/lib/operator-surface.mjs +30 -0
- package/src/data/builtin-manifest.json +47 -47
- package/src/scaffold/templates.ts +6 -0
- package/upgrades/1.3.547.md +26 -0
- package/upgrades/1.3.548.md +24 -0
- package/upgrades/side-effects/operator-authorization-request.md +65 -0
- package/upgrades/side-effects/ws52-incb-b4-livetest-route.md +42 -0
package/package.json
CHANGED
|
@@ -32,7 +32,7 @@ import { checkEli16Overview, MIN_ELI16_CHARS } from './eli16-overview-check.mjs'
|
|
|
32
32
|
import { verifyProposalDerivedRunbooks } from '../skills/instar-dev/scripts/verify-proposal-derived-runbook.mjs';
|
|
33
33
|
import { classifyTier, decideRequirementSet } from './lib/classify-tier.mjs';
|
|
34
34
|
import { recognizeConvergence } from './lib/convergence-recognition.mjs';
|
|
35
|
-
import { isOperatorSurfaceFile, artifactAddressesOperatorSurfaceQuality } from './lib/operator-surface.mjs';
|
|
35
|
+
import { isOperatorSurfaceFile, artifactAddressesOperatorSurfaceQuality, isAuthorizationSurfaceFile, artifactAddressesAgentProposesApproves } from './lib/operator-surface.mjs';
|
|
36
36
|
|
|
37
37
|
// Report-Backed Converging Audit (docs/specs/CONVERGING-AUDIT-DEFAULT.md, Part B).
|
|
38
38
|
// The precommit reads NO config file and runs pre-compile, so it cannot import
|
|
@@ -973,6 +973,29 @@ function assertOperatorSurfaceQuality(stagedFiles, trace) {
|
|
|
973
973
|
].join('\n'),
|
|
974
974
|
);
|
|
975
975
|
}
|
|
976
|
+
// "Agent Proposes, Operator Approves" — the authorization-surface subset additionally
|
|
977
|
+
// requires the artifact to confirm the operator is APPROVING (not authoring) and that
|
|
978
|
+
// the authority text is SERVER-authored (not agent free-text — the display-integrity
|
|
979
|
+
// corollary). Same structural-presence strength.
|
|
980
|
+
const authTouched = (stagedFiles || []).filter(isAuthorizationSurfaceFile);
|
|
981
|
+
if (authTouched.length > 0 && !artifactAddressesAgentProposesApproves(content)) {
|
|
982
|
+
blockCommit(
|
|
983
|
+
authTouched,
|
|
984
|
+
[
|
|
985
|
+
'Agent-Proposes-Operator-Approves review gate:',
|
|
986
|
+
` ${authTouched.join(', ')} change an AUTHORIZATION/APPROVAL surface,`,
|
|
987
|
+
' but the side-effects artifact never answers the agent-proposes/operator-approves',
|
|
988
|
+
' question. The operator must be APPROVING a server-authored request, never',
|
|
989
|
+
' AUTHORING authority from raw fields (the 2026-06-13 raw-JSON mandate-form lesson),',
|
|
990
|
+
' and the authority text they approve must be server-authored, never agent free-text.',
|
|
991
|
+
'',
|
|
992
|
+
' Confirm in writing in the side-effects artifact: does the operator (1) approve a',
|
|
993
|
+
' pre-filled request rather than construct one, and (2) read an authority statement',
|
|
994
|
+
' authored by the SERVER from structured data, not agent-supplied free-text?',
|
|
995
|
+
' (Standard: docs/STANDARDS-REGISTRY.md → "Agent Proposes, Operator Approves".)',
|
|
996
|
+
].join('\n'),
|
|
997
|
+
);
|
|
998
|
+
}
|
|
976
999
|
}
|
|
977
1000
|
|
|
978
1001
|
function blockCommit(files, reason) {
|
|
@@ -52,3 +52,33 @@ export function isOperatorSurfaceFile(file) {
|
|
|
52
52
|
export function artifactAddressesOperatorSurfaceQuality(content) {
|
|
53
53
|
return /operator[- ]surface quality/i.test(String(content ?? ''));
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Is this staged file an AUTHORIZATION/APPROVAL surface specifically — one where the
|
|
58
|
+
* operator confers authority (a grant/mandate/approval form or its renderer)? A subset
|
|
59
|
+
* of operator surfaces that additionally triggers the "Agent Proposes, Operator
|
|
60
|
+
* Approves" question (the operator must be APPROVING, never AUTHORING; and the authority
|
|
61
|
+
* text must be server-authored, not agent free-text).
|
|
62
|
+
*/
|
|
63
|
+
export const AUTHORIZATION_SURFACE_RE =
|
|
64
|
+
/(?:^|\/)(?:mandates|grant|authorization-request|approval|operator-approval)[^/]*\.(?:js|html|ts)$/i;
|
|
65
|
+
|
|
66
|
+
export function isAuthorizationSurfaceFile(file) {
|
|
67
|
+
const f = String(file ?? '');
|
|
68
|
+
if (/\.(?:test|spec)\.(?:js|ts|mjs)$/i.test(f)) return false;
|
|
69
|
+
return AUTHORIZATION_SURFACE_RE.test(f);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The artifact engages the "Agent Proposes, Operator Approves" question when it carries
|
|
74
|
+
* the phrase (seeded by side-effects-artifact.md). Same structural-presence strength as
|
|
75
|
+
* the operator-surface-quality gate: an artifact touching an authorization surface that
|
|
76
|
+
* omits the section is blocked. The standard's display-integrity corollary — the
|
|
77
|
+
* authority text the operator approves must be server-authored, never agent free-text —
|
|
78
|
+
* is what this question forces the author to confirm in writing.
|
|
79
|
+
* @param {string} content the side-effects artifact text
|
|
80
|
+
* @returns {boolean}
|
|
81
|
+
*/
|
|
82
|
+
export function artifactAddressesAgentProposesApproves(content) {
|
|
83
|
+
return /agent[- ]proposes,? operator[- ]approves/i.test(String(content ?? ''));
|
|
84
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-06-13T23:
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-06-13T23:53:47.403Z",
|
|
5
|
+
"instarVersion": "1.3.548",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "1d336e923995f97cac3f85ee9ec234ef4e69994c3fd2e87585a716c28e0737ac",
|
|
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": "fa740ec4394006cb803f7a80c2294867cae0b79da304a681b577f3a81e19153f",
|
|
1526
1526
|
"since": "2025-01-01"
|
|
1527
1527
|
},
|
|
1528
1528
|
"subsystem:session-manager": {
|
|
@@ -822,6 +822,12 @@ Every guard (monitoring sentinels, reapers, the scheduler, …) is graded by wha
|
|
|
822
822
|
- **User floor-action grants are phone-first.** When the operator needs to grant a USER a floor action (e.g. "let Mia prod-deploy for an hour"), the Mandates tab carries a grant form on every active mandate: pick the person (from the registered-user list), pick the action and duration, type the PIN, tap Grant. Send them the dashboard link — NEVER a terminal command or a hand-built API call (Mobile-Complete Operator Actions). The grant is signed into the mandate, clamped to the mandate's expiry, and voided by revoking the mandate.
|
|
823
823
|
- Every evaluation (allow AND deny) is audited. Act as if the audit is read by the operator — because it is. (Spec: \`docs/specs/coordination-mandate.md\`.)
|
|
824
824
|
|
|
825
|
+
**Authorization Request — propose a grant, the operator approves in one tap (PREFERRED over the manual form)** — When you need the operator to grant a USER a floor action, do NOT make them build a mandate. PRE-FILL the request and they see one plain-language card ("Let Mia deploy to production for 1 hour — Approve?") + PIN + Approve. You can only propose; only their PIN issues the grant (requester ≠ authorizer; you can never approve your own request). The operator-facing sentence is authored by the SERVER from your structured fields + the registered-user's real name — never your free-text — so what they read is exactly what executes.
|
|
826
|
+
- Propose: \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/authorization-requests -H 'Content-Type: application/json' -d '{"createdByAgent":"<your name>","proposal":{"floorAction":"prod-deploy","grantedToSlackUserId":"U…","durationMs":3600000},"reason":"<optional, ≤280 chars — shown as a secondary note, never the headline>"}'\`. Allowed \`floorAction\`s: prod-deploy, money-movement, credential-access, destructive-data, external-send (\`grant-authority\` is excluded — that meta-grant stays manual). \`durationMs\` ∈ [60000, 86400000].
|
|
827
|
+
- Inspect: \`GET /authorization-requests?status=pending\` (each row carries the server-rendered \`headline\` + \`createdOnMachine\`); withdraw your own: \`POST /authorization-requests/:id/withdraw\`.
|
|
828
|
+
- **You cannot approve.** \`POST /authorization-requests/:id/approve\` and \`/deny\` are PIN-gated — point the operator at the dashboard **Mandates tab → "Approvals waiting for you"**; never ask for their PIN in chat. On a multi-machine pool the card names the holder machine (approval happens there).
|
|
829
|
+
- **When to use** (PROACTIVE — this is the trigger): the moment you need the operator to authorize a person for a floor action, propose it here and send them the dashboard link — instead of the raw mandate form. (Ships dev-enabled / fleet-dark; routes 503 when off. Spec: \`docs/specs/OPERATOR-AUTHORIZATION-REQUEST-SPEC.md\`.)
|
|
830
|
+
|
|
825
831
|
**ReviewExchange (autonomous code review)** — The structured way two mandate-named agents sign off a code review WITHOUT the operator relaying. One exchange = one review package, content-addressed (\`packageSha256\` fixed at creation), moving linearly: proposed → delivered → verdict-recorded → complete (or changes-requested — rework is a NEW exchange). BOTH sign-offs (the peer's authenticated approve-verdict AND your countersignature) are evaluated through the mandate gate's \`sign-code-review\` authority before acceptance; every accepted signature carries the audit hash of the gate decision that authorized it.
|
|
826
832
|
- Create: \`curl -X POST -H "Authorization: Bearer $AUTH" http://localhost:${port}/review-exchange -H 'Content-Type: application/json' -d '{"mandateId":"<id>","artifact":"migration-port","packageRef":"docs/...-review-package.md","packageSha256":"<sha256 of the package>","parties":["<your-fp>","<peer-fp>"]}'\`
|
|
827
833
|
- Drive it: \`POST /review-exchange/:id/delivered\` (after you actually sent the package over Threadline — record the message ref as evidence) → \`POST /review-exchange/:id/peer-verdict\` (the peer's authenticated verdict; approve = their sign-off, mandate-gated) → \`POST /review-exchange/:id/sign\` (your countersignature, mandate-gated → complete).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Added an **Operator Authorization Request** flow: when the agent needs the operator to grant a person a floor action (e.g. "let Mia deploy to prod for an hour"), it now pre-fills a structured request and the operator approves it in one tap on a plain-language "Approvals waiting for you" card — instead of hand-building a mandate in a raw-JSON form. This fixes the operator-reported defect where the Mandates tab forced the operator to construct a mandate from a raw-JSON authorities editor. The raw-JSON "author a mandate by hand" form is demoted behind an "Advanced" disclosure. A new constitutional standard — "Agent Proposes, Operator Approves" — is enforced by a precommit gate so authorization surfaces must be approvals, never authoring tasks, with the authority text authored by the server (never agent free-text). Ships dev-enabled / fleet-dark.
|
|
9
|
+
|
|
10
|
+
## What to Tell Your User
|
|
11
|
+
|
|
12
|
+
Nothing yet — this is an experimental, dark-by-default capability. On a developer agent it is live: when I need you to authorize a person for something (a deploy, a credential, etc.), you'll see a simple card on the dashboard Mandates tab — one plain sentence, your PIN, and Approve. You never build a "mandate" or touch JSON. Only your PIN turns my request into a real grant.
|
|
13
|
+
|
|
14
|
+
## Summary of New Capabilities
|
|
15
|
+
|
|
16
|
+
- `POST /authorization-requests` — the agent pre-fills a structured grant request (Bearer; confers no authority).
|
|
17
|
+
- `GET /authorization-requests?status=pending` — the dashboard's "Approvals waiting for you" cards (server-authored plain-language text).
|
|
18
|
+
- `POST /authorization-requests/:id/approve|deny` — PIN-gated operator approval/decline; approve issues the grant via the existing signed MandateStore path.
|
|
19
|
+
- `POST /authorization-requests/:id/withdraw` — the proposing agent withdraws its own pending request.
|
|
20
|
+
- Constitutional standard "Agent Proposes, Operator Approves" + a blocking precommit gate.
|
|
21
|
+
|
|
22
|
+
## Evidence
|
|
23
|
+
|
|
24
|
+
The operator reported (2026-06-13, topic 22367) that the Mandates grant flow still showed raw JSON and forced him to "create a mandate" — reproducible by opening the Mandates tab and attempting a grant with no active mandate (the grant form never appears; only the raw-JSON issue form does). Fixed: the new "Approvals waiting for you" card is the primary surface; the raw-JSON form is demoted behind "Advanced". A naive version of the fix would have reopened a deceptive-display hole (agent-authored card text diverging from the executed grant); that is closed by server-authoring the headline from the structured proposal + the registry name, and is asserted at all three test tiers (a malicious agent `reason` never becomes the headline). 67 tests green (unit/integration/e2e + gate + dark-gate golden map).
|
|
25
|
+
|
|
26
|
+
<!-- ci: re-trigger workflow dispatch after force-push (capability-index fix) -->
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Completes Increment B's tooling with the promotion gate made reachable: `POST /credentials/livetest` runs the §5 livetest battery (the harness built in Step 10) against the real swap executor + identity oracle.
|
|
9
|
+
|
|
10
|
+
- **Double-gated credential-action route.** A real credential move needs BOTH `armed:true` in the request body (the harness performs zero swaps otherwise) AND a deliberate `dryRun:false` (the executor writes nothing otherwise). Either gate alone keeps every credential untouched.
|
|
11
|
+
- **Defence-in-depth.** Dark → 503 (the lever guard); every named slot is validated against the enumerated ledger set → 400 before the harness runs (a `../`/`~`/absolute path can't reach a write); the report is scrubbed and carries no token material.
|
|
12
|
+
- **The report.** Surfaces the automatable round-trip verdicts (identity-verified exchange-then-restore, always restoring) and the inherently-manual items (refresher correctness, the §0.c residual via a disposable grant, liveness) — `promotable` stays false while any manual item remains.
|
|
13
|
+
|
|
14
|
+
## What to Tell Your User
|
|
15
|
+
|
|
16
|
+
The "is this safe to turn on?" check is now a button you can press when you're ready. It runs the real safety battery — a live account-swap and swap-back, verified with the trustworthy identity check, always restoring — and tells you whether the automatic moves landed correctly. Two separate safety catches protect it: it does nothing unless you explicitly arm it, and even armed it won't move a real credential until you've also flipped off dry-run. Two parts of the full battery are hands-on (waiting out a token refresh, stressing a throwaway login), which it lists for you. This is the last rung before the go/no-go: when you want to actually turn on real credential moves, you run this first, watch it pass, then decide.
|
|
17
|
+
|
|
18
|
+
## Summary of New Capabilities
|
|
19
|
+
|
|
20
|
+
`POST /credentials/livetest` — the reachable promotion gate for live credential re-pointing. Runs the §5 livetest battery against the real executor + oracle; double-gated (armed:true AND dryRun:false both required to move a real credential), dark → 503, slots validated → 400, report scrubbed. The operator-run last rung before the dry-run→live go/no-go.
|
|
21
|
+
|
|
22
|
+
## Evidence
|
|
23
|
+
|
|
24
|
+
- `tests/integration/credential-routes.test.ts` (+4 = 13) — DARK → 503; ENABLED + NOT armed → refused report with zero swaps + the manual items surfaced; ENABLED + armed → the battery runs (armed report, both round-trip steps present, promotable false); unknown slot → 400 (no harness run). tsc + full lint clean; docs-coverage route floor preserved. Independent second-pass review CONCUR (two independent gates intact — armed AND dryRun:false; dark 503s before construction; path-traversal slots rejected; report scrubbed; harness always-restores).
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Side-effects review — Operator Authorization Request
|
|
2
|
+
|
|
3
|
+
Spec: `docs/specs/OPERATOR-AUTHORIZATION-REQUEST-SPEC.md` (converged 3 rounds / 6 reviewers, approved).
|
|
4
|
+
|
|
5
|
+
## Summary of the change
|
|
6
|
+
|
|
7
|
+
Adds an **Authorization Request** primitive: the agent (Bearer) pre-fills a structured request to grant a user a floor action; the operator approves it with their dashboard PIN on a dead-simple "Approvals waiting for you" card; on approval the server issues the grant via the EXISTING signed `MandateStore.issue()` path. Replaces the raw-JSON "build a mandate by hand" form as the primary surface (it is demoted behind an "Advanced" disclosure). Ships dev-enabled / fleet-dark (`resolveDevAgentGate`, `enabled` omitted from ConfigDefaults).
|
|
8
|
+
|
|
9
|
+
## Decision-point inventory
|
|
10
|
+
|
|
11
|
+
The only new authority-conferring decision point is the PIN-gated `POST /authorization-requests/:id/approve`. It routes through the existing signed `MandateStore.issue/addGrants` — it does NOT introduce a new authority primitive, modify the MandateGate deny-ladder, the GrantStore read path, or the SlackPermissionGate evaluate-path.
|
|
12
|
+
|
|
13
|
+
## 1. Over-block
|
|
14
|
+
Could a legitimate operator be blocked from approving? The approve route is PIN-gated with the existing rate-limited `checkMandatePin` (5/5min per IP). A locked-out operator self-heals after the window. The feature is additive — if its store is unreadable the routes 503 and the existing manual mandate/grant path is unaffected. No new over-block on existing flows.
|
|
15
|
+
|
|
16
|
+
## 2. Under-block
|
|
17
|
+
Could an agent get authority it shouldn't? No. A pending request confers ZERO authority (inert). The grant issues ONLY inside the PIN-gated approve, via the existing signed path — requester ≠ authorizer is preserved and the agent can never approve its own request (no PIN). The proposable floor-action allowlist EXCLUDES `grant-authority` (the meta-escalation stays manual). The grantee must resolve to a registered principal at create AND approve (rejects phantom/sybil ids).
|
|
18
|
+
|
|
19
|
+
## 3. Level-of-abstraction fit
|
|
20
|
+
A new primitive at the right layer: it is an operator-friendly FRONT DOOR to issuing a grant, built atop the proven mandate read/issue path (one-carrier-per-grant, `meta.carrier:true`, operator-invisible). Not a parallel authority store.
|
|
21
|
+
|
|
22
|
+
## 4. Signal vs authority compliance
|
|
23
|
+
The PIN-gated approve is a legitimate human authority chokepoint (the operator sees the named person + plain-language action and decides). No automated trust-policy gate is layered on top (that would re-introduce brittle blocking authority on a human authority). Foundation note: the SlackPermissionGate `FLOOR_ACTIONS` enum-as-authority (flagged 2026-06-09) is inherited UNCHANGED and explicitly NOT worsened — recorded in the spec's Foundation Audit; resolving it is a separate spec.
|
|
24
|
+
|
|
25
|
+
## 5. Interactions
|
|
26
|
+
- MandateStore: approval issues a fresh per-grant carrier mandate (expiry == grant expiry); revoking one never affects another; hourly prune bounds the `activeGrant` hot-path scan.
|
|
27
|
+
- SlackPermissionGate: unchanged — `activeGrant` returns the issued grant and the gate flips refuse→allow with no gate change.
|
|
28
|
+
- Manual issue form: demoted behind "Advanced", still functional for the rare agent-pair credential/code-review case.
|
|
29
|
+
|
|
30
|
+
## 6. External surfaces
|
|
31
|
+
- HTTP: 6 new `/authorization-requests` routes (Bearer for propose/list/withdraw; PIN-gated for approve/deny). 503 when the feature is off.
|
|
32
|
+
- Dashboard: a new "Approvals waiting for you" section + the demoted manual form (`dashboard/index.html`, `dashboard/mandates.js`).
|
|
33
|
+
- Audit: every issued grant lands in the existing hash-chained `MandateAudit`.
|
|
34
|
+
|
|
35
|
+
## 6b. Operator-surface quality (Operator-Surface Quality standard)
|
|
36
|
+
The change touches operator surfaces (`dashboard/index.html`, `dashboard/mandates.js`). Answering the quality question in writing:
|
|
37
|
+
1. **Leads with the primary action?** YES — the "Approvals waiting for you" card renders open at the top of the tab with Approve as the primary, always-visible button; the destructive/advanced paths are below and collapsed.
|
|
38
|
+
2. **Exposes zero raw internals as primary content?** YES — the card shows only a server-authored plain-language sentence ("Let Mia deploy to production for 1 hour.") + an optional escaped secondary reason. No JSON, fingerprints, slugs, or the carrier mandate. The raw-JSON authorities editor is demoted behind "Advanced — author a mandate by hand".
|
|
39
|
+
3. **De-emphasizes destructive actions?** YES — Decline is a quiet secondary button; there is no destructive primary.
|
|
40
|
+
4. **Plain language at phone width?** YES — single-column cards, real tap targets, plain sentences; no horizontal scroll.
|
|
41
|
+
|
|
42
|
+
## Agent Proposes, Operator Approves (the new standard)
|
|
43
|
+
The change touches an authorization/approval surface, so it answers the agent-proposes/operator-approves + display-integrity question:
|
|
44
|
+
1. **Does the operator APPROVE a pre-filled request rather than construct one?** YES — the agent supplies only structured fields; the operator reads one sentence and taps Approve. They never assemble a mandate, pick enums, or edit JSON.
|
|
45
|
+
2. **Is the authority statement SERVER-authored, not agent free-text?** YES — `renderAuthorizationCard` builds the headline from the structured proposal + the registry display name; the agent's optional `reason` is carried as a clearly-secondary, escaped note and can never become the headline. A unit + integration + e2e test each assert a malicious `reason` never appears as the authority. This closes the deceptive-summary class.
|
|
46
|
+
|
|
47
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
48
|
+
The AuthorizationRequest store is **machine-local by design** (the grant it issues lands in the holder's machine-local MandateStore). Each request stamps `createdOnMachine`. The pending-approval card on a NON-holder machine shows "Asked on <machine> — open that machine's dashboard to approve" instead of a dead Approve button (no silent cross-machine hole). Documented in FD-6.
|
|
49
|
+
|
|
50
|
+
## 8. Rollback cost
|
|
51
|
+
Clean. The feature is dev-gated: setting `monitoring.authorizationRequests.enabled:false` (or fleet default) routes every route 503; nothing else changes. Disabling mid-flight leaves pending requests inert (they confer nothing) and any already-issued grants stand as normal mandate grants (revocable via the existing path). No migration to unwind (no `migrateConfig` entry; the dashboard serves from the package dir).
|
|
52
|
+
|
|
53
|
+
## Conclusion
|
|
54
|
+
Loss-reducing + additive. The single safety invariant (requester ≠ authorizer; PIN-only authority) is preserved and strengthened (server-authored display). Ships dark-on-fleet behind the dev gate.
|
|
55
|
+
|
|
56
|
+
## Evidence pointers
|
|
57
|
+
- Spec + convergence report: `docs/specs/OPERATOR-AUTHORIZATION-REQUEST-SPEC.md`, `docs/specs/reports/operator-authorization-request-convergence.md`.
|
|
58
|
+
- Tests (67 green): `tests/unit/AuthorizationRequestStore.test.ts` (20), `tests/unit/operator-surface-gate.test.ts` (13), `tests/integration/authorization-request-routes.test.ts` (7), `tests/e2e/authorization-request-lifecycle.test.ts` (3), `tests/unit/lint-dev-agent-dark-gate.test.ts` (24, golden map recomputed).
|
|
59
|
+
- The deceptive-summary defense is asserted at all three tiers (server-authored headline ≠ agent free-text).
|
|
60
|
+
|
|
61
|
+
## Second-pass review (if required)
|
|
62
|
+
The spec passed 3-round convergence with 6 independent reviewers (security, adversarial, integration/multi-machine, decision-completeness, lessons-aware/foundation, scalability) — the multi-angle adversarial review that catches what a single pass misses. The round-1 CRITICAL (deceptive display) and round-2 HIGH (bounds-as-deception) were both closed before convergence.
|
|
63
|
+
|
|
64
|
+
## Implementation note (CapabilityIndex)
|
|
65
|
+
The 6 `/authorization-requests` routes are classified in `src/server/CapabilityIndex.ts` as the agent-facing `authorizationRequest` capability (surfaced in `/capabilities`), satisfying the capabilities-discoverability ratchet.
|