Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ jobs:
- name: Run tests
run: |
pytest -q

- name: Run decision artefact demo (check mode)
run: |
python examples/minimal_decision_demo.py
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
__pycache__/
*.pyc
*.pyo
.pytest_cache/
*.egg-info/
dist/
build/
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,46 @@ Deterministic, hash-bound commit authority gate — stdlib-only, no network, no
- **CI:** [`commit_gate_ci.yml`](https://github.com/LalaSkye/constraint-workshop/actions/workflows/commit_gate_ci.yml) (Python 3.10/3.11/3.12 matrix)
- **Proof:** Determinism + drift-fail validated across Python 3.10/3.11/3.12.

## Deterministic Decision Artefact Demo

Proves: same inputs → same bytes → same hash → replay works.

### Input parameters

| Field | Value |
|-------|-------|
| `actor_id` | `demo-actor` |
| `action_class` | `FILE` |
| `context.description` | `minimal demo commit` |
| `authority_scope.project` | `demo-project` |
| `invariant_hash` | `0000…0000` (64 zeros) |
| Ruleset | allowlist: `demo-actor / FILE / demo-project` |

### Output artefact

```
verdict : ALLOW
decision_hash : fab8740c920489154038063620e1453460e1ea1549d0cb1f0cb2dca0ce860360
canonical_bytes (base64, first 64 chars):
eyJhcnRlZmFjdF92ZXJzaW9uIjoiMC4xIiwiZGVjaXNpb25faGFzaCI6ImZhYj…
```

Full base64 stored in [`tests/fixtures/golden_canonical_bytes.b64`](tests/fixtures/golden_canonical_bytes.b64).

### Reproduce

```bash
python examples/minimal_decision_demo.py
```

### Replay test

```bash
pytest tests/test_replay_decision.py -v
```

---

## Scope boundaries

`/prometheus` is an **observability-only island**. It must not be imported by any execution path, gate, or pipeline code. It observes and reports; it cannot allow, hold, deny, or silence anything.
Expand Down
Binary file added __pycache__/authority_gate.cpython-312.pyc
Binary file not shown.
Binary file added __pycache__/invariant_litmus.cpython-312.pyc
Binary file not shown.
Binary file added __pycache__/stop_machine.cpython-312.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
67 changes: 67 additions & 0 deletions examples/minimal_decision_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Minimal MGTP decision artefact demonstration.

Proves:
1) Deterministic canonicalisation — same inputs produce identical bytes.
2) Stable decision_hash — sha256 of canonical request+verdict+reasons.
3) Replayable reconstruction — stored bytes can be reloaded and re-verified.

No randomness. Fixed inputs. Fixed timestamp (excluded from hash).
Run with: python examples/minimal_decision_demo.py
"""

import base64
import sys
from pathlib import Path

# Resolve commit_gate package from repository layout
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "commit_gate" / "src"))

from commit_gate.canonicalise import canonicalise
from commit_gate.engine import evaluate

# ---------------------------------------------------------------------------
# Fixed, deterministic inputs (no randomness, no live timestamps)
# ---------------------------------------------------------------------------

REQUEST = {
"actor_id": "demo-actor",
"action_class": "FILE",
"context": {"description": "minimal demo commit"},
"authority_scope": {"project": "demo-project"},
"invariant_hash": "0000000000000000000000000000000000000000000000000000000000000000",
}

RULESET = {
"allowlist": [
{
"actor_id": "demo-actor",
"action_class": "FILE",
"scope_match": {"project": "demo-project"},
}
],
"denylist": [],
"escalation": [],
}

# ---------------------------------------------------------------------------
# Evaluate and serialise
# ---------------------------------------------------------------------------

result = evaluate(REQUEST, RULESET)
canonical_bytes = canonicalise(result)
canonical_b64 = base64.b64encode(canonical_bytes).decode("ascii")
Comment on lines +50 to +52
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This script runs significant logic at import time (evaluate + printing). Wrapping execution in a main() and guarding with if name == 'main' avoids side effects if the module is imported (e.g., by tooling) and makes it easier to reuse in tests.

Copilot uses AI. Check for mistakes.

# ---------------------------------------------------------------------------
# Output — regulator-readable
# ---------------------------------------------------------------------------

print("=== MGTP Minimal Decision Artefact Demo ===")
print()
print(f"verdict : {result['verdict']}")
print(f"reasons : {result['reasons']}")
print(f"artefact_version: {result['artefact_version']}")
print(f"request_hash : {result['request_hash']}")
print(f"decision_hash : {result['decision_hash']}")
print()
print(f"canonical_bytes (base64):")
print(canonical_b64)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 1 addition & 0 deletions tests/fixtures/golden_canonical_bytes.b64
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJhcnRlZmFjdF92ZXJzaW9uIjoiMC4xIiwiZGVjaXNpb25faGFzaCI6ImZhYjg3NDBjOTIwNDg5MTU0MDM4MDYzNjIwZTE0NTM0NjBlMWVhMTU0OWQwY2IxZjBjYjJkY2EwY2U4NjAzNjAiLCJyZWFzb25zIjpbImFsbG93bGlzdF9tYXRjaCJdLCJyZXF1ZXN0X2hhc2giOiJiYTEzN2RkNjM2YjNiNzZiZGI2YmRlZmQzNjc0YjQwMTkxMDY1NGU3YmY3MWFiNDEwNjYwMDg3NTU2ZWE0MmUwIiwidmVyZGljdCI6IkFMTE9XIn0=
72 changes: 72 additions & 0 deletions tests/test_replay_decision.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""Replay test: proves stored golden canonical bytes are byte-identical on re-evaluation.

T-REPLAY-1: canonical_bytes from fixed inputs == stored golden bytes.
T-REPLAY-2: decision_hash derived from re-evaluation == hash in golden bytes.

Fails if any single byte differs.
"""

import base64
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "commit_gate" / "src"))

from commit_gate.canonicalise import canonicalise
from commit_gate.engine import evaluate

FIXTURES_DIR = Path(__file__).resolve().parent / "fixtures"

# Fixed inputs — must exactly match examples/minimal_decision_demo.py
REQUEST = {
"actor_id": "demo-actor",
"action_class": "FILE",
"context": {"description": "minimal demo commit"},
"authority_scope": {"project": "demo-project"},
"invariant_hash": "0000000000000000000000000000000000000000000000000000000000000000",
}

RULESET = {
"allowlist": [
{
"actor_id": "demo-actor",
"action_class": "FILE",
"scope_match": {"project": "demo-project"},
}
],
"denylist": [],
"escalation": [],
}


def _load_golden_bytes():
b64_text = (FIXTURES_DIR / "golden_canonical_bytes.b64").read_text().strip()
return base64.b64decode(b64_text)
Comment on lines +42 to +44
Copy link

Copilot AI Feb 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For deterministic behavior across environments, consider specifying an explicit encoding when reading the fixture (it's ASCII/base64) and using base64.b64decode(..., validate=True) so corruption/non-base64 characters fail loudly with a clear error.

Copilot uses AI. Check for mistakes.


def test_t_replay_1_canonical_bytes_identical():
"""T-REPLAY-1: Re-evaluating fixed inputs produces byte-identical canonical bytes."""
golden = _load_golden_bytes()
result = evaluate(REQUEST, RULESET)
actual = canonicalise(result)
assert actual == golden, (
f"Canonical bytes differ from golden fixture.\n"
f"Expected: {golden!r}\n"
f"Got: {actual!r}"
)


def test_t_replay_2_decision_hash_stable():
"""T-REPLAY-2: decision_hash in re-evaluated result matches hash in golden bytes."""
import json

golden = _load_golden_bytes()
golden_dict = json.loads(golden.decode("utf-8"))
golden_hash = golden_dict["decision_hash"]

result = evaluate(REQUEST, RULESET)
assert result["decision_hash"] == golden_hash, (
f"decision_hash mismatch.\n"
f"Expected: {golden_hash}\n"
f"Got: {result['decision_hash']}"
)