# Ticket #000015 — π* domain library + cross-domain composition

**Status:** closed · landed 2026-05-07
**Opened:** 2026-05-07
**Closed:** 2026-05-07
**Scope:** Stand up a unified library of canonical projections (π*) for
the modalities arborist's QA layer touches, plus the composition theory
needed to chain them. Doc + code: docs covers the algebra of π*
composition; code adds a stub `arborist/pi_star/` namespace with
existing canonicalizers (text, claim-lattice) re-homed and a clear
interface for new modalities (code/AST, time-series, image-graph, etc.).
**Audience:** fox + future authors of new source adapters.
**Hard constraint:** every π* must be deterministic and version-pinned.
Adding a new π* never silently invalidates prior cache; new π* land as
new `name@version` rather than mutating existing canonicalizers.

---

## 1. Problem statement

The SQD whitepaper (§13.3) names the bottleneck:

> Specification brittleness is the real bottleneck. Even with perfect
> optimization, if π* or V encodes the wrong target, you get verified
> nonsense.

arborist has two π*'s in flight today:

| π* | Where | Versioned? |
|---|---|---|
| Text (wikitext → prose) | `arborist/wikitext.py:to_base()` | yes (`wikitext-base-v1`) |
| Claim-lattice | `arborist/qa/parse_claims.py` | yes (folded into `governance_policy_hash`) |

That's it. Real AGI use needs π* for at minimum:

- **Code** (parse → AST → canonical S-expression). Already partially
  approximated by ad-hoc tokenization; no canonical projection.
- **Math/logic** (parse → kernel proof object). v7 § 14.3 sketches
  this; arborist has no implementation.
- **Time-series** (resample → quantize → committed integer vector).
  No π* exists.
- **Image graph** (object/relation extraction → committed scene
  graph). Depends on ticket #000013 (v7-W).
- **Tabular data** (canonical column ordering, type-pinned cells).
  No π*.

Beyond per-domain π*, the **cross-domain composition** problem is
unsolved:

```
π*_text → claim about a code change  (need π*_code chained)
π*_logic → proof citing a tabular dataset  (need π*_tabular chained)
π*_world → object's path described in prose  (need π*_text chained)
```

v7 § 11 multimodal composition handles **structural** composition.
Cross-domain π* composition needs an **algebraic** treatment:

```
when does π*_a ∘ π*_b preserve canonicality?
when does it preserve transitivity of equivalence?
what type-theoretic constraint must hold for the chain to commit?
```

No paper or doc in the corpus answers this.

---

## 2. Design choices

### 2.1 Library shape: one module vs many

**A. One module `arborist/pi_star/` with submodules per domain.**
Centralized; easy to enumerate.

**B. Distributed: each source/domain owns its π*.** Decentralized;
mirrors `sources/` layout. Harder to enforce versioning.

**C. Hybrid: `arborist/pi_star/` defines the protocol + base class;
implementations live alongside the data they canonicalize.**

**Recommendation:** C. Protocol + ABC in `arborist/pi_star/`,
implementations stay near their data. Matches arborist's existing
`SearchBackend` / `Source` protocol pattern.

### 2.2 π* protocol

```python
class PiStar(Protocol):
    name: str             # e.g., "code-py-ast-v1"
    version: str          # always vN; never reused
    def canonicalize(self, raw: bytes) -> bytes:
        """raw → canonical bytes, or raise PiStarError if undefined."""
    def equivalence_class_id(self, raw: bytes) -> str:
        """SHA-256 over canonicalize(raw); the equivalence-class label."""
    def domain(self) -> str:
        """e.g., 'text', 'code', 'logic', 'tabular', 'time-series', 'world'."""
```

Every π* registers itself in a `pi_star.registry` dict keyed by
`f"{name}@{version}"`. Lookups in code use the same key, so changing
π* means changing the key, not mutating in place.

### 2.3 Composition algebra

The core question: **when is π*_a ∘ π*_b a valid canonical
projection?**

A composition is canonical iff:

1. **Type-compatible.** The codomain of π*_b is in the domain of π*_a.
2. **Determinism preserved.** Both individual π*'s are deterministic
   ⇒ composition is deterministic.
3. **Equivalence-class preservation.** If `π*_b(x) = π*_b(y)` then
   `(π*_a ∘ π*_b)(x) = (π*_a ∘ π*_b)(y)`. Trivially true if π*_a is a
   function. Required when π*_a may distinguish things π*_b
   conflated.
4. **Lossy composition is allowed but flagged.** If `(π*_a ∘ π*_b)(x)`
   does not round-trip through `(π*_a ∘ π*_b)`'s codomain, the
   composition is *projective*, not *invertible*. Flagged in the
   composition manifest.

The composition itself is registered as its own π*:
```
"text-then-claim-lattice@v1"  =  π*_claim ∘ π*_text
```
This keeps the registry flat — every composition is a first-class
π* with its own version.

### 2.4 Cross-domain anchor projection

For genuinely cross-domain claims (e.g., "this prose paragraph
describes that code function"), we need an **anchor projection**
π*_anchor : Domain_a × Domain_b → CanonicalAnchor. The anchor is a
joint commitment to both projections' canonical forms:

```
anchor = SHA-256(
    canon_a_id || canon_b_id || relation_kind || relation_payload_canonical
)
```

This is the v7-style "every causally relevant transformation must be
committed" axiom applied to cross-domain joins. Anchors are first-
class committed objects; they fold into `governance_policy_hash` if
operator policy says so.

---

## 3. Recommendation

**A. Code library:** `arborist/pi_star/` namespace with protocol +
ABC + registry. Re-home `wikitext.to_base()` and `qa.parse_claims`
under registry keys (`wikitext-base@v1`, `claim-lattice@v1`).

**B. Stub modalities:** add stub modules for `code-py-ast`,
`logic-kernel`, `time-series-quantized`, `tabular-pinned`. Each
raises `NotImplementedError` with a pointer to its implementation
ticket. This makes the gap visible and registers the namespace.

**C. Composition theory doc:** `docs/pi-star-composition.md` covering
the algebra above with worked examples. This doc + at least one
worked composition (text-then-claim-lattice) is the design
deliverable.

**D. Out-of-scope here:** actually implementing the new modalities.
Each modality's implementation is its own follow-up ticket.

---

## 4. Implementation sketch

### 4.1 Module layout (this ticket)

```
arborist/pi_star/
├── __init__.py          # registry + re-exports
├── protocol.py          # PiStar Protocol, PiStarError
├── registry.py          # name@version dict + lookup helpers
├── compose.py           # composition algebra + worked example
├── text.py              # re-homes wikitext.to_base() → "wikitext-base@v1"
├── claim_lattice.py     # re-homes parse_claims → "claim-lattice@v1"
├── code.py              # stub for "code-py-ast@v1"
├── logic.py             # stub for "logic-kernel@v1"
├── time_series.py       # stub for "time-series-quantized@v1"
└── tabular.py           # stub for "tabular-pinned@v1"
```

### 4.2 Re-home rules

Existing imports (`from arborist.wikitext import to_base`) keep
working — `arborist/wikitext.py` re-exports
`arborist.pi_star.text.canonicalize`. This is the only soft-rename;
the registry key replaces ad-hoc version mention sites over time.

### 4.3 Composition manifest

Each composition gets a manifest:

```yaml
# arborist/pi_star/compositions/text-then-claim-lattice-v1.yaml
name: text-then-claim-lattice
version: v1
domain_in: html_or_wikitext
domain_out: claim_lattice
chain:
  - wikitext-base@v1
  - claim-lattice@v1
properties:
  deterministic: true
  invertible: false
  equivalence-class-preserving: true
```

Manifest is canonicalized + hashed; the hash becomes the
composition's identity in the registry.

### 4.4 Tests

- Round-trip: canonicalize twice → byte-equal.
- Registry: lookup by name@version is stable; missing key raises.
- Composition: re-running `(π*_a ∘ π*_b)(x)` and `π*_a(π*_b(x))`
  must produce identical bytes.
- Existing `wikitext-base-v1` round-trip tests still pass (under
  re-homed module).

### 4.5 Bumps

- No schema bumps. Pure code reorganization + protocol introduction.
- No `governance_policy_hash` impact — π* version names are already
  pinned in policy fields; this ticket just centralizes them.

---

## 5. Out of scope

- Implementing `code-py-ast` / `logic-kernel` / `time-series-quantized`
  / `tabular-pinned`. Each gets its own ticket once #000015 lands.
- Cross-domain anchor projections in code (the algebra is in this
  ticket's doc; first concrete implementation is its own ticket).
- Migration of existing call sites to use `pi_star.registry.lookup(...)`
  instead of direct imports. Soft migration over time.
- Versioning policy for π* deprecation. v1 of this library does not
  retire any π*; deprecation policy needs its own ticket once v2 of
  any modality lands.

---

## 6. Risks & open questions

- **Registry as global mutable state.** Same risk as Python's
  dynamic imports. Mitigation: registry is populated at import time
  only; no runtime mutation. Tests assert registry is read-only
  after import.
- **Composition explosion.** N modalities have N² composition pairs.
  We commit *named* compositions only — the algebra is general but
  the registry is curated.
- **Cross-domain anchor authentication.** The relation_kind in an
  anchor is itself canonical content; if relation_kind is just a
  string, two valid anchors with different intent collide. Mitigate
  by pinning relation_kind to a controlled vocabulary committed in
  a sibling registry.

---

## 7. Status

**Closed 2026-05-07.** Scope delivered:

- New namespace `arborist.pi_star/` with registry pattern.
- `protocol.py` — `PiStar` Protocol, `PiStarError`,
  `equivalence_class_id`, `assert_round_trip`, `registry_key`.
- `registry.py` — name@version registry with no-mutation discipline
  (re-registering a different instance under an existing key
  raises). `list_keys()`, `domains()`, `get()`.
- `compose.py` — `PiStarComposition`, `compose(inner_key,
  outer_key)`, `canonical_composition_id` for governance fingerprint.
- Re-homed canonicalizers:
  - `text.py` — `wikitext-base@v1` wraps `arborist.wikitext.to_base`.
  - `claim_lattice.py` — `claim-lattice@v1` wraps
    `arborist.qa.parse_claims.parse_pointer_claims`.
- Stubs (`NotImplementedError` with pointer to this ticket):
  `code-py-ast@v1`, `logic-kernel@v1`, `time-series-quantized@v1`,
  `tabular-pinned@v1`.
- `docs/pi-star-composition.md` — composition algebra, worked
  text→claim-lattice example, authoring checklist.
- Tests: `tests/test_pi_star.py` — 19 cases covering registry
  contents, round-trip stability, stub-error behavior, composition
  determinism, manifest fingerprint stability. Full suite: 1059
  passed, 36 skipped.

Out-of-scope items (deferred):

- Actual implementation of `code-py-ast`, `logic-kernel`,
  `time-series-quantized`, `tabular-pinned`. Each is its own
  follow-up ticket once needed.
- Cross-domain anchor projections (algebra is in the doc; first
  concrete implementation is its own ticket).
- Migration of legacy call sites to use
  `arborist.pi_star.get(...).canonicalize(...)` instead of
  `arborist.wikitext.to_base(...)` etc. Soft migration over time.
- Versioning policy for π* deprecation. v1 of this library does
  not retire any π*; deprecation policy needs its own ticket.
