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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Noesis is the Abraxas latent-representation research module: a contract-first sy

- Canonical status: `CANON-SHADOW`
- Governance effect: `ADVISORY_ONLY`
- Runtime: `SLICE-001 + SLICE-002A + SLICE-003 + SLICE-004 + SLICE-005 + SLICE-006 + SLICE-007 + SLICE-008 + SLICE-009 + SLICE-010 + SLICE-011 + SLICE-012 + SLICE-013 + SLICE-014 + SLICE-015 + SLICE-016 + SLICE-017 + SLICE-018 + SLICE-019 + SLICE-020 + SLICE-021 + SLICE-022 IMPLEMENTED`
- Runtime: `SLICE-001 + SLICE-002A + SLICE-003 + SLICE-004 + SLICE-005 + SLICE-006 + SLICE-007 + SLICE-008 + SLICE-009 + SLICE-010 + SLICE-011 + SLICE-012 + SLICE-013 + SLICE-014 + SLICE-015 + SLICE-016 + SLICE-017 + SLICE-018 + SLICE-019 + SLICE-020 + SLICE-021 + SLICE-022 + SLICE-023 + SLICE-024 + SLICE-025 + SLICE-026 + SLICE-027 IMPLEMENTED`
- Specification: `COMPLETE`
- N5 latent communication: `BLOCKED` pending AC-N4 + explicit operator authorization

Expand Down
20 changes: 19 additions & 1 deletion STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- Governance effect: `ADVISORY_ONLY`
- Foundation spec: `COMPLETE`
- Canonical requirements/journeys/workflows/state machines/contracts/data/security/acceptance/tasks/verification: `COMPLETE`
- Runtime implementation: `SLICE-001 + SLICE-002A + SLICE-003 + SLICE-004 + SLICE-005 + SLICE-006 + SLICE-007 + SLICE-008 + SLICE-009 + SLICE-010 + SLICE-011 + SLICE-012 + SLICE-013 + SLICE-014 + SLICE-015 + SLICE-016 + SLICE-017 + SLICE-018 + SLICE-019 + SLICE-020 + SLICE-021 + SLICE-022 IMPLEMENTED`
- Runtime implementation: `SLICE-001 + SLICE-002A + SLICE-003 + SLICE-004 + SLICE-005 + SLICE-006 + SLICE-007 + SLICE-008 + SLICE-009 + SLICE-010 + SLICE-011 + SLICE-012 + SLICE-013 + SLICE-014 + SLICE-015 + SLICE-016 + SLICE-017 + SLICE-018 + SLICE-019 + SLICE-020 + SLICE-021 + SLICE-022 + SLICE-023 + SLICE-024 + SLICE-025 + SLICE-026 + SLICE-027 IMPLEMENTED`
- Hyperlex integration: `ADAPTER + PREREGISTRATION BOUNDARY IMPLEMENTED / TRAINED MODEL BINDING PENDING`
- N0 embedding/representation comparison: `AC-N0 ACCEPTED / PINNED REAL-MODEL EVIDENCE`
- N1 hidden-state capture: `AC-N1 ACCEPTED / PINNED REAL-MODEL EVIDENCE`
Expand Down Expand Up @@ -219,6 +219,24 @@ Implemented:

This does not authorize N5 science. It only enforces the gate.

### SLICE-023 — Replication environment fingerprints
Implemented:
- optional original/replica environment maps on `replicate_settlement`;
- `require_equivalent_environment` blocks full `REPLICATED` when fingerprints differ (FR-073);
- fingerprint deltas are disclosed on the report.

### SLICE-024 — Verified Euclidean and CKA
Implemented: `euclidean_of_observations` and `cka_of_observation_sets` load tensors only after hash checks.

### SLICE-025 — Replay envelope
Implemented: `replay_envelope` checks cosine, Euclidean, and artifact-hash equality against manifest thresholds.

### SLICE-026 — Manifest/fixture classification consistency
Implemented: a fixture whose `data_classification` differs from the manifest becomes a `FailureRecord`.

### SLICE-027 — Failure environment fingerprint
Implemented: capture failures record a Python environment fingerprint.

## Remaining blockers

1. Freeze an operator-approved EXP-001 source corpus; Noesis does not invent that semantic corpus.
Expand Down
2 changes: 1 addition & 1 deletion specs/NOESIS-SYSTEM-SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Status: `CANON-SHADOW`
Version: `0.3.0`
Specification state: `COMPLETE`
Runtime state: `SLICE-001 + SLICE-002A + SLICE-003 + SLICE-004 + SLICE-005 + SLICE-006 + SLICE-007 + SLICE-008 + SLICE-009 + SLICE-010 + SLICE-011 + SLICE-012 + SLICE-013 + SLICE-014 + SLICE-015 + SLICE-016 + SLICE-017 + SLICE-018 + SLICE-019 + SLICE-020 + SLICE-021 + SLICE-022 IMPLEMENTED`
Runtime state: `SLICE-001 + SLICE-002A + SLICE-003 + SLICE-004 + SLICE-005 + SLICE-006 + SLICE-007 + SLICE-008 + SLICE-009 + SLICE-010 + SLICE-011 + SLICE-012 + SLICE-013 + SLICE-014 + SLICE-015 + SLICE-016 + SLICE-017 + SLICE-018 + SLICE-019 + SLICE-020 + SLICE-021 + SLICE-022 + SLICE-023 + SLICE-024 + SLICE-025 + SLICE-026 + SLICE-027 IMPLEMENTED`

This document is the canonical system overview. Normative details are decomposed into the referenced specifications below.

Expand Down
7 changes: 7 additions & 0 deletions src/noesis/capture/executor.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from __future__ import annotations

import platform
import re
from dataclasses import dataclass
from typing import Any, Iterable, Mapping

from noesis.adapters.base import ModelAdapter
from noesis.artifacts.store import ContentAddressedStore
from noesis.capture.runner import CaptureRunner
from noesis.capture.fingerprint import environment_fingerprint
from noesis.contracts.registry import ContractRegistry
from noesis.domain.models import CaptureRequest, FailureRecord, InputFixture, RepresentationSite
from noesis.security import authorize_scope, classify_fixture
Expand Down Expand Up @@ -69,6 +71,8 @@ def execute(
"access_scope": fixture.access_scope,
}
)
if fixture.data_classification != manifest["data_classification"]:
raise ValueError("fixture data_classification must match the manifest")
except ValueError as exc:
failures.append(
self._failure(manifest["experiment_id"], run_id, fixture_id, "classification", 0, exc)
Expand Down Expand Up @@ -97,6 +101,9 @@ def _failure(self, experiment_id: str, run_id: str, fixture_id: str, site: str,
error_type=type(exc).__name__,
message=str(exc),
).as_dict()
environment = {"python": platform.python_version()}
environment["fingerprint"] = environment_fingerprint(environment)
record["environment"] = environment
self.registry.validate("failure-record", record)
self.store.put_json(record)
return record
72 changes: 66 additions & 6 deletions src/noesis/metrics/compare.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,78 @@
from __future__ import annotations

from typing import Any, Mapping
from typing import Any, Mapping, Sequence

import numpy as np

from noesis.artifacts.store import ContentAddressedStore
from noesis.metrics.core import cosine_similarity
from noesis.metrics.core import cosine_similarity, euclidean_distance, linear_cka


def cosine_of_observations(
def _vectors(
store: ContentAddressedStore,
left: Mapping[str, Any],
right: Mapping[str, Any],
) -> float:
):
left_art = left["artifact"]
right_art = right["artifact"]
left_vec = store.load_vector(left_art["uri"], left_art["sha256"], left_art["shape"])
right_vec = store.load_vector(right_art["uri"], right_art["sha256"], right_art["shape"])
return (
store.load_vector(left_art["uri"], left_art["sha256"], left_art["shape"]),
store.load_vector(right_art["uri"], right_art["sha256"], right_art["shape"]),
)


def cosine_of_observations(
store: ContentAddressedStore,
left: Mapping[str, Any],
right: Mapping[str, Any],
) -> float:
left_vec, right_vec = _vectors(store, left, right)
return cosine_similarity(left_vec, right_vec)


def euclidean_of_observations(
store: ContentAddressedStore,
left: Mapping[str, Any],
right: Mapping[str, Any],
) -> float:
left_vec, right_vec = _vectors(store, left, right)
return euclidean_distance(left_vec, right_vec)


def cka_of_observation_sets(
store: ContentAddressedStore,
left: Sequence[Mapping[str, Any]],
right: Sequence[Mapping[str, Any]],
) -> float:
if len(left) != len(right) or len(left) < 2:
raise ValueError("CKA requires two aligned observation sets with at least two samples")
left_mat = np.stack(
[store.load_vector(item["artifact"]["uri"], item["artifact"]["sha256"], item["artifact"]["shape"]) for item in left]
)
right_mat = np.stack(
[store.load_vector(item["artifact"]["uri"], item["artifact"]["sha256"], item["artifact"]["shape"]) for item in right]
)
return linear_cka(left_mat, right_mat)


def replay_envelope(
store: ContentAddressedStore,
left: Mapping[str, Any],
right: Mapping[str, Any],
thresholds: Mapping[str, Any],
) -> dict[str, Any]:
cosine = cosine_of_observations(store, left, right)
distance = euclidean_of_observations(store, left, right)
checks: dict[str, bool] = {}
if "replay_cosine" in thresholds:
checks["replay_cosine"] = cosine + 1e-12 >= float(thresholds["replay_cosine"])
if "replay_euclidean" in thresholds:
checks["replay_euclidean"] = distance - 1e-12 <= float(thresholds["replay_euclidean"])
if thresholds.get("replay_artifact_hash_equal") is True:
checks["replay_artifact_hash_equal"] = left["artifact"]["sha256"] == right["artifact"]["sha256"]
return {
"cosine": cosine,
"euclidean": distance,
"checks": checks,
"within_thresholds": all(checks.values()) if checks else True,
}
24 changes: 21 additions & 3 deletions src/noesis/replication/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import hashlib
from dataclasses import dataclass
from typing import Any, Sequence
from typing import Any, Mapping, Sequence

from noesis.capture.fingerprint import compare_environment_fingerprints
from noesis.settlement import EvidenceRef, SettlementRequest, settle_evidence


Expand All @@ -19,6 +20,9 @@ class ReplicationRequest:
tolerance: float
environment_delta: Sequence[str] = ()
artifacts_resolvable: bool = True
original_environment: Mapping[str, Any] | None = None
replica_environment: Mapping[str, Any] | None = None
require_equivalent_environment: bool = False


@dataclass(frozen=True, slots=True)
Expand All @@ -28,6 +32,7 @@ class ReplicationReport:
settlement: dict[str, Any]
original_settlement_id: str
environment_delta: tuple[str, ...]
fingerprint_report: dict[str, Any] | None = None


def replicate_settlement(request: ReplicationRequest) -> ReplicationReport:
Expand All @@ -54,9 +59,18 @@ def replicate_settlement(request: ReplicationRequest) -> ReplicationReport:

independent = request.original_operator != request.replica_operator
has_new_control = bool(request.new_control_ids)
fingerprint_report = None
equivalent_env = True
if request.original_environment is not None and request.replica_environment is not None:
fingerprint_report = compare_environment_fingerprints(
request.original_environment, request.replica_environment
)
equivalent_env = fingerprint_report["status"] == "EQUIVALENT"
if not request.artifacts_resolvable:
classification = "NOT_COMPUTABLE"
elif within and independent and has_new_control:
elif within and independent and has_new_control and (
equivalent_env or not request.require_equivalent_environment
):
classification = "REPLICATED"
elif not within:
classification = "FAILED_REPLICATION"
Expand All @@ -78,6 +92,8 @@ def replicate_settlement(request: ReplicationRequest) -> ReplicationReport:
limitations.append("replica operator is not independent of the original producer")
if not has_new_control:
limitations.append("replication did not introduce a new control or independent fixture")
if request.require_equivalent_environment and not equivalent_env:
limitations.append("replica environment fingerprint differs; new run identity required")
requested = "INFERRED"
independent_flag = False
else:
Expand All @@ -97,12 +113,14 @@ def replicate_settlement(request: ReplicationRequest) -> ReplicationReport:
confidence_basis=(f"classification={classification}",),
)
)
extra_delta = tuple(fingerprint_report.get("delta", []) if fingerprint_report else ())
return ReplicationReport(
classification=classification,
envelope=envelope,
settlement=settlement,
original_settlement_id=str(original["settlement_id"]),
environment_delta=tuple(request.environment_delta),
environment_delta=tuple(request.environment_delta) + extra_delta,
fingerprint_report=fingerprint_report,
)


Expand Down
38 changes: 37 additions & 1 deletion tests/test_artifacts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import pytest

from noesis.artifacts.store import ContentAddressedStore
from noesis.metrics.compare import cosine_of_observations
from noesis.metrics.compare import (
cka_of_observation_sets,
cosine_of_observations,
euclidean_of_observations,
replay_envelope,
)
from noesis.metrics.core import cosine_similarity


Expand Down Expand Up @@ -44,3 +49,34 @@ def test_cosine_of_observations_refuses_substituted_artifact(tmp_path):
with pytest.raises(RuntimeError, match="hash mismatch"):
cosine_of_observations(store, left, right)
assert cosine_similarity((1.0, 0.0), (0.0, 1.0)) == pytest.approx(0.0)


def _obs(store, vector):
sha, path = store.put_vector(vector)
return {"artifact": {"uri": path.as_uri(), "sha256": sha, "shape": [len(vector)]}}


def test_euclidean_and_cka_of_verified_observations(tmp_path):
store = ContentAddressedStore(tmp_path)
a = _obs(store, (1.0, 0.0))
b = _obs(store, (0.0, 1.0))
c = _obs(store, (1.0, 0.0))
d = _obs(store, (0.0, 1.0))
assert euclidean_of_observations(store, a, b) == pytest.approx(float(2**0.5))
assert cka_of_observation_sets(store, (a, b), (c, d)) == pytest.approx(1.0)


def test_replay_envelope_respects_manifest_thresholds(tmp_path):
store = ContentAddressedStore(tmp_path)
left = _obs(store, (1.0, 0.0))
same = _obs(store, (1.0, 0.0))
other = _obs(store, (0.0, 1.0))
ok = replay_envelope(
store,
left,
same,
{"replay_cosine": 1.0, "replay_euclidean": 0.0, "replay_artifact_hash_equal": True},
)
assert ok["within_thresholds"] is True
bad = replay_envelope(store, left, other, {"replay_cosine": 1.0})
assert bad["within_thresholds"] is False
14 changes: 14 additions & 0 deletions tests/test_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ def test_manifest_executor_emits_observation(tmp_path):
assert report.failures == ()


def test_manifest_executor_rejects_classification_mismatch(tmp_path):
adapter = DeterministicFakeAdapter(dimensions=8)
store = ContentAddressedStore(tmp_path)
executor = ManifestExecutor(adapter, store, ContractRegistry("contracts"))
report = executor.execute(
manifest(),
[InputFixture("fixture-001", "stable fixture", data_classification="RESEARCH_INTERNAL")],
"run-mismatch",
)
assert report.observations == ()
assert any("must match the manifest" in item["message"] for item in report.failures)


def test_manifest_executor_rejects_secret_like_fixture_text(tmp_path):
adapter = DeterministicFakeAdapter(dimensions=8)
store = ContentAddressedStore(tmp_path)
Expand Down Expand Up @@ -90,3 +103,4 @@ def test_manifest_executor_persists_missing_fixture_as_failure(tmp_path):
assert len(report.failures) == 1
assert report.failures[0]["error_type"] == "KeyError"
assert report.failures[0]["provenance"] == "OBSERVED"
assert report.failures[0]["environment"]["fingerprint"]
27 changes: 27 additions & 0 deletions tests/test_replication.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import hashlib

from noesis.capture.fingerprint import environment_fingerprint
from noesis.contracts.registry import ContractRegistry
from noesis.replication import ReplicationRequest, replicate_settlement
from noesis.settlement import EvidenceRef, SettlementRequest, settle_evidence
Expand Down Expand Up @@ -117,6 +118,32 @@ def test_same_operator_cannot_fully_replicate():
assert report.settlement["promotion"] != "ELIGIBLE_FOR_REVIEW"


def test_equivalent_environment_requirement_blocks_full_replication():
original = _original()
original_env = {"python": "3.12.3", "adapter": "fake"}
replica_env = {"python": "3.13.0", "adapter": "fake"}
original_env = {**original_env, "fingerprint": environment_fingerprint(original_env)}
replica_env = {**replica_env, "fingerprint": environment_fingerprint(replica_env)}
report = replicate_settlement(
ReplicationRequest(
original_settlement=original,
original_metrics=(0.5,),
replica_metrics=(0.5,),
original_operator="operator:a",
replica_operator="operator:b",
new_control_ids=("ctrl-new-1",),
replica_settlement_id="set-rep-env",
tolerance=0.05,
original_environment=original_env,
replica_environment=replica_env,
require_equivalent_environment=True,
)
)
assert report.classification == "PARTIAL"
assert report.fingerprint_report["status"] == "NEW_RUN_REQUIRED"
assert "python" in report.environment_delta


def test_missing_new_control_is_partial():
original = _original()
report = replicate_settlement(
Expand Down
Loading