proactive-gate 0.2.3 → 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 (41) hide show
  1. package/README.md +63 -0
  2. package/package.json +4 -2
  3. package/spec/CONFORMANCE.md +83 -0
  4. package/spec/SPEC.md +140 -0
  5. package/spec/SPEC_VERSION +1 -0
  6. package/spec/fixtures/adaptive-timing/placeholder.json +58 -0
  7. package/spec/fixtures/budget/bypass-priority.json +100 -0
  8. package/spec/fixtures/budget/daily-atomic-commit.json +140 -0
  9. package/spec/fixtures/budget/near-limit.json +103 -0
  10. package/spec/fixtures/budget/race-second-commit-loses.json +93 -0
  11. package/spec/fixtures/budget/weekly-iso-week.json +134 -0
  12. package/spec/fixtures/consent/required.json +116 -0
  13. package/spec/fixtures/cooldown/three-dismissals.json +132 -0
  14. package/spec/fixtures/dedupe/already-delivered.json +87 -0
  15. package/spec/fixtures/dedupe/no-key-skips.json +49 -0
  16. package/spec/fixtures/defer/snooze-as-defer.json +138 -0
  17. package/spec/fixtures/mode/allow-list.json +139 -0
  18. package/spec/fixtures/ordering/kill-switch.json +56 -0
  19. package/spec/fixtures/ordering/short-circuit.json +235 -0
  20. package/spec/fixtures/policy/unknown-check-is-an-error.json +50 -0
  21. package/spec/fixtures/presets/cn-minor-mode.json +170 -0
  22. package/spec/fixtures/presets/kakao-brand-message.json +93 -0
  23. package/spec/fixtures/presets/kr-network-act-50.json +168 -0
  24. package/spec/fixtures/presets/telegram-bot.json +162 -0
  25. package/spec/fixtures/presets/us-tcpa.json +90 -0
  26. package/spec/fixtures/quiet-hours/apia.json +90 -0
  27. package/spec/fixtures/quiet-hours/caller-supplied-dates.json +197 -0
  28. package/spec/fixtures/quiet-hours/crosses-midnight-by-day.json +221 -0
  29. package/spec/fixtures/quiet-hours/dst-new-york.json +126 -0
  30. package/spec/fixtures/quiet-hours/istanbul.json +160 -0
  31. package/spec/fixtures/quiet-hours/wall-clock.json +94 -0
  32. package/spec/fixtures/quiet-hours/weekday-schedule.json +262 -0
  33. package/spec/fixtures/shadow/reject-continues.json +143 -0
  34. package/spec/fixtures/trust-ramp/first-week.json +154 -0
  35. package/spec/fixtures/utility/bounded-deferral-cap.json +60 -0
  36. package/spec/fixtures/utility/bounded-deferral.json +95 -0
  37. package/spec/fixtures/utility/floor.json +168 -0
  38. package/spec/schema/fixture.schema.json +58 -0
  39. package/spec/schema/policy.schema.json +23 -0
  40. package/spec/skip/python.txt +0 -0
  41. package/spec/skip/ts.txt +0 -0
@@ -0,0 +1,49 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.2.0",
4
+ "name": "dedupe/no-key-skips",
5
+ "description": "With no dedupeKey on the candidate the check skips rather than guessing an identity.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "consent"
11
+ },
12
+ {
13
+ "id": "dedupe"
14
+ }
15
+ ]
16
+ },
17
+ "tests": [
18
+ {
19
+ "description": "a candidate with no dedupeKey is delivered; the check stands aside in the trace",
20
+ "input": {
21
+ "user": {
22
+ "id": "u1",
23
+ "consent": true,
24
+ "proactiveEnabled": true,
25
+ "mode": "normal",
26
+ "intensity": "normal",
27
+ "timezone": "Europe/Istanbul",
28
+ "createdAt": "2026-01-01T00:00:00Z"
29
+ },
30
+ "candidate": {
31
+ "id": "c1",
32
+ "type": "reminder",
33
+ "priority": "normal",
34
+ "surfaces": [
35
+ "push"
36
+ ]
37
+ },
38
+ "now": "2026-09-05T12:00:00Z"
39
+ },
40
+ "expect": {
41
+ "allowed": true,
42
+ "trace": [
43
+ "consent",
44
+ "dedupe"
45
+ ]
46
+ }
47
+ }
48
+ ]
49
+ }
@@ -0,0 +1,138 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.0.0",
4
+ "name": "defer/snooze-as-defer",
5
+ "description": "snooze with defer true produces a deferred decision with retryAt.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "consent"
11
+ },
12
+ {
13
+ "id": "snooze",
14
+ "defer": true
15
+ },
16
+ {
17
+ "id": "dailyBudget"
18
+ }
19
+ ]
20
+ },
21
+ "tests": [
22
+ {
23
+ "description": "snoozed: deferred until the snooze ends",
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
+ "snoozedUntil": "2026-09-04T10:00:00Z"
38
+ },
39
+ "candidate": {
40
+ "id": "c1",
41
+ "type": "reminder",
42
+ "priority": "normal",
43
+ "surfaces": [
44
+ "push",
45
+ "feed"
46
+ ]
47
+ },
48
+ "now": "2026-09-04T09:00:00Z"
49
+ },
50
+ "expect": {
51
+ "allowed": false,
52
+ "deferredBy": "snooze",
53
+ "retryAt": "2026-09-04T10:00:00.000Z",
54
+ "trace": [
55
+ "consent",
56
+ "snooze"
57
+ ]
58
+ }
59
+ },
60
+ {
61
+ "description": "snooze in the past: passes",
62
+ "input": {
63
+ "user": {
64
+ "id": "u1",
65
+ "consent": true,
66
+ "proactiveEnabled": true,
67
+ "mode": "normal",
68
+ "intensity": "normal",
69
+ "timezone": "Europe/Istanbul",
70
+ "quietHours": {
71
+ "start": "22:00",
72
+ "end": "08:00"
73
+ },
74
+ "createdAt": "2026-01-01T00:00:00Z",
75
+ "snoozedUntil": "2026-09-04T08:00:00Z"
76
+ },
77
+ "candidate": {
78
+ "id": "c1",
79
+ "type": "reminder",
80
+ "priority": "normal",
81
+ "surfaces": [
82
+ "push",
83
+ "feed"
84
+ ]
85
+ },
86
+ "now": "2026-09-04T09:00:00Z"
87
+ },
88
+ "expect": {
89
+ "allowed": true,
90
+ "trace": [
91
+ "consent",
92
+ "snooze",
93
+ "dailyBudget"
94
+ ]
95
+ }
96
+ },
97
+ {
98
+ "description": "a deferred decision cannot be committed",
99
+ "input": {
100
+ "user": {
101
+ "id": "u1",
102
+ "consent": true,
103
+ "proactiveEnabled": true,
104
+ "mode": "normal",
105
+ "intensity": "normal",
106
+ "timezone": "Europe/Istanbul",
107
+ "quietHours": {
108
+ "start": "22:00",
109
+ "end": "08:00"
110
+ },
111
+ "createdAt": "2026-01-01T00:00:00Z",
112
+ "snoozedUntil": "2026-09-04T10:00:00Z"
113
+ },
114
+ "candidate": {
115
+ "id": "c2",
116
+ "type": "reminder",
117
+ "priority": "normal",
118
+ "surfaces": [
119
+ "push",
120
+ "feed"
121
+ ]
122
+ },
123
+ "now": "2026-09-04T09:00:00Z"
124
+ },
125
+ "commit": true,
126
+ "expect": {
127
+ "allowed": false,
128
+ "deferredBy": "snooze",
129
+ "retryAt": "2026-09-04T10:00:00.000Z",
130
+ "trace": [
131
+ "consent",
132
+ "snooze"
133
+ ],
134
+ "commit": false
135
+ }
136
+ }
137
+ ]
138
+ }
@@ -0,0 +1,139 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.0.0",
4
+ "name": "mode/allow-list",
5
+ "description": "Only listed operating modes receive proactive messages; an undefined mode passes.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "consent"
11
+ },
12
+ {
13
+ "id": "mode",
14
+ "allow": [
15
+ "normal",
16
+ "commute"
17
+ ]
18
+ },
19
+ {
20
+ "id": "mute"
21
+ }
22
+ ]
23
+ },
24
+ "tests": [
25
+ {
26
+ "description": "focus mode rejects",
27
+ "input": {
28
+ "user": {
29
+ "id": "u1",
30
+ "consent": true,
31
+ "proactiveEnabled": true,
32
+ "mode": "focus",
33
+ "intensity": "normal",
34
+ "timezone": "Europe/Istanbul",
35
+ "quietHours": {
36
+ "start": "22:00",
37
+ "end": "08:00"
38
+ },
39
+ "createdAt": "2026-01-01T00:00:00Z"
40
+ },
41
+ "candidate": {
42
+ "id": "c1",
43
+ "type": "reminder",
44
+ "priority": "normal",
45
+ "surfaces": [
46
+ "push",
47
+ "feed"
48
+ ]
49
+ },
50
+ "now": "2026-09-04T09:00:00Z"
51
+ },
52
+ "expect": {
53
+ "allowed": false,
54
+ "rejectedBy": "mode",
55
+ "trace": [
56
+ "consent",
57
+ "mode"
58
+ ],
59
+ "reason_pattern": "focus"
60
+ }
61
+ },
62
+ {
63
+ "description": "commute passes",
64
+ "input": {
65
+ "user": {
66
+ "id": "u1",
67
+ "consent": true,
68
+ "proactiveEnabled": true,
69
+ "mode": "commute",
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": "c1",
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": true,
91
+ "trace": [
92
+ "consent",
93
+ "mode",
94
+ "mute"
95
+ ]
96
+ }
97
+ },
98
+ {
99
+ "description": "muted type rejects",
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
+ "mutedTypes": [
114
+ "reminder"
115
+ ]
116
+ },
117
+ "candidate": {
118
+ "id": "c1",
119
+ "type": "reminder",
120
+ "priority": "normal",
121
+ "surfaces": [
122
+ "push",
123
+ "feed"
124
+ ]
125
+ },
126
+ "now": "2026-09-04T09:00:00Z"
127
+ },
128
+ "expect": {
129
+ "allowed": false,
130
+ "rejectedBy": "mute",
131
+ "trace": [
132
+ "consent",
133
+ "mode",
134
+ "mute"
135
+ ]
136
+ }
137
+ }
138
+ ]
139
+ }
@@ -0,0 +1,56 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.0.0",
4
+ "name": "ordering/kill-switch",
5
+ "description": "A kill switch that is on stops everything before consent is even read.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "killSwitch",
11
+ "on": true
12
+ },
13
+ {
14
+ "id": "consent"
15
+ }
16
+ ]
17
+ },
18
+ "tests": [
19
+ {
20
+ "description": "rejected by the switch",
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
+ "expect": {
47
+ "allowed": false,
48
+ "rejectedBy": "killSwitch",
49
+ "trace": [
50
+ "killSwitch"
51
+ ],
52
+ "reason_pattern": "kill switch"
53
+ }
54
+ }
55
+ ]
56
+ }
@@ -0,0 +1,235 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.0.0",
4
+ "name": "ordering/short-circuit",
5
+ "description": "Checks run in policy order and stop at the first rejection.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "killSwitch"
11
+ },
12
+ {
13
+ "id": "consent"
14
+ },
15
+ {
16
+ "id": "enabled"
17
+ },
18
+ {
19
+ "id": "mode",
20
+ "allow": [
21
+ "normal"
22
+ ]
23
+ },
24
+ {
25
+ "id": "snooze"
26
+ },
27
+ {
28
+ "id": "mute"
29
+ },
30
+ {
31
+ "id": "intensity"
32
+ },
33
+ {
34
+ "id": "quietHours",
35
+ "priorityFloor": "critical"
36
+ },
37
+ {
38
+ "id": "trustRamp"
39
+ },
40
+ {
41
+ "id": "dismissalCooldown"
42
+ },
43
+ {
44
+ "id": "adaptiveTiming"
45
+ },
46
+ {
47
+ "id": "dailyBudget",
48
+ "limit": 5
49
+ }
50
+ ]
51
+ },
52
+ "tests": [
53
+ {
54
+ "description": "everything passes; the trace lists all twelve",
55
+ "input": {
56
+ "user": {
57
+ "id": "u1",
58
+ "consent": true,
59
+ "proactiveEnabled": true,
60
+ "mode": "normal",
61
+ "intensity": "normal",
62
+ "timezone": "Europe/Istanbul",
63
+ "quietHours": {
64
+ "start": "22:00",
65
+ "end": "08:00"
66
+ },
67
+ "createdAt": "2026-01-01T00:00:00Z"
68
+ },
69
+ "candidate": {
70
+ "id": "c1",
71
+ "type": "reminder",
72
+ "priority": "normal",
73
+ "surfaces": [
74
+ "push",
75
+ "feed"
76
+ ]
77
+ },
78
+ "now": "2026-09-04T09:00:00Z"
79
+ },
80
+ "expect": {
81
+ "allowed": true,
82
+ "surfaces": [
83
+ "push",
84
+ "feed"
85
+ ],
86
+ "trace": [
87
+ "killSwitch",
88
+ "consent",
89
+ "enabled",
90
+ "mode",
91
+ "snooze",
92
+ "mute",
93
+ "intensity",
94
+ "quietHours",
95
+ "trustRamp",
96
+ "dismissalCooldown",
97
+ "adaptiveTiming",
98
+ "dailyBudget"
99
+ ]
100
+ }
101
+ },
102
+ {
103
+ "description": "consent rejects before quiet hours gets a chance",
104
+ "input": {
105
+ "user": {
106
+ "id": "u1",
107
+ "consent": false,
108
+ "proactiveEnabled": true,
109
+ "mode": "normal",
110
+ "intensity": "normal",
111
+ "timezone": "Europe/Istanbul",
112
+ "quietHours": {
113
+ "start": "22:00",
114
+ "end": "08:00"
115
+ },
116
+ "createdAt": "2026-01-01T00:00:00Z"
117
+ },
118
+ "candidate": {
119
+ "id": "c1",
120
+ "type": "reminder",
121
+ "priority": "normal",
122
+ "surfaces": [
123
+ "push",
124
+ "feed"
125
+ ]
126
+ },
127
+ "now": "2026-09-04T20:30:00Z"
128
+ },
129
+ "expect": {
130
+ "allowed": false,
131
+ "rejectedBy": "consent",
132
+ "trace": [
133
+ "killSwitch",
134
+ "consent"
135
+ ]
136
+ }
137
+ },
138
+ {
139
+ "description": "surfaces are the candidate's filtered by the user's",
140
+ "input": {
141
+ "user": {
142
+ "id": "u1",
143
+ "consent": true,
144
+ "proactiveEnabled": true,
145
+ "mode": "normal",
146
+ "intensity": "normal",
147
+ "timezone": "Europe/Istanbul",
148
+ "quietHours": {
149
+ "start": "22:00",
150
+ "end": "08:00"
151
+ },
152
+ "createdAt": "2026-01-01T00:00:00Z",
153
+ "surfaces": [
154
+ "feed"
155
+ ]
156
+ },
157
+ "candidate": {
158
+ "id": "c1",
159
+ "type": "reminder",
160
+ "priority": "normal",
161
+ "surfaces": [
162
+ "push",
163
+ "feed"
164
+ ]
165
+ },
166
+ "now": "2026-09-04T09:00:00Z"
167
+ },
168
+ "expect": {
169
+ "allowed": true,
170
+ "surfaces": [
171
+ "feed"
172
+ ],
173
+ "trace": [
174
+ "killSwitch",
175
+ "consent",
176
+ "enabled",
177
+ "mode",
178
+ "snooze",
179
+ "mute",
180
+ "intensity",
181
+ "quietHours",
182
+ "trustRamp",
183
+ "dismissalCooldown",
184
+ "adaptiveTiming",
185
+ "dailyBudget"
186
+ ]
187
+ }
188
+ },
189
+ {
190
+ "description": "critical priority passes quiet hours",
191
+ "input": {
192
+ "user": {
193
+ "id": "u1",
194
+ "consent": true,
195
+ "proactiveEnabled": true,
196
+ "mode": "normal",
197
+ "intensity": "normal",
198
+ "timezone": "Europe/Istanbul",
199
+ "quietHours": {
200
+ "start": "22:00",
201
+ "end": "08:00"
202
+ },
203
+ "createdAt": "2026-01-01T00:00:00Z"
204
+ },
205
+ "candidate": {
206
+ "id": "c1",
207
+ "type": "reminder",
208
+ "priority": "critical",
209
+ "surfaces": [
210
+ "push",
211
+ "feed"
212
+ ]
213
+ },
214
+ "now": "2026-09-04T20:30:00Z"
215
+ },
216
+ "expect": {
217
+ "allowed": true,
218
+ "trace": [
219
+ "killSwitch",
220
+ "consent",
221
+ "enabled",
222
+ "mode",
223
+ "snooze",
224
+ "mute",
225
+ "intensity",
226
+ "quietHours",
227
+ "trustRamp",
228
+ "dismissalCooldown",
229
+ "adaptiveTiming",
230
+ "dailyBudget"
231
+ ]
232
+ }
233
+ }
234
+ ]
235
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "spec_version": "1.2.0",
3
+ "since": "1.0.0",
4
+ "name": "policy/unknown-check-is-an-error",
5
+ "description": "The runner never sees this fixture's expectation matter; it documents that compile errors are for the schema, not the runtime. A minimal valid policy passes.",
6
+ "policy": {
7
+ "specVersion": "1.0.0",
8
+ "checks": [
9
+ {
10
+ "id": "consent"
11
+ }
12
+ ]
13
+ },
14
+ "tests": [
15
+ {
16
+ "description": "consent only",
17
+ "input": {
18
+ "user": {
19
+ "id": "u1",
20
+ "consent": true,
21
+ "proactiveEnabled": true,
22
+ "mode": "normal",
23
+ "intensity": "normal",
24
+ "timezone": "Europe/Istanbul",
25
+ "quietHours": {
26
+ "start": "22:00",
27
+ "end": "08:00"
28
+ },
29
+ "createdAt": "2026-01-01T00:00:00Z"
30
+ },
31
+ "candidate": {
32
+ "id": "c1",
33
+ "type": "reminder",
34
+ "priority": "normal",
35
+ "surfaces": [
36
+ "push",
37
+ "feed"
38
+ ]
39
+ },
40
+ "now": "2026-09-04T09:00:00Z"
41
+ },
42
+ "expect": {
43
+ "allowed": true,
44
+ "trace": [
45
+ "consent"
46
+ ]
47
+ }
48
+ }
49
+ ]
50
+ }