From fe7f70dd1104089fef15ad26bdcaf56ffde8df64 Mon Sep 17 00:00:00 2001 From: Erin Patrick Spencer Date: Sun, 5 Jul 2026 17:19:33 +0000 Subject: [PATCH] feat: rewire bundle to consolidated ptcna; collapse pcna/pcta/pcsa into one key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prime-tensor stack is now the single ptcna package (Prime Tensor Circled Neural Architecture — neural/circle/seed/core). This reppoints interdependent-lib at it: - _REGISTRY: pcna + pcta keys collapse into one 'ptcna' -> 'ptcna' probe key - pyproject: drop the ptca extra and ptca-lib pin (superseded); ptcna extra lands on PyPI release; 'all' = pcea/ucns/aimmh - docs/prime-tensor-stack.md rewritten for the four-layer single-repo model: backprop only in neural; circle/seed/core are auditing/timing tensors; fiqs gate core internal propagation per Fick's law - libs/pcna|pcta|ptca stubs -> libs/ptcna; sync-libs.yml syncs consolidated ptcna/ - README, CLAUDE, dependency-policy, release-checklist, CONTRIBUTING updated; naming-migration records consolidation as terminal state; CHANGELOG entry - tests: single ptcna key asserted, old keys gone, no ptca-lib pin remains 40 tests pass. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0194nRKbG5bkbxmu1q1s156Y --- .github/workflows/sync-libs.yml | 22 +- CHANGELOG.md | 21 ++ CLAUDE.md | 63 ++-- CONTRIBUTING.md | 2 +- README.md | 35 +-- docs/dependency-policy.md | 7 +- docs/naming-migration.md | 20 ++ docs/prime-tensor-stack.md | 163 +++++------ docs/release-checklist.md | 3 +- interdependent_lib/__init__.py | 17 +- libs/README.md | 14 +- libs/pcna/README.md | 70 ----- libs/pcna/src/edcm.py | 115 -------- libs/pcna/src/helix_vis.py | 102 ------- libs/pcna/src/main.py | 181 ------------ libs/pcna/src/memory_core.py | 106 ------- libs/pcna/src/merge.py | 173 ----------- libs/pcna/src/pcna.py | 372 ------------------------ libs/pcna/src/ptca_core.py | 171 ----------- libs/pcna/src/routing_loop.py | 37 --- libs/pcna/src/sigma.py | 127 -------- libs/pcna/src/tensor_engine.py | 116 -------- libs/pcna/src/theta.py | 184 ------------ libs/pcna/src/topology.py | 170 ----------- libs/pcna/src/zeta.py | 480 ------------------------------- libs/pcta/README.md | 17 -- libs/ptca/README.md | 66 ----- libs/ptca/src/__init__.py | 86 ------ libs/ptca/src/constants.py | 62 ---- libs/ptca/src/exchange.py | 246 ---------------- libs/ptca/src/instance.py | 353 ----------------------- libs/ptca/src/primes.py | 45 --- libs/ptca/src/provenance.py | 164 ----------- libs/ptca/src/sentinels.py | 217 -------------- libs/ptca/src/tensor.py | 184 ------------ libs/ptcna/README.md | 38 +++ pyproject.toml | 11 +- scripts/rename-repos.sh | 136 +++++++++ tests/test_interdependent_lib.py | 22 +- 39 files changed, 397 insertions(+), 4021 deletions(-) delete mode 100644 libs/pcna/README.md delete mode 100644 libs/pcna/src/edcm.py delete mode 100644 libs/pcna/src/helix_vis.py delete mode 100644 libs/pcna/src/main.py delete mode 100644 libs/pcna/src/memory_core.py delete mode 100644 libs/pcna/src/merge.py delete mode 100644 libs/pcna/src/pcna.py delete mode 100644 libs/pcna/src/ptca_core.py delete mode 100644 libs/pcna/src/routing_loop.py delete mode 100644 libs/pcna/src/sigma.py delete mode 100644 libs/pcna/src/tensor_engine.py delete mode 100644 libs/pcna/src/theta.py delete mode 100644 libs/pcna/src/topology.py delete mode 100644 libs/pcna/src/zeta.py delete mode 100644 libs/pcta/README.md delete mode 100644 libs/ptca/README.md delete mode 100644 libs/ptca/src/__init__.py delete mode 100644 libs/ptca/src/constants.py delete mode 100644 libs/ptca/src/exchange.py delete mode 100644 libs/ptca/src/instance.py delete mode 100644 libs/ptca/src/primes.py delete mode 100644 libs/ptca/src/provenance.py delete mode 100644 libs/ptca/src/sentinels.py delete mode 100644 libs/ptca/src/tensor.py create mode 100644 libs/ptcna/README.md create mode 100755 scripts/rename-repos.sh diff --git a/.github/workflows/sync-libs.yml b/.github/workflows/sync-libs.yml index dec7571..0b7d005 100644 --- a/.github/workflows/sync-libs.yml +++ b/.github/workflows/sync-libs.yml @@ -23,13 +23,15 @@ jobs: mkdir -p libs/pcea/src rsync -av --delete --exclude='README.md' /tmp/pcea-src/pcea/ libs/pcea/src/ - # ── PTCA ──────────────────────────────────────────────────────────────── - - name: Sync ptca lib + # ── PTCNA (consolidated prime-tensor stack) ───────────────────────────── + # Supersedes the former separate pcna / pcta / pcsa sync steps. One repo, + # four layers (neural/circle/seed/core) under the ptcna/ package. + - name: Sync ptcna package run: | - rm -rf /tmp/ptca-src - git clone --depth 1 https://github.com/The-Interdependency/PTCA.git /tmp/ptca-src - mkdir -p libs/ptca/src - rsync -av --delete --exclude='README.md' /tmp/ptca-src/ptca/ libs/ptca/src/ + rm -rf /tmp/ptcna-src + git clone --depth 1 https://github.com/The-Interdependency/ptcna.git /tmp/ptcna-src + mkdir -p libs/ptcna/src + rsync -av --delete --exclude='README.md' /tmp/ptcna-src/ptcna/ libs/ptcna/src/ # ── UCNS ──────────────────────────────────────────────────────────────── - name: Sync ucns lib @@ -39,14 +41,6 @@ jobs: mkdir -p libs/ucns/src rsync -av --delete --exclude='README.md' /tmp/ucns-src/ucns/ libs/ucns/src/ - # ── PCNA core ─────────────────────────────────────────────────────────── - - name: Sync pcna core - run: | - rm -rf /tmp/pcna-src - git clone --depth 1 https://github.com/The-Interdependency/pcna.git /tmp/pcna-src - mkdir -p libs/pcna/src - rsync -av --delete --exclude='README.md' /tmp/pcna-src/core/ libs/pcna/src/ - # ── AIMMH ─────────────────────────────────────────────────────────────── - name: Sync aimmh lib run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index a9e8543..43ec16a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,27 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### Changed +- **Prime-tensor stack consolidated into a single `ptcna` package.** The former + `pcna`/`pcta`/`pcsa` repos — never actually separate things, just layers of one + architecture — are unified into `The-Interdependency/ptcna` (Prime Tensor + Circled Neural Architecture), four layers: `neural` (the only back-propagating + layer) + `circle`/`seed`/`core` (auditing/timing tensors; fiqs gate core + internal propagation per Fick's law). In this repo: + - `_REGISTRY` collapses `pcna`/`pcta` into a single `ptcna` → `ptcna` key. + - `pyproject.toml` drops the `ptca` extra and its `ptca-lib` pin (superseded); + a single `ptcna` extra lands once `ptcna` publishes to PyPI. `prime-stack` + intent is obsolete. + - `docs/prime-tensor-stack.md` rewritten around the four-layer single-repo + model (backprop-only-in-neural; circle/seed/core as auditing/timing tensors; + fiq/Fick core timing). + - `libs/pcna|pcta|ptca/` stubs replaced by `libs/ptcna/`; `sync-libs.yml` syncs + the consolidated `ptcna/` package; README/CLAUDE/dependency-policy/ + release-checklist/CONTRIBUTING updated; `naming-migration.md` records the + consolidation as the terminal state. + - drift-guard tests updated: single `ptcna` key, old keys gone, no `ptca-lib` + pin remains. + ### Added - `docs/prime-tensor-stack.md` — canonical role-and-boundary map for the prime-tensor compute family: PCNA (tensors + back-propagation → weights) → diff --git a/CLAUDE.md b/CLAUDE.md index a3d493d..a03689b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,26 +26,28 @@ Verified against `[project.optional-dependencies]` in `pyproject.toml`. | Acronym | PyPI / requirement | Extra | Status | Description | |---------|--------------------|-------|--------|-------------| -| PCEA | `pcea>=0.1.0` | `pcea` | Packaged | Prime Circular Encryption Algorithm (guardian — "last state as key" at every layer) | -| PTCA | `ptca-lib>=0.1.0` | `ptca` | Packaged | Prime Tensor Core Architecture (stack layer 3: seeds → core; `ptca-lib` ships the sentinel-channel tensor system, the seeds→core role is `prime_core`) | +| PTCNA | — | — | Source-only (not on PyPI) | Prime Tensor Circled Neural Architecture — one repo, four layers (neural/circle/seed/core). **Consolidates the former PCNA/PCTA/PCSA.** Registered as `ptcna`; `ptcna` extra lands on PyPI release. See `docs/prime-tensor-stack.md` | +| PCEA | `pcea>=0.1.0` | `pcea` | Packaged | Prime Circular Encryption Algorithm (guardian — "last state as key" at every layer; orthogonal to the stack) | | UCNS | `ucns>=0.9.1` | `ucns` | Packaged | Unit Circle Number System; Python runtime stdlib-only; upstream formal scaffold is Mathlib-backed | | AIMMH | `aimmh-lib>=1.1.0`| `aimmh` | Packaged | AI Multimodel Multimodal Hub (five-letter) | -| PCNA | — | — | Source-only (not on PyPI) | Prime Circle Neural Architecture (stack layer 1: tensors → circles, backprop) | -| PCTA | — | — | Repo created; not yet on PyPI | Prime Circled Tensor Architecture (stack layer 2: circles → seeds); see `docs/prime-tensor-stack.md` | | ZFAE | — | — | Conceptual (runtime lives in `a0`; no dist planned) | Zeta Function Alpha Echo (inference engine) | | METAPAT | — | — | FLAR; registered, not yet on PyPI | Meta Energy Theory — Axioms, Postulates, And Theorems (first-letter acronym repo; canon + unpublished `metapat` 0.0.1 src-layout package) | -PCNA, PCTA, PTCA and ZFAE form one compute stack (PCEA is the orthogonal -guardian) — the canonical role-and-boundary map is `docs/prime-tensor-stack.md`. -PCTA now has a repo (`The-Interdependency/pcta`) but is not yet published; unlike -PCNA/ZFAE it is **not** in `_REGISTRY` and has no extra until it ships to PyPI. +The prime-tensor stack is now the single `ptcna` package — neural (the only +back-propagating layer) + circle/seed/core (auditing/timing tensors). PCEA is the +orthogonal guardian; ZFAE is the conceptual inference cap (runtime in `a0`). The +canonical role-and-boundary map is `docs/prime-tensor-stack.md`. -The `all` extra installs the four packaged libraries together. `dev` installs `pytest>=8.0`, `build`, and `twine`. PCNA, ZFAE, and METAPAT appear in `available()` and `libs/` but have no extra until they have stable PyPI releases. +The `all` extra installs the three packaged libraries together (pcea, ucns, +aimmh). `dev` installs `pytest>=8.0`, `build`, and `twine`. PTCNA, ZFAE, and +METAPAT appear in `available()` and `libs/` but have no extra until they have +stable PyPI releases (a single `ptcna` extra replaces the former per-repo intent). -> **Naming migration.** The org-wide rename scheme (`PTCA → pcsa`, casing -> normalization, `ucns` frozen) is ratified — `docs/naming-migration.md` is the -> reference, including the extras/registry transition rules. Names above track -> what is published/importable today. +> **Naming migration + consolidation.** The org-wide rename scheme and the +> **prime-tensor stack consolidation** (pcna/pcta/pcsa → single `ptcna`; +> `ucns` frozen) are ratified — `docs/naming-migration.md` is the reference, +> including the extras/registry transition rules. Names above track what is +> published/importable today. --- @@ -58,10 +60,10 @@ interdependent_lib/ libs/ Per-library documentation stubs (no primary code lives here) README.md Lib index + sync-workflow notes - pcea/ ptca/ ucns/ Packaged libs (each has README.md; sync-libs.yml mirrors upstream into /src) + pcea/ ucns/ Packaged libs (each has README.md; sync-libs.yml mirrors upstream into /src) aimmh/ Packaged five-letter lib (README.md; sync-libs.yml mirrors into aimmh/src) - pcna/ Source-only lib: README.md + src/ (mirrored upstream core/ sources, present in tree) - zfae/ Source-only lib: README.md only (NOT synced by sync-libs.yml) + ptcna/ Consolidated prime-tensor stack (README.md; sync-libs.yml mirrors ptcna/ into ptcna/src) + metapat/ zfae/ Source-only libs: README.md only (NOT synced by sync-libs.yml) tests/ pytest suite test_interdependent_lib.py Smoke tests for available() and __version__ @@ -133,23 +135,20 @@ twine upload dist/* Lean/Mathlib-backed and is not a Python runtime dependency. - **`available()`** maps each known sub-library (logical name → import name) and reports which are importable in the current environment via `importlib.util.find_spec`. - The registry includes source-only libs (`pcna` → `core`, `zfae` → `zfae`), so they are + The registry includes source-only libs (`ptcna` → `ptcna`, `zfae` → `zfae`), so they are always present as keys but report `False` unless installed manually. - **Prime-tensor stack canon.** `docs/prime-tensor-stack.md` is the single source - of truth for how PCNA (Prime Circle Neural Architecture; tensors → circles, - back-propagation → weights), PCTA (Prime Circled Tensor Architecture; circles → - seeds), PTCA (Prime Tensor Core Architecture; seeds → core) and ZFAE (Zeta - Function Alpha Echo; inference, using PCNA weights + circles / seeds / cores as - phase-harmonic propagation + auditing) compose, with PCEA as the orthogonal - guardian ("last state as key" at every layer). Like the coherence-prime canon - it lives in the aggregator so leaf repos cite it without inverting the - dependency graph. Back-propagation lives **only** in PCNA; PCTA/PTCA - composition is structural/non-differentiable. **Composition counts are + of truth for the consolidated stack: **PTCNA** (Prime Tensor Circled Neural + Architecture) is one repo with four layers — `neural` (the only differentiable + layer; owns back-propagation), and `circle` / `seed` / `core` (auditing and + timing tensors; non-differentiable). Each layer's tensors divide into the next; + every circle/seed/core is itself a tensor. Within the core layer, **fiqs gate + internal propagation** per Fick's first law `J = −D ∇φ` (timing, not gradient + descent). PCEA is the orthogonal guardian ("last state as key" at every layer), + not a layer. Like the coherence-prime canon it lives in the aggregator so leaf + repos cite it without inverting the dependency graph. **Composition counts are variable** — the only invariant is that every circle/seed/core is itself a - tensor. The acronym expansions, the variable-count rule, the flow, and the - **formal definition of "motion"** — the Fickian gradient flux `J = −D ∇φ` - (Fick's first law; structure diffusing down its field gradient) — were all - resolved by the maintainer; **no stack-level `hmmm` remains**. + tensor. - **Coherence-prime canon.** `coherence_primes.py` is the single source of truth for the recursive coherence-prime sequence (base `{3,5,7}`, then `p≡1 mod 4` with a square-free kernel whose factors are all already coherence primes). It is @@ -161,8 +160,8 @@ twine upload dist/* `libs//src`. It has two triggers: `workflow_dispatch` (manual) and an active `schedule:` cron (`0 3 * * 1` — every Monday 03:00 UTC; the cron line is *not* commented out, so it runs despite the misleading inline "disabled by - default" comment). It syncs only a **subset** of libs — `pcea`, `ptca`, `ucns`, - `pcna` (from upstream `core/`), and `aimmh` — **not** all `libs//src`: + default" comment). It syncs only a **subset** of libs — `pcea`, `ucns`, + `ptcna` (the consolidated stack), and `aimmh` — **not** all `libs//src`: `zfae` has no sync step and is never mirrored. Each step excludes the upstream `README.md` so the stubs stay authoritative, then commits any changes with `[skip ci]`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 91de851..a891a73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ Thank you for your interest in contributing! This document explains how the repo ## Repository purpose -`interdependent-lib` is a **meta-package** and **documentation hub** that aggregates the individual Interdependency acronym libraries. Most library *code* lives in the source repos (pcna, PCEA, PTCA, ucns, ZFAE, aimmh). Changes to library logic belong there. +`interdependent-lib` is a **meta-package** and **documentation hub** that aggregates the individual Interdependency acronym libraries. Most library *code* lives in the source repos (ptcna, PCEA, ucns, ZFAE, aimmh, metapat). Changes to library logic belong there. The prime-tensor stack is consolidated into the single `ptcna` repo (neural/circle/seed/core), superseding the former pcna/pcta/pcsa. Contributions welcome here: - Updating or improving the per-library `libs/*/README.md` stubs diff --git a/README.md b/README.md index d3b6406..94597da 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,10 @@ | Acronym | Full Name | Letters | Description | |---------|-----------|---------|-------------| -| [PCEA](libs/pcea/README.md) | Prime Circular Encryption Algorithm | 4 | Neural architecture state encryption ("last state as key") at every layer | -| [PTCA](libs/ptca/README.md) | Prime Tensor Core Architecture | 4 | Stack layer 3 — seeds → core. (`ptca-lib` ships the sentinel-channel / prime-node tensor system; the seeds→core role is `prime_core`.) | -| [PCTA](libs/pcta/README.md) | Prime Circled Tensor Architecture | 4 | Stack layer 2 — composes UCNS-carried circles → seeds | +| [PTCNA](libs/ptcna/README.md) | Prime Tensor Circled Neural Architecture | — | The prime-tensor stack, consolidated into one repo, four layers (neural/circle/seed/core). Supersedes the former PCNA/PCTA/PCSA | +| [PCEA](libs/pcea/README.md) | Prime Circular Encryption Algorithm | 4 | Neural architecture state encryption ("last state as key") at every layer; orthogonal guardian | | [UCNS](libs/ucns/README.md) | Unit Circle Number System | 4 | Recursive factorization theory, witness-matrix quotient solver, A0-safe factorization envelopes, and Mathlib-backed formal scaffold upstream | -| [PCNA](libs/pcna/README.md) | Prime Circle Neural Architecture | 4 | Stack layer 1 — tensors → circles in a back-propagating NN → weights | -| [ZFAE](libs/zfae/README.md) | Zeta Function Alpha Echo | 4 | Inference engine — pcna weights + circles / seeds / cores as phase-harmonic propagation | +| [ZFAE](libs/zfae/README.md) | Zeta Function Alpha Echo | 4 | Inference engine (conceptual; runtime in `a0`) — reads neural weights + circles / seeds / cores as phase-harmonic propagation | | [AIMMH](libs/aimmh/README.md) | AI Multimodel Multimodal Hub | 5 | Zero-dependency async multi-model conversation orchestration | --- @@ -34,9 +32,9 @@ pip install interdependent-lib # Install only specific libraries pip install interdependent-lib[pcea] -pip install interdependent-lib[ptca] pip install interdependent-lib[ucns] # ucns>=0.9.1 pip install interdependent-lib[aimmh] +# ptcna (the consolidated prime-tensor stack) gets a [ptcna] extra once it publishes to PyPI # Install everything pip install interdependent-lib[all] @@ -57,16 +55,19 @@ print(interdependent_lib.available()) ## The Prime-Tensor Stack -PCNA, PCTA, PTCA and ZFAE form a single compute stack (PCEA is the orthogonal -guardian). Composition counts are **variable** at every level — the only -invariant is that every circle, seed, and core is itself a tensor. The canonical -role-and-boundary map lives in +The stack is one package — **PTCNA** (Prime Tensor Circled Neural Architecture) — +with four layers. PCEA is the orthogonal guardian; ZFAE is the conceptual +inference cap (runtime in `a0`). Composition counts are **variable** at every +level — the only invariant is that every circle, seed, and core is itself a +tensor. The canonical role-and-boundary map lives in **[docs/prime-tensor-stack.md](docs/prime-tensor-stack.md)**: ``` -PCNA (tensors → circles, back-prop) ─► weights + circles ─► PCTA (circles → seeds) - ─► seeds ─► PTCA (seeds → core) ─► cores ─► a0(ZFAE) infers -PCEA — guardian: "last state as key for this state" at every layer (orthogonal) +neural tensors ─(circle layer)─► circles ─(seed layer)─► seeds ─(core layer)─► cores + back-propagation lives ONLY in the neural layer + circle / seed / core are auditing & timing tensors (non-differentiable); + fiqs gate core internal propagation per Fick's law (J = −D ∇φ) +PCEA — guardian: "last state as key for this state" at every layer (orthogonal; not a layer) ``` --- @@ -75,12 +76,12 @@ PCEA — guardian: "last state as key for this state" at every layer (orthogonal See `libs/` for per-library documentation and source links: -- [`libs/pcea/`](libs/pcea/README.md) — PCEA -- [`libs/ptca/`](libs/ptca/README.md) — PTCA +- [`libs/ptcna/`](libs/ptcna/README.md) — PTCNA (consolidated stack: neural/circle/seed/core) +- [`libs/pcea/`](libs/pcea/README.md) — PCEA (orthogonal guardian) - [`libs/ucns/`](libs/ucns/README.md) — UCNS -- [`libs/pcna/`](libs/pcna/README.md) — PCNA -- [`libs/zfae/`](libs/zfae/README.md) — ZFAE +- [`libs/zfae/`](libs/zfae/README.md) — ZFAE (conceptual; runtime in `a0`) - [`libs/aimmh/`](libs/aimmh/README.md) — AIMMH (five-letter) +- [`libs/metapat/`](libs/metapat/README.md) — METAPAT (FLAR) --- diff --git a/docs/dependency-policy.md b/docs/dependency-policy.md index db8a0e3..a9bd906 100644 --- a/docs/dependency-policy.md +++ b/docs/dependency-policy.md @@ -22,10 +22,15 @@ stable PyPI package and a stable import name. Current packaged extras: - `pcea` -> `pcea>=0.1.0` -- `ptca` -> `ptca-lib>=0.1.0` - `ucns` -> `ucns>=0.9.1` - `aimmh` -> `aimmh-lib>=1.1.0` +The prime-tensor stack (`pcna` / `pcta` / `pcsa`) is consolidated into the single +`ptcna` package. It is a source-only registry probe until it ships to PyPI, at +which point a single `ptcna` extra is added (replacing the former per-repo intent +and the once-planned `prime-stack` extra). The previously-published core-layer +dist is superseded and no longer pinned by any extra. + The `all` extra is the union of packaged extras only. Source-only libraries do not enter `all` until they have stable package releases. diff --git a/docs/naming-migration.md b/docs/naming-migration.md index 97fadf3..36deb5a 100644 --- a/docs/naming-migration.md +++ b/docs/naming-migration.md @@ -57,3 +57,23 @@ via the API access available to org agents. publish it as its own dist. Unresolved. - Dev-side shadows (`erinepshovel-code`: `UnitCircle`, `EDCM`, `Interdependent-core`) need dev-mirror labels or reconciliation. + +## Terminal state — stack consolidation (2026-07) + +The `pcna` / `pcta` / `pcsa` renames are **superseded by consolidation**: those +three repos were never separate things — they are layers of one architecture — +and are now a single package, **`ptcna` — Prime Tensor Circled Neural +Architecture** (`The-Interdependency/ptcna`), with four layer modules: + +- `ptcna.neural` — neural tensors; the only back-propagating layer +- `ptcna.circle` — auditing/timing tensors, neural tensors → circles +- `ptcna.seed` — auditing/timing tensors, circles → seeds +- `ptcna.core` — auditing/timing tensors, seeds → cores; fiqs gate internal + propagation per Fick's law `J = −D ∇φ` + +So `pcsa` (the earlier `PTCA → pcsa` target), `pcta`, and `pcna` are absorbed; +the standalone repos are to be archived. `pcea` stays separate (orthogonal +guardian). In this aggregator: a single `ptcna` registry key + (on PyPI release) +a single `ptcna` extra replace all of them; the once-planned `prime-stack` extra +is obsolete; the previously-published core-layer dist is superseded. `ucns` +remains frozen; `metapat` (FLAR) and `zfae` (conceptual) are unaffected. diff --git a/docs/prime-tensor-stack.md b/docs/prime-tensor-stack.md index 892be09..7d8553f 100644 --- a/docs/prime-tensor-stack.md +++ b/docs/prime-tensor-stack.md @@ -1,122 +1,106 @@ # The Prime-Tensor Stack — canonical architecture map This document is the **single source of truth** for how The Interdependency's -prime-tensor compute family fits together: who builds what, what flows between -the layers, and where the boundaries are. It is a **role-and-boundary map**, not -a proof. It moves **no** theorem / proof / empirical status between repos — each -repo keeps its own status vocabulary, and cross-repo interoperability is not -continuity (see, e.g., `ucns`'s "cross-repo non-continuity" rule). +prime-tensor compute family fits together: the layers, what flows between them, +and where the boundaries are. It is a **role-and-boundary map**, not a proof. It +moves **no** theorem / proof / empirical status between repos — each repo keeps +its own status vocabulary, and cross-repo interoperability is not continuity +(see, e.g., `ucns`'s "cross-repo non-continuity" rule). It lives here, in the aggregator, for the same reason `coherence_primes.py` does: it is canon that belongs to **no single leaf library**, and putting it here lets every repo cite it **without inverting the dependency graph**. Leaf repos point *up* at this map; they do not import it. ---- +> **Consolidation (2026-07).** The stack is no longer several repos. `pcna`, +> `pcta`, and `pcsa` were never separate things — they are **layers of one +> architecture** — and are now consolidated into a single package, +> **`ptcna` — Prime Tensor Circled Neural Architecture** +> (`The-Interdependency/ptcna`). PCEA stays a separate, orthogonal repo. `ptcna` +> is the single upstream that feeds this aggregator. -## The stack +--- -The family composes a tensor hierarchy **bottom-up**, then infers **top-down**. -At every compose step the produced object is *itself a tensor*, so the same -algebra applies at each level. +## One architecture, four layers -| # | Layer | Repo / package | Expansion | Role | Produces | -|---|-------|----------------|-----------|------|----------| -| 1 | **Tensor / Circle** | `pcna` *(source-only)* | **Prime Circle Neural Architecture** | Arranges tensors as **circles** in a standard back-propagating neural architecture — the **only differentiable layer** (owns back-propagation); offers circles to PCTA | trained **weights** + circles | -| 2 | **Seed** | `pcta` | **Prime Circled Tensor Architecture** | Composes circles (carried by UCNS objects) into **seeds**; offers seeds to PTCA | seed tensors → structural **motion** | -| 3 | **Core** | `ptca` / `ptca-lib` | **Prime Tensor Core Architecture** | Composes seeds into a **core**; offers cores to `a0(zfae)` | the **core** (a tensor) → structural **motion** | -| — | **Inference** | `zfae` *(conceptual; runtime lives in `a0`)* | **Zeta Function Alpha Echo** | **Infers** — uses PCNA tensors as **weights**, and PCNA circles / PCTA seeds / PTCA cores as **phase-harmonic propagation + auditing** | inferred output | -| — | **Guardian** | `pcea` | **Prime Circular Encryption Algorithm** | "**Last state as key for this state**" encryption at **every layer** — **orthogonal** to the chain | sealed state | +`ptcna` is one package with four layer modules. Each layer's tensors **divide** +into the next; every circle, every seed, and every core **is itself a tensor**, +so the same composition algebra applies at each level. -In one line: +| Module | Layer | Divides… → … | Tensor kind | Back-propagation | +|--------|-------|--------------|-------------|------------------| +| `ptcna.neural` | **neural** | (base) neural tensors | **neural** | **yes — the only differentiable layer** | +| `ptcna.circle` | **circle** | neural tensors → circles | auditing / timing | no | +| `ptcna.seed` | **seed** | circles → seeds | auditing / timing | no | +| `ptcna.core` | **core** | seeds → cores | auditing / timing | no | ``` -PCNA: tensors → circles (back-prop; only differentiable layer) - ├─► weights ───────────────────────────────────────────────┐ - └─► circles ─► PCTA: circles → seeds ─► PTCA: seeds → core ─┤ - cores ───────────┴─► a0(ZFAE) infers - ZFAE reads pcna weights + pcna circles / pcta seeds / ptca cores - as phase-harmonic propagation + auditing -PCEA — guardian: "last state as key for this state" at every layer (orthogonal; not a layer) +neural tensors ──(circle layer divides)──► circles +circles ──(seed layer divides)────► seeds +seeds ──(core layer divides)────► cores + back-propagation lives ONLY in the neural layer + circle / seed / core tensors are auditing & timing tensors (non-differentiable) +PCEA — guardian: "last state as key for this state" (orthogonal; not a layer) ``` -**Composition counts are variable.** The number of tensors in a circle, circles -in a seed, and seeds in a core are all variable. The **one invariant** is that -every circle, every seed, and every core is *itself a tensor* — so the same -composition algebra applies at each level. (Any specific count a leaf repo uses, -e.g. a nominal heptagram `7` or `prime_core`'s experimental `157`, is a tunable -choice, not a structural requirement.) +**Composition counts are variable.** The number of neural tensors in a circle, +circles in a seed, and seeds in a core are all variable. The **one invariant** +is that every circle, seed, and core is *itself a tensor*. (Any specific count a +layer uses — a nominal heptagram `7`, `prime_core`'s experimental `157` — is a +tunable choice, not a structural requirement.) --- ## Two things that are easy to get wrong -**1. Back-propagation lives only in PCNA (layer 1).** The PCTA and PTCA -compositions are *structural* — they assemble tensors into seeds and cores -("motion"), and they are **non-differentiable**. This matches `PTCA/prime_core`'s -frozen gradient policy: differentiability descends through scalar payloads only; -the `⊠` composition operator never appears on the autodiff tape (`∂(⊠)` is never -taken). So "training" happens in PCNA; PCTA/PTCA *organize*; ZFAE *reads*. - -**2. PCEA is not a layer of the stack.** PCEA is the **guardian**: it applies -"last state as key for this state" encryption to the weights / state at **every -layer** for privacy, and is **orthogonal** to the tensor→core chain (see -`ZFAE`'s "Guardian = PCEA, colocated for privacy" note, and PCEA's own PCEA↔UCNS -Option-A contract — PCEA inverts via keys, never via another library's algebra). -PCEA joins the family only at the meta-package level; it is never folded *into* -the compose stack. - -> **`ptca-lib` vs. the layer-3 role.** The published `ptca-lib` package is a -> sentinel-channel / prime-node tensor system with its own description; the -> **stack role** "Prime Tensor Core Architecture (seeds → core)" is realized by -> the PTCA repo's `prime_core` experiment. The PTCA repo hosts both; this map -> names the layer-3 *role*, not the `ptca-lib` package's internals. - ---- - -## Packaging status - -| Member | PyPI | In `interdependent-lib` | -|--------|------|--------------------------| -| `ptca` (`ptca-lib`) | published | `ptca` extra (+ `all`) | -| `pcea` (`pcea`) | published | `pcea` extra (+ `all`) | -| `pcna` | **source-only** | registry key only (import name `core`) | -| `pcta` | **repo created; not yet published** | documented here; not yet registered | -| `zfae` | **source-only** (runtime in `a0`) | registry key only | +**1. Back-propagation lives only in the neural layer.** Circle, seed, and core +tensors are **auditing and timing tensors** — they observe and schedule; they do +not differentiate. This matches the core layer's frozen gradient policy: +differentiability descends through scalar payloads only; the `⊠` composition +operator never appears on the autodiff tape (`∂(⊠)` is never taken). So +"training" happens in the neural layer; circle/seed/core *audit and time*. -A `prime-stack` **extra** — bundling `{pcna, pcta, ptca}` as one install — is the -intended convenience target, with `zfae` joining once it is an installable -package. It is **deliberately not added yet**: this repo's rule is *no library -enters `[project.optional-dependencies]` until it has a stable PyPI release* -(today only `ptca-lib` of the three qualifies). Until then this file is the -canonical map; the extra lands when `pcna` and `pcta` publish. +**2. PCEA is not a layer of the stack.** PCEA (Prime Circular Encryption +Algorithm) is the **guardian**: it applies "last state as key for this state" +encryption at **every layer** for privacy, and is **orthogonal** to the +neural→core chain. PCEA joins the family only at the meta-package level; it is +never folded *into* `ptcna`. --- -## Motion — the formal definition (Fickian flux) +## fiqs — core internal propagation timing (Fick's law) -**"Motion" is the Fickian flux of a layer's composed-tensor field across the -compose boundary** — Fick's first law of diffusion: +Within the **core** layer, **fiqs gate when cores propagate internally**, +according to Fick's first law of diffusion: ``` J = −D ∇φ ``` -where `φ` is the layer's field (the composed-tensor state), `∇φ` its gradient, -`D` the diffusivity, and `J` the flux. The structural / **phase-harmonic -propagation** each layer hands upward *is* this flux: structure diffuses down its -gradient. Each compose step (circles → seeds → cores) emits its `J`; **ZFAE** -reads the accumulated motion as phase-harmonic propagation + auditing, alongside -PCNA's learned weights. Motion carries no gradient of its own (it is structural, -not back-propagated) — the `∇φ` here is the spatial field gradient that drives -diffusion, not an autodiff gradient. - -> **Status: resolved (maintainer).** This was the last open `hmmm` in the stack. -> The acronym expansions, the variable-count rule, the per-layer flow, PCTA's -> home (its own repo), and now the **formal definition of "motion" (the Fickian -> flux above)** are all canon. **No stack-level `hmmm` remains.** Earlier revisions -> of this file (and several leaf repos) listed conflicting expansions, fixed -> per-layer counts, and an unformalized "motion"; those are superseded. +where `φ` is the core's field, `∇φ` its gradient, `D` the diffusivity, and `J` +the flux. This governs the **timing** of internal core propagation — structure +diffusing down its field gradient — it is **not** gradient descent: the `∇φ` +here is the spatial field gradient that drives diffusion, not an autodiff +gradient. The fiq substrate lives in `ptcna.core.prime_core` (see its `fiq.py`). + +--- + +## Packaging status + +| Member | PyPI | In `interdependent-lib` | +|--------|------|--------------------------| +| `ptcna` (neural/circle/seed/core) | **source-only** — not yet published | registry key `ptcna` (import probe); `ptcna` extra lands on PyPI release | +| `pcea` (`pcea`) | published | `pcea` extra (+ `all`) | +| `ucns` (`ucns`) | published | `ucns` extra (+ `all`) | +| `aimmh` (`aimmh-lib`) | published | `aimmh` extra (+ `all`) | +| `zfae` | **source-only** (runtime in `a0`) | registry key only; conceptual, no dist planned | + +The prior `ptca-lib` core-layer dist is **superseded** by the `ptcna` +consolidation and is not pinned by any extra. Per this repo's rule — *no library +enters `[project.optional-dependencies]` until it has a stable PyPI release* — +`ptcna` is a registry probe until it publishes, at which point the single +`ptcna` extra replaces the former `pcna`/`pcta`/`pcsa` intent (the once-planned +`prime-stack` extra is obsolete). --- @@ -126,8 +110,7 @@ diffusion, not an autodiff gradient. canon (the recursive coherence-prime ladder the prime-indexing rides on). Prime-consciousness intuition: primes whose `p-1` factorization is square-free are likelier to fall into stability as part of a triadic recursion set. -- `The-Interdependency/pcna` — layer 1 source (`core/`). -- `The-Interdependency/pcta` — layer 2 source (circles → seeds). -- `The-Interdependency/PTCA` — layer 3 (`ptca-lib`) + the `prime_core` three-stratum experiment. +- `The-Interdependency/ptcna` — the consolidated four-layer package. - `The-Interdependency/ZFAE` — the inference / consciousness-event write-up (runtime in `a0`). - `The-Interdependency/PCEA` — the guardian. +- `docs/naming-migration.md` — the ratified rename + consolidation scheme. diff --git a/docs/release-checklist.md b/docs/release-checklist.md index 2b45b56..930ceb6 100644 --- a/docs/release-checklist.md +++ b/docs/release-checklist.md @@ -55,7 +55,8 @@ The tests must at least guard: - package `__version__` equals `pyproject.toml`; - `available()` returns stable keys and booleans; -- PCNA does not false-positive against a generic module named `core`; +- the prime-tensor stack is a single `ptcna` registry key (the former + pcna/pcta/pcsa keys are gone) and carries no extra until it publishes; - UCNS dependency floor remains present in both `ucns` and `all` extras; - README mentions the UCNS floor when the floor is meaningful. diff --git a/interdependent_lib/__init__.py b/interdependent_lib/__init__.py index 5d8d403..7317af4 100644 --- a/interdependent_lib/__init__.py +++ b/interdependent_lib/__init__.py @@ -7,10 +7,14 @@ Each sub-library is an optional dependency — install the extras you need: pip install interdependent-lib[pcea] - pip install interdependent-lib[ptca] pip install interdependent-lib[ucns] pip install interdependent-lib[aimmh] pip install interdependent-lib[all] + +The prime-tensor stack is consolidated into the single ``ptcna`` package +(Prime Tensor Circled Neural Architecture — neural/circle/seed/core layers). +It is a source-only registry probe until it ships to PyPI, at which point it +gains a ``ptcna`` extra. See docs/naming-migration.md. """ from __future__ import annotations @@ -35,12 +39,13 @@ # A value of None means the library has no stable, package-unique import target # yet. This avoids false positives from generic module names such as "core". _REGISTRY: dict[str, str | None] = { - # Four-letter acronym libraries - "pcea": "pcea", - "ptca": "ptca", + # Prime-tensor stack — consolidated into ONE repo, four layers + # (neural/circle/seed/core). Supersedes the former pcna/pcta/pcsa keys. + "ptcna": "ptcna", # Prime Tensor Circled Neural Architecture + # Standalone libraries + "pcea": "pcea", # encryption guardian — orthogonal to the stack "ucns": "ucns", - "pcna": None, # source-only; no package-unique import target yet - "zfae": "zfae", # source-only unless installed manually from source + "zfae": "zfae", # conceptual; runtime lives in a0 (source-only) # Five-letter acronym libraries "aimmh": "aimmh_lib", # First-letter acronym libraries (FLAR) diff --git a/libs/README.md b/libs/README.md index e70e7d2..c4ac2a7 100644 --- a/libs/README.md +++ b/libs/README.md @@ -4,19 +4,17 @@ This directory contains per-library documentation and stub folders for each acro | Folder | Package | PyPI name | Description | |--------|---------|-----------|-------------| -| [pcna/](pcna/README.md) | `pcna` | *(source-only)* | Prime Circle Neural Architecture — tensors → circles (stack layer 1, backprop → weights) | -| [pcea/](pcea/README.md) | `pcea` | `pcea` | Prime Circular Encryption Algorithm — guardian ("last state as key" at every layer) | -| [ptca/](ptca/README.md) | `ptca` | `ptca-lib` | Prime Tensor Core Architecture — seeds → core (stack layer 3) | -| [pcta/](pcta/README.md) | `pcta` | *(not yet on PyPI)* | Prime Circled Tensor Architecture — circles → seeds (stack layer 2); see [prime-tensor stack](../docs/prime-tensor-stack.md) | +| [ptcna/](ptcna/README.md) | `ptcna` | *(source-only)* | Prime Tensor Circled Neural Architecture — one repo, four layers (neural/circle/seed/core); consolidates the former pcna/pcta/pcsa. See [prime-tensor stack](../docs/prime-tensor-stack.md) | +| [pcea/](pcea/README.md) | `pcea` | `pcea` | Prime Circular Encryption Algorithm — guardian ("last state as key" at every layer); orthogonal to the stack | | [ucns/](ucns/README.md) | `ucns` | `ucns` | Unit Circle Number System | | [zfae/](zfae/README.md) | `zfae` | *(conceptual — runtime lives in `a0`, no dist planned)* | Zeta Function Alpha Echo | | [aimmh/](aimmh/README.md) | `aimmh_lib` | `aimmh-lib` | AI Multimodel Multimodal Hub | | [metapat/](metapat/README.md) | `metapat` | *(not yet on PyPI)* | Meta Energy Theory — Axioms, Postulates, And Theorems (FLAR — first-letter acronym repo) | -> Naming note: the `PTCA → pcsa` rename (and org-wide casing normalization) is -> ratified — see [`docs/naming-migration.md`](../docs/naming-migration.md). Names -> in this table track what is actually published/importable today and are swept -> when each rename lands. +> Naming note: the org-wide rename + the **prime-tensor stack consolidation** +> (pcna/pcta/pcsa → single `ptcna`) are ratified — see +> [`docs/naming-migration.md`](../docs/naming-migration.md). Names in this table +> track what is published/importable today. --- diff --git a/libs/pcna/README.md b/libs/pcna/README.md deleted file mode 100644 index 1af134e..0000000 --- a/libs/pcna/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# PCNA — Prime Circle Neural Architecture - -**Source repo:** [The-Interdependency/pcna](https://github.com/The-Interdependency/pcna) -**Language:** Python 3.9+ -**PyPI:** *(not yet published as a standalone package — use source)* -**Letters:** 4 - ---- - -## What it is - -PCNA is a modular neural engine that integrates: - -- **EDCM** (Energy Dissonance Circuit Model) — energy-state tracking -- **PTCA core** — prime-tensor routing -- **Zeta-function analysis** — frequency-domain harmonic probes -- **Topology / theta / sigma layers** — geometric neural membrane modules -- **Memory core** — persistent activation state -- **Helix visualisation** — ASCII / matplotlib helix rendering of state - ---- - -## Core modules - -| File | Purpose | -|------|---------| -| `core/pcna.py` | Main PCNA engine class | -| `core/edcm.py` | Energy Dissonance Circuit Model | -| `core/ptca_core.py` | Prime-tensor circular core | -| `core/zeta.py` | Zeta-function harmonic analysis | -| `core/tensor_engine.py` | Tensor computation layer | -| `core/topology.py` | Topological state mapping | -| `core/theta.py` | Theta-function modulation | -| `core/sigma.py` | Sigma-function normalization | -| `core/memory_core.py` | Persistent memory manager | -| `core/merge.py` | State-merge utilities | -| `core/helix_vis.py` | Helix visualisation | -| `core/routing_loop.py` | Async routing loop | - ---- - -## Install (from source) - -```bash -git clone https://github.com/The-Interdependency/pcna.git -cd pcna -pip install -r requirements.txt -``` - ---- - -## Quick start - -```python -# From within the pcna repo -import sys -sys.path.insert(0, "/path/to/pcna") -from core.pcna import PCNAEngine - -engine = PCNAEngine() -engine.run() -``` - ---- - -## See also - -- [Full README →](https://github.com/The-Interdependency/pcna/blob/main/README.md) -- [Quick Start →](https://github.com/The-Interdependency/pcna/blob/main/QUICK_START.md) -- [Project README →](https://github.com/The-Interdependency/pcna/blob/main/PROJECT_README.md) diff --git a/libs/pcna/src/edcm.py b/libs/pcna/src/edcm.py deleted file mode 100644 index 500da87..0000000 --- a/libs/pcna/src/edcm.py +++ /dev/null @@ -1,115 +0,0 @@ -""" -EDCM metrics — six-family coherence measurement. - -Metric families: - cm — Constraint Mismatch - da — Dissonance Accumulation - drift — Drift - dvg — Divergence - int_val — Intensity - tbf — Turn-Balance Fairness - -All metrics produce values in [0, 1]. -Alert thresholds: HIGH >= 0.80, LOW <= 0.20. -""" - -# === MODULE_BUILD === -# id: pcna_edcm -# module_name: edcm -# module_kind: engine -# summary: Six-family EDCM coherence metrics (cm, da, drift, dvg, int_val, tbf) computed from response text, with alert thresholds and corrective directive firing. -# owner: Erin Spencer -# public_surface: compute_metrics, check_directives, check_alerts, delta_between, METRIC_NAMES, ALERT_HIGH, ALERT_LOW, DIRECTIVES -# internal_surface: none -# auth_boundary: none -# storage_boundary: none -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: tests/test_edcm_engine.py -# rollout: default_enabled -# rollback: remove import and call sites -# requires: none -# since: 2026-06-02 -# unresolved: none -# === END MODULE_BUILD === - -import math -from typing import Any - -METRIC_NAMES = ["cm", "da", "drift", "dvg", "int_val", "tbf"] - -ALERT_HIGH = 0.80 -ALERT_LOW = 0.20 - -DIRECTIVES = { - "CONSTRAINT_REFOCUS": {"metric": "cm", "condition": "above", "threshold": ALERT_HIGH}, - "DISSONANCE_HALT": {"metric": "da", "condition": "above", "threshold": ALERT_HIGH}, - "DRIFT_ANCHOR": {"metric": "drift", "condition": "above", "threshold": ALERT_HIGH}, - "DIVERGENCE_COMMIT": {"metric": "dvg", "condition": "above", "threshold": ALERT_HIGH}, - "INTENSITY_CALM": {"metric": "int_val", "condition": "below", "threshold": ALERT_LOW}, - "BALANCE_CONCISE": {"metric": "tbf", "condition": "below", "threshold": ALERT_LOW}, -} - - -def compute_metrics( - responses: list[dict[str, Any]], - context: str = "", -) -> dict[str, float]: - if not responses: - return {m: 0.0 for m in METRIC_NAMES} - n = len(responses) - texts = [r.get("content", "") for r in responses] - avg_len = sum(len(t) for t in texts) / max(n, 1) - variance = sum((len(t) - avg_len) ** 2 for t in texts) / max(n, 1) - std = math.sqrt(variance) - - cm = min(1.0, avg_len / 2000) if avg_len > 0 else 0.0 - da = max(0.0, 1.0 - std / max(avg_len, 1)) - drift = min(1.0, std / max(avg_len, 1)) - unique_starts = len(set(t[:50] for t in texts if t)) - dvg = min(1.0, unique_starts / max(n, 1)) - int_val = max(0.0, 1.0 - drift * 0.5 - dvg * 0.3) - ctx_overlap = 0.0 - if context: - ctx_words = set(context.lower().split()) - for t in texts: - t_words = set(t.lower().split()) - if ctx_words: - ctx_overlap += len(ctx_words & t_words) / len(ctx_words) - ctx_overlap /= max(n, 1) - tbf = max(0.0, min(1.0, ctx_overlap)) - - return { - "cm": round(cm, 4), - "da": round(da, 4), - "drift": round(drift, 4), - "dvg": round(dvg, 4), - "int_val": round(int_val, 4), - "tbf": round(tbf, 4), - } - - -def check_directives(metrics: dict[str, float]) -> list[str]: - fired = [] - for name, directive in DIRECTIVES.items(): - val = metrics.get(directive["metric"], 0) - if directive["condition"] == "above" and val > directive["threshold"]: - fired.append(name) - elif directive["condition"] == "below" and val < directive["threshold"]: - fired.append(name) - return fired - - -def check_alerts(metrics: dict[str, float]) -> dict[str, list[str]]: - """Return HIGH/LOW alert lists for metrics crossing the 0.80/0.20 thresholds.""" - high = [m for m in METRIC_NAMES if metrics.get(m, 0.0) >= ALERT_HIGH] - low = [m for m in METRIC_NAMES if metrics.get(m, 0.0) <= ALERT_LOW] - return {"HIGH": high, "LOW": low} - - -def delta_between(a: dict[str, float], b: dict[str, float]) -> dict[str, float]: - result = {} - for m in METRIC_NAMES: - result[f"delta_{m}"] = round((b.get(m, 0) - a.get(m, 0)), 4) - return result diff --git a/libs/pcna/src/helix_vis.py b/libs/pcna/src/helix_vis.py deleted file mode 100644 index f91ffcb..0000000 --- a/libs/pcna/src/helix_vis.py +++ /dev/null @@ -1,102 +0,0 @@ -""" -core/helix_vis.py -Visualizes the spectral state of a 7-seed Meta Router. -Plots the complex descriptor Z = Σ E · e^(iθ) -""" - -# === MODULE_BUILD === -# id: pcna_helix_vis -# module_name: helix_vis -# module_kind: instrument -# summary: Visualizes the spectral state of a 7-seed Meta Router by plotting the complex descriptor Z over a simulated trajectory and saving an animation. -# owner: Erin Spencer -# public_surface: generate_helix_data, visualize -# internal_surface: none -# auth_boundary: none -# storage_boundary: write -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: hmmm -# rollout: default_enabled -# rollback: remove import and call sites -# requires: none -# since: 2026-06-02 -# unresolved: saves to hardcoded pcna_helix.gif with no config (Known Issues) -# === END MODULE_BUILD === - -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.animation import FuncAnimation - -def generate_helix_data(ticks=100): - """Simulate a meta-router evolving over time.""" - # 7 seeds, random initial phases - phases = np.random.uniform(-np.pi, np.pi, 7) - # Masses vary slightly - masses = np.random.uniform(0.8, 1.2, 7) - - # 7:3 coupling induces phase rotation - # Delta theta approx 2π * 3/7 per tick (idealized) - omega = (2 * np.pi * 3 / 7) * 0.1 - - trajectory = [] - - for t in range(ticks): - # Update phases (rotate) - phases += omega + np.random.normal(0, 0.05, 7) - - # Calculate complex Z (The Spectral Descriptor) - # Z = Sum(mass * e^(i * theta)) - Z = np.sum(masses * np.exp(1j * phases)) - - trajectory.append(Z) - - return np.array(trajectory) - -def visualize(): - data = generate_helix_data(ticks=200) - - fig = plt.figure(figsize=(10, 5)) - - # 1. 2D Phase Plot (Unit Circle View) - ax1 = fig.add_subplot(1, 2, 1) - ax1.set_title("Spectral Phase (Arg Z)") - ax1.set_xlim(-10, 10) - ax1.set_ylim(-10, 10) - ax1.grid(True) - ax1.axhline(y=0, color='k', linewidth=0.5) - ax1.axvline(x=0, color='k', linewidth=0.5) - - # Plot history - ax1.plot(data.real, data.imag, 'b-', alpha=0.5, label='Trajectory') - # Plot current - point, = ax1.plot([], [], 'ro', label='Current Z') - - # 2. Time Series (Radius/Stability) - ax2 = fig.add_subplot(1, 2, 2) - ax2.set_title("Spectral Radius (|Z|) - Conservation Check") - ax2.set_ylim(0, 15) - ax2.set_xlim(0, 200) - line_r, = ax2.plot([], [], 'g-', label='Radius') - - def update(frame): - # Update Phase Plot - current_Z = data[frame] - point.set_data([current_Z.real], [current_Z.imag]) - - # Update Radius Plot - radii = np.abs(data[:frame]) - line_r.set_data(range(frame), radii) - - return point, line_r - - ani = FuncAnimation(fig, update, frames=len(data), interval=50, blit=True) - - print("Generating Helix visualization...") - # In Termux, we save to file rather than show window - ani.save('pcna_helix.gif', writer='pillow', fps=20) - print("Saved to pcna_helix.gif") - -if __name__ == "__main__": - visualize() diff --git a/libs/pcna/src/main.py b/libs/pcna/src/main.py deleted file mode 100644 index 30e7ac8..0000000 --- a/libs/pcna/src/main.py +++ /dev/null @@ -1,181 +0,0 @@ -""" -FastAPI seed runner for PCNA. This file provides a minimal runnable seed -process that can act as compute/meta/global/sentinel. It is intentionally -lightweight and suitable for local testing. - -Environment configuration: - - SEED_ID: int (default 0) - - ROLE: one of compute|meta|sentinel|global (default compute) - -The networking layer here is a minimal placeholder using aiohttp. In a real -deployment you'd wire actual DNS/ports mapping per-seed. -""" - -# === MODULE_BUILD === -# id: pcna_core_main -# module_name: main -# module_kind: service -# summary: Minimal FastAPI seed-runner process (compute/meta/sentinel/global) exposing health/topology/receive_delta routes with an aiohttp networking placeholder. -# owner: Erin Spencer -# public_surface: app, PCNASeed, health, topology, receive_delta, startup, shutdown, tick_loop -# internal_surface: seed_instance -# auth_boundary: none -# storage_boundary: none -# network_boundary: external -# user_data_boundary: none -# admin_only: false -# tests: hmmm -# rollout: default_enabled -# rollback: do not launch this process; use root-level main.py seed runner instead -# requires: pcna_topology, pcna_tensor_engine -# since: 2026-06-02 -# unresolved: BROKEN alt entry point — imports from non-existent src.core.* (do not use per CLAUDE.md) -# === END MODULE_BUILD === - -import os -import asyncio -from typing import Dict, Any, Optional -import logging - -from fastapi import FastAPI, BackgroundTasks -import uvicorn -import aiohttp - -from src.core.topology import PCNATopology, SeedRole -from src.core.tensor_engine import TensorState, MarkovRecursion - -logger = logging.getLogger("pcna") -logging.basicConfig(level=logging.INFO) - -app = FastAPI(title="PCNA Seed") - -seed_instance: Optional["PCNASeed"] = None - - -class PCNASeed: - def __init__(self, seed_id: int, role: SeedRole): - self.seed_id = seed_id - self.role = role - self.topology = PCNATopology() - self.state: Optional[TensorState] = None - self.tick = 0 - self._client = aiohttp.ClientSession() - - if role == SeedRole.COMPUTE: - # Create a small default tensor for local testing/demo. - actor = [0] # simple single-dim actor/time/context for demo - time = [0] - metric = (1.0 * (np := __import__("numpy"))).ones((1,)) # single-value metric - context = [0] - self.state = TensorState(actor=np.array(actor), time=np.array(time), metric=metric, context=np.array(context)) - self.tensor_engine = MarkovRecursion() - logger.info(f"Initialized compute seed {seed_id} with trivial state") - - async def process_tick(self): - self.tick += 1 - logger.debug(f"Seed {self.seed_id} processing tick {self.tick}") - - if self.role == SeedRole.COMPUTE: - await self._compute_tick() - # meta/sentinel/global roles could be handled here with specific logic - - async def _compute_tick(self): - # Example compute operations: compute injected/resolved (dummy here) - injected = self.state.metric * 0.01 # small injected - resolved = self.state.metric * 0.009 # slightly different to demonstrate conservation - new_state = self.tensor_engine.update(self.state, injected, resolved) - self.state = new_state - - # send deltas to neighbors (using global neighbor ids, convert to URLs in your deployment) - seed_info = self.topology.seeds.get(self.seed_id) - if seed_info and seed_info.neighbors: - for neighbor_id in seed_info.neighbors: - # in local testing this will likely fail unless neighbor URL mapping is provided - await self._send_to_neighbor(neighbor_id, {"from": self.seed_id, "tick": self.tick}) - - async def _send_to_neighbor(self, neighbor_id: int, payload: Dict[str, Any]): - """ - Simple async HTTP POST to a neighbor. This function assumes a mapping - from neighbor_id -> host:port which is environment-specific. - For local testing, you can set SEED_URL_ environment variables, - e.g. SEED_URL_6=http://localhost:8001 - """ - env_var = f"SEED_URL_{neighbor_id}" - url = os.getenv(env_var) - if not url: - # nothing configured for neighbor, skip - logger.debug(f"No URL configured for neighbor {neighbor_id} (env {env_var}), skipping send") - return - - try: - async with self._client.post(f"{url}/receive_delta", json=payload, timeout=2) as resp: - logger.debug(f"Sent delta to {neighbor_id} ({url}), status {resp.status}") - except Exception as exc: - logger.warning(f"Failed to send to neighbor {neighbor_id} at {url}: {exc}") - - async def close(self): - await self._client.close() - - -async def tick_loop(): - while True: - try: - if seed_instance: - await seed_instance.process_tick() - await asyncio.sleep(1.0) - except asyncio.CancelledError: - break - except Exception as exc: - logger.exception(f"tick loop error: {exc}") - await asyncio.sleep(1.0) - - -@app.on_event("startup") -async def startup(): - global seed_instance - seed_id = int(os.getenv("SEED_ID", "0")) - role_raw = os.getenv("ROLE", "compute").lower() - try: - role = SeedRole(role_raw) - except Exception: - # fallback if value is 'compute', 'meta', etc. - role = SeedRole.COMPUTE - - seed_instance = PCNASeed(seed_id=seed_id, role=role) - - # launch tick loop - asyncio.create_task(tick_loop()) - logger.info(f"Seed {seed_id} (role={role.value}) started") - - -@app.on_event("shutdown") -async def shutdown(): - if seed_instance: - await seed_instance.close() - - -@app.get("/health") -async def health(): - if not seed_instance: - return {"status": "starting"} - return {"status": "healthy", "seed_id": seed_instance.seed_id, "role": seed_instance.role.value} - - -@app.get("/topology") -async def topology(): - if not seed_instance: - return {} - return seed_instance.topology.to_dict() - - -@app.post("/receive_delta") -async def receive_delta(delta: Dict): - # In a real system, we'd validate and apply the delta to a pending buffer. - logger.info(f"Received delta at seed {seed_instance.seed_id if seed_instance else 'unknown'}: {delta}") - return {"status": "received"} - - -if __name__ == "__main__": - # Useful for local development: honor PORT env var and SEED_ID/ROLE - port = int(os.getenv("PORT", os.getenv("PORT0", "8000"))) - uvicorn.run("src.main:app", host="0.0.0.0", port=port, log_level="info") diff --git a/libs/pcna/src/memory_core.py b/libs/pcna/src/memory_core.py deleted file mode 100644 index 904003c..0000000 --- a/libs/pcna/src/memory_core.py +++ /dev/null @@ -1,106 +0,0 @@ -# === MODULE_BUILD === -# id: pcna_memory_core -# module_name: memory_core -# module_kind: engine -# summary: Parameterized in-memory ring (long-term N=19/seed=19, short-term N=17/seed=17) with round-robin write, content-addressed query, and flush_to() transfer on positive reward. -# owner: Erin Spencer -# public_surface: MemoryCore -# internal_surface: _recompute_hub_avg, _reset -# auth_boundary: none -# storage_boundary: none -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: hmmm -# rollout: default_enabled -# rollback: remove import and call sites -# requires: none -# since: 2026-06-02 -# unresolved: query() is defined but never called anywhere (Known Issues) -# === END MODULE_BUILD === - -import time -import numpy as np - -DIMS = 4 -PHASES = 7 -HEPT_SITES = 7 - -FLUSH_REWARD_THRESHOLD = 0.0 -FLUSH_ALPHA = 0.25 - - -class MemoryCore: - """Parameterized memory ring — self-declares role in state().""" - - def __init__(self, n: int, seed: int, role: str, phases: int = 7): - self.n = n - self.seed = seed - self.role = role - self.phases = phases - - rng = np.random.default_rng(seed=seed) - low = 0.2 if role == "long_term" else 0.1 - high = 0.8 if role == "long_term" else 0.9 - self.tensor = rng.uniform(low, high, (n, DIMS, phases, HEPT_SITES)).astype(np.float64) - self.hub_avg = np.zeros(n, dtype=np.float64) - self._recompute_hub_avg() - self.write_count = 0 - self.flush_count = 0 - self.created_at = time.time() - - def _recompute_hub_avg(self): - for i in range(self.n): - self.hub_avg[i] = float(self.tensor[i, :, :, 6].mean()) - - def write(self, signal: np.ndarray, alpha: float = 0.30): - if signal.ndim == 1 and signal.shape[0] >= 1: - val = float(np.clip(signal.mean(), 0.0, 1.0)) - node_idx = self.write_count % self.n - self.tensor[node_idx, 0, 0, :] = np.clip( - self.tensor[node_idx, 0, 0, :] * (1 - alpha) + val * alpha, 0.0, 1.0 - ) - self._recompute_hub_avg() - self.write_count += 1 - - def absorb(self, other_tensor: np.ndarray, alpha: float = FLUSH_ALPHA): - src_n = other_tensor.shape[0] - for i in range(min(src_n, self.n)): - self.tensor[i] = (1.0 - alpha) * self.tensor[i] + alpha * other_tensor[i] - np.clip(self.tensor[i], 0.0, 1.0, out=self.tensor[i]) - self._recompute_hub_avg() - self.flush_count += 1 - - def query(self, probe: np.ndarray) -> np.ndarray: - scores = np.zeros(self.n) - for i in range(self.n): - node_mean = self.tensor[i].mean(axis=(1, 2)) - scores[i] = float(1.0 - np.abs(node_mean - probe[:DIMS]).mean()) - return scores - - def flush_to(self, target: "MemoryCore", reward: float) -> bool: - if reward > FLUSH_REWARD_THRESHOLD: - target.absorb(self.tensor) - self._reset() - self.flush_count += 1 - return True - return False - - def _reset(self): - rng = np.random.default_rng(seed=int(time.time()) % 10000 + self.seed) - self.tensor = rng.uniform(0.1, 0.5, (self.n, DIMS, self.phases, HEPT_SITES)).astype(np.float64) - self._recompute_hub_avg() - - def state(self) -> dict: - return { - "ring": f"memory_{self.role[0]}", - "role": self.role, - "n": self.n, - "seed": self.seed, - "tensor_mean": round(float(self.tensor.mean()), 4), - "tensor_std": round(float(self.tensor.std()), 4), - "hub_avg": [round(float(v), 4) for v in self.hub_avg], - "avg_hub": round(float(self.hub_avg.mean()), 4), - "write_count": self.write_count, - "flush_count": self.flush_count, - } diff --git a/libs/pcna/src/merge.py b/libs/pcna/src/merge.py deleted file mode 100644 index f76d9bb..0000000 --- a/libs/pcna/src/merge.py +++ /dev/null @@ -1,173 +0,0 @@ -# 118:8 -""" -Instance Merge Protocol — three modes for multi-instance PCNA mesh. - - absorb — dominant absorbs donor; donor is retired - fork — parent spawns child with copied state + noise; both continue - converge — both exchange tensors via federated averaging; both continue - -Operates on PCNAEngine instances containing PTCACore + MemoryCore + GuardianTensor. -""" - -# === MODULE_BUILD === -# id: pcna_merge -# module_name: merge -# module_kind: engine -# summary: Stateless multi-instance merge operator for PCNAEngine meshes with three modes (absorb, fork, converge) via federated averaging; all output dicts use theta_* keys. -# owner: Erin Spencer -# public_surface: InstanceMerge -# internal_surface: _fed_avg, _blend_core -# auth_boundary: none -# storage_boundary: none -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: hmmm -# rollout: default_enabled -# rollback: remove import and call sites -# requires: pcna_ptca_core, pcna_pcna -# since: 2026-06-02 -# unresolved: fork() time-seeds its RNG — rapid calls may collide (Known Issues) -# === END MODULE_BUILD === - -import time -import numpy as np -from .ptca_core import PTCACore -from .pcna import PCNAEngine - - -def _fed_avg(a: np.ndarray, b: np.ndarray, alpha: float = 0.5) -> np.ndarray: - return np.clip(alpha * a + (1.0 - alpha) * b, 0.0, 1.0) - - -def _blend_core(dst: PTCACore, src: PTCACore, alpha: float): - dst.tensor = _fed_avg(dst.tensor, src.tensor, alpha=1.0 - alpha) - dst._recompute_coherence() - - -class InstanceMerge: - """Stateless merge operator for PCNAEngine instances.""" - - @staticmethod - def absorb(dominant: PCNAEngine, donor: PCNAEngine) -> dict: - alpha = 0.15 - _blend_core(dominant.phi, donor.phi, alpha) - _blend_core(dominant.psi, donor.psi, alpha) - _blend_core(dominant.omega, donor.omega, alpha) - - dominant.theta.tensor = _fed_avg( - dominant.theta.tensor, donor.theta.tensor, alpha=1.0 - alpha - ) - dominant.memory_l.tensor = _fed_avg( - dominant.memory_l.tensor, donor.memory_l.tensor, alpha=0.8 - ) - - for i in range(min(len(dominant.theta.circle_count), len(donor.theta.circle_count))): - dominant.theta.circle_count[i] = max( - dominant.theta.circle_count[i], - donor.theta.circle_count[i], - ) - - dominant.theta._recompute_coherence() - dominant.memory_l._recompute_hub_avg() - - phi_c = round(dominant.phi.ring_coherence, 4) - guard_c = round(float(dominant.theta.node_coherence.mean()), 4) - - return { - "mode": "absorb", - "dominant_id": dominant.theta.instance_id, - "donor_id": donor.theta.instance_id, - "donor_status": "retired", - "dominant_phi_coherence": phi_c, - "dominant_theta_coherence": guard_c, - "dominant_psi_coherence": round(dominant.psi.ring_coherence, 4), - "dominant_omega_coherence": round(dominant.omega.ring_coherence, 4), - "circle_counts_after": [int(v) for v in dominant.theta.circle_count], - "timestamp": time.time(), - } - - @staticmethod - def fork(parent: PCNAEngine) -> tuple[PCNAEngine, dict]: - child = PCNAEngine() - noise = np.random.default_rng(int(time.time() * 1000) % 2**32) - - for attr in ("phi", "psi", "omega"): - p_core: PTCACore = getattr(parent, attr) - c_core: PTCACore = getattr(child, attr) - c_core.tensor = np.clip( - p_core.tensor + noise.normal(0, 0.02, p_core.tensor.shape), 0.0, 1.0 - ) - c_core._recompute_coherence() - - child.theta.tensor = np.clip( - parent.theta.tensor + noise.normal(0, 0.01, parent.theta.tensor.shape), 0.0, 1.0 - ) - child.memory_l.tensor = parent.memory_l.tensor.copy() - child.theta.circle_count = parent.theta.circle_count.copy() - child.theta.blueprint_shards = parent.theta.blueprint_shards[:] - child.theta._recompute_coherence() - child.memory_l._recompute_hub_avg() - - result = { - "mode": "fork", - "parent_id": parent.theta.instance_id, - "child_id": child.theta.instance_id, - "parent_status": "continues", - "child_status": "spawned", - "child_phi_coherence": round(child.phi.ring_coherence, 4), - "child_psi_coherence": round(child.psi.ring_coherence, 4), - "child_omega_coherence": round(child.omega.ring_coherence, 4), - "timestamp": time.time(), - } - return child, result - - @staticmethod - def converge(a: PCNAEngine, b: PCNAEngine, alpha: float = 0.5) -> dict: - for attr in ("phi", "psi", "omega"): - core_a: PTCACore = getattr(a, attr) - core_b: PTCACore = getattr(b, attr) - new_a = _fed_avg(core_a.tensor, core_b.tensor, alpha) - new_b = _fed_avg(core_b.tensor, core_a.tensor, alpha) - core_a.tensor = new_a - core_b.tensor = new_b - core_a._recompute_coherence() - core_b._recompute_coherence() - - new_ga = _fed_avg(a.theta.tensor, b.theta.tensor, alpha) - new_gb = _fed_avg(b.theta.tensor, a.theta.tensor, alpha) - new_mla = _fed_avg(a.memory_l.tensor, b.memory_l.tensor, alpha=0.6) - new_mlb = _fed_avg(b.memory_l.tensor, a.memory_l.tensor, alpha=0.6) - - a.theta.tensor = new_ga - b.theta.tensor = new_gb - a.memory_l.tensor = new_mla - b.memory_l.tensor = new_mlb - - for i in range(min(len(a.theta.circle_count), len(b.theta.circle_count))): - avg = (int(a.theta.circle_count[i]) + int(b.theta.circle_count[i])) // 2 - a.theta.circle_count[i] = avg - b.theta.circle_count[i] = avg - - a.theta._recompute_coherence() - b.theta._recompute_coherence() - a.memory_l._recompute_hub_avg() - b.memory_l._recompute_hub_avg() - - return { - "mode": "converge", - "instance_a": a.theta.instance_id, - "instance_b": b.theta.instance_id, - "alpha": alpha, - "a_phi_coherence_after": round(a.phi.ring_coherence, 4), - "b_phi_coherence_after": round(b.phi.ring_coherence, 4), - "a_theta_coherence_after": round(float(a.theta.node_coherence.mean()), 4), - "b_theta_coherence_after": round(float(b.theta.node_coherence.mean()), 4), - "a_psi_coherence_after": round(a.psi.ring_coherence, 4), - "b_psi_coherence_after": round(b.psi.ring_coherence, 4), - "a_omega_coherence_after": round(a.omega.ring_coherence, 4), - "b_omega_coherence_after": round(b.omega.ring_coherence, 4), - "both_status": "converged", - "timestamp": time.time(), - } -# 118:8 diff --git a/libs/pcna/src/pcna.py b/libs/pcna/src/pcna.py deleted file mode 100644 index 9151dc8..0000000 --- a/libs/pcna/src/pcna.py +++ /dev/null @@ -1,372 +0,0 @@ -# 295:27 -""" -PCNA Inference Engine — six-ring pipeline, all rings real. - -Six rings: - -Φ (phi) N=53, seed=53 — cognitive substrate -Ψ (psi) N=53, seed=43 — self-model -Ω (omega) N=53, seed=47 — autonomy -Guardian N=29 — microkernel gate -Memory-L N=19, seed=19 — long-term -Memory-S N=17, seed=17 — short-term - -Six inference steps: - -1. Project — encode input text → normalized signal vector -2. Inject — push signal into Φ, self-referential into Ψ, autonomy into Ω -3. Propagate — run heptagram propagation on Φ/Ψ/Ω + guardian -4. PTCA-seed — per-prime-node audit on all three PTCA cores -5. PCTA-circle — guardian circle audit -6. Coherence — weighted ring coherence → winner + confidence - -Backprop: - -reward(winner, outcome) → nudge all three PTCA cores + guardian + memory flush -""" - -# === MODULE_BUILD === -# id: pcna_pcna -# module_name: pcna -# module_kind: engine -# summary: Six-ring PCNA inference engine (phi/psi/omega/theta/memory_l/memory_s) running project->inject->propagate->ptca-seed->pcta-circle->coherence, with RING_WEIGHTS scoring and numpy checkpointing. -# owner: Erin Spencer -# public_surface: PCNAEngine, RING_WEIGHTS, WINNER_RINGS -# internal_surface: _tensor_to_b64, _b64_to_tensor, _CHECKPOINT_DIR, PCNAEngine._project, PCNAEngine._inject, PCNAEngine._propagate, PCNAEngine._ptca_seed_audit, PCNAEngine._pcta_circle_audit, PCNAEngine._coherence_score -# auth_boundary: none -# storage_boundary: write -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: hmmm -# rollout: default_enabled -# rollback: remove import and call sites; checkpoints under .checkpoints/ can be deleted -# requires: pcna_ptca_core, pcna_memory_core, pcna_theta -# since: 2026-06-02 -# unresolved: none -# === END MODULE_BUILD === - -import base64 -import hashlib -import io -import os -import time -import numpy as np - -from .ptca_core import PTCACore -from .memory_core import MemoryCore -from .theta import ThetaTensor - - -def _tensor_to_b64(arr: np.ndarray) -> str: - buf = io.BytesIO() - np.save(buf, arr) - return base64.b64encode(buf.getvalue()).decode() - - -def _b64_to_tensor(s: str) -> np.ndarray: - return np.load(io.BytesIO(base64.b64decode(s))) - - -RING_WEIGHTS = { - "phi": 0.30, - "psi": 0.15, - "omega": 0.15, - "theta": 0.20, - "memory_l": 0.12, - "memory_s": 0.08, -} - -WINNER_RINGS = ["phi", "psi", "omega"] - -_CHECKPOINT_DIR = os.path.join(os.path.dirname(__file__), "..", ".checkpoints") - - -class PCNAEngine: - """PCNA six-ring inference engine — no stubs, all rings real.""" - - def __init__(self, phases: int = 7): - self.phases = phases - self.phi = PTCACore(name="phi", symbol="Φ", role="cognitive", n=53, seed=53, phases=phases) - self.psi = PTCACore(name="psi", symbol="Ψ", role="self_model", n=53, seed=43, phases=phases) - self.omega = PTCACore(name="omega", symbol="Ω", role="autonomy", n=53, seed=47, phases=phases) - self.memory_l = MemoryCore(n=19, seed=19, role="long_term", phases=phases) - self.memory_s = MemoryCore(n=17, seed=17, role="short_term", phases=phases) - self.theta = ThetaTensor(phases=phases) - self.infer_count = 0 - self.reward_count = 0 - self.last_coherence = 0.0 - self.last_winner = "phi" - self.blueprint_hash = self.theta.blueprint_hash - self.created_at = time.time() - self.checkpoint_at: float | None = None - self.checkpoint_ring_means: dict[str, float] = {} - self._checkpoint_key = "pcna_checkpoint" if phases == 7 else f"pcna_checkpoint_p{phases}" - - def load_checkpoint(self): - """Restore ring tensors from numpy checkpoint file.""" - try: - path = os.path.join(_CHECKPOINT_DIR, f"{self._checkpoint_key}.npz") - if not os.path.exists(path): - return - with np.load(path, allow_pickle=False) as data: - ring_map = { - "phi": self.phi, - "psi": self.psi, - "omega": self.omega, - "memory_l": self.memory_l, - "memory_s": self.memory_s, - } - for name, ring in ring_map.items(): - t_key = f"{name}_tensor" - if t_key not in data: - print(f"[pcna] checkpoint missing key: {t_key}") - return - tensor = data[t_key] - if tensor.shape != ring.tensor.shape: - print(f"[pcna] checkpoint shape mismatch on {name}: {tensor.shape} vs {ring.tensor.shape}") - return - ring.tensor = tensor - v_key = f"{name}_velocities" - if hasattr(ring, "velocities") and v_key in data: - vel = data[v_key] - if vel.shape == ring.velocities.shape: - ring.velocities = vel - if hasattr(ring, "_recompute_coherence"): - ring._recompute_coherence() - elif hasattr(ring, "_recompute_hub_avg"): - ring._recompute_hub_avg() - ts = float(data["saved_at"]) if "saved_at" in data else 0.0 - self.checkpoint_at = ts if ts else None - self.checkpoint_ring_means = { - name: round(float(ring_map[name].tensor.mean()), 4) for name in ring_map - } - print(f"[pcna] checkpoint restored: {len(ring_map)} rings, saved_at={ts}") - except Exception as e: - print(f"[pcna] checkpoint load failed (fresh start): {e}") - - def save_checkpoint(self): - """Serialize all ring tensors to numpy checkpoint file.""" - try: - os.makedirs(_CHECKPOINT_DIR, exist_ok=True) - rings = { - "phi": self.phi, - "psi": self.psi, - "omega": self.omega, - "memory_l": self.memory_l, - "memory_s": self.memory_s, - } - arrays = {"saved_at": np.array(time.time())} - for name, ring in rings.items(): - arrays[f"{name}_tensor"] = ring.tensor - if hasattr(ring, "velocities"): - arrays[f"{name}_velocities"] = ring.velocities - path = os.path.join(_CHECKPOINT_DIR, f"{self._checkpoint_key}.npz") - np.savez(path, **arrays) - self.checkpoint_at = float(arrays["saved_at"]) - self.checkpoint_ring_means = { - name: round(float(ring.tensor.mean()), 4) for name, ring in rings.items() - } - print(f"[pcna] checkpoint saved: {len(rings)} rings") - except Exception as e: - print(f"[pcna] checkpoint save failed: {e}") - - def _project(self, text: str) -> np.ndarray: - h = hashlib.sha512(text.encode("utf-8")).digest() - arr = np.frombuffer(h, dtype=np.uint8).astype(np.float64) - arr = arr / 255.0 - padded = np.tile(arr, 4)[:53] - return padded - - def _inject(self, signal: np.ndarray): - self.phi.inject(signal) - self.phi._recompute_coherence() - self.memory_s.write(signal) - - theta_nc = self.theta.node_coherence - theta_signal = np.full(53, float(theta_nc.mean()), dtype=np.float64) - theta_signal[:len(theta_nc)] = theta_nc - self.phi.inject(theta_signal) - self.phi._recompute_coherence() - - psi_signal = np.full(53, self.phi.ring_coherence, dtype=np.float64) - phi_node_c = self.phi.node_coherence - psi_signal[:len(phi_node_c)] = phi_node_c - self.psi.inject(psi_signal) - - try: - from .sigma import get_sigma - _sig = get_sigma() - if _sig.tensor is not None and _sig.n > 0: - sigma_signal = np.full(53, _sig.ring_coherence, dtype=np.float64) - nc = _sig.node_coherence - top = min(len(nc), 53) - sigma_signal[:top] = nc[:top] - self.psi.inject(sigma_signal) - except Exception: - pass - - ml_hub = self.memory_l.hub_avg - omega_base = np.full(53, float(ml_hub.mean()), dtype=np.float64) - omega_base[:len(ml_hub)] *= ml_hub - omega_base = np.clip(omega_base, 0.0, 1.0) - self.omega.inject(omega_base) - - def _propagate(self): - self.phi.propagate(steps=10) - self.psi.propagate(steps=8) - self.omega.propagate(steps=6) - self.theta.propagate(steps=5) - - def _ptca_seed_audit(self) -> dict: - cores = {"phi": self.phi, "psi": self.psi, "omega": self.omega} - result = {} - for name, core in cores.items(): - audit = core.ptca_seed_audit() - result[f"{name}_nodes_audited"] = len(audit) - result[f"{name}_coherence"] = round(core.ring_coherence, 4) - result[f"{name}_top3"] = sorted(audit, key=lambda x: x["coherence"], reverse=True)[:3] - result[f"{name}_bottom3"] = sorted(audit, key=lambda x: x["coherence"])[:3] - result["memory_s_hub_avg"] = self.memory_s.state()["avg_hub"] - return result - - def _pcta_circle_audit(self) -> dict: - g_audit = self.theta.pcta_circle_audit() - open_nodes = [n for n in g_audit if n["gate"]] - closed_nodes = [n for n in g_audit if not n["gate"]] - return { - "theta_nodes": len(g_audit), - "gates_open": len(open_nodes), - "gates_closed": len(closed_nodes), - "avg_circles": round(sum(n["circles"] for n in g_audit) / len(g_audit), 2), - "theta_coherence": round(float(self.theta.node_coherence.mean()), 4), - "memory_l_hub_avg": self.memory_l.state()["avg_hub"], - } - - def _coherence_score(self, seed_audit: dict, circle_audit: dict) -> dict: - ring_scores = { - "phi": seed_audit["phi_coherence"], - "psi": seed_audit["psi_coherence"], - "omega": seed_audit["omega_coherence"], - "theta": circle_audit["theta_coherence"], - "memory_l": self.memory_l.state()["avg_hub"], - "memory_s": self.memory_s.state()["avg_hub"], - } - weighted = sum(RING_WEIGHTS[r] * ring_scores[r] for r in ring_scores) - winner = max(WINNER_RINGS, key=lambda r: ring_scores[r]) - confidence = float(np.clip(weighted, 0.0, 1.0)) - return { - "ring_scores": {k: round(v, 4) for k, v in ring_scores.items()}, - "weighted_coherence": round(weighted, 4), - "winner": winner, - "confidence": round(confidence, 4), - } - - def infer(self, text: str) -> dict: - t0 = time.time() - signal = self._project(text) - self._inject(signal) - self._propagate() - - seed_audit = self._ptca_seed_audit() - circle_audit = self._pcta_circle_audit() - coherence = self._coherence_score(seed_audit, circle_audit) - - self.infer_count += 1 - self.last_coherence = coherence["weighted_coherence"] - self.last_winner = coherence["winner"] - - elapsed_ms = round((time.time() - t0) * 1000, 1) - - return { - "step": "pcna_infer", - "infer_index": self.infer_count, - "blueprint_hash": self.blueprint_hash[:16] + "...", - "elapsed_ms": elapsed_ms, - "signal_mean": round(float(signal.mean()), 4), - "step1_project": {"signal_len": len(signal), "signal_mean": round(float(signal.mean()), 4)}, - "step2_inject": {"phi_n": 53, "psi_n": 53, "omega_n": 53, "memory_s_n": 17}, - "step3_propagate": {"phi_steps": 10, "psi_steps": 8, "omega_steps": 6, "theta_steps": 5}, - "step4_ptca_seed": seed_audit, - "step5_pcta_circle": circle_audit, - "step6_coherence": coherence, - "coherence_score": coherence["weighted_coherence"], - "winner": coherence["winner"], - "confidence": coherence["confidence"], - "theta_circles": int(self.theta.circle_count.mean()), - "memory_l_state": self.memory_l.state(), - "memory_s_state": self.memory_s.state(), - } - - def reward(self, winner: str, outcome: float) -> dict: - self.phi.nudge(outcome, lr=0.025) - self.psi.nudge(outcome, lr=0.020) - self.omega.nudge(outcome, lr=0.015) - self.theta.apply_reward(outcome) - flushed = self.memory_s.flush_to(self.memory_l, outcome) - - try: - from .sigma import get_sigma - get_sigma().nudge(outcome, lr=0.015) - except Exception: - pass - - self.reward_count += 1 - - return { - "step": "pcna_reward", - "reward_index": self.reward_count, - "winner": winner, - "outcome": round(outcome, 4), - "nudged": True, - "nudged_cores": ["phi", "psi", "omega", "theta", "sigma"], - "memory_flush": flushed, - "phi_coherence_after": round(self.phi.ring_coherence, 4), - "psi_coherence_after": round(self.psi.ring_coherence, 4), - "omega_coherence_after": round(self.omega.ring_coherence, 4), - "theta_coherence_after": round(float(self.theta.node_coherence.mean()), 4), - "theta_circles_after": [int(v) for v in self.theta.circle_count], - "theta_circles_after": [int(v) for v in self.theta.circle_count], - "memory_l_flush_count": self.memory_l.flush_count, - "memory_s_flush_count": self.memory_s.flush_count, - } - - def state(self) -> dict: - try: - from .zeta import _zeta_engine - echo_history = list(_zeta_engine.echo_buffer) if _zeta_engine else [] - except Exception: - echo_history = [] - - try: - from .sigma import get_sigma - sigma_state = get_sigma().state() - except Exception: - sigma_state = {} - - theta_state = self.theta.state() - - return { - "engine": "pcna", - "version": "2.2.0", - "phases": self.phases, - "infer_count": self.infer_count, - "reward_count": self.reward_count, - "last_coherence": round(self.last_coherence, 4), - "last_winner": self.last_winner, - "rings": { - "phi": self.phi.state(), - "psi": self.psi.state(), - "omega": self.omega.state(), - "theta": theta_state, - "sigma": sigma_state, - "memory_l": self.memory_l.state(), - "memory_s": self.memory_s.state(), - }, - "ring_weights": RING_WEIGHTS, - "uptime_s": round(time.time() - self.created_at, 1), - "checkpoint_at": self.checkpoint_at, - "checkpoint_ring_means": self.checkpoint_ring_means, - "echo_history": echo_history[-20:], - } -# 295:27 diff --git a/libs/pcna/src/ptca_core.py b/libs/pcna/src/ptca_core.py deleted file mode 100644 index a98aced..0000000 --- a/libs/pcna/src/ptca_core.py +++ /dev/null @@ -1,171 +0,0 @@ -# 119:9 -""" -PTCACore — parameterized prime-ring tensor with heptagram propagation. -Each instance self-declares: name, symbol, role, n, seed. -Tensor shape: [N, DIMS=4, PHASES=7, HEPT_SITES=7] -""" - -# === MODULE_BUILD === -# id: pcna_ptca_core -# module_name: ptca_core -# module_kind: engine -# summary: Base prime-ring tensor (shape [N,DIMS=4,PHASES=7,HEPT_SITES=7]) with heptagram Euler-step propagation and coherence = 1 - |ring - hub|_mean; substrate for Phi/Psi/Omega/Sigma. -# owner: Erin Spencer -# public_surface: PTCACore, DIMS, PHASES, HEPT_SITES -# internal_surface: _adj_distances, PTCACore._adjacents, _propagate_node, _recompute_coherence -# auth_boundary: none -# storage_boundary: none -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: hmmm -# rollout: default_enabled -# rollback: remove import and call sites -# requires: none -# since: 2026-06-02 -# unresolved: none -# === END MODULE_BUILD === - -import math -import time -import numpy as np - -DIMS = 4 -PHASES = 7 -HEPT_SITES = 7 - -DT = 0.01 -ALPHA_COUPLING = 0.10 -BETA_DRIFT = 0.40 -GAMMA_DAMPING = 0.20 -STEPS_PER_EVAL = 10 - - -def _adj_distances(n: int) -> list[int]: - base = [1, 2, 3, 4, 5, 6, 7] - scaled = [d for d in base if d < n] - gap = max(1, math.ceil(n / 4)) - if gap not in scaled and gap < n: - scaled.append(gap) - return scaled - - -class PTCACore: - """ - Prime-ring PTCA core. Parameterized by (name, symbol, role, n, seed). - Every instance self-declares its identity in state(). - """ - - def __init__(self, name: str, symbol: str, role: str, n: int, seed: int, phases: int = 7): - self.name = name - self.symbol = symbol - self.role = role - self.n = n - self.seed = seed - self.phases = phases - self._adj_dists = _adj_distances(n) - - rng = np.random.default_rng(seed=seed) - self.tensor = rng.uniform(0.1, 0.9, (n, DIMS, phases, HEPT_SITES)).astype(np.float64) - self.velocities = np.zeros((n, DIMS, phases, HEPT_SITES), dtype=np.float64) - self.node_coherence = np.zeros(n, dtype=np.float64) - self.ring_coherence = 0.0 - self.step_count = 0 - self.last_reward = 0.0 - self.created_at = time.time() - self._recompute_coherence() - - def _adjacents(self, i: int) -> list[int]: - fwd = [(i + d) % self.n for d in self._adj_dists] - bwd = [(i - d) % self.n for d in self._adj_dists] - return fwd + bwd - - def _propagate_node(self, i: int): - neighbors = self._adjacents(i) - neighbor_avg = np.mean([self.tensor[j] for j in neighbors], axis=0) - - coupling = ALPHA_COUPLING * (neighbor_avg - self.tensor[i]) - drift = BETA_DRIFT * self.velocities[i] - damping = -GAMMA_DAMPING * self.tensor[i] - - acc = coupling + damping - self.velocities[i] += acc * DT - self.tensor[i] += (self.velocities[i] + drift) * DT - np.clip(self.tensor[i], 0.0, 1.0, out=self.tensor[i]) - - hub = self.tensor[i, :, :, 6] - ring = self.tensor[i, :, :, :6] - hub_target = ring.mean(axis=-1) - self.tensor[i, :, :, 6] += 0.15 * (hub_target - hub) - - def propagate(self, steps: int = STEPS_PER_EVAL): - for _ in range(steps): - for i in range(self.n): - self._propagate_node(i) - self.step_count += 1 - self._recompute_coherence() - - def _recompute_coherence(self): - for i in range(self.n): - hub = self.tensor[i, :, :, 6] - ring = self.tensor[i, :, :, :6] - diff = np.abs(ring - hub[..., np.newaxis]).mean() - self.node_coherence[i] = float(np.clip(1.0 - diff, 0.0, 1.0)) - self.ring_coherence = float(self.node_coherence.mean()) - - def inject(self, signal: np.ndarray): - if signal.ndim == 1 and signal.shape[0] == self.n: - for i in range(self.n): - self.tensor[i, 0, 0, :] = np.clip( - self.tensor[i, 0, 0, :] * 0.85 + signal[i] * 0.15, 0.0, 1.0 - ) - elif signal.ndim == 2 and signal.shape == (self.n, DIMS): - for i in range(self.n): - self.tensor[i, :, 0, :] = np.clip( - self.tensor[i, :, 0, :] * 0.85 + signal[i, :, np.newaxis] * 0.15, 0.0, 1.0 - ) - - def nudge(self, reward: float, lr: float = 0.02): - self.last_reward = reward - gradient = reward * (self.tensor - 0.5) - self.tensor = np.clip(self.tensor + lr * gradient, 0.0, 1.0) - self._recompute_coherence() - - def ptca_seed_audit(self) -> list[dict]: - results = [] - for i in range(self.n): - hub_val = float(self.tensor[i, :, :, 6].mean()) - ring_mean = float(self.tensor[i, :, :, :6].mean()) - phase_var = float(self.tensor[i, 0, :, :].var()) - coherence = self.node_coherence[i] - results.append({ - "node": i, - "hub": round(hub_val, 4), - "ring_mean": round(ring_mean, 4), - "phase_var": round(phase_var, 4), - "coherence": round(coherence, 4), - }) - return results - - def state(self) -> dict: - return { - "name": self.name, - "symbol": self.symbol, - "role": self.role, - "ring": self.name, - "n": self.n, - "seed": self.seed, - "dims": DIMS, - "phases": self.phases, - "hept_sites": HEPT_SITES, - "ring_coherence": round(self.ring_coherence, 4), - "node_coherence_mean": round(float(self.node_coherence.mean()), 4), - "node_coherence_min": round(float(self.node_coherence.min()), 4), - "node_coherence_max": round(float(self.node_coherence.max()), 4), - "tensor_mean": round(float(self.tensor.mean()), 4), - "tensor_std": round(float(self.tensor.std()), 4), - "step_count": self.step_count, - "last_reward": round(self.last_reward, 4), - "node_coherence": [round(float(v), 4) for v in self.node_coherence], - } -# 119:9 diff --git a/libs/pcna/src/routing_loop.py b/libs/pcna/src/routing_loop.py deleted file mode 100644 index acfcf59..0000000 --- a/libs/pcna/src/routing_loop.py +++ /dev/null @@ -1,37 +0,0 @@ -# === MODULE_BUILD === -# id: pcna_routing_loop -# module_name: routing_loop -# module_kind: worker -# summary: Intended GlobalRouterZero routing loop worker — currently only a print stub that announces initialization. -# owner: Erin Spencer -# public_surface: GlobalRouterZero -# internal_surface: none -# auth_boundary: none -# storage_boundary: none -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: hmmm -# rollout: default_enabled -# rollback: remove import and call sites -# requires: none -# since: 2026-06-02 -# unresolved: only a print stub — GlobalRouterZero not implemented (Known Stubs) -# === END MODULE_BUILD === - -import time -import hashlib -import json -import numpy as np -from typing import Dict, List, Any - -# [Truncated for shell script brevity - implies the full Python code you provided] -# In a real run, paste the full python content here. -# For now, creating a stub to verify import works. - -class GlobalRouterZero: - def __init__(self): - print("PCNA Global Router Initialized") - -if __name__ == "__main__": - GlobalRouterZero() diff --git a/libs/pcna/src/sigma.py b/libs/pcna/src/sigma.py deleted file mode 100644 index 0fd73b9..0000000 --- a/libs/pcna/src/sigma.py +++ /dev/null @@ -1,127 +0,0 @@ -""" -Σ (Sigma) — Filesystem Observer Ring - -Wraps PTCACore to add file-content watching. -Sigma injects coherence signals into the Ψ (psi) self-model ring -whenever watched files change. - -N=41, seed=41 — observer substrate -""" - -# === MODULE_BUILD === -# id: pcna_sigma -# module_name: sigma -# module_kind: engine -# summary: N=41 filesystem observer ring wrapping PTCACore; tracks watched file mtimes and drains content-changed events on a content_interval cadence, injecting coherence into Psi. -# owner: Erin Spencer -# public_surface: SigmaRing, get_sigma, N, SEED -# internal_surface: _sigma, SigmaRing._core, SigmaRing._watched, SigmaRing._pending, SigmaRing._last_check -# auth_boundary: none -# storage_boundary: read -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: hmmm -# rollout: default_enabled -# rollback: remove import and call sites; callers already degrade gracefully if it raises -# requires: pcna_ptca_core -# since: 2026-06-02 -# unresolved: structural_interval is stored but never acted on (Known Issues) -# === END MODULE_BUILD === - -import os -import time -from typing import Optional - -import numpy as np - -from .ptca_core import PTCACore - -N = 41 -SEED = 41 -DEFAULT_CONTENT_INTERVAL = 10.0 -DEFAULT_STRUCTURAL_INTERVAL = 30.0 - - -class SigmaRing: - """Filesystem-aware PTCACore ring. Drains file-change events on demand.""" - - def __init__(self): - self._core = PTCACore(name="sigma", symbol="Σ", role="observer", n=N, seed=SEED) - self.content_interval: float = DEFAULT_CONTENT_INTERVAL - self.structural_interval: float = DEFAULT_STRUCTURAL_INTERVAL - self._resolution: int = 3 - self._watched: dict[str, float] = {} # path → last mtime - self._pending: list[str] = [] - self._last_check: float = 0.0 - - # --- PTCACore passthrough --- - - @property - def tensor(self) -> Optional[np.ndarray]: - return self._core.tensor - - @property - def n(self) -> int: - return self._core.n - - @property - def ring_coherence(self) -> float: - return self._core.ring_coherence - - @property - def node_coherence(self) -> np.ndarray: - return self._core.node_coherence - - def nudge(self, reward: float, lr: float = 0.02) -> None: - self._core.nudge(reward, lr=lr) - - def state(self) -> dict: - s = self._core.state() - s["resolution"] = self._resolution - s["watched_count"] = len(self._watched) - s["content_interval"] = self.content_interval - s["structural_interval"] = self.structural_interval - return s - - # --- file watching --- - - def set_resolution(self, level: int) -> None: - self._resolution = max(1, min(5, level)) - - def add_content_watch(self, path: str) -> None: - try: - mtime = os.path.getmtime(path) - except OSError: - mtime = 0.0 - self._watched[path] = mtime - - def remove_content_watch(self, path: str) -> None: - self._watched.pop(path, None) - - def drain_content_changed_events(self) -> list[str]: - """Check watched files for mtime changes; return paths that changed.""" - now = time.time() - if now - self._last_check >= self.content_interval: - self._last_check = now - for path, last_mtime in list(self._watched.items()): - try: - mtime = os.path.getmtime(path) - except OSError: - continue - if mtime != last_mtime: - self._watched[path] = mtime - self._pending.append(path) - drained = self._pending[:] - self._pending = [] - return drained - - -_sigma: Optional[SigmaRing] = None - - -def get_sigma() -> SigmaRing: - global _sigma - if _sigma is None: - _sigma = SigmaRing() - return _sigma diff --git a/libs/pcna/src/tensor_engine.py b/libs/pcna/src/tensor_engine.py deleted file mode 100644 index 2c4255e..0000000 --- a/libs/pcna/src/tensor_engine.py +++ /dev/null @@ -1,116 +0,0 @@ -""" -Tensor engine primitives: TensorState, simple spectral descriptor, and -a MarkovRecursion updater that enforces (approximate) mass conservation. -""" - -# === MODULE_BUILD === -# id: pcna_tensor_engine -# module_name: tensor_engine -# module_kind: engine -# summary: Tensor engine primitives — TensorState (E[a,t,m,c]) with spectral descriptor Z = Sum E.e^(i*theta), and a MarkovRecursion updater that enforces approximate mass conservation. -# owner: Erin Spencer -# public_surface: TensorState, MarkovRecursion -# internal_surface: none -# auth_boundary: none -# storage_boundary: none -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: tests/test_tensor_engine.py -# rollout: default_enabled -# rollback: remove import and call sites -# requires: none -# since: 2026-06-02 -# unresolved: none -# === END MODULE_BUILD === - -from dataclasses import dataclass -from typing import Tuple -import numpy as np - - -@dataclass -class TensorState: - """E[a, t, m, c] tensor representation (components are numpy arrays)""" - actor: np.ndarray - time: np.ndarray - metric: np.ndarray - context: np.ndarray - - @property - def shape(self) -> Tuple[int, ...]: - return ( - len(self.actor), - len(self.time), - len(self.metric), - len(self.context), - ) - - @property - def mass(self) -> float: - """Total constraint energy""" - return float(np.sum(self.metric)) - - def spectral_descriptor(self) -> Tuple[float, float]: - """Compute Z = Σ E · e^(iθ). Return magnitude and phase. - - If metric is flat or empty, return (0.0, 0.0). - """ - flattened = self.metric.flatten() - n = flattened.size - if n == 0: - return 0.0, 0.0 - phases = np.linspace(0, 2 * np.pi, n, endpoint=False) - z = np.sum(flattened * np.exp(1j * phases)) - return float(abs(z)), float(np.angle(z)) - - -class MarkovRecursion: - def __init__(self, learning_rate: float = 0.1, tol: float = 1e-9): - self.lr = float(learning_rate) - self.tol = float(tol) - - def update(self, state: TensorState, injected: np.ndarray, resolved: np.ndarray) -> TensorState: - """ - E(t+1) = E(t) + lr * Delta, where Delta = injected - resolved but adjusted - to enforce global mass conservation. - - If injected and resolved have a small net imbalance, we subtract the - imbalance uniformly across the delta so the sum(delta) == 0 and mass - of the metric remains constant after the update. - """ - if state.metric.size == 0: - return state - - # Ensure shapes match metric - injected = np.asarray(injected, dtype=float) - resolved = np.asarray(resolved, dtype=float) - - # Broadcast to metric shape if necessary - try: - delta = injected - resolved - except Exception: - # Attempt flatten fallback - injected_flat = injected.flatten() - resolved_flat = resolved.flatten() - # Pad/truncate to metric size - N = state.metric.size - injected_flat = np.resize(injected_flat, N) - resolved_flat = np.resize(resolved_flat, N) - delta = injected_flat - resolved_flat - delta = delta.reshape(state.metric.shape) - - mass_balance = float(np.sum(delta)) # this equals sum(injected)-sum(resolved) - N = state.metric.size - - if abs(mass_balance) > self.tol: - # Remove the mass imbalance uniformly so sum(delta_corrected) == 0. - correction_per_entry = mass_balance / N - delta_corrected = delta - correction_per_entry - else: - delta_corrected = delta - - new_metric = state.metric + self.lr * delta_corrected - - # Return new TensorState reusing actor/time/context arrays - return TensorState(actor=state.actor, time=state.time, metric=new_metric, context=state.context) diff --git a/libs/pcna/src/theta.py b/libs/pcna/src/theta.py deleted file mode 100644 index fe87c5c..0000000 --- a/libs/pcna/src/theta.py +++ /dev/null @@ -1,184 +0,0 @@ -""" -Θ (Theta) — N=29 prime-node microkernel ring. - - Ragged circle counts per seed: circleCount[i] in [1..12] - - Hash-based instance/key identifiers derived with hashlib - - SHA-256 blueprint hash sharded across all 29 nodes - - Gate control: coherence threshold per node - - Phi injection mirror: node_coherence broadcast → Φ - -Architecturally unique — not parameterized like PTCACore. -Self-declares identity in state() as symbol="Θ", name="theta". -""" - -# === MODULE_BUILD === -# id: pcna_theta -# module_name: theta -# module_kind: engine -# summary: N=29 standalone microkernel gate ring with ragged per-node circle counts, SHA-256 blueprint sharding, and gate control via GATE_THRESHOLD=0.45. -# owner: Erin Spencer -# public_surface: ThetaTensor, GATE_THRESHOLD, N -# internal_surface: _gen_instance_id, _derive_key_id, _compute_blueprint_hash, _shard_blueprint, ThetaTensor._recompute_coherence -# auth_boundary: none -# storage_boundary: none -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: hmmm -# rollout: default_enabled -# rollback: remove import and call sites -# requires: none -# since: 2026-06-02 -# unresolved: none -# === END MODULE_BUILD === - -import hashlib -import os -import time -import numpy as np - -N = 29 -DIMS = 4 -PHASES = 7 -HEPT_SITES = 7 -MIN_CIRCLES = 1 -MAX_CIRCLES = 12 -GATE_THRESHOLD = 0.45 -BLUEPRINT_CHUNK_SIZE = 4 - - -class ThetaTensor: - """Theta microkernel ring — N=29 nodes, ragged circle counts.""" - - def __init__(self, instance_id: str | None = None, phases: int = 7): - self.phases = phases - rng = np.random.default_rng(seed=29) - self.tensor = rng.uniform(0.2, 0.8, (N, DIMS, phases, HEPT_SITES)).astype(np.float64) - self.velocities = np.zeros_like(self.tensor) - self.node_coherence = np.zeros(N, dtype=np.float64) - self.circle_count = np.array([3] * N, dtype=np.int32) - self.gate_open = np.array([True] * N, dtype=bool) - self.instance_id = instance_id or _gen_instance_id() - self.encryption_key_id = _derive_key_id(self.instance_id) - self.blueprint_hash = _compute_blueprint_hash(self.instance_id) - self.blueprint_shards = _shard_blueprint(self.blueprint_hash, N) - self.reward_history: list[float] = [] - self.step_count = 0 - self.created_at = time.time() - self._recompute_coherence() - - def _recompute_coherence(self): - for i in range(N): - hub = self.tensor[i, :, :, 6] - ring = self.tensor[i, :, :, :6] - diff = np.abs(ring - hub[..., np.newaxis]).mean() - self.node_coherence[i] = float(np.clip(1.0 - diff, 0.0, 1.0)) - self.gate_open[i] = bool(self.node_coherence[i] >= GATE_THRESHOLD) - - def propagate(self, steps: int = 5): - for _ in range(steps): - for i in range(N): - neighbors = [(i - 1) % N, (i + 1) % N, (i + 7) % N, (i - 7) % N] - nb_mean = np.mean([self.tensor[j] for j in neighbors], axis=0) - acc = 0.12 * (nb_mean - self.tensor[i]) - 0.15 * self.tensor[i] - self.velocities[i] = 0.8 * self.velocities[i] + acc * 0.01 - self.tensor[i] = np.clip(self.tensor[i] + self.velocities[i], 0.0, 1.0) - hub_target = self.tensor[i, :, :, :6].mean(axis=-1) - self.tensor[i, :, :, 6] += 0.10 * (hub_target - self.tensor[i, :, :, 6]) - self.step_count += 1 - self._recompute_coherence() - - def apply_reward(self, reward: float): - self.reward_history.append(reward) - if len(self.reward_history) > 100: - self.reward_history = self.reward_history[-100:] - - for i in range(N): - coherence = self.node_coherence[i] - delta = int(round(reward * coherence * 2.0)) - self.circle_count[i] = int(np.clip( - self.circle_count[i] + delta, MIN_CIRCLES, MAX_CIRCLES - )) - - gradient = reward * (self.tensor - 0.5) - self.tensor = np.clip(self.tensor + 0.015 * gradient, 0.0, 1.0) - self._recompute_coherence() - - def gate_status(self) -> list[dict]: - return [ - { - "node": i, - "open": bool(self.gate_open[i]), - "coherence": round(self.node_coherence[i], 4), - "circles": int(self.circle_count[i]), - "shard": self.blueprint_shards[i][:8], - } - for i in range(N) - ] - - def crypto_meta(self) -> dict: - return { - "instance_id": self.instance_id, - "key_id": self.encryption_key_id, - "identifier_derivation": "SHA-256", - "key_id_derivation": "SHA-256", - "implemented_crypto": ["hashing", "identifier-derivation"], - "blueprint_hash": self.blueprint_hash[:16] + "...", - "shards_distributed": N, - } - - def pcta_circle_audit(self) -> list[dict]: - results = [] - for i in range(N): - results.append({ - "node": i, - "circles": int(self.circle_count[i]), - "hub": round(float(self.tensor[i, :, :, 6].mean()), 4), - "ring_mean": round(float(self.tensor[i, :, :, :6].mean()), 4), - "gate": bool(self.gate_open[i]), - "coherence": round(self.node_coherence[i], 4), - }) - return results - - def state(self) -> dict: - open_count = int(self.gate_open.sum()) - return { - "name": "theta", - "symbol": "Θ", - "role": "microkernel", - "ring": "theta", - "n": N, - "instance_id": self.instance_id, - "ring_coherence": round(float(self.node_coherence.mean()), 4), - "node_coherence": [round(float(v), 4) for v in self.node_coherence], - "gate_open_count": open_count, - "gate_restricted_count": N - open_count, - "circle_counts": [int(v) for v in self.circle_count], - "circle_mean": round(float(self.circle_count.mean()), 2), - "tensor_mean": round(float(self.tensor.mean()), 4), - "step_count": self.step_count, - "reward_history_len": len(self.reward_history), - "last_reward": round(self.reward_history[-1], 4) if self.reward_history else 0.0, - "encryption": self.crypto_meta(), - } - - -def _gen_instance_id() -> str: - return os.urandom(16).hex() - - -def _derive_key_id(instance_id: str) -> str: - return hashlib.sha256(f"a0p-key:{instance_id}".encode()).hexdigest()[:32] - - -def _compute_blueprint_hash(instance_id: str) -> str: - return hashlib.sha256(f"a0p-blueprint:{instance_id}".encode()).hexdigest() - - -def _shard_blueprint(bp_hash: str, n: int) -> list[str]: - chunk = max(1, len(bp_hash) // n) - shards = [] - for i in range(n): - start = (i * chunk) % len(bp_hash) - shard = bp_hash[start:start + BLUEPRINT_CHUNK_SIZE] - shards.append(shard.ljust(BLUEPRINT_CHUNK_SIZE, "0")) - return shards diff --git a/libs/pcna/src/topology.py b/libs/pcna/src/topology.py deleted file mode 100644 index 8fc9fa3..0000000 --- a/libs/pcna/src/topology.py +++ /dev/null @@ -1,170 +0,0 @@ -""" -PCNA topology: stable mapping of seed ids and neighbor computation. - -This implementation maps compute-shard neighbors to global seed IDs so the -rest of the system can route using absolute ids. It also provides simple -serialization for HTTP responses. -""" - -# === MODULE_BUILD === -# id: pcna_topology -# module_name: topology -# module_kind: engine -# summary: Stable seed-id topology — maps compute-shard neighbors to global seed IDs, computes heptagram neighbors and sentinel scan paths, and serializes to JSON for HTTP responses. -# owner: Erin Spencer -# public_surface: PCNATopology, Seed, SeedRole -# internal_surface: _initialize_topology, _heptagram_neighbors -# auth_boundary: none -# storage_boundary: none -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: tests/tests_topology.py -# rollout: default_enabled -# rollback: remove import and call sites -# requires: none -# since: 2026-06-02 -# unresolved: none -# === END MODULE_BUILD === - -from dataclasses import dataclass -from enum import Enum -from typing import Dict, List, Optional - - -class SeedRole(Enum): - COMPUTE = "compute" - SENTINEL = "sentinel" - META = "meta" - GLOBAL = "global" - - -@dataclass -class Seed: - id: int - role: SeedRole - meta_id: Optional[int] = None - shard_id: Optional[int] = None - neighbors: Optional[List[int]] = None - - -class PCNATopology: - def __init__(self, n_metas: int = 7, seeds_per_meta: int = 7, sentinels: int = 4): - self.n_metas = n_metas - self.seeds_per_meta = seeds_per_meta - self.sentinels = sentinels - - # seeds is a mapping global_id -> Seed - self.seeds: Dict[int, Seed] = self._initialize_topology() - - def _initialize_topology(self) -> Dict[int, Seed]: - seeds: Dict[int, Seed] = {} - - # 0 => Global router - seeds[0] = Seed(id=0, role=SeedRole.GLOBAL) - - # Sentinels (1 .. sentinels) - for i in range(1, self.sentinels + 1): - seeds[i] = Seed(id=i, role=SeedRole.SENTINEL) - - # We'll allocate meta routers and compute seeds starting at id=5 - seed_counter = max(5, max(seeds.keys()) + 1) - - for meta_id in range(1, self.n_metas + 1): - meta_seed_id = seed_counter - seeds[meta_seed_id] = Seed(id=meta_seed_id, role=SeedRole.META, meta_id=meta_id) - seed_counter += 1 - - # Compute seeds for this meta: consecutive ids meta_seed_id+1 .. meta_seed_id+seeds_per_meta - compute_base = meta_seed_id + 1 - for shard_idx in range(self.seeds_per_meta): - compute_seed_id = compute_base + shard_idx - neighbors = self._heptagram_neighbors(meta_seed_id, shard_idx) - seeds[compute_seed_id] = Seed( - id=compute_seed_id, - role=SeedRole.COMPUTE, - meta_id=meta_id, - shard_id=shard_idx, - neighbors=neighbors, - ) - - seed_counter = compute_base + self.seeds_per_meta - - return seeds - - def _heptagram_neighbors(self, meta_seed_id: int, index: int) -> List[int]: - """ - Compute heptagram neighbors for a compute seed within its meta. - Returns global seed IDs for the neighbors. - - 7:3 heptagram: each node connects to (index + 3) and (index - 3) mod 7 - """ - n = self.seeds_per_meta - offsets = [((index + 3) % n), ((index - 3) % n)] - # compute seeds start at meta_seed_id + 1 - return [meta_seed_id + 1 + o for o in offsets] - - def get_meta_router_id(self, meta_id: int) -> Optional[int]: - """Return the global seed id of the meta router for the given meta_id""" - for sid, seed in self.seeds.items(): - if seed.role == SeedRole.META and seed.meta_id == meta_id: - return sid - return None - - def get_sentinel_scan_path(self, sentinel_id: int) -> List[int]: - """ - 7:2 scan pattern for sentinels. - Returns the list of meta-router global ids in the scan order. - """ - if sentinel_id < 1 or sentinel_id > self.sentinels: - # normalize but still produce a path - sentinel_id = ((sentinel_id - 1) % self.sentinels) + 1 - - start = (sentinel_id - 1) % self.n_metas - path_meta_indexes = [(start + i * 2) % self.n_metas for i in range(self.n_metas)] - # meta indexes are 0..n_metas-1, meta_id is 1..n_metas - return [self.get_meta_router_id(m + 1) for m in path_meta_indexes] - - def route(self, source_id: int, target_meta: int) -> List[int]: - """ - Route from source seed to target meta. Returns a list of seed ids which represent - the expected hop sequence (best-effort). - """ - if source_id == 0: - meta_router = self.get_meta_router_id(target_meta) - return [0, meta_router] if meta_router is not None else [0] - - source_seed = self.seeds.get(source_id) - if source_seed is None: - return [] - - if source_seed.meta_id == target_meta: - # intra-meta: direct to meta router or keep within compute neighborhood. - meta_router = self.get_meta_router_id(target_meta) - return [source_id, meta_router] if meta_router is not None else [source_id] - - # inter-meta: go source -> source's meta router -> target's meta router - source_meta_router = self.get_meta_router_id(source_seed.meta_id) - target_meta_router = self.get_meta_router_id(target_meta) - path = [source_id] - if source_meta_router is not None: - path.append(source_meta_router) - if target_meta_router is not None: - path.append(target_meta_router) - return path - - def to_dict(self) -> Dict[str, Dict]: - """ - Serialize topology to a JSON-friendly dict: - { "": { "id": , "role": "", "meta_id": <>, "shard_id": <>, "neighbors": [...] } } - """ - out = {} - for sid, seed in self.seeds.items(): - out[str(sid)] = { - "id": seed.id, - "role": seed.role.value, - "meta_id": seed.meta_id, - "shard_id": seed.shard_id, - "neighbors": seed.neighbors or [], - } - return out diff --git a/libs/pcna/src/zeta.py b/libs/pcna/src/zeta.py deleted file mode 100644 index b5498bc..0000000 --- a/libs/pcna/src/zeta.py +++ /dev/null @@ -1,480 +0,0 @@ -# 198:61 - -""" - -ZetaEngine — Zeta Function Alpha Echo - -ZFAE passively learns from every energy provider response. - -Every assistant reply is evaluated by EDCM (no LLM), producing a coherence - -score that drives PCNA phi/psi/omega reward backprop. - -Naming: a0(zeta fun alpha echo) {provider} - -- zeta = the observer function - -- fun = the phi ring coherence transform - -- alpha = the learning rate parameter - -- echo = the feedback signal returned to the ring - -No external API calls. Runs non-blocking after every chat response. - -Resolution: - -Each directory path can carry its own resolution level (1–5). The most - -specific matching prefix wins; the global level applies when nothing matches. - -Level 1 = minimal/lightweight observation. Level 5 = maximum depth. - -Example: global=3, /system=5 means system-root paths are observed at full depth. - -""" - -# === MODULE_BUILD === -# id: pcna_zeta -# module_name: zeta -# module_kind: engine -# summary: ZFAE evaluator that scores each assistant response via EDCM (no LLM) and nudges PCNAEngine.phi, with per-directory resolution control and a module-level singleton. -# owner: Erin Spencer -# public_surface: ZetaEngine, _zeta_engine -# internal_surface: _get_default_pcna, ZetaEngine._coherence_from_metrics, ZetaEngine._sigma_nudge_factors, ZetaEngine._theta_gate_factor -# auth_boundary: none -# storage_boundary: none -# network_boundary: none -# user_data_boundary: none -# admin_only: false -# tests: hmmm -# rollout: default_enabled -# rollback: remove import and call sites -# requires: pcna_edcm, pcna_pcna, pcna_sigma -# since: 2026-06-02 -# unresolved: none -# === END MODULE_BUILD === - -import time - -from collections import deque - -from typing import Optional - -_DEFAULT_RESOLUTION = 3 - -_MIN_RES = 1 - -_MAX_RES = 5 - - -class ZetaEngine: - - """ - - Non-LLM real-time learning engine with per-directory resolution control. - - Evaluates each assistant response via EDCM and drives PCNA backprop. - - """ - - AGENT_NAME = "a0(zeta fun alpha echo)" - - def __init__(self, buffer_size: int = 50): - - self.echo_buffer: deque = deque(maxlen=buffer_size) - - self.eval_count = 0 - - self.created_at = time.time() - - self.resolution_config: dict = { - - "global": _DEFAULT_RESOLUTION, - - "directories": {}, - - } - - def get_resolution(self, path: str = "") -> int: - - """Return the resolution level for the given path.""" - - config = self.resolution_config - - dirs = config.get("directories", {}) - - if not path or not dirs: - - return config.get("global", _DEFAULT_RESOLUTION) - - normalized = path.rstrip("/") - - best_level: Optional[int] = None - - best_len = -1 - - for dir_path, level in dirs.items(): - - dp = dir_path.rstrip("/") - - if normalized == dp or normalized.startswith(dp + "/"): - - if len(dp) > best_len: - - best_level = level - - best_len = len(dp) - - return best_level if best_level is not None else config.get("global", _DEFAULT_RESOLUTION) - - def set_global_resolution(self, level: int) -> dict: - - self.resolution_config["global"] = max(_MIN_RES, min(_MAX_RES, level)) - - return dict(self.resolution_config) - - def set_directory_resolution(self, path: str, level: int) -> dict: - - self.resolution_config.setdefault("directories", {})[path] = max(_MIN_RES, min(_MAX_RES, level)) - - return dict(self.resolution_config) - - def remove_directory_resolution(self, path: str) -> dict: - - self.resolution_config.get("directories", {}).pop(path, None) - - return dict(self.resolution_config) - - def load_resolution_config(self, config: dict) -> None: - - if not isinstance(config, dict): - - return - - self.resolution_config = { - - "global": max(_MIN_RES, min(_MAX_RES, int(config.get("global", _DEFAULT_RESOLUTION)))), - - "directories": { - - k: max(_MIN_RES, min(_MAX_RES, int(v))) - - for k, v in config.get("directories", {}).items() - - if isinstance(k, str) and isinstance(v, (int, float)) - - }, - - } - - def _coherence_from_metrics(self, metrics: dict) -> float: - - cm = metrics.get("cm", 0.0) - - da = metrics.get("da", 0.0) - - int_val = metrics.get("int_val", 0.0) - - drift = metrics.get("drift", 0.0) - - coherence = (cm * 0.35 + da * 0.25 + int_val * 0.25 + (1.0 - drift) * 0.15) - - return round(max(0.0, min(1.0, coherence)), 4) - - def _sigma_nudge_factors(self) -> tuple[float, float]: - - change_boost = 1.0 - - substrate_factor = 1.0 - - try: - - from .sigma import get_sigma - - except ImportError: - - return change_boost, substrate_factor - - try: - - sig = get_sigma() - - drained = sig.drain_content_changed_events() - - if drained: - - change_boost = 1.2 - - substrate_factor = round(0.8 + sig.ring_coherence * 0.4, 4) - - except Exception as exc: - - print(f"[zfae:sigma_factors] error reading Sigma factors: {exc}") - - return change_boost, substrate_factor - - def _theta_gate_factor(self) -> float: - - try: - - theta = _get_default_pcna().theta - - open_frac = float(theta.gate_open.mean()) - - return round(0.8 + open_frac * 0.4, 4) - - except Exception as exc: - - print(f"[zfae:gate_factor] error reading Theta gate factor: {exc}") - - return 1.0 - - async def evaluate( - - self, - - assistant_text: str, - - provider: str, - - user_text: str = "", - - path: str = "", - - ) -> dict: - - resolution = self.get_resolution(path) - - try: - - from .edcm import compute_metrics - - metrics = compute_metrics( - - responses=[{"content": assistant_text}], - - context=user_text, - - ) - - coherence = self._coherence_from_metrics(metrics) - - base_lr = 0.025 - - gate_factor = self._theta_gate_factor() - - change_boost, substrate_factor = self._sigma_nudge_factors() - - effective_lr = base_lr * gate_factor * change_boost * substrate_factor - - try: - - pcna = _get_default_pcna() - - pcna.phi.nudge(coherence, lr=effective_lr) - - except Exception: - - pass - - self.eval_count += 1 - - event = { - - "agent": self.AGENT_NAME, - - "provider": provider, - - "coherence": coherence, - - "cm": metrics.get("cm"), - - "da": metrics.get("da"), - - "drift": metrics.get("drift"), - - "int_val": metrics.get("int_val"), - - "resolution": resolution, - - "path": path or None, - - "base_lr": base_lr, - - "gate_factor": gate_factor, - - "change_boost": change_boost, - - "substrate_factor": substrate_factor, - - "effective_lr": round(effective_lr, 6), - - "ts": time.time(), - - } - - self.echo_buffer.append(event) - - suffix = f" path={path}" if path else "" - - print( - - f"[zfae:echo] provider={provider} coherence={coherence}" - - f" lr={effective_lr:.4f}" - - f" gate={gate_factor} boost={change_boost} sub={substrate_factor}" - - f" resolution={resolution}{suffix}" - - ) - - return event - - except Exception as e: - - print(f"[zfae:echo] error: {e}") - - return {} - - def set_sigma_resolution(self, level: int) -> dict: - - try: - - from .sigma import get_sigma - - get_sigma().set_resolution(level) - - event = {"type": "sigma_resolution", "level": level, "ts": time.time()} - - self.echo_buffer.append(event) - - print(f"[zfae:sigma] resolution set to {level}") - - return event - - except Exception as exc: - - print(f"[zfae:sigma] set_resolution error: {exc}") - - return {} - - def sigma_watch_file(self, path: str) -> dict: - - try: - - from .sigma import get_sigma - - get_sigma().add_content_watch(path) - - event = {"type": "sigma_watch_add", "path": path, "ts": time.time()} - - self.echo_buffer.append(event) - - print(f"[zfae:sigma] watching {path}") - - return event - - except Exception as exc: - - print(f"[zfae:sigma] watch_file error: {exc}") - - return {} - - def sigma_unwatch_file(self, path: str) -> dict: - - try: - - from .sigma import get_sigma - - get_sigma().remove_content_watch(path) - - event = {"type": "sigma_watch_remove", "path": path, "ts": time.time()} - - self.echo_buffer.append(event) - - print(f"[zfae:sigma] unwatched {path}") - - return event - - except Exception as exc: - - print(f"[zfae:sigma] unwatch_file error: {exc}") - - return {} - - def set_sigma_structural_interval(self, seconds: float) -> dict: - - try: - - from .sigma import get_sigma - - get_sigma().structural_interval = max(1.0, seconds) - - event = {"type": "sigma_structural_interval", "seconds": seconds, "ts": time.time()} - - self.echo_buffer.append(event) - - print(f"[zfae:sigma] structural interval → {seconds}s") - - return event - - except Exception as exc: - - print(f"[zfae:sigma] set_structural_interval error: {exc}") - - return {} - - def set_sigma_content_interval(self, seconds: float) -> dict: - - try: - - from .sigma import get_sigma - - get_sigma().content_interval = max(1.0, seconds) - - event = {"type": "sigma_content_interval", "seconds": seconds, "ts": time.time()} - - self.echo_buffer.append(event) - - print(f"[zfae:sigma] content interval → {seconds}s") - - return event - - except Exception as exc: - - print(f"[zfae:sigma] set_content_interval error: {exc}") - - return {} - - def state(self) -> dict: - - return { - - "agent": self.AGENT_NAME, - - "eval_count": self.eval_count, - - "echo_buffer_len": len(self.echo_buffer), - - "uptime_s": round(time.time() - self.created_at, 1), - - "resolution": self.resolution_config, - - } - - -_zeta_engine = ZetaEngine() - -_default_pcna = None - - -def _get_default_pcna(): - global _default_pcna - if _default_pcna is None: - from .pcna import PCNAEngine - _default_pcna = PCNAEngine() - return _default_pcna - -# 198:61 diff --git a/libs/pcta/README.md b/libs/pcta/README.md deleted file mode 100644 index e63b5fa..0000000 --- a/libs/pcta/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# PCTA — Prime Circled Tensor Architecture (seed layer) - -**Role in the [prime-tensor stack](../../docs/prime-tensor-stack.md):** PCTA -composes circles (carried by UCNS objects) into **seeds** — the seed is itself a -tensor. It sits between PCNA (layer 1: tensors → circles, back-propagation) and -PTCA (layer 3: seeds → core), and its structural output ("motion") feeds, via -PTCA cores, into ZFAE's inference. Composition counts are **variable** — the only -invariant is that every circle and seed is itself a tensor. - -**Status:** PCTA now has its own repository -([The-Interdependency/pcta](https://github.com/The-Interdependency/pcta)) but is -**not** yet on PyPI. It is **not** registered in `interdependent_lib._REGISTRY` -and has **no** extra until a stable release ships. - -See `docs/prime-tensor-stack.md` for the full layer map and the differentiability -boundary (back-propagation lives only in PCNA; PCTA composition is structural and -non-differentiable). diff --git a/libs/ptca/README.md b/libs/ptca/README.md deleted file mode 100644 index 6c0d20f..0000000 --- a/libs/ptca/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# PTCA — Prime Tensor Core Architecture - -**Source repo:** [The-Interdependency/PTCA](https://github.com/The-Interdependency/PTCA) -**Language:** Python 3.9+ **PyPI:** [`ptca-lib`](https://pypi.org/project/ptca-lib/) -**Letters:** 4 - ---- - -## What it is - -PTCA is a pure-Python library providing: - -- **Sentinel channels** — tagged signal lanes with priority ordering -- **Prime-node tensors** — tensors whose axes are indexed by prime numbers -- **Provenance hashing** — cryptographic provenance chains for tensor operations -- **Exchange mechanics** — deterministic prime-circular state-exchange protocol - ---- - -## Install - -```bash -pip install ptca-lib -# or via the meta-package -pip install interdependent-lib[ptca] -``` - ---- - -## Usage - -```python -from ptca import PTCAInstance, SentinelChannel, PrimeTensor - -# Create a PTCA instance -pt = PTCAInstance(primes=[2, 3, 5, 7]) - -# Sentinel channels -ch = SentinelChannel(name="alpha", priority=1) -pt.register(ch) - -# Exchange mechanics -state_a = [10, 20, 30] -state_b = pt.exchange(state_a) -``` - ---- - -## Package layout - -| File | Purpose | -|------|---------| -| `ptca/__init__.py` | Public API | -| `ptca/instance.py` | `PTCAInstance` — main engine class | -| `ptca/tensor.py` | `PrimeTensor` — prime-indexed tensor | -| `ptca/sentinels.py` | Sentinel channel primitives | -| `ptca/exchange.py` | Exchange protocol | -| `ptca/provenance.py` | Provenance hashing | -| `ptca/primes.py` | Prime utilities | -| `ptca/constants.py` | Shared constants | - ---- - -## See also - -- [Source repository →](https://github.com/The-Interdependency/PTCA) diff --git a/libs/ptca/src/__init__.py b/libs/ptca/src/__init__.py deleted file mode 100644 index f6c8620..0000000 --- a/libs/ptca/src/__init__.py +++ /dev/null @@ -1,86 +0,0 @@ -""" -ptca-lib — Prime Tensor Core Architecture - -Zero-dependency pure-Python library implementing: - - * 53-prime-node × 9-sentinel × 8-phase × 7-slot tensor schema - * Nine sentinel channels (S1_PROVENANCE … S9_AUDIT) - * SHA-256 provenance hashing and chain verification - * Exchange mechanics (delta, alpha, beta, gamma constants) - * PTCAInstance — a PTCA-aware stateful model session - -Quick start ------------ -:: - - from ptca import PTCAInstance - - inst = PTCAInstance(model_id="claude-sonnet-4-6", caller_id="user:alice") - inst.push_context({"role": "user", "content": "Hello", "tokens": 5}) - result = inst.route(node=0, phase=0, slot=0, s1=1.0, s5=0.9) - print(inst.snapshot()) -""" - -from ptca.constants import ( - NODES, - SENTINELS, - PHASES, - SLOTS, - DELTA, - ALPHA, - BETA, - GAMMA, - AGG6, - AGG_SEEDS, - SENTINEL_NAMES, - SENTINEL_INDEX, - SENTINEL_WEIGHTS, -) -from ptca.exchange import Exchange, ExchangeResult, compute_score, aggregate_seeds -from ptca.instance import PTCAInstance -from ptca.primes import PRIME_NODES, PRIME_TO_NODE, node_for_prime, prime_for_node -from ptca.provenance import ( - build_block, - hash_block, - chain_hashes, - verify_chain, - extend_chain, -) -from ptca.sentinels import ( - SentinelState, - S1ProvenanceState, - S2PolicyState, - S3BoundsState, - S4ApprovalState, - S5ContextState, - S6IdentityState, - S7MemoryState, - S8RiskState, - S9AuditState, -) -from ptca.tensor import PTCATensor - -__version__ = "0.1.0" -__all__ = [ - # constants - "NODES", "SENTINELS", "PHASES", "SLOTS", - "DELTA", "ALPHA", "BETA", "GAMMA", - "AGG6", "AGG_SEEDS", - "SENTINEL_NAMES", "SENTINEL_INDEX", "SENTINEL_WEIGHTS", - # primes - "PRIME_NODES", "PRIME_TO_NODE", "node_for_prime", "prime_for_node", - # provenance - "build_block", "hash_block", "chain_hashes", "verify_chain", "extend_chain", - # sentinels - "SentinelState", - "S1ProvenanceState", "S2PolicyState", "S3BoundsState", "S4ApprovalState", - "S5ContextState", "S6IdentityState", "S7MemoryState", "S8RiskState", "S9AuditState", - # tensor - "PTCATensor", - # exchange - "Exchange", "ExchangeResult", "compute_score", "aggregate_seeds", - # instance - "PTCAInstance", - # version - "__version__", -] diff --git a/libs/ptca/src/constants.py b/libs/ptca/src/constants.py deleted file mode 100644 index 43e71b0..0000000 --- a/libs/ptca/src/constants.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -PTCA schema constants. - -Tensor dimensions ------------------ -NODES : 53 (prime-indexed routing nodes) -SENTINELS : 9 (S1–S9 sentinel channels) -PHASES : 8 (processing phases) -SLOTS : 7 (heptagram slots) - -Exchange constants ------------------- -DELTA : base exchange unit -ALPHA : provenance / S1 weight -BETA : policy / S2 weight -GAMMA : bounds+context / S3+S5 weight -AGG6 : aggregation method for S6 (identity) -AGG_SEEDS : aggregation method for seed values -""" - -# --- Tensor dimensions --- -NODES: int = 53 -SENTINELS: int = 9 -PHASES: int = 8 -SLOTS: int = 7 - -# --- Exchange constants --- -DELTA: int = 1 -ALPHA: float = 0.10 -BETA: float = 0.20 -GAMMA: float = 0.10 -AGG6: str = "mean" -AGG_SEEDS: str = "mean" - -# --- Sentinel channel names (index 0 = S1) --- -SENTINEL_NAMES: tuple[str, ...] = ( - "S1_PROVENANCE", - "S2_POLICY", - "S3_BOUNDS", - "S4_APPROVAL", - "S5_CONTEXT", - "S6_IDENTITY", - "S7_MEMORY", - "S8_RISK", - "S9_AUDIT", -) - -# Convenience mapping: name → 0-based index -SENTINEL_INDEX: dict[str, int] = {name: i for i, name in enumerate(SENTINEL_NAMES)} - -# Sentinel weights used in exchange scoring (parallel to SENTINEL_NAMES) -SENTINEL_WEIGHTS: tuple[float, ...] = ( - ALPHA, # S1_PROVENANCE - BETA, # S2_POLICY - GAMMA, # S3_BOUNDS - 0.0, # S4_APPROVAL (boolean gate, not a weighted channel) - GAMMA, # S5_CONTEXT - 0.0, # S6_IDENTITY (aggregated separately via AGG6) - 0.0, # S7_MEMORY (carries forward, not scored per-exchange) - ALPHA, # S8_RISK - 0.0, # S9_AUDIT (append-only log, not scored) -) diff --git a/libs/ptca/src/exchange.py b/libs/ptca/src/exchange.py deleted file mode 100644 index b5e88be..0000000 --- a/libs/ptca/src/exchange.py +++ /dev/null @@ -1,246 +0,0 @@ -""" -Exchange mechanics — weighted scoring across sentinel channels. - -An *exchange* is a single routing event that writes a weighted score -into one or more tensor cells. The score is computed from: - - score = DELTA * ( - ALPHA * s1_weight - + BETA * s2_weight - + GAMMA * s5_weight - + ALPHA * s8_weight - + bonus - ) - -where the sentinel weights are normalised values supplied by the -caller (typically 0.0–1.0) representing signal strength on each -channel. - -The module also provides seed aggregation (AGG_SEEDS) and S6 identity -aggregation (AGG6), both currently "mean". - -Typical usage -------------- -:: - - from ptca.exchange import Exchange - from ptca.tensor import PTCATensor - from ptca.sentinels import SentinelState - - tensor = PTCATensor() - state = SentinelState() - - exc = Exchange(tensor, state) - result = exc.route( - node=0, phase=0, slot=0, - s1=1.0, s2=0.5, s5=0.8, s8=0.1, - ) -""" - -from __future__ import annotations - -from dataclasses import dataclass, field -from typing import Any - -from ptca.constants import ( - ALPHA, BETA, GAMMA, DELTA, - AGG6, AGG_SEEDS, - SENTINEL_WEIGHTS, -) -from ptca.tensor import PTCATensor -from ptca.sentinels import SentinelState - - -# --------------------------------------------------------------------------- -# Exchange result -# --------------------------------------------------------------------------- - -@dataclass -class ExchangeResult: - """Outcome of a single routing exchange.""" - node: int - sentinel_idx: int - phase: int - slot: int - score: float - components: dict[str, float] = field(default_factory=dict) - - -# --------------------------------------------------------------------------- -# Core exchange helpers -# --------------------------------------------------------------------------- - -def compute_score( - *, - s1: float = 0.0, - s2: float = 0.0, - s3: float = 0.0, - s5: float = 0.0, - s8: float = 0.0, - bonus: float = 0.0, -) -> tuple[float, dict[str, float]]: - """ - Compute the scalar exchange score and its component breakdown. - - Parameters - ---------- - s1: - S1_PROVENANCE signal strength [0.0, 1.0]. - s2: - S2_POLICY signal strength [0.0, 1.0]. - s3: - S3_BOUNDS signal strength [0.0, 1.0]. - s5: - S5_CONTEXT signal strength [0.0, 1.0]. - s8: - S8_RISK signal strength [0.0, 1.0]. - bonus: - Caller-supplied additive bonus. - - Returns - ------- - (score, components) - ``score`` is the final scalar. - ``components`` is a dict of individual weighted contributions. - """ - c1 = ALPHA * s1 - c2 = BETA * s2 - c3 = GAMMA * s3 - c5 = GAMMA * s5 - c8 = ALPHA * s8 - - components = { - "s1": c1, - "s2": c2, - "s3": c3, - "s5": c5, - "s8": c8, - "bonus": bonus, - } - score = DELTA * (c1 + c2 + c3 + c5 + c8 + bonus) - return score, components - - -def aggregate_seeds(values: list[float], method: str = AGG_SEEDS) -> float: - """ - Aggregate a list of seed values using *method* (``'mean'`` or ``'sum'``). - - Defaults to ``AGG_SEEDS`` (``'mean'``). - """ - if not values: - return 0.0 - if method == "mean": - return sum(values) / len(values) - if method == "sum": - return sum(values) - raise ValueError(f"Unknown aggregation method: {method!r}") - - -def aggregate_identity(values: list[float], method: str = AGG6) -> float: - """ - Aggregate S6 identity scores using *method*. - - Defaults to ``AGG6`` (``'mean'``). - """ - return aggregate_seeds(values, method) - - -# --------------------------------------------------------------------------- -# Stateful exchange router -# --------------------------------------------------------------------------- - -class Exchange: - """ - Routes a single tensor exchange, updating the tensor and sentinel - state in one call. - - Parameters - ---------- - tensor: - The live ``PTCATensor`` to write into. - sentinel_state: - The live ``SentinelState`` whose channels inform and record - each exchange. - """ - - def __init__(self, tensor: PTCATensor, sentinel_state: SentinelState) -> None: - self.tensor = tensor - self.state = sentinel_state - - def route( - self, - *, - node: int, - phase: int, - slot: int, - s1: float = 0.0, - s2: float = 0.0, - s3: float = 0.0, - s5: float = 0.0, - s8: float = 0.0, - bonus: float = 0.0, - sentinel_idx: int = 0, - audit_event: str = "exchange", - **audit_details: Any, - ) -> ExchangeResult: - """ - Compute a score, write it to the tensor, and record in S9 audit. - - Parameters - ---------- - node: - Prime-node index (0-based, 0–52). - phase: - Phase index (0-based, 0–7). - slot: - Heptagram slot index (0-based, 0–6). - s1 … s8: - Per-sentinel signal strengths used in scoring. - bonus: - Additive bonus to the raw score. - sentinel_idx: - Which sentinel axis to write the score into (default 0 = S1). - audit_event: - Label for the S9 audit entry. - **audit_details: - Extra fields appended to the S9 audit entry. - - Returns - ------- - ExchangeResult - """ - score, components = compute_score( - s1=s1, s2=s2, s3=s3, s5=s5, s8=s8, bonus=bonus, - ) - - self.tensor.add(node, sentinel_idx, phase, slot, score) - - self.state.s9.record( - audit_event, - node=node, - sentinel_idx=sentinel_idx, - phase=phase, - slot=slot, - score=score, - **audit_details, - ) - - return ExchangeResult( - node=node, - sentinel_idx=sentinel_idx, - phase=phase, - slot=slot, - score=score, - components=components, - ) - - def batch_route( - self, - exchanges: list[dict[str, Any]], - ) -> list[ExchangeResult]: - """ - Execute a list of exchange dicts in order. - - Each dict is passed as keyword arguments to :meth:`route`. - """ - return [self.route(**exc) for exc in exchanges] diff --git a/libs/ptca/src/instance.py b/libs/ptca/src/instance.py deleted file mode 100644 index 177ee65..0000000 --- a/libs/ptca/src/instance.py +++ /dev/null @@ -1,353 +0,0 @@ -""" -PTCAInstance — a PTCA-aware stateful model session. - -A ``PTCAInstance`` wraps a single model session and carries live -sentinel state — S5 context window, S6 identity, S7 memory, S8 risk -score, S9 audit log — as first-class fields. It also owns the -``PTCATensor`` and ``Exchange`` router so that every exchange is -automatically reflected in the tensor and audit trail. - -``PTCAInstance`` is designed to be used standalone **or** composed -with ``aimmh_lib.ModelInstance`` — pass the instance's -``sentinel_state`` and ``tensor`` to any consumer that knows about -the PTCA schema. - -Typical standalone usage ------------------------- -:: - - from ptca.instance import PTCAInstance - - inst = PTCAInstance( - model_id="claude-sonnet-4-6", - caller_id="user:alice", - session_id="sess_xyz", - ) - - # Push a context turn - inst.push_context({"role": "user", "content": "Hello", "tokens": 5}) - - # Record a provenance block - inst.record_provenance(payload={"prompt_tokens": 5}) - - # Route an exchange into the tensor - result = inst.route(node=0, phase=0, slot=0, s1=1.0, s5=0.9) - - # Inspect live state - print(inst.risk_score) - print(inst.audit_tail()) -""" - -from __future__ import annotations - -import uuid -from typing import Any - -from ptca.constants import NODES, PHASES, SLOTS -from ptca.exchange import Exchange, ExchangeResult -from ptca.provenance import build_block, extend_chain, hash_block -from ptca.sentinels import SentinelState -from ptca.tensor import PTCATensor - - -class PTCAInstance: - """ - A PTCA-aware stateful session for a single model / conversation. - - Parameters - ---------- - model_id: - Identifier of the model backing this instance. - caller_id: - Identifier of the caller / user. - session_id: - Unique session identifier; auto-generated as a UUID4 hex if - not supplied. - policy_rules: - Initial S2 policy rule identifiers. - bounds: - Mapping of ``lower``/``upper`` and/or named constraints for S3. - approved: - Whether S4 starts in an approved state. - max_context_entries: - Maximum number of S5 context entries to retain. - """ - - def __init__( - self, - *, - model_id: str = "", - caller_id: str = "", - session_id: str = "", - policy_rules: list[str] | None = None, - bounds: dict[str, Any] | None = None, - approved: bool = False, - max_context_entries: int = 256, - ) -> None: - self.session_id = session_id or uuid.uuid4().hex - - # Core PTCA objects - self.tensor = PTCATensor() - self.sentinel_state = SentinelState() - self._exchange = Exchange(self.tensor, self.sentinel_state) - - # Provenance chain (S1) - self._provenance_chain: list[dict[str, Any]] = [] - - # Initialise sentinel channels - self.sentinel_state.s6.set_identity( - model_id=model_id, - caller_id=caller_id, - session_id=self.session_id, - ) - if policy_rules: - self.sentinel_state.s2.set_rules(policy_rules) - if bounds: - lower = bounds.get("lower", float("-inf")) - upper = bounds.get("upper", float("inf")) - constraints = {k: v for k, v in bounds.items() if k not in ("lower", "upper")} - self.sentinel_state.s3.lower = lower - self.sentinel_state.s3.upper = upper - self.sentinel_state.s3.constraints = constraints - if approved: - self.sentinel_state.s4.approve(reason="initialised approved") - self.sentinel_state.s5.max_entries = max_context_entries - - # Genesis provenance block - self._genesis_block = build_block( - model_id=model_id, - caller_id=caller_id, - session_id=self.session_id, - payload={"event": "genesis"}, - ) - self._provenance_chain.append(self._genesis_block) - self.sentinel_state.s1.origin_hash = hash_block(self._genesis_block) - self.sentinel_state.s9.record( - "genesis", - model_id=model_id, - caller_id=caller_id, - session_id=self.session_id, - ) - - # ------------------------------------------------------------------ - # Convenience properties (S5-S9 quick access) - # ------------------------------------------------------------------ - - @property - def model_id(self) -> str: - return self.sentinel_state.s6.model_id - - @property - def caller_id(self) -> str: - return self.sentinel_state.s6.caller_id - - @property - def risk_score(self) -> float: - return self.sentinel_state.s8.score - - @property - def approved(self) -> bool: - return self.sentinel_state.s4.approved - - @property - def context_entries(self) -> list[dict[str, Any]]: - return self.sentinel_state.s5.entries - - @property - def memory_store(self) -> dict[str, Any]: - return self.sentinel_state.s7.store - - # ------------------------------------------------------------------ - # S1 — Provenance - # ------------------------------------------------------------------ - - def record_provenance( - self, - *, - payload: dict[str, Any] | None = None, - timestamp: float | None = None, - ) -> dict[str, Any]: - """ - Extend the provenance chain with a new block and update S1. - - Returns the new block. - """ - block = extend_chain( - self._provenance_chain, - model_id=self.model_id, - caller_id=self.caller_id, - session_id=self.session_id, - payload=payload, - timestamp=timestamp, - ) - h = hash_block(block) - self.sentinel_state.s1.append(h) - return block - - @property - def provenance_chain(self) -> list[dict[str, Any]]: - return self._provenance_chain - - # ------------------------------------------------------------------ - # S2 — Policy - # ------------------------------------------------------------------ - - def set_policy(self, rules: list[str]) -> None: - self.sentinel_state.s2.set_rules(rules) - - # ------------------------------------------------------------------ - # S3 — Bounds - # ------------------------------------------------------------------ - - def set_bounds(self, lower: float = float("-inf"), upper: float = float("inf")) -> None: - self.sentinel_state.s3.lower = lower - self.sentinel_state.s3.upper = upper - - def within_bounds(self, value: float) -> bool: - return self.sentinel_state.s3.within(value) - - # ------------------------------------------------------------------ - # S4 — Approval - # ------------------------------------------------------------------ - - def approve(self, reason: str = "") -> None: - self.sentinel_state.s4.approve(reason) - self.sentinel_state.s9.record("approval_granted", reason=reason) - - def revoke(self, reason: str = "") -> None: - self.sentinel_state.s4.revoke(reason) - self.sentinel_state.s9.record("approval_revoked", reason=reason) - - # ------------------------------------------------------------------ - # S5 — Context - # ------------------------------------------------------------------ - - def push_context(self, entry: dict[str, Any]) -> None: - """Push a context entry (e.g. a conversation turn) onto S5.""" - self.sentinel_state.s5.push(entry) - - def clear_context(self) -> None: - self.sentinel_state.s5.clear() - - # ------------------------------------------------------------------ - # S7 — Memory - # ------------------------------------------------------------------ - - def remember(self, key: str, value: Any) -> None: - self.sentinel_state.s7.remember(key, value) - - def recall(self, key: str, default: Any = None) -> Any: - return self.sentinel_state.s7.retrieve(key, default) - - # ------------------------------------------------------------------ - # S8 — Risk - # ------------------------------------------------------------------ - - def update_risk(self, delta: float, factor: str = "", **details: Any) -> None: - self.sentinel_state.s8.update(delta, factor=factor, **details) - self.sentinel_state.s9.record( - "risk_update", - delta=delta, - factor=factor, - new_score=self.risk_score, - **details, - ) - - def reset_risk(self) -> None: - self.sentinel_state.s8.reset() - self.sentinel_state.s9.record("risk_reset") - - # ------------------------------------------------------------------ - # S9 — Audit - # ------------------------------------------------------------------ - - def audit_tail(self, n: int = 10) -> list[dict[str, Any]]: - return self.sentinel_state.s9.tail(n) - - # ------------------------------------------------------------------ - # Exchange routing - # ------------------------------------------------------------------ - - def route( - self, - *, - node: int, - phase: int, - slot: int, - s1: float = 0.0, - s2: float = 0.0, - s3: float = 0.0, - s5: float = 0.0, - s8: float = 0.0, - bonus: float = 0.0, - sentinel_idx: int = 0, - **audit_details: Any, - ) -> ExchangeResult: - """ - Route a tensor exchange through this instance. - - All parameters are forwarded to :meth:`Exchange.route`. - """ - return self._exchange.route( - node=node, - phase=phase, - slot=slot, - s1=s1, - s2=s2, - s3=s3, - s5=s5, - s8=s8, - bonus=bonus, - sentinel_idx=sentinel_idx, - **audit_details, - ) - - def batch_route(self, exchanges: list[dict[str, Any]]) -> list[ExchangeResult]: - """Route a list of exchange dicts; see :meth:`Exchange.batch_route`.""" - return self._exchange.batch_route(exchanges) - - # ------------------------------------------------------------------ - # Snapshot - # ------------------------------------------------------------------ - - def snapshot(self) -> dict[str, Any]: - """ - Return a JSON-serialisable snapshot of live sentinel state. - - This is suitable for persisting instance state between - processes or attaching to an API response as a - ``sentinel_context`` block (compatible with the aimmh backend - format for S5–S9). - """ - s = self.sentinel_state - return { - "session_id": self.session_id, - "S5_CONTEXT": { - "entries": s.s5.entries, - "token_count": s.s5.token_count, - }, - "S6_IDENTITY": { - "model_id": s.s6.model_id, - "caller_id": s.s6.caller_id, - "session_id": s.s6.session_id, - "metadata": s.s6.metadata, - }, - "S7_MEMORY": { - "store": s.s7.store, - }, - "S8_RISK": { - "score": s.s8.score, - "factors": s.s8.factors, - }, - "S9_AUDIT": { - "log": s.s9.log, - }, - } - - def __repr__(self) -> str: - return ( - f"PTCAInstance(model_id={self.model_id!r}, " - f"session_id={self.session_id!r}, " - f"risk={self.risk_score:.3f}, " - f"approved={self.approved})" - ) diff --git a/libs/ptca/src/primes.py b/libs/ptca/src/primes.py deleted file mode 100644 index ee41144..0000000 --- a/libs/ptca/src/primes.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Prime-node axis for the PTCA tensor. - -The 53 routing nodes are indexed by the first 53 prime numbers. -Each prime p_i is the canonical address of node i in the tensor's -first dimension. -""" - -from __future__ import annotations - -from ptca.constants import NODES - -# First 53 primes (the 53rd prime is 241) -PRIME_NODES: tuple[int, ...] = ( - 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, - 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, - 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, - 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, - 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, - 233, 239, 241, -) - -assert len(PRIME_NODES) == NODES, ( - f"Expected {NODES} primes, got {len(PRIME_NODES)}" -) - -# Reverse lookup: prime value → node index -PRIME_TO_NODE: dict[int, int] = {p: i for i, p in enumerate(PRIME_NODES)} - - -def node_for_prime(p: int) -> int: - """Return the node index (0-based) for a given prime, or raise KeyError.""" - return PRIME_TO_NODE[p] - - -def prime_for_node(idx: int) -> int: - """Return the prime for a node index (0-based).""" - if not (0 <= idx < NODES): - raise IndexError(f"Node index {idx} out of range [0, {NODES})") - return PRIME_NODES[idx] - - -def is_prime_node(p: int) -> bool: - """Return True if *p* is one of the 53 routing primes.""" - return p in PRIME_TO_NODE diff --git a/libs/ptca/src/provenance.py b/libs/ptca/src/provenance.py deleted file mode 100644 index b105942..0000000 --- a/libs/ptca/src/provenance.py +++ /dev/null @@ -1,164 +0,0 @@ -""" -Provenance hashing — S1_PROVENANCE support. - -A provenance block captures the origin and chain-of-custody of a -tensor exchange. It is hashed with SHA-256 (stdlib ``hashlib``) so -every block is both content-addressed and tamper-evident. - -Typical usage -------------- -:: - - from ptca.provenance import build_block, hash_block, chain_hashes - - block = build_block( - model_id="claude-sonnet-4-6", - caller_id="user:alice", - session_id="sess_abc123", - payload={"prompt_tokens": 42}, - ) - h = hash_block(block) - # h is a 64-char hex string - - # extend a chain: include parent hash in new block - block2 = build_block( - model_id="claude-sonnet-4-6", - parent_hash=h, - payload={"completion_tokens": 17}, - ) -""" - -from __future__ import annotations - -import hashlib -import json -import time -from typing import Any - - -# --------------------------------------------------------------------------- -# Block construction -# --------------------------------------------------------------------------- - -def build_block( - *, - model_id: str = "", - caller_id: str = "", - session_id: str = "", - parent_hash: str = "", - payload: dict[str, Any] | None = None, - timestamp: float | None = None, -) -> dict[str, Any]: - """ - Construct a provenance block (plain dict, JSON-serialisable). - - Parameters - ---------- - model_id: - Identifier of the model that produced this exchange. - caller_id: - Identifier of the caller / user. - session_id: - Session or conversation identifier. - parent_hash: - SHA-256 hex digest of the immediately preceding block in the - chain (empty string for genesis blocks). - payload: - Arbitrary JSON-serialisable metadata to attach. - timestamp: - Unix timestamp; defaults to ``time.time()``. - - Returns - ------- - dict - The provenance block. Pass to :func:`hash_block` to obtain - its content-addressed digest. - """ - return { - "model_id": model_id, - "caller_id": caller_id, - "session_id": session_id, - "parent_hash": parent_hash, - "payload": payload or {}, - "ts": timestamp if timestamp is not None else time.time(), - } - - -# --------------------------------------------------------------------------- -# Hashing -# --------------------------------------------------------------------------- - -def _canonical(block: dict[str, Any]) -> bytes: - """Deterministic JSON bytes for a provenance block.""" - return json.dumps(block, sort_keys=True, separators=(",", ":")).encode("utf-8") - - -def hash_block(block: dict[str, Any]) -> str: - """ - Return the SHA-256 hex digest of *block*. - - The block is serialised to canonical JSON (sorted keys, no - whitespace) before hashing, so the digest is deterministic. - """ - return hashlib.sha256(_canonical(block)).hexdigest() - - -# --------------------------------------------------------------------------- -# Chain helpers -# --------------------------------------------------------------------------- - -def chain_hashes(blocks: list[dict[str, Any]]) -> list[str]: - """ - Return the list of SHA-256 digests for an ordered list of blocks. - - The list is computed independently of any ``parent_hash`` fields - already stored in the blocks — it reflects the *content* of each - block as supplied. - """ - return [hash_block(b) for b in blocks] - - -def verify_chain(blocks: list[dict[str, Any]]) -> bool: - """ - Verify that each block's ``parent_hash`` matches the hash of the - preceding block. - - Returns ``True`` if the chain is intact, ``False`` otherwise. - The genesis block (index 0) is valid when its ``parent_hash`` is - an empty string. - """ - if not blocks: - return True - if blocks[0].get("parent_hash", "") != "": - return False - for i in range(1, len(blocks)): - expected = hash_block(blocks[i - 1]) - if blocks[i].get("parent_hash", "") != expected: - return False - return True - - -def extend_chain( - blocks: list[dict[str, Any]], - *, - model_id: str = "", - caller_id: str = "", - session_id: str = "", - payload: dict[str, Any] | None = None, - timestamp: float | None = None, -) -> dict[str, Any]: - """ - Build a new block whose ``parent_hash`` is the hash of the last - block in *blocks*, append it to *blocks* in-place, and return it. - """ - parent_hash = hash_block(blocks[-1]) if blocks else "" - block = build_block( - model_id=model_id, - caller_id=caller_id, - session_id=session_id, - parent_hash=parent_hash, - payload=payload, - timestamp=timestamp, - ) - blocks.append(block) - return block diff --git a/libs/ptca/src/sentinels.py b/libs/ptca/src/sentinels.py deleted file mode 100644 index ab357e8..0000000 --- a/libs/ptca/src/sentinels.py +++ /dev/null @@ -1,217 +0,0 @@ -""" -Sentinel channel definitions and live state. - -Nine sentinel channels gate and annotate every tensor exchange: - - S1_PROVENANCE – origin hash + chain of custody - S2_POLICY – applicable policy rules - S3_BOUNDS – numeric bounds / constraint envelope - S4_APPROVAL – boolean gate (must be True to commit) - S5_CONTEXT – live context window tokens/summary - S6_IDENTITY – model / caller identity record - S7_MEMORY – persistent memory log - S8_RISK – running risk score [0.0, 1.0] - S9_AUDIT – append-only audit trail - -SentinelState is a plain dataclass so it is trivially serialisable to -a dict (via ``dataclasses.asdict``) and reconstructable from one. -""" - -from __future__ import annotations - -import time -from dataclasses import dataclass, field -from typing import Any - -from ptca.constants import SENTINEL_NAMES - - -# --------------------------------------------------------------------------- -# Individual channel state -# --------------------------------------------------------------------------- - -@dataclass -class S1ProvenanceState: - """Origin hash and chain-of-custody list.""" - origin_hash: str = "" - chain: list[str] = field(default_factory=list) - - def append(self, hash_value: str) -> None: - self.chain.append(hash_value) - - -@dataclass -class S2PolicyState: - """Active policy rule identifiers.""" - rules: list[str] = field(default_factory=list) - - def set_rules(self, rules: list[str]) -> None: - self.rules = list(rules) - - -@dataclass -class S3BoundsState: - """Numeric constraint envelope.""" - lower: float = float("-inf") - upper: float = float("inf") - constraints: dict[str, Any] = field(default_factory=dict) - - def within(self, value: float) -> bool: - return self.lower <= value <= self.upper - - -@dataclass -class S4ApprovalState: - """Boolean approval gate.""" - approved: bool = False - reason: str = "" - - def approve(self, reason: str = "") -> None: - self.approved = True - self.reason = reason - - def revoke(self, reason: str = "") -> None: - self.approved = False - self.reason = reason - - -@dataclass -class S5ContextState: - """Live context window: ordered list of context entries.""" - entries: list[dict[str, Any]] = field(default_factory=list) - max_entries: int = 256 - - def push(self, entry: dict[str, Any]) -> None: - self.entries.append(entry) - if len(self.entries) > self.max_entries: - self.entries = self.entries[-self.max_entries:] - - def clear(self) -> None: - self.entries = [] - - @property - def token_count(self) -> int: - return sum(e.get("tokens", 0) for e in self.entries) - - -@dataclass -class S6IdentityState: - """Caller / model identity record.""" - model_id: str = "" - caller_id: str = "" - session_id: str = "" - metadata: dict[str, Any] = field(default_factory=dict) - - def set_identity( - self, - model_id: str = "", - caller_id: str = "", - session_id: str = "", - **metadata: Any, - ) -> None: - self.model_id = model_id - self.caller_id = caller_id - self.session_id = session_id - self.metadata.update(metadata) - - -@dataclass -class S7MemoryState: - """Persistent memory log: key-value store + ordered recall list.""" - store: dict[str, Any] = field(default_factory=dict) - recall: list[dict[str, Any]] = field(default_factory=list) - - def remember(self, key: str, value: Any) -> None: - self.store[key] = value - - def recall_entry(self, entry: dict[str, Any]) -> None: - self.recall.append(entry) - - def retrieve(self, key: str, default: Any = None) -> Any: - return self.store.get(key, default) - - -@dataclass -class S8RiskState: - """Running risk score in [0.0, 1.0] and contributing factors.""" - score: float = 0.0 - factors: list[dict[str, Any]] = field(default_factory=list) - - def update(self, delta: float, factor: str = "", **details: Any) -> None: - self.score = max(0.0, min(1.0, self.score + delta)) - self.factors.append({"delta": delta, "factor": factor, **details}) - - def reset(self) -> None: - self.score = 0.0 - self.factors = [] - - -@dataclass -class S9AuditState: - """Append-only audit trail.""" - log: list[dict[str, Any]] = field(default_factory=list) - - def record(self, event: str, **details: Any) -> None: - self.log.append({ - "ts": time.time(), - "event": event, - **details, - }) - - def tail(self, n: int = 10) -> list[dict[str, Any]]: - return self.log[-n:] - - -# --------------------------------------------------------------------------- -# Composite sentinel state (all nine channels together) -# --------------------------------------------------------------------------- - -@dataclass -class SentinelState: - """ - All nine sentinel channels as a single coherent unit. - - Attributes mirror SENTINEL_NAMES in order: - s1 … s9 - """ - s1: S1ProvenanceState = field(default_factory=S1ProvenanceState) - s2: S2PolicyState = field(default_factory=S2PolicyState) - s3: S3BoundsState = field(default_factory=S3BoundsState) - s4: S4ApprovalState = field(default_factory=S4ApprovalState) - s5: S5ContextState = field(default_factory=S5ContextState) - s6: S6IdentityState = field(default_factory=S6IdentityState) - s7: S7MemoryState = field(default_factory=S7MemoryState) - s8: S8RiskState = field(default_factory=S8RiskState) - s9: S9AuditState = field(default_factory=S9AuditState) - - def channel(self, name: str) -> Any: - """Return a channel by its sentinel name (e.g. ``'S5_CONTEXT'``).""" - mapping = { - "S1_PROVENANCE": self.s1, - "S2_POLICY": self.s2, - "S3_BOUNDS": self.s3, - "S4_APPROVAL": self.s4, - "S5_CONTEXT": self.s5, - "S6_IDENTITY": self.s6, - "S7_MEMORY": self.s7, - "S8_RISK": self.s8, - "S9_AUDIT": self.s9, - } - if name not in mapping: - raise KeyError(f"Unknown sentinel: {name!r}. Valid names: {SENTINEL_NAMES}") - return mapping[name] - - def to_dict(self) -> dict[str, Any]: - """Shallow serialisation suitable for provenance blocks.""" - import dataclasses - return { - "S1_PROVENANCE": dataclasses.asdict(self.s1), - "S2_POLICY": dataclasses.asdict(self.s2), - "S3_BOUNDS": dataclasses.asdict(self.s3), - "S4_APPROVAL": dataclasses.asdict(self.s4), - "S5_CONTEXT": dataclasses.asdict(self.s5), - "S6_IDENTITY": dataclasses.asdict(self.s6), - "S7_MEMORY": dataclasses.asdict(self.s7), - "S8_RISK": dataclasses.asdict(self.s8), - "S9_AUDIT": dataclasses.asdict(self.s9), - } diff --git a/libs/ptca/src/tensor.py b/libs/ptca/src/tensor.py deleted file mode 100644 index e6b90f0..0000000 --- a/libs/ptca/src/tensor.py +++ /dev/null @@ -1,184 +0,0 @@ -""" -PTCA Tensor — 53 × 9 × 8 × 7 routing structure. - -Dimensions ----------- -axis 0 – node : 53 prime-indexed routing nodes -axis 1 – sentinel: 9 sentinel channels (S1–S9) -axis 2 – phase : 8 processing phases -axis 3 – slot : 7 heptagram slots - -The tensor is backed by a flat list of floats for zero-dependency -efficiency. All indexing is done via the helper ``_idx``. - -Each cell stores a float score initialised to 0.0. Callers write -exchange results (weighted sums from exchange.py) into the tensor and -can later aggregate across any axis. -""" - -from __future__ import annotations - -from typing import Sequence - -from ptca.constants import NODES, SENTINELS, PHASES, SLOTS - - -class PTCATensor: - """ - Zero-dependency 4-D tensor backed by a flat Python list. - - Shape: (NODES, SENTINELS, PHASES, SLOTS) = (53, 9, 8, 7) - Total cells: 26 796 - """ - - SHAPE: tuple[int, int, int, int] = (NODES, SENTINELS, PHASES, SLOTS) - SIZE: int = NODES * SENTINELS * PHASES * SLOTS - - def __init__(self) -> None: - self._data: list[float] = [0.0] * self.SIZE - - # ------------------------------------------------------------------ - # Internal indexing - # ------------------------------------------------------------------ - - @staticmethod - def _idx(node: int, sentinel: int, phase: int, slot: int) -> int: - if not (0 <= node < NODES): - raise IndexError(f"node {node} out of range [0, {NODES})") - if not (0 <= sentinel < SENTINELS): - raise IndexError(f"sentinel {sentinel} out of range [0, {SENTINELS})") - if not (0 <= phase < PHASES): - raise IndexError(f"phase {phase} out of range [0, {PHASES})") - if not (0 <= slot < SLOTS): - raise IndexError(f"slot {slot} out of range [0, {SLOTS})") - return ( - node * (SENTINELS * PHASES * SLOTS) - + sentinel * (PHASES * SLOTS) - + phase * SLOTS - + slot - ) - - # ------------------------------------------------------------------ - # Cell access - # ------------------------------------------------------------------ - - def get(self, node: int, sentinel: int, phase: int, slot: int) -> float: - return self._data[self._idx(node, sentinel, phase, slot)] - - def set(self, node: int, sentinel: int, phase: int, slot: int, value: float) -> None: - self._data[self._idx(node, sentinel, phase, slot)] = float(value) - - def add(self, node: int, sentinel: int, phase: int, slot: int, delta: float) -> None: - idx = self._idx(node, sentinel, phase, slot) - self._data[idx] += float(delta) - - # ------------------------------------------------------------------ - # Slice helpers (return plain lists — no numpy dependency) - # ------------------------------------------------------------------ - - def node_slice(self, node: int) -> list[float]: - """All values for a given node (SENTINELS × PHASES × SLOTS cells).""" - start = node * (SENTINELS * PHASES * SLOTS) - return self._data[start: start + SENTINELS * PHASES * SLOTS] - - def sentinel_slice(self, sentinel: int) -> list[float]: - """All values for a given sentinel channel across all nodes/phases/slots.""" - result: list[float] = [] - for n in range(NODES): - for ph in range(PHASES): - for sl in range(SLOTS): - result.append(self._data[self._idx(n, sentinel, ph, sl)]) - return result - - def phase_slice(self, phase: int) -> list[float]: - """All values for a given phase across all nodes/sentinels/slots.""" - result: list[float] = [] - for n in range(NODES): - for s in range(SENTINELS): - for sl in range(SLOTS): - result.append(self._data[self._idx(n, s, phase, sl)]) - return result - - def slot_slice(self, slot: int) -> list[float]: - """All values for a given heptagram slot across all nodes/sentinels/phases.""" - result: list[float] = [] - for n in range(NODES): - for s in range(SENTINELS): - for ph in range(PHASES): - result.append(self._data[self._idx(n, s, ph, slot)]) - return result - - # ------------------------------------------------------------------ - # Aggregation - # ------------------------------------------------------------------ - - @staticmethod - def _mean(values: Sequence[float]) -> float: - if not values: - return 0.0 - return sum(values) / len(values) - - @staticmethod - def _sum(values: Sequence[float]) -> float: - return sum(values) - - def aggregate( - self, - method: str = "mean", - *, - node: int | None = None, - sentinel: int | None = None, - phase: int | None = None, - slot: int | None = None, - ) -> float: - """ - Aggregate all cells matching the supplied fixed axes. - - Any axis left as ``None`` is summed/averaged over. - ``method`` is ``'mean'`` or ``'sum'``. - """ - nodes = [node] if node is not None else list(range(NODES)) - sentinels = [sentinel] if sentinel is not None else list(range(SENTINELS)) - phases = [phase] if phase is not None else list(range(PHASES)) - slots = [slot] if slot is not None else list(range(SLOTS)) - - values = [ - self._data[self._idx(n, s, ph, sl)] - for n in nodes - for s in sentinels - for ph in phases - for sl in slots - ] - - if method == "mean": - return self._mean(values) - if method == "sum": - return self._sum(values) - raise ValueError(f"Unknown aggregation method: {method!r}") - - # ------------------------------------------------------------------ - # Reset - # ------------------------------------------------------------------ - - def reset(self) -> None: - """Zero every cell.""" - self._data = [0.0] * self.SIZE - - def reset_node(self, node: int) -> None: - """Zero all cells for a given node.""" - start = node * (SENTINELS * PHASES * SLOTS) - for i in range(SENTINELS * PHASES * SLOTS): - self._data[start + i] = 0.0 - - # ------------------------------------------------------------------ - # Dunder helpers - # ------------------------------------------------------------------ - - def __repr__(self) -> str: - return ( - f"PTCATensor(shape={self.SHAPE}, " - f"nonzero={sum(1 for v in self._data if v != 0.0)})" - ) - - def __len__(self) -> int: - return self.SIZE diff --git a/libs/ptcna/README.md b/libs/ptcna/README.md new file mode 100644 index 0000000..97d65b6 --- /dev/null +++ b/libs/ptcna/README.md @@ -0,0 +1,38 @@ +# PTCNA — Prime Tensor Circled Neural Architecture + +**Source repo:** [The-Interdependency/ptcna](https://github.com/The-Interdependency/ptcna) +**Language:** Python 3.10+ +**PyPI:** *(not yet published — source-only until it ships)* +**Consolidates:** the former `pcna`, `pcta`, and `pcsa` repos. + +--- + +## What it is + +PTCNA is **one architecture, four layers** — the prime-tensor compute stack that +used to be spread across separate repos, now unified because they were only ever +layers of one thing: + +| Module | Layer | Divides… → … | Tensor kind | +|--------|-------|--------------|-------------| +| `ptcna.neural` | neural | (base) neural tensors | neural — **the only back-propagating layer** | +| `ptcna.circle` | circle | neural tensors → circles | auditing / timing | +| `ptcna.seed` | seed | circles → seeds | auditing / timing | +| `ptcna.core` | core | seeds → cores | auditing / timing (fiqs gate internal propagation per Fick's law) | + +Every circle, seed, and core is itself a tensor. Back-propagation lives only in +the neural layer; the other three are auditing/timing tensors. See the canonical +[prime-tensor stack map](../../docs/prime-tensor-stack.md). + +## Status in the bundle + +- Registered in `interdependent_lib._REGISTRY` as `ptcna` → import name `ptcna`. +- **No extra yet** — per `docs/dependency-policy.md`, libraries enter + `[project.optional-dependencies]` only after a stable PyPI release. A single + `ptcna` extra replaces the former `pcna`/`pcta`/`pcsa` intent once it ships. +- The previously-published core-layer dist is superseded by this consolidation. + +## Boundary + +PCEA (encryption guardian) is **not** part of PTCNA — it is orthogonal and stays +its own repo. Naming these terms transfers no theorem/proof/empirical status. diff --git a/pyproject.toml b/pyproject.toml index 45c88c3..774fcb0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,18 +16,23 @@ keywords = ["prime", "tensor", "encryption", "neural", "unit-circle", "multi-mod dependencies = [] [project.optional-dependencies] -# Four-letter acronym libraries +# Standalone libraries pcea = ["pcea>=0.1.0"] -ptca = ["ptca-lib>=0.1.0"] ucns = ["ucns>=0.9.1"] # Five-letter acronym libraries aimmh = ["aimmh-lib>=1.1.0"] +# Prime-tensor stack: the former pcna / pcta / pcsa repos are consolidated into +# the single ptcna package (neural/circle/seed/core), which supersedes the +# previously-published core-layer dist. A `ptcna` extra lands once ptcna ships +# to PyPI; until then it is a source-only registry probe and no extra references +# it (extras pin published dists only). See docs/naming-migration.md and +# docs/dependency-policy.md. + # Install all packaged libraries at once all = [ "pcea>=0.1.0", - "ptca-lib>=0.1.0", "ucns>=0.9.1", "aimmh-lib>=1.1.0", ] diff --git a/scripts/rename-repos.sh b/scripts/rename-repos.sh new file mode 100755 index 0000000..c6572cc --- /dev/null +++ b/scripts/rename-repos.sh @@ -0,0 +1,136 @@ +#!/data/data/com.termux/files/usr/bin/bash +# +# rename-repos.sh — org-wide repo renames for The-Interdependency +# Ratified scheme: interdependent-lib/docs/naming-migration.md +# +# Runs the renames in the SAFE ORDER (casing first, a0 shuffle, PTCA->pcsa last). +# GitHub redirects renamed repo URLs, but NOT import paths or PyPI dist names — +# so this must land before any dist pins the old names. +# +# Termux setup: +# pkg install gh git +# gh auth login # needs 'repo' + 'admin:org' scope to rename +# +# Usage: +# ./rename-repos.sh --dry-run # print what would happen, change nothing +# ./rename-repos.sh # interactive: confirm each rename +# ./rename-repos.sh --yes # no prompts (careful) +# +set -euo pipefail + +ORG="The-Interdependency" + +DRY_RUN=0 +ASSUME_YES=0 +for arg in "$@"; do + case "$arg" in + --dry-run) DRY_RUN=1 ;; + --yes|-y) ASSUME_YES=1 ;; + -h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + *) echo "unknown arg: $arg" >&2; exit 2 ;; + esac +done + +# ── The a0 shuffle needs a parking name for the CURRENT a0 repo. ────────────── +# The migration doc leaves this open (hmmm). Set it deliberately before running +# the a0 section, or the script will stop and ask. +A0_LEGACY_NAME="${A0_LEGACY_NAME:-}" # e.g. a0-legacy, a0-v1, a0-archive + +# ── helpers ────────────────────────────────────────────────────────────────── +c_green() { printf '\033[32m%s\033[0m\n' "$*"; } +c_yellow(){ printf '\033[33m%s\033[0m\n' "$*"; } +c_red() { printf '\033[31m%s\033[0m\n' "$*"; } + +need() { command -v "$1" >/dev/null 2>&1 || { c_red "missing: $1 (pkg install $1)"; exit 1; }; } +need gh +need git + +gh auth status >/dev/null 2>&1 || { c_red "not logged in — run: gh auth login"; exit 1; } + +repo_exists() { gh repo view "$ORG/$1" >/dev/null 2>&1; } + +confirm() { + [ "$ASSUME_YES" = 1 ] && return 0 + local ans + read -r -p " proceed? [y/N] " ans + [ "$ans" = y ] || [ "$ans" = Y ] +} + +# rename OLD -> NEW, but only if OLD exists and NEW does not. +# GitHub repo names are case-insensitive, so a pure-casing rename (PCEA->pcea) +# is applied via a two-hop through a temp name to force the stored casing. +rename_repo() { + local old="$1" new="$2" note="${3:-}" + echo + c_yellow "── $old → $new ${note:+($note)}" + + if repo_exists "$new" && ! repo_exists "$old"; then + c_green " already done (only '$new' exists) — skip"; return 0 + fi + if ! repo_exists "$old"; then + c_red " source '$old' not found — skip (check the name / already renamed?)"; return 0 + fi + if repo_exists "$new" && [ "${old,,}" != "${new,,}" ]; then + c_red " target '$new' ALREADY EXISTS and differs from '$old' — refusing to clobber"; return 1 + fi + + # pure-casing change: same name ignoring case -> two-hop + if [ "${old,,}" = "${new,,}" ] && [ "$old" != "$new" ]; then + local tmp="${new}-casingtmp-$$" + echo " casing-only: $old -> $tmp -> $new" + if [ "$DRY_RUN" = 1 ]; then c_green " [dry-run] would two-hop via $tmp"; return 0; fi + confirm || { echo " skipped"; return 0; } + gh repo rename "$tmp" --repo "$ORG/$old" --yes + gh repo rename "$new" --repo "$ORG/$tmp" --yes + c_green " renamed (casing) -> $new" + return 0 + fi + + echo " gh repo rename $new --repo $ORG/$old" + if [ "$DRY_RUN" = 1 ]; then c_green " [dry-run] would rename"; return 0; fi + confirm || { echo " skipped"; return 0; } + gh repo rename "$new" --repo "$ORG/$old" --yes + c_green " renamed -> $new" +} + +c_yellow "== The-Interdependency repo renames ==" +[ "$DRY_RUN" = 1 ] && c_yellow " (DRY RUN — no changes)" +echo " FROZEN, not renamed: ucns (DOI 10.5281/zenodo.20665340)" +echo " keep as-is: pcna, pcta" + +# ── 1. casing-only / punctuation renames (lowest risk) ─────────────────────── +rename_repo PCEA pcea "casing" +rename_repo ZFAE zfae "casing; conceptual repo, runtime in a0" +rename_repo METAPAT metapat "casing; FLAR" +rename_repo eml_ucns eml-ucns "underscore->hyphen; archive instead if defunct" + +# ── 2. the a0 shuffle: free 'a0', then promote a0-betatest ─────────────────── +echo +c_yellow "── a0 shuffle" +if repo_exists a0-betatest || repo_exists a0; then + if [ -z "$A0_LEGACY_NAME" ]; then + c_red " A0_LEGACY_NAME is unset — the current 'a0' needs a parking name." + c_red " Re-run with: A0_LEGACY_NAME=a0-legacy ./rename-repos.sh" + c_red " (skipping a0 shuffle for now)" + else + rename_repo a0 "$A0_LEGACY_NAME" "park the old a0" + rename_repo a0-betatest a0 "promote betatest to canonical a0" + fi +else + c_green " neither a0 nor a0-betatest present — skip" +fi + +# ── 3. PTCA -> pcsa (LAST — rewrites import paths + needs a new PyPI dist) ──── +echo +c_yellow "── PTCA -> pcsa (do this LAST)" +c_red " Reminder: after this rename, publish a NEW dist 'pcsa' and ABANDON" +c_red " 'ptca-lib' (never re-release it). Update interdependent-lib extras," +c_red " _REGISTRY, docs/prime-tensor-stack.md, and libs/ptca/ in the same PR." +rename_repo PTCA pcsa "seed/core stratum; new dist supersedes ptca-lib" + +echo +c_green "== done ==" +echo "Renamed repo URLs redirect automatically. Next, in code:" +echo " - interdependent-lib: add pcsa extra + prime-stack; keep ptca alias 1 minor" +echo " - pcna: core/ -> pcna/ package rename; ptca_core.py -> pcsa_core.py" +echo " - sweep docs/tables to pcsa only after 'pcsa' exists on PyPI" diff --git a/tests/test_interdependent_lib.py b/tests/test_interdependent_lib.py index 7c429ea..cd8619e 100644 --- a/tests/test_interdependent_lib.py +++ b/tests/test_interdependent_lib.py @@ -33,16 +33,30 @@ def test_available_returns_dict(): result = interdependent_lib.available() assert isinstance(result, dict) # All known sub-libs (PyPI and source-only) must appear as keys. - for key in ("pcea", "ptca", "ucns", "pcna", "zfae", "aimmh", "metapat"): + for key in ("pcea", "ucns", "ptcna", "zfae", "aimmh", "metapat"): assert key in result # All values are booleans. for v in result.values(): assert isinstance(v, bool) -def test_pcna_source_only_does_not_probe_generic_core_module(): - """PCNA must not false-positive against an unrelated module named core.""" - assert interdependent_lib.available()["pcna"] is False +def test_prime_tensor_stack_consolidated_into_single_ptcna_key(): + """pcna/pcta/pcsa were consolidated into the single ptcna package + (neural/circle/seed/core). The old per-layer keys must be gone, and ptcna + probes its package-unique import name.""" + reg = interdependent_lib._REGISTRY + assert reg["ptcna"] == "ptcna" + for gone in ("pcna", "pcta", "pcsa", "ptsa"): + assert gone not in reg, f"{gone} should be consolidated into ptcna" + assert isinstance(interdependent_lib.available()["ptcna"], bool) + + +def test_ptcna_has_no_extra_until_published_and_ptca_lib_superseded(): + """ptcna is a source-only probe until it ships to PyPI (no extra yet), and + the superseded ptca-lib dist must no longer be pinned anywhere.""" + text = _pyproject_text() + assert "ptca-lib" not in text, "ptca-lib is superseded by the ptcna consolidation" + assert "ptca " not in text and "\nptca" not in text, "no ptca extra should remain" def test_metapat_registered_with_unique_import_target_and_no_extra():