Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

sinmonto

Version française

tests License: Apache-2.0 Python 3.11+

Explainable, deterministic event-driven decision engine, in pure Python — zero dependencies.

From the Fon Sɛ́n mɔto ("rule engine"). Every decision carries its own proof: why a rule matched, why another didn't, in what order, with what actual values at the time of evaluation.

Status: 0.1.0rc6 — technical preview. The core is tested (53 tests in tests/ + end-to-end integration) and the silent bugs found in multi-AI cross-review are fixed (see "Known limitations" below for what's deliberately left open). Staying in pre-release until a first round of real external usage feedback — the 0.x API isn't locked yet.

Why

  • Zero dependencies — installs and audits anywhere, no dependency tree for a security team to sign off on.
  • Native explainability — every decision produces a complete trace tree, not a log bolted on afterward.
  • Effects-as-data — a rule never makes a network call or a database write. It describes an Effect. A separate executor applies it. This makes the engine testable and replayable by construction.
  • Per-entity persistent state — a ContextStore keeps an entity's memory (user, device, transaction) from one event to the next, without which no running count or cumulative score is possible.
  • Deterministic — same inputs, same rule-registration order ⇒ same output, bit for bit, including on priority ties. (Explicit exception: DecisionTrace.trace_id, a UUID generated on every evaluation, not reproducible by construction — the guarantee covers matched rules, effects, evaluation order, and context state, not generated identifiers.)

Installation

pip install sinmonto

(Since the version is a pre-release (0.1.0rc6), a plain pip install sinmonto won't pick it up once published on PyPI — you'll need pip install --pre sinmonto, consistent with the preview status above. Until then: pip install -e . from a local copy of the repo.)

Example

from decimal import Decimal
from uuid import uuid4

from sinmonto import DecisionEngine, Effect, Fact, Field, Signal, rule

engine = DecisionEngine()

@rule(name="high_amount_alert", priority=100,
      condition=(Field("amount") > 1000) & (Field("vip") == False),
      engine=engine)
def check_high_amount(ctx, fact):
    return Effect("FLAG_TRANSACTION", {"reason": "high_amount_non_vip"}, "high_amount_alert")

engine.compile()

fact = Fact(fact_id=uuid4(), entity_id="usr_99", fact_type="payment",
            _payload={"amount": 2500, "vip": False}, timestamp=Decimal("0"))
signal = Signal(signal_id=uuid4(), fact=fact, signal_type="payment_received", timestamp=Decimal("0"))

decision = engine.evaluate(signal)

print(decision.effects)
# (Effect(effect_type='FLAG_TRANSACTION', payload={'reason': 'high_amount_non_vip'}, rule_id='high_amount_alert'),)

trace = decision.trace.rule_traces[0]
print(trace.condition_tree.description, "->", trace.condition_tree.result)
# amount gt 1000 -> True

A second signal for usr_99 automatically picks up the context from the first — see examples/end_to_end.py and ContextStore.

Cross-review status (2026-08 – 2026-09)

The repo went through a multi-AI code cross-review (ChatGPT, Grok, DeepSeek, Kimi, Qwen, Meta AI). Six silent bugs — the ones that betrayed the explainability/determinism promise rather than simple documented gaps — were fixed before this preview: deep-copied context, real rule atomicity (snapshot/restore, including direct ctx mutation), Signal.entity_id/condition-operator/action-return validation, defensive payload copying, chained causality. A second pass (rc2 → rc3), on the actual repo this time rather than a zip, fixed three additional points: deep (not just shallow) copying on Fact.payload/Effect.payload, clarifying that bit-for-bit determinism excludes trace_id, and a packaging issue on CLAUDE.md. A third pass (rc3 → rc4, 2026-09) closed the last item deferred from the initial preview: derived-signal cascading (EvaluationResult.derived_signals) is now processed by an internal FIFO queue inside evaluate(), with max_derived_depth actually enforced and causality chained across hops — synthesized from independent reports by Kimi, ChatGPT, Grok, Gemini and Qwen, including two contract deviations caught by replaying each report's code against the already-locked spec rather than taking any one at face value. Full bug-by-bug detail: journal-integration.md (French).

Known limitations (v0.1.0-preview) — assumed, not bugs

  • RuleTrace.duration_ms is always Decimal("0") — no real execution-time measurement yet.
  • Nested AND chains aren't flattened in the trace — cosmetic, the logic and short-circuiting remain correct.
  • No retention policy on InMemoryContextStore/InMemoryFactStore — in-memory stores meant for tests/demos/prototypes, not long-running production without a dedicated adapter.
  • No time windows, state transitions (FSM), or engine.replay() yet — see roadmap-vision.md (French).
  • Fact.payload/Effect.payload/FrozenContext.values are protected by deep copy at construction (an external mutation no longer affects them) and read-only MappingProxyType at the top level — but MappingProxyType only protects that top level: mutating a nested value through the proxy (obj.payload["nested"]["x"] = ...) is still possible. Not a security hole (an external caller can no longer do anything from their own reference), but not a recursive deep-freeze either.

Using sinmonto with the help of an AI

UTILISATION.md (French) — a contributor who uses sinmonto (rather than modifying its core) should start there, human or AI. It avoids guessing the API — a real problem: several AIs without repo access invented plausible but fictional class names while working on this project. To contribute to the core itself, see CONTRIBUTING.md and AGENTS.md instead.

Architecture and governance

This project is built with strict documentation discipline, in cross-review with several AIs. Currently French-only — these are process documents, not usage documentation:

License

Apache License 2.0 — see LICENSE.

Release files for sinmonto 0.1.0rc6

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

Source distribution (sdist)

Source distribution for sinmonto 0.1.0rc6
File Size Uploaded
sinmonto-0.1.0rc6.tar.gz 92.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sinmonto 0.1.0rc6
File Interpreter ABI Platform
sinmonto-0.1.0rc6-py3-none-any.whl Python 3 none any Details

Total release size: 122.8 kB

Release files / sinmonto-0.1.0rc6.tar.gz

Download URL sinmonto-0.1.0rc6.tar.gz
Size 92.5 kB
Tags Source
SHA-256 checksum
How to use checksums
8d985d82fb9bac2d561812f54538393e308e5f5cc09d2a5b66993a35db6ed1d3
BLAKE2b-256 checksum
How to use checksums
c24157fa0347fc264b5843af7aa5f07f08552b8a5be46ba4a9113951995545c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / sinmonto-0.1.0rc6-py3-none-any.whl

Download URL sinmonto-0.1.0rc6-py3-none-any.whl
Size 30.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
78f1189fa78002cb3e283433068b0aa75a937d955e4fd466f5307e1c0a1d71f8
BLAKE2b-256 checksum
How to use checksums
2099f7e1582a949eff86740a43cabb6d82e89ef2c049450c6406a658b3d21aa1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release history Release notifications | RSS feed

This release

0.1.0rc6 This release

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