Skip to main content

proactive-gate for Python

Decide whether a proactive assistant may speak to this person right now. Ordered checks (consent, mode, snooze, mute, intensity, quiet hours, trust ramp, dismissal cooldown, budgets), JSON policies, platform and regulatory presets, and a trace that says which check stopped the message and why.

This is the Python sibling of the TypeScript package. Both implement the same behaviour contract in spec/ and run the same fixtures, so a policy written for one behaves the same in the other.

pip install proactive-gate

Python 3.11 or newer, no runtime dependencies. Add [redis] (pip install "proactive-gate[redis]") for the Redis store, or [aiosqlite] for the async SQLite store.

To run an unreleased state, install from the repository instead: pip install "proactive-gate @ git+https://github.com/Bubblegunn/proactive-gate#subdirectory=python".

The package is published from the release workflow through PyPI trusted publishing, so each file carries a publish attestation naming the repository and the workflow that built it. Releases before 0.2.2 were uploaded from a local build with a token and carry none.

Use

from datetime import datetime, timezone
from proactive_gate import Candidate, EvaluateInput, Gate, UserState, default_checks

gate = Gate(default_checks(daily_limit=3))
user = UserState.from_dict({
    "id": "u1", "consent": True, "timezone": "Europe/Istanbul",
    "quietHours": {"start": "22:00", "end": "08:00"}, "createdAt": "2026-01-01T00:00:00Z",
})
candidate = Candidate(id="c1", type="reminder", priority="normal", surfaces=("push", "feed"))
inp = EvaluateInput(user, candidate, datetime.now(timezone.utc))

decision = gate.evaluate(inp)
if decision.allowed and gate.commit(decision, inp):
    send(candidate, decision.surfaces)
else:
    print(decision.rejected_by, decision.reason)

evaluate reads; commit is the atomic increment right before you send, and it is idempotent on decision.id. Every decision carries trace, one entry per check that ran.

explain(decision) renders a decision as sentences a non-engineer follows, a pure function of the trace with the same wording as the TypeScript catalog: explain(decision).summary for the decision as one sentence, .checks for one per check that ran. language defaults to "en"; another language is a mapping over the same keys in catalogs, merged over English so a partial translation still renders.

A policy is data

import json
from proactive_gate import Gate

gate = Gate.from_policy(json.load(open("policy.json")))

policy.json is the same document the TypeScript package and the CLI read ({"specVersion": "1.0.0", "checks": [{"id": "consent"}, {"preset": "usTcpa"}, ...]}). Unknown check ids and presets raise ValueError naming the known ones; shadow: true keeps a check observing without letting it decide.

Async

from proactive_gate import AsyncGate, RedisStore
import redis.asyncio as redis

gate = AsyncGate.from_policy(policy, RedisStore(redis.from_url("redis://localhost")))
decision = await gate.evaluate(inp)

Gate and AsyncGate share one decision loop (Evaluation): a check names the store keys it needs and decides purely from their values, so the two gates cannot drift. decide() runs that loop with values you supply, for tests with no store at all.

Stores

MemoryStore (in-process), SqliteStore (standard library, one file per host), AsyncMemoryStore, AsyncSqliteStore over aiosqlite (contributed by @LouisDeconinck in #32; the same file and expiry rules as SqliteStore, without blocking the loop), and RedisStore over redis.asyncio (INCR, and EXPIRE on the first increment, run as one Lua script through the client's eval, so a failure between the two cannot leave a counter with no TTL). Any object with get, set, incr and delete works. SqliteStore removes an expired row when a read touches it and clears every already-expired row on each set or incr, so a key nobody reads again still disappears.

Presets

proactive_gate.presets holds the same seventeen presets as the TypeScript package, each with its source URLs and a note on what it leaves out: LINE, WeChat, WeCom, Kakao, Korea's Network Act, Japan's anti-spam law, China's minor mode, India's TCCCPR, Brazil's LGPD, US TCPA, EU ePrivacy, Telegram, Slack and WhatsApp Business. They are reviewable defaults, not legal advice.

Conformance

pytest

runs every fixture under ../spec/fixtures through both gates. A fixture the Python package cannot yet satisfy is listed in spec/skip/python.txt with a reason; the list is empty.

License

MIT.

Metadata

Release files for proactive-gate 0.8.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for proactive-gate 0.8.1
File Size Uploaded
proactive_gate-0.8.1.tar.gz 46.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for proactive-gate 0.8.1
File Interpreter ABI Platform
proactive_gate-0.8.1-py3-none-any.whl Python 3 none any Details

Total release size: 84.8 kB

Release files / proactive_gate-0.8.1.tar.gz

Download URL proactive_gate-0.8.1.tar.gz
Size 46.5 kB
Tags Source
SHA-256 checksum
How to use checksums
6b8e2005319aa00b6f9cba16696cd37bc30dfeaed9e2c675ca301da0f42044a4
BLAKE2b-256 checksum
How to use checksums
e1aad64a8844d7434f0af0d87daf3f11fddb1cc36ab590ac3ff87cb7e47de7a3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release files / proactive_gate-0.8.1-py3-none-any.whl

Download URL proactive_gate-0.8.1-py3-none-any.whl
Size 38.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b8f4ff121be3538c47cc6884c0f60c7b08abfa3921977eb7e61ef9e3751aab1e
BLAKE2b-256 checksum
How to use checksums
f70ac1bd79d1161c23fc7003f1145e07e30347cfaf9488225d83563b90a68818
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.8.1 This release

2 release files

0.8.0

2 release files

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page