Skip to content
This repository was archived by the owner on Jul 29, 2026. It is now read-only.
Merged
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
18 changes: 9 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@ This file gives AI assistants (Claude Code and others) the context needed to wor

| Field | Value |
|---|---|
| Package | `edcmbone` |
| Version | `0.1.0` |
| Description | Structural fidelity measurement for AI interactions — quantifies how much meaning an AI system deletes when transforming structured user input |
| Status | 3 - Alpha |
| Python | >=3.8 (classifiers: 3.8, 3.9, 3.10, 3.11, 3.12) |
| Package | `edcmbone-backend` |
| Version | `0.2.0` |
| Description | MSDMD-compliant UCNS-only backend for EDCM boundary objects |
| Status | hmmm |
| Python | >=3.8 |
| License | MPL-2.0 |
| Build backend | `hatchling.build` |
| Author(s) | Erin Patrick Spencer <wayseer@interdependentway.org> |
| Repository | https://github.com/The-Interdependency/edcmbone |
| Author(s) | hmmm |
| Repository | hmmm |
| Runtime dependencies | none (stdlib only) |
| Optional extras | none |
| Keywords | AI, measurement, structural fidelity, cognitive accessibility, NLP, EDCM |
| Keywords | none |
| CI workflows | `ci.yml`, `manifest-check.yml` |
| Top-level directories | `aimmh-lib/` · `backend/` · `canon_eng/` · `core/` · `docs/` · `edcmbone/` · `frontend/` · `tests/` |
| Top-level directories | `aimmh-lib/` · `backend/` · `backend_old/` · `canon_eng/` · `core/` · `docs/` · `edcmbone/` · `frontend/` · `tests/` |

<sub>Derived from `backend/pyproject.toml` + the repo tree. Unknown fields surface as `hmmm` rather than a guess.</sub>
<!-- END GENERATED:manifest -->
Expand Down
26 changes: 23 additions & 3 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
# edcmbone-backend

This backend is intentionally small: its only runtime import is `ucns`.
It turns unresolved constraints into explicit boundary objects rather than
silently resolving them.
This backend is intentionally small: the active backend package imports only
`ucns`. It turns unresolved constraints into explicit boundary objects rather
than silently resolving them.

The prior backend has been preserved at `backend_old/`.

## Few-click path

From the repository root:

```bash
python -m pip install -e backend
python backend/examples/boundary_quickstart.py
```

Expected result: a JSON object with `delivered`, structured `hmmm`, and `ucns`
fields. If that prints, the backend is installed and usable.

## Usage Guidance

After installation, use the backend directly:

```python
## Usage Guidance

Install or run from this repository with both the repository root and backend
Expand Down Expand Up @@ -39,6 +56,9 @@ PY

### Integration notes

- Runtime dependency boundary: active backend source imports only `ucns`.
- Packaging boundary: the backend wheel includes a local `ucns` package so users
do not need to put the repository root on `PYTHONPATH`.
- Runtime dependency boundary: backend source imports only `ucns`.
- Risk boundary: in-memory only; no auth, storage, network, admin, or secret
side effects.
Expand Down
28 changes: 28 additions & 0 deletions backend/examples/boundary_quickstart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Copy-pasteable edcmbone-backend smoke demo.

Usage:
python backend/examples/boundary_quickstart.py

If the backend is not installed, run this first from the repo root:
python -m pip install -e backend
"""

from __future__ import annotations

import json

import edcmbone_backend as backend


def main() -> None:
first = backend.make_boundary(
"Delivered: backend can create a boundary object.",
"Unresolved: wire this into the next UI or API surface.",
)
second = backend.make_boundary("Delivered: backend can compose boundaries.")
merged = backend.merge_boundaries(first, second)
print(json.dumps(backend.serialize_boundary(merged), indent=2, sort_keys=True))


if __name__ == "__main__":
main()
205 changes: 205 additions & 0 deletions backend/src/ucns/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,208 @@
"""
ucns_v04 — UCNS Engine (turn-fraction angle convention)
Angles are stored as Fraction objects representing fractions of a full turn:
0 = 0 deg, 1/4 = 90 deg, 1/2 = 180 deg, 2 = 720 deg = 0 on doubled cover.

The algebra operates on the doubled cover of the unit circle, so the
fundamental period is 2 (two full turns = identity). Normalization shifts
the first anchor to theta=0 and reduces all thetas mod 2.

n_min is the LCM of denominators of all non-zero anchor thetas, computed
directly from the Fraction denominators (no pi-unit conversion).

Public API
----------
AnchorPayload(theta, payload)
UCNSObject(n_dec, n_min, anchors_pos, faces_pos)
.anchors_pos : tuple[AnchorPayload, ...]
.faces_pos : tuple[int, ...]
.n_dec : int
.n_min : int
.normalize() : UCNSObject (returns self; normalization done in __init__)
.equivalent(other) : bool
unit_obj() : UCNSObject (multiplicative unit)
is_unit_payload(obj): bool
multiply(A, B) : UCNSObject (A ⊠ B)
"""

from __future__ import annotations

from fractions import Fraction
from math import gcd
from functools import reduce
from typing import Optional, Tuple

__all__ = [
"AnchorPayload",
"UCNSObject",
"unit_obj",
"is_unit_payload",
"multiply",
]


def _lcm(a: int, b: int) -> int:
return a * b // gcd(a, b)


def _reduce_lcm(denoms):
return reduce(_lcm, denoms, 1)


class AnchorPayload:
"""Named container for a (theta, payload) anchor entry."""
__slots__ = ("theta", "payload")

def __init__(self, theta, payload):
self.theta = Fraction(theta)
self.payload = payload # UCNSObject or None

def __repr__(self) -> str:
return f"AnchorPayload(theta={self.theta}, payload={self.payload!r})"


class UCNSObject:
"""
A UCNS algebraic object on the doubled unit circle.

anchors_pos : tuple of AnchorPayload (theta in turn-fractions, payload)
faces_pos : tuple of int (face label per anchor, 0 or 1)
n_dec : declared carrier size (context hint; upper bound on n_min)
n_min : minimal carrier = LCM of denominators of non-zero thetas
"""

def __init__(
self,
n_dec: int,
n_min: int,
anchors_pos,
faces_pos,
):
self.n_dec = int(n_dec)
self._anchors_raw = tuple(anchors_pos)
self._faces_raw = tuple(faces_pos)
# Normalization populates .anchors_pos, .faces_pos, .n_min.
self.anchors_pos: Tuple[AnchorPayload, ...] = self._anchors_raw
self.faces_pos: Tuple[int, ...] = self._faces_raw
self.n_min = int(n_min)
self._do_normalize()

def _do_normalize(self):
"""Shift so first anchor is at 0; recompute n_min from thetas."""
if not self._anchors_raw:
self.anchors_pos = ()
self.faces_pos = ()
self.n_min = 1
return

theta0 = self._anchors_raw[0].theta
normalized = []
for ap in self._anchors_raw:
new_theta = (ap.theta - theta0) % 2
normalized.append(AnchorPayload(new_theta, ap.payload))

self.anchors_pos = tuple(normalized)
self.faces_pos = self._faces_raw

non_zero_denoms = [
ap.theta.denominator
for ap in self.anchors_pos
if ap.theta != 0
]
self.n_min = _reduce_lcm(non_zero_denoms) if non_zero_denoms else 1

def normalize(self) -> "UCNSObject":
"""Return self (normalization happens at construction time)."""
return self

def equivalent(self, other: "UCNSObject") -> bool:
"""Deep structural equivalence."""
if not isinstance(other, UCNSObject):
return False
a = self
b = other
if len(a.anchors_pos) != len(b.anchors_pos):
return False
if a.faces_pos != b.faces_pos:
return False
for ap, bp in zip(a.anchors_pos, b.anchors_pos):
if ap.theta != bp.theta:
return False
if ap.payload is None and bp.payload is None:
continue
if ap.payload is None or bp.payload is None:
return False
if not ap.payload.equivalent(bp.payload):
return False
return True

def __repr__(self) -> str:
thetas = [str(ap.theta) for ap in self.anchors_pos]
return f"UCNSObject(n_dec={self.n_dec}, n_min={self.n_min}, thetas={thetas})"


def unit_obj() -> UCNSObject:
"""Return the multiplicative unit: single anchor at theta=0, no payload."""
return UCNSObject(
n_dec=1,
n_min=1,
anchors_pos=(AnchorPayload(Fraction(0), None),),
faces_pos=(0,),
)


def is_unit_payload(obj: Optional[UCNSObject]) -> bool:
"""True if obj is None (no payload) or structurally equivalent to the unit."""
if obj is None:
return True
return obj.equivalent(unit_obj())


def multiply(A: UCNSObject, B: UCNSObject) -> UCNSObject:
"""
UCNS product A ⊠ B.

Each anchor a_k of A is combined with each anchor b_j of B to yield
a result anchor with:
theta = (a_k.theta + b_j.theta) % 2
payload = multiply(a_k.payload, b_j.payload) [recursive; None is unit]
face = a_k.face XOR b_j.face

The result has len(A.anchors_pos) * len(B.anchors_pos) anchors, ordered
A-major (outer loop over A, inner loop over B).

The single-anchor unit_obj() is a two-sided identity under this product.
The product is associative.
"""
new_anchors = []
new_faces = []

for ai, ak in enumerate(A.anchors_pos):
for bi, bj in enumerate(B.anchors_pos):
theta = (ak.theta + bj.theta) % 2

pa = ak.payload
pb = bj.payload
if pa is None and pb is None:
payload = None
elif pa is None:
payload = pb
elif pb is None:
payload = pa
else:
payload = multiply(pa, pb)

face = A.faces_pos[ai] ^ B.faces_pos[bi]
new_anchors.append(AnchorPayload(theta, payload))
new_faces.append(face)

n_dec = _lcm(A.n_dec, B.n_dec)
return UCNSObject(
n_dec=n_dec,
n_min=1,
anchors_pos=tuple(new_anchors),
faces_pos=tuple(new_faces),
)
"""Compatibility name for the UCNS v0.4 engine."""

from .ucns_v04 import AnchorPayload, UCNSObject, is_unit_payload, multiply, unit_obj
Expand Down
21 changes: 21 additions & 0 deletions backend/tests/test_backend_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,24 @@ def test_boundaries_record_no_hidden_side_effects():
assert "# storage_boundary: none" in source
assert "# network_boundary: none" in source
assert "# admin_only: false" in source


def test_backend_src_path_is_self_contained(tmp_path):
import subprocess
import sys

code = """
import edcmbone_backend as backend
boundary = backend.make_boundary('delivered', 'unresolved')
assert boundary.ucns_object.n_min == 1
assert backend.serialize_boundary(boundary)['hmmm']['text'] == 'unresolved'
"""
result = subprocess.run(
[sys.executable, "-c", code],
cwd=tmp_path,
env={"PYTHONPATH": str(Path(__file__).resolve().parents[1] / "src")},
text=True,
capture_output=True,
check=False,
)
Comment on lines +79 to +96
assert result.returncode == 0, result.stderr
Loading