Reversible prompt-context restoration with audit trails
Project description
jabr (الجبر) — Reversible Prompt-Context Restoration
Part of the Mizan stack — the Arabic-first reliability scale for AI agents.
Reversible. Audited. Deterministic. The Restore operation as a standalone primitive for AI agent pipelines.
What it does
Takes an under-specified prompt and a context, returns the prompt with implicit terms made explicit, plus a full audit trail. The original prompt is always recoverable from the output.
from datetime import datetime, timezone
from jabr import restore, unrestore, RestorationContext
ctx = RestorationContext(
now=datetime(2026, 4, 25, 14, 30, tzinfo=timezone.utc),
referents={"her": "Alice"},
defaults={"channel": "#engineering"},
)
result = restore("tell her tomorrow to post in <channel>", ctx)
print(result.output)
# tell her[[jabr:abc123:pronoun:Alice]] tomorrow[[jabr:def456:date:2026-04-26]]
# to post in <channel>[[jabr:ghi789:default:#engineering]]
# Reversible
assert unrestore(result.output, result.trace) == "tell her tomorrow to post in <channel>"
Design properties
The library guarantees four properties:
-
Reversibility.
unrestore(restore(p, ctx).output, trace) == pfor any(p, ctx). Verified by 31 tests including round-trip property tests. -
Tag-only insertion. Every added term is bracketed with
[[jabr:<id>:<kind>:<value>]]. Original characters are never modified, only interspersed with tags. -
Trace integrity. Every tag in the output has a corresponding entry in the trace.
unrestore()with a wrong trace raisesRestorationError. -
Determinism. Given the same
(prompt, context, resolvers), the output is byte-identical across runs.
Why this matters
In modern LLM agent pipelines, prompts are routinely rewritten, summarized, and "expanded" silently. The original cannot be recovered. Debugging is dream interpretation.
jabr makes that explicit. Every restoration is tagged, every tag is in the trace, every trace entry has provenance (source, confidence, original span). When the agent does something unexpected, you can replay the input through the same restoration and see exactly what was added.
Built-in resolvers
| Resolver | What it handles | Confidence |
|---|---|---|
DateTimeResolver |
today, tomorrow, yesterday, this morning/afternoon/evening, next/last , now | 0.85–1.0 |
PronounResolver |
he/she/it/they/etc., resolved against explicit context.referents |
1.0 |
DefaultResolver |
<placeholder> and @PLACEHOLDER resolved against context.defaults |
1.0 |
QuantityResolver |
a few, several, many, a couple, lots of → numeric ranges | 0.5–0.85 |
Add your own resolver by implementing the Resolver Protocol:
from jabr import Resolver, RestorationEntry, RestorationContext
class MyResolver:
name = "my_domain"
def find(self, input: str, context: RestorationContext) -> list[RestorationEntry]:
...
CLI
# Restore
jabr restore --prompt "tell her tomorrow" --context ctx.json
# Restore with system clock
jabr restore --prompt "ping me tomorrow" --now system
# Round-trip verify
jabr roundtrip --prompt "tomorrow morning" --now system
# Unrestore (recover original from output)
jabr unrestore --output "[output with tags]"
Where ctx.json looks like:
{
"now": "2026-04-25T14:30:00+00:00",
"timezone": "UTC",
"referents": {"her": "Alice"},
"defaults": {"channel": "#engineering"}
}
Install
pip install -e .
Tests
pytest tests/ -v
31/31 pass on Python 3.10+. No runtime dependencies.
What it is not
- Not an LLM-powered prompt rewriter. There is no model in
jabr. All resolvers are deterministic regex/dict-based functions. - Not a coreference-resolution library. Pronoun restoration only happens when an explicit referent map is supplied. There is no inference from prior conversation.
- Not a guesser. If a term cannot be resolved (e.g., "tomorrow" without a
now),jabrdoes nothing. Silence is the correct behavior on missing context.
Structure
jabr/
├── jabr/
│ ├── __init__.py Public API
│ ├── core.py restore() + unrestore() + dataclasses
│ ├── resolvers.py 4 built-in resolvers
│ └── cli.py command-line interface
├── tests/
│ ├── test_core.py core property tests
│ └── test_resolvers.py resolver-specific tests
├── pyproject.toml
├── README.md
├── LICENSE
└── FAILURES.md
Failure modes
See FAILURES.md for the honest list of what this library does not handle.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jabr-0.1.0.tar.gz.
File metadata
- Download URL: jabr-0.1.0.tar.gz
- Upload date:
- Size: 18.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90657697d890d9fd42b66e78f89601569f90893e3ddf747f946fa26f42e83e1d
|
|
| MD5 |
62e1e1a06bfac2c03e179b4283392e24
|
|
| BLAKE2b-256 |
13c54462b5c31b62e0d9ea7773273c4ebc381273f98ccb63643663a394756b57
|
Provenance
The following attestation bundles were made for jabr-0.1.0.tar.gz:
Publisher:
release.yml on Moshe-ship/jabr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jabr-0.1.0.tar.gz -
Subject digest:
90657697d890d9fd42b66e78f89601569f90893e3ddf747f946fa26f42e83e1d - Sigstore transparency entry: 1688006419
- Sigstore integration time:
-
Permalink:
Moshe-ship/jabr@9d2b6233ad9ba997136a22ca70b32bf5c4e4f8f9 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Moshe-ship
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9d2b6233ad9ba997136a22ca70b32bf5c4e4f8f9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file jabr-0.1.0-py3-none-any.whl.
File metadata
- Download URL: jabr-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78456aa2ebb7de0ee6bacd6e20ec3424c268308925e7f100228e69b30c49901b
|
|
| MD5 |
6d663d0cfb4337c25857a15e1f62219a
|
|
| BLAKE2b-256 |
074ea9171549ba0b40a7b0a15e6d55f291adaebdcb01744f8aa0d5423d2cb5a7
|
Provenance
The following attestation bundles were made for jabr-0.1.0-py3-none-any.whl:
Publisher:
release.yml on Moshe-ship/jabr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jabr-0.1.0-py3-none-any.whl -
Subject digest:
78456aa2ebb7de0ee6bacd6e20ec3424c268308925e7f100228e69b30c49901b - Sigstore transparency entry: 1688006453
- Sigstore integration time:
-
Permalink:
Moshe-ship/jabr@9d2b6233ad9ba997136a22ca70b32bf5c4e4f8f9 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Moshe-ship
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9d2b6233ad9ba997136a22ca70b32bf5c4e4f8f9 -
Trigger Event:
push
-
Statement type: