Update from The-Interdependency/a0 engine implementation - #2
Conversation
Add canonical engine modules from a0/python/engine:
- core/ptca_core.py: PTCACore with Euler propagation (dt=0.01, 10 steps),
adjacency distances {1,2,3,4,5,6,7,14}, heptagram hub-ring coherence
- core/guardian.py: GuardianTensor (N=29) microkernel ring with gate control,
AES-256-GCM key derivation, blueprint sharding
- core/memory_core.py: MemoryCore parameterized long/short-term memory rings
- core/pcna.py: PCNAEngine six-ring pipeline (Φ/53, Ψ/53, Ω/53, Θ/29,
Memory-L/19, Memory-S/17) with file-based checkpointing
- core/merge.py: InstanceMerge protocol (absorb, fork, converge)
- core/zeta.py: ZetaEngine EDCM-driven PCNA reward backprop with
per-directory resolution control
- core/edcm.py: Six-family EDCM metrics (CM, DA, DRIFT, DVG, INT, TBF)
with directives and 0.80/0.20 alert thresholds from a0 canonical spec
Update backend/edcm_engine.py to use six-family EDCM metrics and fire
the six behavioral directives (CONSTRAINT_REFOCUS, DISSONANCE_HALT,
DRIFT_ANCHOR, DIVERGENCE_COMMIT, INTENSITY_CALM, BALANCE_CONCISE).
https://claude.ai/code/session_018vyPzNQrgsLKq34wyyNY7W
- backend/researcher_outreach.py: remove backslash-escaped triple-quotes in f-string (E999 SyntaxError) - backend/server.py: remove unused `global active_seeds` declaration (F824) https://claude.ai/code/session_018vyPzNQrgsLKq34wyyNY7W
- tests/test_tensor_engine.py, tests/tests_topology.py: import directly from core.* instead of main.core.* (main.py is a module, not a package) - main.py: fix imports from src.core.* → core.* and uvicorn.run reference from src.main → main (no src/ package exists in this repo) https://claude.ai/code/session_018vyPzNQrgsLKq34wyyNY7W
The previous .gitignore was malformed (repeated -e lines) and missing standard Python ignores, causing __pycache__ dirs to show as untracked. https://claude.ai/code/session_018vyPzNQrgsLKq34wyyNY7W
The CI workflow installs from requirements.txt if present. Without it, numpy was never installed and pytest failed at collection time with ModuleNotFoundError when importing core/tensor_engine.py. https://claude.ai/code/session_018vyPzNQrgsLKq34wyyNY7W
|
@copilot |
Agent-Logs-Url: https://github.com/The-Interdependency/pcna/sessions/2a6d7c43-ae32-4d1a-9c80-364eebb54717 Co-authored-by: erinepshovel-code <250928284+erinepshovel-code@users.noreply.github.com>
erinepshovel-code
left a comment
There was a problem hiding this comment.
figured
There was a problem hiding this comment.
Pull request overview
Ports the canonical a0 engine components into this repo’s core/ layer and updates the backend EDCM analyzer to use a six-family metrics + directive framework, aligning imports/tests with the new module layout.
Changes:
- Added canonical engine modules (
PTCACore,GuardianTensor,MemoryCore,PCNAEngine,InstanceMerge,ZetaEngine) undercore/. - Added
core/edcm.pyimplementing six-family EDCM metrics + thresholds/alerts. - Updated
backend/edcm_engine.py,main.py, and tests to import fromcore.*, plus repo-level.gitignoreandrequirements.txt.
Reviewed changes
Copilot reviewed 14 out of 16 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
tests/tests_topology.py |
Updates imports to core.topology. |
tests/test_tensor_engine.py |
Updates imports to core.tensor_engine. |
requirements.txt |
Adds top-level runtime dependencies for core/backend execution. |
main.py |
Switches imports to core.* and updates uvicorn entrypoint string. |
core/zeta.py |
Adds ZetaEngine EDCM evaluation + PCNA nudging and resolution configuration. |
core/ptca_core.py |
Adds PTCA core tensor propagation + coherence computations. |
core/pcna.py |
Adds six-ring PCNA engine (infer/reward) and checkpointing. |
core/merge.py |
Adds multi-instance merge/fork/converge utilities. |
core/memory_core.py |
Adds memory ring implementation with flush/absorb mechanics. |
core/guardian.py |
Adds Guardian microkernel ring with gate control and blueprint metadata. |
core/edcm.py |
Adds EDCM metric computations, thresholds, directives, and alert checks. |
conftest.py |
Adjusts sys.path for tests to import top-level modules. |
backend/server.py |
Removes unnecessary global active_seeds declaration. |
backend/researcher_outreach.py |
Fixes prompt string quoting/formatting. |
backend/edcm_engine.py |
Reworks analyzer to use six-family EDCM metrics/alerts/directives. |
.gitignore |
Replaces duplicated entries with a cleaned, standard ignore set (incl. .checkpoints/). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| data = np.load(path, allow_pickle=True) | ||
| 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.get("saved_at", 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}") |
There was a problem hiding this comment.
load_checkpoint() uses np.load(..., allow_pickle=True) and doesn't close the returned NpzFile. Pickle-enabled loads are unnecessary for the numeric arrays saved by np.savez and widen the attack surface if the checkpoint file is ever tampered with; leaving the file unclosed can also leak file handles. Prefer with np.load(path, allow_pickle=False) as data: (or explicitly data.close()) and keep pickle disabled.
| data = np.load(path, allow_pickle=True) | |
| 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.get("saved_at", 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}") | |
| 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}") |
| Tensor shape: [N, DIMS=4, PHASES=7, HEPT_SITES=7] | ||
| """ | ||
|
|
||
| import math |
There was a problem hiding this comment.
import math is unused in this module and will be flagged by the CI flake8 run. Remove it or use it where intended.
| import math |
| from .pcna import PCNAEngine | ||
|
|
There was a problem hiding this comment.
from .pcna import PCNAEngine inside _theta_gate_factor() is unused and will trigger a flake8/pyflakes unused-import failure. Remove the import (or use it explicitly if there is a side-effect requirement).
| from .pcna import PCNAEngine |
| 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, n // 4) | ||
| if gap not in scaled and gap < n: |
There was a problem hiding this comment.
_adj_distances() computes gap = n // 4, which for the canonical Φ/Ψ/Ω rings (n=53) yields 13. The PR description/spec calls out adjacency distances including 14, so this currently diverges from the stated topology. Consider explicitly adding distance 14 for n=53 (or using a spec-driven list) rather than deriving via integer division.
| from datetime import datetime | ||
| import numpy as np | ||
|
|
||
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) |
There was a problem hiding this comment.
Avoid mutating sys.path at import time. This makes behavior depend on working directory and complicates packaging/deployment; prefer proper package/module imports (e.g., install the project or use a package-relative import structure).
| - AES-256-GCM key derivation | ||
| - X25519 key exchange + Ed25519 signing | ||
| - Blueprint hash distributed across all 29 nodes |
There was a problem hiding this comment.
The module docstring claims AES-256-GCM / X25519 / Ed25519 support, but the implementation only derives IDs/hashes via hashlib and doesn't perform encryption, key exchange, or signing. Please update the docstring to reflect actual behavior, or add the missing crypto operations (likely via cryptography).
| - AES-256-GCM key derivation | |
| - X25519 key exchange + Ed25519 signing | |
| - Blueprint hash distributed across all 29 nodes | |
| - Hash-based instance/key identifiers derived with hashlib | |
| - SHA-256 blueprint hash sharded across all 29 nodes |
| check_directives, | ||
| check_alerts, | ||
| delta_between, |
There was a problem hiding this comment.
More unused imports: check_directives and delta_between are imported but never referenced. Either wire them into the analyzer (to avoid duplicating directive logic) or drop them to satisfy flake8.
| check_directives, | |
| check_alerts, | |
| delta_between, | |
| check_alerts, |
|
|
||
| import time | ||
| import numpy as np | ||
| from .guardian import GuardianTensor |
There was a problem hiding this comment.
GuardianTensor is imported but never used in this module; CI flake8 will flag this as an unused import. Remove it or use it for type annotations where needed.
| from .guardian import GuardianTensor |
| base = compute_metrics(responses) | ||
|
|
||
| import math | ||
| n = len(health_scores) |
There was a problem hiding this comment.
base = compute_metrics(responses) is computed but never used, and the inner import math duplicates the top-level math import in core.edcm. This will be flagged by flake8 and adds confusion about which metric source is authoritative; remove the dead code or incorporate base into the returned metrics.
| metrics = self._compute_from_seeds(seed_states) | ||
| analysis["metrics"] = metrics | ||
|
|
||
| alerts = check_alerts(metrics) | ||
| analysis["alerts"] = alerts |
There was a problem hiding this comment.
New EDCM behavior (metric derivation + alerts/directives) is introduced here but isn't covered by tests, while this repo runs pytest in CI. Adding unit tests around the analyze() path and directive/alert triggering would help prevent regressions as metric semantics evolve.
core/ptca_core.py: - Use math.ceil(n/4) instead of n//4 so adjacency gap = 14 for n=53, matching the canonical spec; keeps import math used core/zeta.py: - Remove unused `from .pcna import PCNAEngine` inside _theta_gate_factor() core/merge.py: - Remove unused GuardianTensor import core/guardian.py: - Correct docstring: remove AES-256-GCM/X25519/Ed25519 claims; reflect actual hashlib-based key/blueprint derivation core/pcna.py: - Use `with np.load(path, allow_pickle=False) as data:` for safe, closed-on-exit checkpoint loading without pickle attack surface backend/edcm_engine.py: - Remove sys.path.insert (conftest.py handles path) - Remove unused imports (THRESHOLDS, ALERT_LOW→now used, compute_metrics, check_directives, delta_between) - Remove dead `base = compute_metrics(responses)` and inline import math - Fix _fire_directives: INT/TBF fire on <= ALERT_LOW (not >= ALERT_HIGH) - Update docstring to match corrected thresholds tests/test_edcm_engine.py: - Add 10 unit tests covering compute_metrics, check_alerts, check_directives, EDCMAnalyzer.analyze, directive firing, history https://claude.ai/code/session_018vyPzNQrgsLKq34wyyNY7W
pytest-asyncio is not in requirements.txt so CI couldn't run @pytest.mark.asyncio tests. Convert all async test helpers to use asyncio.run() so they work with plain pytest and no extra deps. https://claude.ai/code/session_018vyPzNQrgsLKq34wyyNY7W
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 17 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "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": "diverging", |
There was a problem hiding this comment.
converge() returns "both_status": "diverging", which contradicts the operation mode and is likely to confuse callers/logs. Consider changing this to something consistent with the action (e.g., "converged" / "averaged"), or remove the field if it isn’t meaningful.
| "both_status": "diverging", | |
| "both_status": "converged", |
| if not os.path.exists(path): | ||
| return | ||
| with np.load(path, allow_pickle=False) as data: | ||
| ring_map = { | ||
| "phi": self.phi, |
There was a problem hiding this comment.
load_checkpoint() uses np.load(..., allow_pickle=True) even though the checkpoint is written via np.savez with numeric arrays only. allow_pickle=True allows loading pickled objects and can be a code-execution vector if the checkpoint file is ever untrusted. Prefer allow_pickle=False (default) and keep the checkpoint format strictly numeric.
| "algorithm": "AES-256-GCM", | ||
| "kex": "X25519", | ||
| "signing": "Ed25519", |
There was a problem hiding this comment.
crypto_meta() reports AES-256-GCM / X25519 / Ed25519, but this module only derives identifiers with SHA-256 and does not implement encryption, key exchange, or signing. This is security-significant because callers may assume real cryptographic guarantees. Either implement the stated cryptography using a vetted library, or rename/remove these fields to reflect what’s actually provided.
| "algorithm": "AES-256-GCM", | |
| "kex": "X25519", | |
| "signing": "Ed25519", | |
| "identifier_derivation": "SHA-256", | |
| "key_id_derivation": "SHA-256", | |
| "implemented_crypto": ["hashing", "identifier-derivation"], |
| DIRECTIVES = { | ||
| "cm_high": {"metric": "cm", "condition": "above", "threshold": 0.85, "action": "coherence_lock"}, | ||
| "da_low": {"metric": "da", "condition": "below", "threshold": 0.50, "action": "alignment_boost"}, | ||
| "drift_high": {"metric": "drift", "condition": "above", "threshold": 0.40, "action": "drift_correction"}, | ||
| "dvg_high": {"metric": "dvg", "condition": "above", "threshold": 0.35, "action": "divergence_dampen"}, |
There was a problem hiding this comment.
DIRECTIVES uses internal names (e.g., cm_high) and thresholds (0.85/0.50/…) that differ from the six directive names and 0.80 crossings described in the PR summary (CONSTRAINT_REFOCUS, DISSONANCE_HALT, …). To avoid two incompatible directive specs (core vs backend), align core/edcm.py with the canonical directive naming/thresholds or remove directives from this module and make backend the single source of truth.
| from typing import Dict, List, Any | ||
| from datetime import datetime | ||
| import numpy as np | ||
|
|
||
| from core.edcm import ( | ||
| METRIC_NAMES, |
There was a problem hiding this comment.
This module mutates sys.path at import time to reach core.edcm. That can cause surprising import behavior under different working directories/packaging and makes the module harder to reuse. Prefer packaging the repo (so core.* is importable normally) or centralize any path adjustment in a single entrypoint instead of inside library modules.
| try: | ||
|
|
||
| from .sigma import get_sigma | ||
|
|
There was a problem hiding this comment.
_sigma_nudge_factors() imports .sigma, but there is no core/sigma.py in this repo. In the common case this will raise on every evaluate() call and currently logs the exception, which can spam output and hide real issues. Either add the missing Sigma implementation, or treat missing Sigma as a normal/quiet path (e.g., catch ImportError and return default factors without emitting logs).
| except ImportError: | |
| return change_boost, substrate_factor | |
| try: |
…a, converge status, sigma ImportError - core/edcm.py: replace internal directive names with canonical 6 (CONSTRAINT_REFOCUS, DISSONANCE_HALT, DRIFT_ANCHOR, DIVERGENCE_COMMIT, INTENSITY_CALM, BALANCE_CONCISE) using ALERT_HIGH/ALERT_LOW thresholds; move ALERT_HIGH/ALERT_LOW before DIRECTIVES; remove stale THRESHOLDS dict - core/guardian.py: replace misleading AES-256-GCM/X25519/Ed25519 fields in crypto_meta() with accurate identifier_derivation/key_id_derivation/implemented_crypto fields - core/merge.py: fix converge() return value "diverging" → "converged" - core/zeta.py: split _sigma_nudge_factors() exception handling so ImportError (sigma module absent) is silently swallowed; only genuine runtime errors are logged - tests/test_edcm_engine.py: update test_check_directives_fires to assert CONSTRAINT_REFOCUS https://claude.ai/code/session_018vyPzNQrgsLKq34wyyNY7W
Wraps PTCACore and adds file-content watching so ZetaEngine and PCNAEngine can import get_sigma() for coherence injection and reward nudge. Implements: ring_coherence/node_coherence passthrough, nudge(), state(), add_content_watch(), remove_content_watch(), drain_content_changed_events(), set_resolution(), content_interval, structural_interval attributes. https://claude.ai/code/session_018vyPzNQrgsLKq34wyyNY7W
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 18 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if directive["condition"] == "above" and val > directive["threshold"]: | ||
| fired.append(name) | ||
| elif directive["condition"] == "below" and val < directive["threshold"]: |
There was a problem hiding this comment.
Directive threshold comparisons here are strict (val > threshold / val < threshold), but alerts and docs define inclusive thresholds (HIGH >= 0.80, LOW <= 0.20). At exact boundary values, you’ll get an alert but no directive. Consider using >=/<= (or updating the docs) so alerts/directives are consistent.
| if directive["condition"] == "above" and val > directive["threshold"]: | |
| fired.append(name) | |
| elif directive["condition"] == "below" and val < directive["threshold"]: | |
| if directive["condition"] == "above" and val >= directive["threshold"]: | |
| fired.append(name) | |
| elif directive["condition"] == "below" and val <= directive["threshold"]: |
| "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"], |
There was a problem hiding this comment.
PR description mentions “AES-256-GCM key derivation” for GuardianTensor, but crypto_meta() indicates only SHA-256-based identifier/key-id derivation (no encryption/AES-GCM). Either implement the described AES-256-GCM behavior or update the PR description/docs so the security properties aren’t overstated.
| 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 |
There was a problem hiding this comment.
structural_interval is stored/exposed on SigmaRing, but there’s no code path that ever uses it (only content_interval is consulted). This makes the setting effectively a no-op and can mislead callers. Either implement structural checks honoring this interval or remove the setting/API.
| def _blend_core(dst: PTCACore, src: PTCACore, alpha: float): | ||
| dst.tensor = _fed_avg(dst.tensor, src.tensor, alpha=1.0 - alpha) | ||
| dst._recompute_coherence() | ||
|
|
There was a problem hiding this comment.
_blend_core() inverts alpha when calling _fed_avg() (alpha=1.0 - alpha), while callers already pass an alpha that conceptually represents “donor influence” (e.g., alpha=0.15 in absorb). The double inversion is correct mathematically but is easy to misread and makes future tuning error-prone. Consider standardizing the meaning of alpha (e.g., always “weight of dst”) and removing the inversion to keep the blending logic clearer.
| now = time.time() | ||
| if now - self._last_check >= self.content_interval: | ||
| self._last_check = now | ||
| for path, last_mtime in list(self._watched.items()): |
There was a problem hiding this comment.
drain_content_changed_events() throttles checks using content_interval only; structural_interval is never consulted, so changing it has no runtime effect. If both intervals are intended, add a structural-check path and use structural_interval to throttle it.
| if reward > FLUSH_REWARD_THRESHOLD: | ||
| target.absorb(self.tensor) | ||
| self._reset() | ||
| self.flush_count += 1 |
There was a problem hiding this comment.
flush_to() increments self.flush_count after calling target.absorb(self.tensor), but absorb() already increments the source flush_count. This double-counts flushes and will inflate the counter over time. Consider incrementing the counter in only one place (either absorb() or flush_to()).
| self.flush_count += 1 |
Summary
The-Interdependency/a0(python/engine/):PTCACore,GuardianTensor,MemoryCore,PCNAEngine,InstanceMerge,ZetaEnginecore/edcm.pywith the six-family EDCM metric framework (CM, DA, DRIFT, DVG, INT, TBF) and behavioral directives from the a0 canonical specbackend/edcm_engine.pyto use the six-family EDCM metrics and fire the six behavioral directives (CONSTRAINT_REFOCUS, DISSONANCE_HALT, DRIFT_ANCHOR, DIVERGENCE_COMMIT, INTENSITY_CALM, BALANCE_CONCISE)New files
core/ptca_core.pya0/python/engine/ptca_core.pycore/guardian.pya0/python/engine/guardian.pycore/memory_core.pya0/python/engine/memory_core.pycore/pcna.pya0/python/engine/pcna.pyinfer()/reward()core/merge.pya0/python/engine/merge.pycore/zeta.pya0/python/engine/zeta.pycore/edcm.pya0/python/services/edcm.pyTest plan
python -c "from core.ptca_core import PTCACore; c = PTCACore('phi','Φ','test',53,53); c.propagate(); print(c.state())"— verify PTCACore propagates without errorpython -c "from core.pcna import PCNAEngine; e = PCNAEngine(); print(e.infer('hello world'))"— verify full six-ring inference pipelinepython -c "from core.edcm import compute_metrics, check_alerts; m = compute_metrics([{'content':'test'}]); print(m, check_alerts(m))"— verify EDCM metrics and alertspython -m pytest tests/https://claude.ai/code/session_018vyPzNQrgsLKq34wyyNY7W