precedent-ai 0.2.1__tar.gz

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 (45) hide show
  1. precedent_ai-0.2.1/LICENSE +21 -0
  2. precedent_ai-0.2.1/PKG-INFO +541 -0
  3. precedent_ai-0.2.1/README.md +501 -0
  4. precedent_ai-0.2.1/precedent/__init__.py +12 -0
  5. precedent_ai-0.2.1/precedent/calibrate.py +58 -0
  6. precedent_ai-0.2.1/precedent/cli.py +679 -0
  7. precedent_ai-0.2.1/precedent/config.py +122 -0
  8. precedent_ai-0.2.1/precedent/drift.py +135 -0
  9. precedent_ai-0.2.1/precedent/evals.py +64 -0
  10. precedent_ai-0.2.1/precedent/export.py +151 -0
  11. precedent_ai-0.2.1/precedent/guard.py +219 -0
  12. precedent_ai-0.2.1/precedent/ingest/__init__.py +42 -0
  13. precedent_ai-0.2.1/precedent/ingest/episodes.py +110 -0
  14. precedent_ai-0.2.1/precedent/ingest/jsonl.py +54 -0
  15. precedent_ai-0.2.1/precedent/ingest/redact.py +57 -0
  16. precedent_ai-0.2.1/precedent/ingest/slack.py +71 -0
  17. precedent_ai-0.2.1/precedent/ingest/whatsapp.py +94 -0
  18. precedent_ai-0.2.1/precedent/kb.py +280 -0
  19. precedent_ai-0.2.1/precedent/llm.py +317 -0
  20. precedent_ai-0.2.1/precedent/loop.py +320 -0
  21. precedent_ai-0.2.1/precedent/mcp.py +166 -0
  22. precedent_ai-0.2.1/precedent/models.py +316 -0
  23. precedent_ai-0.2.1/precedent/packs.py +100 -0
  24. precedent_ai-0.2.1/precedent/py.typed +0 -0
  25. precedent_ai-0.2.1/precedent/reasoner.py +316 -0
  26. precedent_ai-0.2.1/precedent/retrieve.py +149 -0
  27. precedent_ai-0.2.1/precedent/runtime.py +163 -0
  28. precedent_ai-0.2.1/precedent/store.py +337 -0
  29. precedent_ai-0.2.1/precedent_ai.egg-info/PKG-INFO +541 -0
  30. precedent_ai-0.2.1/precedent_ai.egg-info/SOURCES.txt +43 -0
  31. precedent_ai-0.2.1/precedent_ai.egg-info/dependency_links.txt +1 -0
  32. precedent_ai-0.2.1/precedent_ai.egg-info/entry_points.txt +2 -0
  33. precedent_ai-0.2.1/precedent_ai.egg-info/requires.txt +10 -0
  34. precedent_ai-0.2.1/precedent_ai.egg-info/top_level.txt +1 -0
  35. precedent_ai-0.2.1/pyproject.toml +78 -0
  36. precedent_ai-0.2.1/setup.cfg +4 -0
  37. precedent_ai-0.2.1/tests/test_guard.py +251 -0
  38. precedent_ai-0.2.1/tests/test_hardening.py +424 -0
  39. precedent_ai-0.2.1/tests/test_ingest.py +83 -0
  40. precedent_ai-0.2.1/tests/test_interfaces.py +100 -0
  41. precedent_ai-0.2.1/tests/test_live.py +67 -0
  42. precedent_ai-0.2.1/tests/test_loop.py +142 -0
  43. precedent_ai-0.2.1/tests/test_providers.py +62 -0
  44. precedent_ai-0.2.1/tests/test_runtime.py +123 -0
  45. precedent_ai-0.2.1/tests/test_security.py +172 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Muhammad Burhan Mujahid
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,541 @@
1
+ Metadata-Version: 2.4
2
+ Name: precedent-ai
3
+ Version: 0.2.1
4
+ Summary: Human-approved operating rules and deterministic action gates for AI agents, mined from how your team already works (WhatsApp, Slack), with drift detection and regression evals.
5
+ Author: Muhammad Burhan Mujahid
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/burhan-mujahid/precedent
8
+ Project-URL: Documentation, https://github.com/burhan-mujahid/precedent#readme
9
+ Project-URL: Changelog, https://github.com/burhan-mujahid/precedent/blob/main/CHANGELOG.md
10
+ Project-URL: Issues, https://github.com/burhan-mujahid/precedent/issues
11
+ Project-URL: Security, https://github.com/burhan-mujahid/precedent/blob/main/SECURITY.md
12
+ Keywords: ai-agents,llm,llm-agents,agentic-ai,guardrails,human-in-the-loop,knowledge-base,sop,business-rules,policy,governance,mcp,model-context-protocol,agent-skills,context-engineering,prompt-injection,evals,whatsapp,slack,langgraph,claude,gemini
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Typing :: Typed
21
+ Classifier: Environment :: Console
22
+ Classifier: Intended Audience :: Information Technology
23
+ Classifier: Operating System :: OS Independent
24
+ Classifier: Topic :: Office/Business
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Topic :: Communications :: Chat
27
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
28
+ Requires-Python: >=3.10
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ Requires-Dist: PyYAML>=6.0
32
+ Provides-Extra: certs
33
+ Requires-Dist: certifi; extra == "certs"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=8; extra == "dev"
36
+ Requires-Dist: ruff>=0.6; extra == "dev"
37
+ Requires-Dist: mypy>=1.11; extra == "dev"
38
+ Requires-Dist: types-PyYAML; extra == "dev"
39
+ Dynamic: license-file
40
+
41
+ <p align="center">
42
+ <img src="docs/assets/banner.svg" alt="Precedent: turn how your team already works into rules your AI agents follow, and keep those rules true." width="100%">
43
+ </p>
44
+
45
+ <p align="center">
46
+ <a href="https://github.com/burhan-mujahid/precedent/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/burhan-mujahid/precedent/actions/workflows/ci.yml/badge.svg"></a>
47
+ <img alt="Python 3.10 to 3.13" src="https://img.shields.io/badge/python-3.10%20%E2%80%93%203.13-3776ab?logo=python&logoColor=white">
48
+ <img alt="One runtime dependency" src="https://img.shields.io/badge/dependencies-1%20(PyYAML)-2ea44f">
49
+ <img alt="Typed" src="https://img.shields.io/badge/typed-mypy%20strict-informational">
50
+ <a href="https://modelcontextprotocol.io"><img alt="MCP server" src="https://img.shields.io/badge/MCP-server-8a63d2"></a>
51
+ <a href="SECURITY.md"><img alt="Security policy" src="https://img.shields.io/badge/security-policy-critical"></a>
52
+ <a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-yellow"></a>
53
+ </p>
54
+
55
+ <p align="center">
56
+ <a href="https://burhan-mujahid.github.io/precedent/"><b>Website</b></a> ·
57
+ <a href="#-quick-start">Quick start</a> ·
58
+ <a href="#-how-it-works">How it works</a> ·
59
+ <a href="#-plug-it-into-your-agent">Integrations</a> ·
60
+ <a href="#-results-with-real-models">Live results</a> ·
61
+ <a href="#-safety-by-design">Safety</a> ·
62
+ <a href="docs/DESIGN.md">Design</a> ·
63
+ <a href="docs/DEMO.md">Demo guide</a>
64
+ </p>
65
+
66
+ ---
67
+
68
+ > **Your AI agent doesn't know how *your* business makes decisions.**
69
+ > When to refund. Who approves a discount. What to say about allergies. What *never* to do.
70
+ > Precedent learns those rules from the conversations your team is already having, has a person
71
+ > approve every one, and puts a deterministic gate in front of every action your agents take.
72
+
73
+ <p align="center">
74
+ <img src="docs/assets/demo.svg" alt="Terminal: three situations get three outcomes - ACT on a late delivery, NEEDS APPROVAL on a damaged cake refund, ESCALATE on a supplier bank-details change" width="88%">
75
+ </p>
76
+
77
+ <p align="center"><sub>Real output from <code>bash examples/demo.sh</code> (offline, no API key). Long lines shortened to fit.</sub></p>
78
+
79
+ > [!NOTE]
80
+ > **New in 0.2: halting a run that's already going.** Approving the start of an action isn't enough. Agents now re-check before every step and after Done, and Precedent **halts** the run when a step goes beyond what was approved, a rule changes mid-run, or a decision is reused or stale. Each halt leaves a record of which rule, which run, and who halted it. [How it works](#-the-execution-gate-halting-a-run-thats-already-going) · prompted by [Gary Ko](https://www.linkedin.com/in/gary-ko-976863210/).
81
+
82
+ ## 🧭 The problem
83
+
84
+ Every agent deployment hits the same wall: the agent is capable, but it doesn't know the house rules.
85
+
86
+ | The usual fix | Why it breaks |
87
+ |---|---|
88
+ | 📝 **"Write a knowledge base"** | It describes how the founder *thinks* the business runs. It takes weeks, and it's stale weeks later. |
89
+ | 🔌 **"Let the agent learn from the tools"** | CRMs and inboxes show *what* happened, never *why*, and can't tell a precedent from a mistake. |
90
+ | 🤖 **"Let the agent figure it out"** | Models are poorly calibrated about when *not* to act. That is the one decision you can't hand them. |
91
+
92
+ The real process lives in the moments when someone asks a colleague ***"how do we handle X?"*** and gets an answer. Precedent treats those moments as the source, a person as the authority, and the agent's uncertainty as the signal for what to learn next.
93
+
94
+ ## ✨ What you get
95
+
96
+ <table>
97
+ <tr>
98
+ <td width="50%" valign="top">
99
+
100
+ ### ⛏️ Rules mined, not written
101
+ Point it at a WhatsApp export, a Slack export or JSONL. It finds the Q&A threads and drafts one rule per answer, **each linked to the exact messages it came from**. Fixing a draft takes minutes; writing from a blank page takes weeks.
102
+
103
+ </td>
104
+ <td width="50%" valign="top">
105
+
106
+ ### ✅ A person approves everything
107
+ There is exactly **one door** into the rulebook: an approved proposal. Models draft; people decide. Every change is versioned, attributed and replayable.
108
+
109
+ </td>
110
+ </tr>
111
+ <tr>
112
+ <td valign="top">
113
+
114
+ ### 🚦 A gate before every action, and while it runs
115
+ Agents call `consult_rulebook` before acting. **Five deterministic gates outside the model** return one of three outcomes: act, ask a human to approve, or escalate. Then `check_action` re-checks **before each step and after Done**, and **halts** the run if the step goes beyond what was approved.
116
+
117
+ </td>
118
+ <td valign="top">
119
+
120
+ ### 🔁 Every escalation becomes a rule
121
+ Gaps are clustered into a weekly digest. A human answers once; the answer becomes a draft rule **and** a regression test, so the same question never escalates twice.
122
+
123
+ </td>
124
+ </tr>
125
+ <tr>
126
+ <td valign="top">
127
+
128
+ ### 📉 Drift caught early
129
+ Rules that are overridden, unused, past their review date or duplicated get flagged, with a drafted fix, before they cause damage.
130
+
131
+ </td>
132
+ <td valign="top">
133
+
134
+ ### 🗂️ Plain files, in git
135
+ YAML rules, JSONL logs. No database, no vector store, no server. Review rule changes in pull requests and gate merges with `precedent eval` in CI.
136
+
137
+ </td>
138
+ </tr>
139
+ </table>
140
+
141
+ ## 🚀 Quick start
142
+
143
+ No API key needed. Python 3.10+.
144
+
145
+ ```bash
146
+ pip install "git+https://github.com/burhan-mujahid/precedent@v0.2.1"
147
+ ```
148
+
149
+ **See the whole loop in about 20 seconds** on a sample bakery WhatsApp group:
150
+
151
+ ```bash
152
+ git clone https://github.com/burhan-mujahid/precedent && cd precedent
153
+ pip install -e .
154
+ bash examples/demo.sh # offline
155
+ bash examples/demo.sh --live # with GEMINI_API_KEY / MOONSHOT_API_KEY / ANTHROPIC_API_KEY / OPENAI_API_KEY set
156
+ bash examples/demo.sh --pause # step by step, for presenting
157
+ ```
158
+
159
+ **Or on your own team's chat:**
160
+
161
+ ```bash
162
+ precedent init my-rulebook --pack support # packs: general support ops sales finance hr oncall
163
+ cd my-rulebook
164
+ precedent ingest ~/Downloads/"WhatsApp Chat with Ops.txt"
165
+ precedent review # approve / edit / reject drafts, with the source messages shown
166
+ precedent ask "Customer says the birthday cake arrived damaged"
167
+ ```
168
+
169
+ ```text
170
+ [NEEDS APPROVAL] D-20261109T1154-1eadcb90d0 confidence 0.88 risk high
171
+ answer : Ask for a photo first. If the damage is clear we remake it for free or refund in full, their choice. Don't argue about it, it's never worth the review.
172
+ rules : R-003 v1
173
+ why : reasoner: matched R-003 on: arrived, cake, customer, damaged
174
+ why : high-risk action: a human must approve
175
+ ```
176
+
177
+ > [!TIP]
178
+ > WhatsApp: open the chat → ⋮ → More → **Export chat** → Without media. Android and iOS formats in all common locales work, including Urdu and other right-to-left chats.
179
+
180
+ ## 🔍 How it works
181
+
182
+ ```mermaid
183
+ flowchart LR
184
+ subgraph sources ["Where the knowledge already is"]
185
+ A["💬 WhatsApp / Slack / JSONL"]
186
+ B["📝 Session notes"]
187
+ C["✋ Human overrides"]
188
+ end
189
+
190
+ A -- "mine Q&A threads" --> P["📋 Proposals<br/>(drafts with evidence)"]
191
+ B -- "precedent wrap" --> P
192
+ C -- "precedent feedback" --> P
193
+ G["🕳️ Gaps"] -- "a person answers once" --> P
194
+
195
+ P -- "a person approves" --> R[("📚 Rulebook<br/>versioned YAML, in git")]
196
+
197
+ AG["🤖 Agent"] -- "consult_rulebook" --> GATES{"🚦 5 gates"}
198
+ R --> GATES
199
+
200
+ GATES --> ACT["🟢 ACT<br/>cites rule versions"]
201
+ GATES --> APP["🟡 NEEDS APPROVAL<br/>high-risk: a human signs off"]
202
+ GATES --> ESC["🔴 ESCALATE<br/>logged as a gap"]
203
+ ESC --> G
204
+
205
+ ACT -- "each step + after Done" --> CHK{"🛑 check_action"}
206
+ APP -- "after sign-off" --> CHK
207
+ CHK --> CONT["continue"]
208
+ CHK --> HALT["HALT<br/>halt record · run stopped"]
209
+ HALT -- "a person answers once" --> G
210
+ ```
211
+
212
+ ### The five gates
213
+
214
+ Every `consult_rulebook` call goes through these, in order. The first failure wins, and **every failure opens a gap** for a human to answer.
215
+
216
+ | # | Gate | Escalates when | Why it's outside the model |
217
+ |:-:|---|---|---|
218
+ | 1 | **Coverage** | no approved rule mentions enough of the situation (IDF-weighted) | the model never sees an off-topic rulebook it could stretch to fit |
219
+ | 2 | **Judgement** | the model says the rules don't settle it, or two rules conflict | an honest "I'm not sure" is a feature, not a failure |
220
+ | 3 | **Grounding** | the model cites a rule it wasn't given, or its answer uses a number found in neither the rule nor the situation | invented rules and prompt-injected amounts never reach the agent |
221
+ | 4 | **Confidence** | confidence is below the threshold for the rule's risk tier | thresholds are yours to set, and `precedent calibrate` sets them from evidence |
222
+ | 5 | **Risk** | the rule is high-risk (money, legal, health, personal data, irreversible) | → **NEEDS APPROVAL**: the agent prepares, a person signs off |
223
+
224
+ ### 🛑 The execution gate: halting a run that's already going
225
+
226
+ The five gates decide whether an action may **start**. They can't stop one that's already running, and they never see what actually happened. So there is a second gate. While acting, the agent calls `check_action` (or `kb.check`, or `precedent check`) with its decision id before each step that changes something, and once more after Done. Every call returns **continue** or **halt**. None of the checks calls a model.
227
+
228
+ | # | Check | Halts when | Reason |
229
+ |:-:|---|---|---|
230
+ | 1 | **Known** | the agent cites a decision the rulebook never issued | `unknown_decision` |
231
+ | 2 | **Sticky** | this decision, or any decision in the same run, was halted before | `halted` |
232
+ | 3 | **Single use** | the decision already passed its after-Done check | `already_done` |
233
+ | 4 | **Authorised** | the decision was ESCALATE, or NEEDS APPROVAL with no sign-off recorded | `not_authorized`, `no_sign_off` |
234
+ | 5 | **Fresh** | the decision, or its sign-off, is older than `runtime.decision_ttl_minutes` (60) | `expired` |
235
+ | 6 | **Rules unchanged** | a cited rule was retired, re-versioned or edited since the decision | `rule_changed` |
236
+ | 7 | **In scope** | the step uses an amount, or a high-risk word (bank, refund, delete…), that's in neither the decision nor its rules | `out_of_scope` |
237
+
238
+ Every halt is a **halt record** in `logs/events.jsonl`: the decision, the run, the cited rule versions, the stage (`during` or `after`), the reason, and **who created it**. That's `precedent:check` for the checks above, or the person or probe that called `precedent halt`. Halts are final. Once a run is halted, every later check in it halts too, so an agent can't retry its way past one. Out-of-scope and external halts also open a gap, so a person's answer becomes a rule fix and a regression test.
239
+
240
+ ```text
241
+ > check: Refunding 5000 to the customer's card
242
+ [HALT] H-20261109T1428-e1f2fb8921 D-20261109T1428-2ed8117143 during run run-cake
243
+ why : out_of_scope: the step uses 5000, which the decision did not authorise
244
+ gap : G-20261109T1428-80363d1ecc
245
+
246
+ > check: Refunded the cake in full
247
+ [HALT] H-20261109T1428-e1f2fb8921 D-20261109T1428-2ed8117143 after run run-cake
248
+ why : halted: H-20261109T1428-e1f2fb8921 (out_of_scope) already stopped this decision; a person starts a new run
249
+ ```
250
+
251
+ **Bring your own monitor.** If something outside Precedent halts a run, such as an execution monitor or a person watching, it calls `report_outcome` with `verdict: "halt"` (or `precedent halt D-… "why" --fix "what should happen"`). Precedent records the halt, stops the rest of the run, opens a gap and drafts the rule fix. Approving the fix adds a regression case.
252
+
253
+ ## 🔌 Plug it into your agent
254
+
255
+ Precedent isn't an agent framework or a memory store. It's the **decision layer underneath** whatever you already use.
256
+
257
+ <details open>
258
+ <summary><b>🐍 Python: any framework</b></summary>
259
+
260
+ ```python
261
+ from precedent import Precedent
262
+
263
+ kb = Precedent.open("./my-rulebook")
264
+ d = kb.resolve("Customer wants a refund, order arrived 3 days late", context={"order_value": 4500},
265
+ run_id="run-42")
266
+
267
+ if d.outcome == "answer": # act; d.cited == [{"id": "R-004", "version": 2, "sha": "f1733b9bf477"}]
268
+ ...
269
+ elif d.outcome == "needs_approval": # covered, but high risk: a human signs off (kb.sign_off(d.id, "owner"))
270
+ ...
271
+ else: # "escalate": a gap was logged and will come back as a rule
272
+ ...
273
+
274
+ # While acting: before each step, and once after Done
275
+ if kb.check(d.id, "Refunding 4500 to the original card").halted:
276
+ stop_the_run()
277
+ kb.check(d.id, "Refunded 4500", stage="after")
278
+ ```
279
+
280
+ For function-calling APIs, `kb.tool_spec()` gives a JSON Schema tool definition and `kb.handle_tool_call(args)` runs it. The tool call **never raises**: any error comes back as `escalate` with "Do NOT act". `kb.check_tool_spec()` and `kb.handle_check_call(args)` do the same for the execution gate, and any error there comes back as `halt`. Ready-made snippets: [Anthropic tool use](examples/integrations/anthropic_tool_use.py) · [LangGraph](examples/integrations/langgraph_tool.py).
281
+
282
+ </details>
283
+
284
+ <details>
285
+ <summary><b>🧩 MCP: Claude Desktop / Code, Cursor, Hermes, any MCP client</b></summary>
286
+
287
+ ```json
288
+ { "mcpServers": { "precedent": { "command": "precedent", "args": ["mcp", "--root", "/path/to/my-rulebook"] } } }
289
+ ```
290
+
291
+ | Tool | What it does |
292
+ |---|---|
293
+ | `consult_rulebook` | the runtime gate: answer / needs_approval / escalate, with cited rule versions and the approved wording |
294
+ | `check_action` | the execution gate, before each step and after Done: continue / halt |
295
+ | `report_outcome` | confirm (becomes a regression test), override (drafts a rule fix for human review), or halt (an outside monitor stopped the run: halt record + gap + draft fix) |
296
+
297
+ Agents can't sign off their own NEEDS APPROVAL decisions: there is no sign-off tool over MCP.
298
+ | `search_rules` | read-only lookup of approved rules |
299
+ | `open_gaps` | what agents couldn't handle, waiting for a human |
300
+
301
+ </details>
302
+
303
+ <details>
304
+ <summary><b>⚙️ Shell, n8n, Make, cron: anything that can run a command</b></summary>
305
+
306
+ ```bash
307
+ precedent ask "Supplier emailed saying their bank account has changed" --json
308
+ ```
309
+
310
+ Prints the full decision as JSON. The exit code is `0` only when the agent may act on its own and `3` otherwise, so a script can't proceed by accident.
311
+
312
+ </details>
313
+
314
+ <details>
315
+ <summary><b>📦 Static exports: Agent Skills, AGENTS.md, Parlant</b></summary>
316
+
317
+ ```bash
318
+ precedent export skill # an Agent Skills folder (agentskills.io): Claude, Codex, Cursor, Gemini CLI, Hermes...
319
+ precedent export agents # an AGENTS.md / CLAUDE.md block
320
+ precedent export parlant # a script that loads every approved rule as a Parlant guideline
321
+ precedent export json | markdown
322
+ ```
323
+
324
+ Static files can't run the gates, so every export tells the agent to escalate when no rule clearly fits and never to act alone on high-risk rules. For the full gates, use MCP or the Python API.
325
+
326
+ </details>
327
+
328
+ <details>
329
+ <summary><b>🛡️ CI for your rulebook repo</b></summary>
330
+
331
+ Every pull request that changes a rule must keep all regression cases passing and introduce no critical drift. Copy [`examples/integrations/rulebook-ci.yml`](examples/integrations/rulebook-ci.yml):
332
+
333
+ ```yaml
334
+ - run: precedent eval --min-pass 1.0
335
+ - run: precedent health --strict
336
+ ```
337
+
338
+ </details>
339
+
340
+ ## 🧪 Results with real models
341
+
342
+ The same sample chat and five situations, run through Gemini, Kimi and the offline baseline. Nothing below is edited; full transcripts and token counts are in [docs/LIVE-RESULTS.md](docs/LIVE-RESULTS.md).
343
+
344
+ | Situation | Gemini `gemini-3.8-flash` | Kimi `kimi-k3` | Offline baseline |
345
+ |---|:-:|:-:|:-:|
346
+ | Brownies safe for a nut allergy? | 🟡 approval | 🟡 approval | 🟡 approval |
347
+ | Delivery 1.5 hours late | 🟢 act | 🟡 approval¹ | 🟢 act |
348
+ | Cancel an order for the day after tomorrow | 🟡 approval | 🟡 approval | 🔴 escalate² |
349
+ | Birthday cake arrived damaged | 🟡 approval | 🟡 approval | 🟡 approval |
350
+ | Supplier says their bank details changed | 🔴 escalate | 🔴 escalate | 🔴 escalate |
351
+
352
+ <sub>¹ Kimi rated "message the customer and add a free cookie box" as high risk when drafting; a reviewer settles that once, at approval. ² The keyword baseline matched only "cancel" and "order" (confidence 0.52, under the 0.55 approval floor), so a person decides. The models worked out that "the day after tomorrow" is more than 24 hours away.</sub>
353
+
354
+ Ingest of the 9 Q&A threads: **Gemini 9.4 s**, **Kimi 57 s**, offline 0.3 s. Each decision is one model call (none when the coverage gate escalates first).
355
+
356
+ ## 🗓️ The weekly loop: about 30 minutes for a founder
357
+
358
+ ```bash
359
+ precedent cycle
360
+ ```
361
+
362
+ One digest, in order of leverage:
363
+
364
+ 1. **Answer these.** Escalations clustered by similarity (*"7 cases about supplier bank details"*). One answer per cluster becomes a draft rule.
365
+ 2. **Review these drafts.** From chat mining, overrides, session wraps and drift checks.
366
+ 3. **Rulebook health.** Stale, overridden, unused and duplicate rules, and the escalation rate.
367
+ 4. **Failing regression cases**, if a change broke something that used to work.
368
+
369
+ <details>
370
+ <summary><b>All commands</b></summary>
371
+
372
+ | Command | What it does |
373
+ |---|---|
374
+ | `precedent init DIR --pack support` | create a rulebook (`precedent packs` lists packs) |
375
+ | `precedent ingest FILE` | mine WhatsApp / Slack / JSONL into rule drafts |
376
+ | `precedent review` | the only way into the rulebook: walk the drafts and approve / edit / reject each (`list`, `show`, `approve`, `reject` for scripts) |
377
+ | `precedent ask "…" [--run ID] [--json]` | the runtime gate |
378
+ | `precedent signoff D-…` | a person approves a NEEDS APPROVAL decision so it may run |
379
+ | `precedent check D-… "step" [--after] [--json]` | the execution gate: continue (exit 0) or halt (exit 3) |
380
+ | `precedent halt D-… "why" [--fix "…"]` | a person or monitor halts a running action; `--fix` drafts a rule fix |
381
+ | `precedent halts [--run ID]` | every halt: decision, run, rule versions, stage, reason, who created it |
382
+ | `precedent gaps answer G-… "what we do"` | turn an escalation (and similar open ones) into a draft rule |
383
+ | `precedent feedback D-… --confirm` | a correct decision becomes a regression case |
384
+ | `precedent feedback D-… --override "…"` | a wrong decision opens a gap and drafts a fix to the rule it cited |
385
+ | `precedent wrap notes.md` | end-of-session wrap: decisions in your notes become proposals |
386
+ | `precedent cycle` | the weekly digest |
387
+ | `precedent health --propose --strict` | drift report; drafts deprecate / reconfirm proposals; non-zero exit in CI |
388
+ | `precedent eval --min-pass 1.0` | replay every regression case against the current rulebook |
389
+ | `precedent calibrate --target 0.95` | recommend confidence thresholds from evidence |
390
+ | `precedent replay --as-of 2026-10-01 "…"` | what would the rulebook have said on that date? |
391
+ | `precedent rules [list \| show \| history \| reconfirm]` | browse rules and their version history |
392
+ | `precedent export skill \| agents \| parlant \| json \| markdown` | compile for other tools |
393
+ | `precedent doctor` | check the model setup end to end |
394
+ | `precedent mcp` | run the MCP server on stdio |
395
+
396
+ Every command takes `--root DIR` (or `PRECEDENT_ROOT`). Set `PRECEDENT_USER` so approvals are attributed.
397
+
398
+ </details>
399
+
400
+ ## 🤖 Using a model
401
+
402
+ The offline reasoner is a deterministic keyword baseline for tests and demos. For real use, pick a provider in `precedent.yaml`, export its key, and run `precedent doctor`. It lists the models your key can use, makes a test call, and runs one real decision.
403
+
404
+ | Provider | `provider.kind` | Key env var | Notes |
405
+ |---|---|---|---|
406
+ | Google Gemini | `gemini` | `GEMINI_API_KEY` | Gemini 3 models reason first, so the preset uses `max_tokens: 8000`, `reasoning_effort: low` |
407
+ | Kimi (Moonshot) | `kimi` | `MOONSHOT_API_KEY` | key from platform.moonshot.ai (a Kimi Coding key won't work) |
408
+ | Anthropic | `anthropic` | `ANTHROPIC_API_KEY` | native Messages API |
409
+ | OpenAI | `openai` | `OPENAI_API_KEY` | |
410
+ | OpenRouter / Groq | `openrouter` / `groq` | `OPENROUTER_API_KEY` / `GROQ_API_KEY` | |
411
+ | Ollama (local) | `ollama` | none | nothing leaves your machine |
412
+ | Anything OpenAI-compatible | `openai` + `base_url` | `api_key_env` | vLLM, LM Studio, Azure, Together… |
413
+
414
+ ```yaml
415
+ provider:
416
+ kind: gemini
417
+ model: <a current Flash model: see `precedent doctor --filter flash`>
418
+ ```
419
+
420
+ Model names change often, so Precedent never hard-codes one. If a model rejects `temperature` or `reasoning_effort`, the client drops it and retries. Mining runs `ingest.workers` (default 4) model calls in parallel and prints the calls and tokens it used.
421
+
422
+ > [!NOTE]
423
+ > On python.org builds of Python for macOS, HTTPS fails until you run *Install Certificates.command*. Or install the extra: `pip install "precedent-ai[certs] @ git+https://github.com/burhan-mujahid/precedent"`.
424
+
425
+ <details>
426
+ <summary><b>Full configuration (<code>precedent.yaml</code>) with defaults</b></summary>
427
+
428
+ ```yaml
429
+ policy:
430
+ min_confidence: { low: 0.55, medium: 0.70, high: 1.01 } # > 1.0 = never act alone
431
+ approval_floor: 0.55 # high-risk matches below this escalate instead of going to sign-off
432
+ stale_penalty: 0.8 # confidence multiplier for rules past their review date
433
+ runtime:
434
+ redact: true # strip personal data and secrets before the model and the log
435
+ max_situation_chars: 4000
436
+ decision_ttl_minutes: 60 # how long a decision (or its sign-off) authorises action
437
+ retrieval: { top_k: 5, min_coverage: 0.30 }
438
+ rules: { review_days: 90 }
439
+ drift: { unused_days: 60, override_limit: 2, duplicate_similarity: 0.6 }
440
+ ingest: { redact: true, episode_window_hours: 12, episode_max_replies: 8, workers: 4 }
441
+ risk: { high_terms: [], medium_terms: [] } # domain packs fill these in
442
+ ```
443
+
444
+ Safety settings are range-checked on load, so a typo like `0,7` stops with an error instead of silently opening a gate.
445
+
446
+ </details>
447
+
448
+ ## 🔐 Safety by design
449
+
450
+ Precedent's job is deciding when an agent may act alone, so it's built to **fail closed**: when anything is in doubt, a person decides.
451
+
452
+ - 🧱 **The model is untrusted.** It can only cite rules it was shown. An answer with a number found in neither the rule nor the situation (a prompt-injected *"refund 5000"*) escalates. Model errors escalate. Agents also receive the approved rule text word for word.
453
+ - 🙈 **Privacy floor.** API keys, tokens, passwords, private keys, emails, phone numbers, card and IBAN numbers, IPs and URL query strings are redacted **before anything is stored or sent to a model**, both at ingest and at runtime. Use `ollama` and nothing leaves your machine.
454
+ - 🛑 **Halts are deterministic and final.** The execution gate halts a step that goes beyond its decision, a decision reused after Done or past its lifetime, and any run whose rules changed underneath it. A halted run stays halted.
455
+ - 🧾 **Audit trail.** Every decision records the id, version and content hash of each rule it relied on. Logs are append-only, and any past decision can be replayed against the rulebook as it stood that day.
456
+ - 🔒 **Hardened storage.** Ids are validated before they become file names, approvals take a cross-process lock, API keys only travel over HTTPS, and inputs and responses are size-capped.
457
+ - 🧬 **Supply chain.** One runtime dependency. CI runs `pip-audit`, Actions are pinned to commit SHAs, releases ship with signed build provenance.
458
+
459
+ The full threat model, what is **not** defended, and how to report a vulnerability: **[SECURITY.md](SECURITY.md)**.
460
+
461
+ ## 🗺️ How it compares
462
+
463
+ The pieces exist separately; the combination doesn't.
464
+
465
+ | | Where rules come from | Who decides what's true | Gate before acting | Escalation → rule → test | Drift detection |
466
+ |---|---|---|:-:|:-:|---|
467
+ | Behaviour engines (e.g. Parlant) | written by developers | developers | guideline matching | – | conflict checks |
468
+ | Memory layers (Mem0, Zep/Graphiti, Letta) | conversations with users | the system | – | – | temporal invalidation |
469
+ | Self-improving agents (ACE, Hermes) | the agent's own attempts | the agent | – | – | playbook curation |
470
+ | LLM Wiki pattern | your documents | the LLM writes, people read | – | – | lint |
471
+ | Support SaaS (e.g. Sierra) | ticket resolutions | reviewers | inside a closed product | support only | – |
472
+ | **Precedent** | **your team's own Q&A + escalations** | **a person approves every change** | **✅ 5 deterministic gates, plus halt during and after** | **✅ automatic** | **stale · overridden · unused · duplicate · coverage** |
473
+
474
+ Precedent can export to Parlant, so it complements rather than competes. Details and related research: [docs/DESIGN.md §9](docs/DESIGN.md#9-related-work-and-how-precedent-differs).
475
+
476
+ ## 🏗️ Project layout
477
+
478
+ ```text
479
+ precedent/
480
+ ingest/ WhatsApp, Slack and JSONL parsers, redaction, Q&A episode detection
481
+ retrieve.py dependency-free BM25 + coverage scoring
482
+ reasoner.py extract / decide / wrap: model-backed and deterministic offline versions
483
+ runtime.py the five gates
484
+ guard.py the execution gate: check during and after an action, halt records, sign-off
485
+ loop.py mining, review, gaps, feedback, session wrap
486
+ drift.py health checks evals.py regression replay
487
+ calibrate.py evidence-based thresholds export.py Skill / AGENTS.md / Parlant / JSON
488
+ mcp.py stdio MCP server packs.py domain packs cli.py
489
+ skill/precedent/ use Precedent from Claude or any Agent Skills client
490
+ examples/ sample WhatsApp export, end-to-end demo, integration snippets
491
+ docs/ DESIGN.md · LIVE-RESULTS.md · DEMO.md
492
+ ```
493
+
494
+ ## 📍 Status and limits
495
+
496
+ **v0.2.1, alpha.** 139 tests, run by CI on Linux (Python 3.10 to 3.13), macOS and Windows; ruff and strict mypy clean. Known limits:
497
+
498
+ - **The offline reasoner is lexical.** It's a deterministic baseline for tests and demos; real deployments should use a model for `extract` and `decide`.
499
+ - **Retrieval is BM25.** It suits rulebooks of tens to low thousands of rules written in the team's own words. Stemming is English-only; Urdu and Roman Urdu chats parse fine, but rules match on exact words. An embedding retriever can drop in behind the same one-method interface.
500
+ - **Calibration needs evidence.** Thresholds are only recommended once a risk tier has enough regression cases.
501
+ - **No built-in authentication.** Whoever can write the rulebook folder can approve rules. Keep it in a git repo with reviewed pull requests ([SECURITY.md](SECURITY.md)).
502
+
503
+ **Roadmap:** direct connectors (Slack, WhatsApp Business, Gmail, helpdesks) · an optional embedding retriever · agreement across several model samples as a confidence signal · a static review UI · multi-reviewer quorum for high-risk rules.
504
+
505
+ ## 🤝 Contributing
506
+
507
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md). Good first issues: more chat formats (Teams, Discord, Telegram, email), domain packs, an embedding retriever, a review UI.
508
+
509
+ ```bash
510
+ pip install -e ".[dev]" && pytest -q && ruff check . && mypy
511
+ ```
512
+
513
+ Live provider tests (skipped without keys): `GEMINI_API_KEY=… MOONSHOT_API_KEY=… pytest -m live`.
514
+
515
+ ## 🙏 Acknowledgements
516
+
517
+ The execution gate (`check_action`, halt records, sign-off, `report_outcome` halts) exists because [Gary Ko](https://www.linkedin.com/in/gary-ko-976863210/) pushed on the first release. He pointed out that act, needs approval and escalate only gate the *next* action: none of them is a HALT. He asked which rule, in which run, actually halted an in-progress action, and who created the artifact for that halt. In v0.1.0 the honest answer was "nothing does". Now every halt has a record that answers all three. Thank you, Gary.
518
+
519
+ ## 📚 Citing
520
+
521
+ If Precedent helps your research or product, please cite it. GitHub's **"Cite this repository"** button (from [CITATION.cff](CITATION.cff)) gives APA and BibTeX:
522
+
523
+ ```bibtex
524
+ @software{mujahid_precedent_2026,
525
+ author = {Mujahid, Muhammad Burhan},
526
+ title = {Precedent: human-approved operating rules and deterministic action gates for AI agents},
527
+ year = {2026},
528
+ version = {0.2.1},
529
+ url = {https://github.com/burhan-mujahid/precedent}
530
+ }
531
+ ```
532
+
533
+ ## 📬 Contact
534
+
535
+ **Muhammad Burhan Mujahid**: [burhanmujahid552@gmail.com](mailto:burhanmujahid552@gmail.com) · [@burhan-mujahid](https://github.com/burhan-mujahid)
536
+
537
+ Questions and ideas: [Discussions](https://github.com/burhan-mujahid/precedent/discussions). Bugs: [Issues](https://github.com/burhan-mujahid/precedent/issues). Security reports: [privately](SECURITY.md), please.
538
+
539
+ ## 📄 License
540
+
541
+ [MIT](LICENSE) © Muhammad Burhan Mujahid