proactive-gate 0.2.2 → 0.2.4

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.
Files changed (46) hide show
  1. package/README.md +126 -2
  2. package/README.tr.md +48 -0
  3. package/dist/src/checks.d.ts +43 -0
  4. package/dist/src/checks.js +62 -0
  5. package/dist/src/policy.js +1 -0
  6. package/dist/src/types.d.ts +10 -0
  7. package/package.json +5 -3
  8. package/spec/CONFORMANCE.md +83 -0
  9. package/spec/SPEC.md +140 -0
  10. package/spec/SPEC_VERSION +1 -0
  11. package/spec/fixtures/adaptive-timing/placeholder.json +58 -0
  12. package/spec/fixtures/budget/bypass-priority.json +100 -0
  13. package/spec/fixtures/budget/daily-atomic-commit.json +140 -0
  14. package/spec/fixtures/budget/near-limit.json +103 -0
  15. package/spec/fixtures/budget/race-second-commit-loses.json +93 -0
  16. package/spec/fixtures/budget/weekly-iso-week.json +134 -0
  17. package/spec/fixtures/consent/required.json +116 -0
  18. package/spec/fixtures/cooldown/three-dismissals.json +132 -0
  19. package/spec/fixtures/dedupe/already-delivered.json +87 -0
  20. package/spec/fixtures/dedupe/no-key-skips.json +49 -0
  21. package/spec/fixtures/defer/snooze-as-defer.json +138 -0
  22. package/spec/fixtures/mode/allow-list.json +139 -0
  23. package/spec/fixtures/ordering/kill-switch.json +56 -0
  24. package/spec/fixtures/ordering/short-circuit.json +235 -0
  25. package/spec/fixtures/policy/unknown-check-is-an-error.json +50 -0
  26. package/spec/fixtures/presets/cn-minor-mode.json +170 -0
  27. package/spec/fixtures/presets/kakao-brand-message.json +93 -0
  28. package/spec/fixtures/presets/kr-network-act-50.json +168 -0
  29. package/spec/fixtures/presets/telegram-bot.json +162 -0
  30. package/spec/fixtures/presets/us-tcpa.json +90 -0
  31. package/spec/fixtures/quiet-hours/apia.json +90 -0
  32. package/spec/fixtures/quiet-hours/caller-supplied-dates.json +197 -0
  33. package/spec/fixtures/quiet-hours/crosses-midnight-by-day.json +221 -0
  34. package/spec/fixtures/quiet-hours/dst-new-york.json +126 -0
  35. package/spec/fixtures/quiet-hours/istanbul.json +160 -0
  36. package/spec/fixtures/quiet-hours/wall-clock.json +94 -0
  37. package/spec/fixtures/quiet-hours/weekday-schedule.json +262 -0
  38. package/spec/fixtures/shadow/reject-continues.json +143 -0
  39. package/spec/fixtures/trust-ramp/first-week.json +154 -0
  40. package/spec/fixtures/utility/bounded-deferral-cap.json +60 -0
  41. package/spec/fixtures/utility/bounded-deferral.json +95 -0
  42. package/spec/fixtures/utility/floor.json +168 -0
  43. package/spec/schema/fixture.schema.json +58 -0
  44. package/spec/schema/policy.schema.json +23 -0
  45. package/spec/skip/python.txt +0 -0
  46. package/spec/skip/ts.txt +0 -0
@@ -0,0 +1,58 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.0.0",
4
+ "name": "adaptive-timing/placeholder",
5
+ "description": "A JSON policy cannot carry a next-good-moment function; the placeholder passes and keeps the trace shape.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "consent"
11
+ },
12
+ {
13
+ "id": "adaptiveTiming"
14
+ },
15
+ {
16
+ "id": "dailyBudget"
17
+ }
18
+ ]
19
+ },
20
+ "tests": [
21
+ {
22
+ "description": "passes",
23
+ "input": {
24
+ "user": {
25
+ "id": "u1",
26
+ "consent": true,
27
+ "proactiveEnabled": true,
28
+ "mode": "normal",
29
+ "intensity": "normal",
30
+ "timezone": "Europe/Istanbul",
31
+ "quietHours": {
32
+ "start": "22:00",
33
+ "end": "08:00"
34
+ },
35
+ "createdAt": "2026-01-01T00:00:00Z"
36
+ },
37
+ "candidate": {
38
+ "id": "c1",
39
+ "type": "reminder",
40
+ "priority": "normal",
41
+ "surfaces": [
42
+ "push",
43
+ "feed"
44
+ ]
45
+ },
46
+ "now": "2026-09-04T09:00:00Z"
47
+ },
48
+ "expect": {
49
+ "allowed": true,
50
+ "trace": [
51
+ "consent",
52
+ "adaptiveTiming",
53
+ "dailyBudget"
54
+ ]
55
+ }
56
+ }
57
+ ]
58
+ }
@@ -0,0 +1,100 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.0.0",
4
+ "name": "budget/bypass-priority",
5
+ "description": "A bypass priority passes the budget without reading or consuming it.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "consent"
11
+ },
12
+ {
13
+ "id": "dailyBudget",
14
+ "limit": 1,
15
+ "bypassPriority": "critical"
16
+ }
17
+ ]
18
+ },
19
+ "store_seed": {
20
+ "budget:u1:2026-09-04": "1"
21
+ },
22
+ "tests": [
23
+ {
24
+ "description": "critical passes a full budget and commit consumes nothing",
25
+ "input": {
26
+ "user": {
27
+ "id": "u1",
28
+ "consent": true,
29
+ "proactiveEnabled": true,
30
+ "mode": "normal",
31
+ "intensity": "normal",
32
+ "timezone": "Europe/Istanbul",
33
+ "quietHours": {
34
+ "start": "22:00",
35
+ "end": "08:00"
36
+ },
37
+ "createdAt": "2026-01-01T00:00:00Z"
38
+ },
39
+ "candidate": {
40
+ "id": "c1",
41
+ "type": "reminder",
42
+ "priority": "critical",
43
+ "surfaces": [
44
+ "push",
45
+ "feed"
46
+ ]
47
+ },
48
+ "now": "2026-09-04T09:00:00Z"
49
+ },
50
+ "commit": true,
51
+ "expect": {
52
+ "allowed": true,
53
+ "trace": [
54
+ "consent",
55
+ "dailyBudget"
56
+ ],
57
+ "commit": true,
58
+ "store_after": {
59
+ "budget:u1:2026-09-04": "1"
60
+ }
61
+ }
62
+ },
63
+ {
64
+ "description": "normal is rejected",
65
+ "input": {
66
+ "user": {
67
+ "id": "u1",
68
+ "consent": true,
69
+ "proactiveEnabled": true,
70
+ "mode": "normal",
71
+ "intensity": "normal",
72
+ "timezone": "Europe/Istanbul",
73
+ "quietHours": {
74
+ "start": "22:00",
75
+ "end": "08:00"
76
+ },
77
+ "createdAt": "2026-01-01T00:00:00Z"
78
+ },
79
+ "candidate": {
80
+ "id": "c2",
81
+ "type": "reminder",
82
+ "priority": "normal",
83
+ "surfaces": [
84
+ "push",
85
+ "feed"
86
+ ]
87
+ },
88
+ "now": "2026-09-04T09:00:00Z"
89
+ },
90
+ "expect": {
91
+ "allowed": false,
92
+ "rejectedBy": "dailyBudget",
93
+ "trace": [
94
+ "consent",
95
+ "dailyBudget"
96
+ ]
97
+ }
98
+ }
99
+ ]
100
+ }
@@ -0,0 +1,140 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.0.0",
4
+ "name": "budget/daily-atomic-commit",
5
+ "description": "evaluate reads the counter, commit consumes it, the extra commit is refused.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "consent"
11
+ },
12
+ {
13
+ "id": "dailyBudget",
14
+ "limit": 2
15
+ }
16
+ ]
17
+ },
18
+ "store_seed": {
19
+ "budget:u1:2026-09-04": "1"
20
+ },
21
+ "tests": [
22
+ {
23
+ "description": "one used, one left",
24
+ "input": {
25
+ "user": {
26
+ "id": "u1",
27
+ "consent": true,
28
+ "proactiveEnabled": true,
29
+ "mode": "normal",
30
+ "intensity": "normal",
31
+ "timezone": "Europe/Istanbul",
32
+ "quietHours": {
33
+ "start": "22:00",
34
+ "end": "08:00"
35
+ },
36
+ "createdAt": "2026-01-01T00:00:00Z"
37
+ },
38
+ "candidate": {
39
+ "id": "c1",
40
+ "type": "reminder",
41
+ "priority": "normal",
42
+ "surfaces": [
43
+ "push",
44
+ "feed"
45
+ ]
46
+ },
47
+ "now": "2026-09-04T09:00:00Z"
48
+ },
49
+ "commit": true,
50
+ "expect": {
51
+ "allowed": true,
52
+ "trace": [
53
+ "consent",
54
+ "dailyBudget"
55
+ ],
56
+ "commit": true,
57
+ "store_after": {
58
+ "budget:u1:2026-09-04": "2"
59
+ }
60
+ }
61
+ },
62
+ {
63
+ "description": "two used: rejected at evaluate",
64
+ "input": {
65
+ "user": {
66
+ "id": "u1",
67
+ "consent": true,
68
+ "proactiveEnabled": true,
69
+ "mode": "normal",
70
+ "intensity": "normal",
71
+ "timezone": "Europe/Istanbul",
72
+ "quietHours": {
73
+ "start": "22:00",
74
+ "end": "08:00"
75
+ },
76
+ "createdAt": "2026-01-01T00:00:00Z"
77
+ },
78
+ "candidate": {
79
+ "id": "c2",
80
+ "type": "reminder",
81
+ "priority": "normal",
82
+ "surfaces": [
83
+ "push",
84
+ "feed"
85
+ ]
86
+ },
87
+ "now": "2026-09-04T09:00:00Z"
88
+ },
89
+ "expect": {
90
+ "allowed": false,
91
+ "rejectedBy": "dailyBudget",
92
+ "trace": [
93
+ "consent",
94
+ "dailyBudget"
95
+ ],
96
+ "reason_pattern": "2 used \\(2\\)"
97
+ }
98
+ },
99
+ {
100
+ "description": "next local day resets (00:30 Istanbul)",
101
+ "input": {
102
+ "user": {
103
+ "id": "u1",
104
+ "consent": true,
105
+ "proactiveEnabled": true,
106
+ "mode": "normal",
107
+ "intensity": "normal",
108
+ "timezone": "Europe/Istanbul",
109
+ "quietHours": {
110
+ "start": "22:00",
111
+ "end": "08:00"
112
+ },
113
+ "createdAt": "2026-01-01T00:00:00Z"
114
+ },
115
+ "candidate": {
116
+ "id": "c3",
117
+ "type": "reminder",
118
+ "priority": "normal",
119
+ "surfaces": [
120
+ "push",
121
+ "feed"
122
+ ]
123
+ },
124
+ "now": "2026-09-04T21:30:00Z"
125
+ },
126
+ "commit": true,
127
+ "expect": {
128
+ "allowed": true,
129
+ "trace": [
130
+ "consent",
131
+ "dailyBudget"
132
+ ],
133
+ "commit": true,
134
+ "store_after": {
135
+ "budget:u1:2026-09-05": "1"
136
+ }
137
+ }
138
+ }
139
+ ]
140
+ }
@@ -0,0 +1,103 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.0.0",
4
+ "name": "budget/near-limit",
5
+ "description": "A pass close to the limit carries nearLimit.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "consent"
11
+ },
12
+ {
13
+ "id": "dailyBudget",
14
+ "limit": 5,
15
+ "nearLimit": 0.8
16
+ }
17
+ ]
18
+ },
19
+ "store_seed": {
20
+ "budget:u1:2026-09-04": "4",
21
+ "budget:u9:2026-09-04": "3"
22
+ },
23
+ "tests": [
24
+ {
25
+ "description": "4 of 5 used",
26
+ "input": {
27
+ "user": {
28
+ "id": "u1",
29
+ "consent": true,
30
+ "proactiveEnabled": true,
31
+ "mode": "normal",
32
+ "intensity": "normal",
33
+ "timezone": "Europe/Istanbul",
34
+ "quietHours": {
35
+ "start": "22:00",
36
+ "end": "08:00"
37
+ },
38
+ "createdAt": "2026-01-01T00:00:00Z"
39
+ },
40
+ "candidate": {
41
+ "id": "c1",
42
+ "type": "reminder",
43
+ "priority": "normal",
44
+ "surfaces": [
45
+ "push",
46
+ "feed"
47
+ ]
48
+ },
49
+ "now": "2026-09-04T09:00:00Z"
50
+ },
51
+ "expect": {
52
+ "allowed": true,
53
+ "trace": [
54
+ "consent",
55
+ "dailyBudget"
56
+ ],
57
+ "nearLimit": [
58
+ {
59
+ "check": "dailyBudget",
60
+ "used": 4,
61
+ "limit": 5
62
+ }
63
+ ]
64
+ }
65
+ },
66
+ {
67
+ "description": "3 of 5 used carries nothing",
68
+ "input": {
69
+ "user": {
70
+ "id": "u9",
71
+ "consent": true,
72
+ "proactiveEnabled": true,
73
+ "mode": "normal",
74
+ "intensity": "normal",
75
+ "timezone": "Europe/Istanbul",
76
+ "quietHours": {
77
+ "start": "22:00",
78
+ "end": "08:00"
79
+ },
80
+ "createdAt": "2026-01-01T00:00:00Z"
81
+ },
82
+ "candidate": {
83
+ "id": "c2",
84
+ "type": "reminder",
85
+ "priority": "normal",
86
+ "surfaces": [
87
+ "push",
88
+ "feed"
89
+ ]
90
+ },
91
+ "now": "2026-09-04T09:00:00Z"
92
+ },
93
+ "expect": {
94
+ "allowed": true,
95
+ "trace": [
96
+ "consent",
97
+ "dailyBudget"
98
+ ],
99
+ "nearLimit": []
100
+ }
101
+ }
102
+ ]
103
+ }
@@ -0,0 +1,93 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.0.0",
4
+ "name": "budget/race-second-commit-loses",
5
+ "description": "Two evaluations before either commit: only the first commit wins.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "consent"
11
+ },
12
+ {
13
+ "id": "dailyBudget",
14
+ "limit": 1
15
+ }
16
+ ]
17
+ },
18
+ "tests": [
19
+ {
20
+ "description": "first commit consumes the only unit",
21
+ "input": {
22
+ "user": {
23
+ "id": "u1",
24
+ "consent": true,
25
+ "proactiveEnabled": true,
26
+ "mode": "normal",
27
+ "intensity": "normal",
28
+ "timezone": "Europe/Istanbul",
29
+ "quietHours": {
30
+ "start": "22:00",
31
+ "end": "08:00"
32
+ },
33
+ "createdAt": "2026-01-01T00:00:00Z"
34
+ },
35
+ "candidate": {
36
+ "id": "c1",
37
+ "type": "reminder",
38
+ "priority": "normal",
39
+ "surfaces": [
40
+ "push",
41
+ "feed"
42
+ ]
43
+ },
44
+ "now": "2026-09-04T09:00:00Z"
45
+ },
46
+ "commit": true,
47
+ "expect": {
48
+ "allowed": true,
49
+ "trace": [
50
+ "consent",
51
+ "dailyBudget"
52
+ ],
53
+ "commit": true
54
+ }
55
+ },
56
+ {
57
+ "description": "the second candidate was evaluated when the counter read 1; its commit is refused",
58
+ "input": {
59
+ "user": {
60
+ "id": "u1",
61
+ "consent": true,
62
+ "proactiveEnabled": true,
63
+ "mode": "normal",
64
+ "intensity": "normal",
65
+ "timezone": "Europe/Istanbul",
66
+ "quietHours": {
67
+ "start": "22:00",
68
+ "end": "08:00"
69
+ },
70
+ "createdAt": "2026-01-01T00:00:00Z"
71
+ },
72
+ "candidate": {
73
+ "id": "c2",
74
+ "type": "reminder",
75
+ "priority": "normal",
76
+ "surfaces": [
77
+ "push",
78
+ "feed"
79
+ ]
80
+ },
81
+ "now": "2026-09-04T09:00:00Z"
82
+ },
83
+ "expect": {
84
+ "allowed": false,
85
+ "rejectedBy": "dailyBudget",
86
+ "trace": [
87
+ "consent",
88
+ "dailyBudget"
89
+ ]
90
+ }
91
+ }
92
+ ]
93
+ }
@@ -0,0 +1,134 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.0.0",
4
+ "name": "budget/weekly-iso-week",
5
+ "description": "The weekly counter is keyed on the local ISO week.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "consent"
11
+ },
12
+ {
13
+ "id": "weeklyBudget",
14
+ "limit": 2
15
+ }
16
+ ]
17
+ },
18
+ "store_seed": {
19
+ "weeklyBudget:u1:2026-W36": "1"
20
+ },
21
+ "tests": [
22
+ {
23
+ "description": "Friday 4 Sep 2026 is week 36",
24
+ "input": {
25
+ "user": {
26
+ "id": "u1",
27
+ "consent": true,
28
+ "proactiveEnabled": true,
29
+ "mode": "normal",
30
+ "intensity": "normal",
31
+ "timezone": "Europe/Istanbul",
32
+ "quietHours": {
33
+ "start": "22:00",
34
+ "end": "08:00"
35
+ },
36
+ "createdAt": "2026-01-01T00:00:00Z"
37
+ },
38
+ "candidate": {
39
+ "id": "c1",
40
+ "type": "reminder",
41
+ "priority": "normal",
42
+ "surfaces": [
43
+ "push",
44
+ "feed"
45
+ ]
46
+ },
47
+ "now": "2026-09-04T09:00:00Z"
48
+ },
49
+ "commit": true,
50
+ "expect": {
51
+ "allowed": true,
52
+ "trace": [
53
+ "consent",
54
+ "weeklyBudget"
55
+ ],
56
+ "commit": true,
57
+ "store_after": {
58
+ "weeklyBudget:u1:2026-W36": "2"
59
+ }
60
+ }
61
+ },
62
+ {
63
+ "description": "week 36 is full",
64
+ "input": {
65
+ "user": {
66
+ "id": "u1",
67
+ "consent": true,
68
+ "proactiveEnabled": true,
69
+ "mode": "normal",
70
+ "intensity": "normal",
71
+ "timezone": "Europe/Istanbul",
72
+ "quietHours": {
73
+ "start": "22:00",
74
+ "end": "08:00"
75
+ },
76
+ "createdAt": "2026-01-01T00:00:00Z"
77
+ },
78
+ "candidate": {
79
+ "id": "c2",
80
+ "type": "reminder",
81
+ "priority": "normal",
82
+ "surfaces": [
83
+ "push",
84
+ "feed"
85
+ ]
86
+ },
87
+ "now": "2026-09-04T09:00:00Z"
88
+ },
89
+ "expect": {
90
+ "allowed": false,
91
+ "rejectedBy": "weeklyBudget",
92
+ "trace": [
93
+ "consent",
94
+ "weeklyBudget"
95
+ ]
96
+ }
97
+ },
98
+ {
99
+ "description": "Monday 7 Sep is week 37",
100
+ "input": {
101
+ "user": {
102
+ "id": "u1",
103
+ "consent": true,
104
+ "proactiveEnabled": true,
105
+ "mode": "normal",
106
+ "intensity": "normal",
107
+ "timezone": "Europe/Istanbul",
108
+ "quietHours": {
109
+ "start": "22:00",
110
+ "end": "08:00"
111
+ },
112
+ "createdAt": "2026-01-01T00:00:00Z"
113
+ },
114
+ "candidate": {
115
+ "id": "c3",
116
+ "type": "reminder",
117
+ "priority": "normal",
118
+ "surfaces": [
119
+ "push",
120
+ "feed"
121
+ ]
122
+ },
123
+ "now": "2026-09-07T09:00:00Z"
124
+ },
125
+ "expect": {
126
+ "allowed": true,
127
+ "trace": [
128
+ "consent",
129
+ "weeklyBudget"
130
+ ]
131
+ }
132
+ }
133
+ ]
134
+ }