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
33 changes: 33 additions & 0 deletions .github/workflows/semantic-ring-oracle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Semantic ring reference oracle

on:
pull_request:
paths:
- '.github/workflows/semantic-ring-oracle.yml'
- 'docs/research/versioned-semantic-candidates-fixtures.json'
- 'docs/research/semantic-ring-bound-gold-v1.json'
- 'tools/semantic_ring_oracle/**'
push:
branches: [main]
paths:
- '.github/workflows/semantic-ring-oracle.yml'
- 'docs/research/versioned-semantic-candidates-fixtures.json'
- 'docs/research/semantic-ring-bound-gold-v1.json'
- 'tools/semantic_ring_oracle/**'

permissions:
contents: read

jobs:
reference-oracle:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Assert C-01/C-02 and negative controls
run: |
python3 tools/semantic_ring_oracle/ring_reference.py
python3 -m unittest discover -s tools/semantic_ring_oracle -p 'test_*.py' -v
72 changes: 72 additions & 0 deletions docs/research/semantic-ring-bound-gold-v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"format": "lance-graph-semantic-ring-bound-gold/v2",
"status": "manually-bound-symbolic-test-gold-not-production-parser-output",
"scope": "C-01/C-02: identities remain stable; a sealed version witnesses an identity assertion at its claim address",
"sentences": [
"That key opens a door.",
"That door hides a garden.",
"That garden shelters a fox.",
"That fox carries a key."
],
"facts": [
{
"sentence_index": 0,
"subject": "first_sentence.key",
"predicate": "opens",
"object": "door"
},
{
"sentence_index": 1,
"subject": "door",
"predicate": "hides",
"object": "garden"
},
{
"sentence_index": 2,
"subject": "garden",
"predicate": "shelters",
"object": "fox"
},
{
"sentence_index": 3,
"subject": "fox",
"predicate": "carries",
"object": "last_sentence.key"
}
],
"read_heads": {
"C-01": {
"dataset": "symbolic-ring",
"branch": "main",
"version": 17
},
"C-02": {
"dataset": "symbolic-ring",
"branch": "main",
"version": 16
}
},
"attested_identity": [
{
"lhs": "last_sentence.key",
"rhs": "first_sentence.key",
"claim_address": "corefers(last_sentence.key,first_sentence.key)",
"sealed_in": {
"dataset": "symbolic-ring",
"branch": "main",
"version": 17
},
"source_roots": [
"independent_coref_witness_1"
]
}
],
"caveats": [
"Identifiers here are fixed manually authored symbolic claim/referent coordinates, not runtime-minted NodeGuids.",
"The version of a claim at its thought address is its historical witness; no separate witness entity or foreign-key lookup is created.",
"The source root is separately retained in the version receipt for evidential independence. Neither the thought slot nor the version number by itself proves that sources are independent.",
"An attestation being present in a version witnesses the assertion in the test history; it does not establish externally observed coreference truth.",
"Read horizons use dataset + branch + version, never a naked version or latest/unbounded temporal default.",
"No production R2IL relation join, CE64 truth promotion, actual Lance manifest or real-world source attestation is claimed."
]
}
58 changes: 58 additions & 0 deletions tools/semantic_ring_oracle/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# C-01/C-02 semantic ring reference oracle

**Test-only independent oracle. Not the Rust semantic candidate runtime.**

This experiment is a versioned identity assertion, not a newly minted
'witness object'. The four bound SPO relations live at fixed symbolic
identities. The identity assertion itself has a **fixed claim address**
`corefers(last_sentence.key,first_sentence.key)`. Its **sealed version** is
the witness of its admission:

- C-02 reads `(symbolic-ring, main, v16)`: the claim does not yet exist.
Four sentences look cyclic, but their distinct key identities make the
cycle open.
- C-01 reads `(symbolic-ring, main, v17)`: the exact same semantic identities
are read; version 17 has witnessed the added identity assertion, and the
cycle closes in all four rotations.
- Re-reading C-02 at v17 closes the cycle **without reminting any identity**.
An older horizon stays unresolved even if its narrative says a witness
exists. Version visibility is not inferred from a source-root label.

This directly follows the D-TGA decision in
`.claude/plans/thought-git-api-v1.md`: a sealed version is the PR entry;
`blame` returns a landing version plus original source bits. The active
thought slot `0..63` is recyclable. The history witness is the claim's
**thought address at the sealed version**, not a reusable slot number, an
external pointer, a new witness NodeGuid or a bare numeric Lance version.
The tuple in this **symbolic** fixture includes dataset and branch because
real durable versions need those to disambiguate identity.

**Evidence root != witness coordinate.** Independent source roots are
metadata in the version receipt for NAL/Gadamer revision. Three thoughts
copying one observation do not yield three independent sources, and neither
a version number nor an address bit alone proves a claim *true*. The fixture
contains a synthetic source root, not external proof of real-world coreference.

The preregistered C-01/C-02 source JSON in
`docs/research/versioned-semantic-candidates-fixtures.json` remains unchanged.
Its older `external_identity_witness` field is retained for traceability
but is **not** the executable witness index. The new v2 gold is held in
`docs/research/semantic-ring-bound-gold-v1.json` (original path retained),
with explicitly pinned read horizons and a sealed claim-address receipt.

```sh
python3 tools/semantic_ring_oracle/ring_reference.py
python3 -m unittest discover -s tools/semantic_ring_oracle -p 'test_*.py' -v
```

The reference never consults `case.expected` during evaluation. Controls
include four rotations, pre/post version, wrong dataset/branch, spurious
root labels, wrong claim address, missing version, predicate mutation,
truncated facts and source-root de-duplication.

**Next integration gate:** have the actual DeepNSM/UD binder produce
canonical bound claim addresses and their stamped, versioned thought writes
(or `Unbound`), then differential-check real mask/R2IL execution against this
reference. No parser, real-world evidence authentication, NodeGuid allocation,
Lance13 integration, generalized relational join, causal inference or
production C-01/C-02 semantic closure is claimed here.
152 changes: 152 additions & 0 deletions tools/semantic_ring_oracle/ring_reference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
"""Independent symbolic C-01/C-02 reference, never a production graph evaluator.

Identity is the fixed coordinate. A sealed version witnesses that the
coreference claim was recorded at that coordinate. Source roots remain a
separate evidential property of the version receipt, not another ID.
This harness does NOT parse language, mint NodeGuids, or execute R2IL.
"""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any

ROOT = Path(__file__).resolve().parents[2]
FIXTURES = ROOT / "docs/research/versioned-semantic-candidates-fixtures.json"
GOLD = ROOT / "docs/research/semantic-ring-bound-gold-v1.json"


def load_case(case_id: str) -> dict[str, Any]:
fixtures = json.loads(FIXTURES.read_text(encoding="utf-8"))
if fixtures["format"] != "lance-graph-versioned-semantic-candidates/fixtures-v1":
raise ValueError("unexpected fixture version")
matches = [c for c in fixtures["cases"] if c["id"] == case_id]
if len(matches) != 1:
raise ValueError(f"expected one fixture for {case_id}")
return matches[0]


def load_gold() -> dict[str, Any]:
gold = json.loads(GOLD.read_text(encoding="utf-8"))
if gold["format"] != "lance-graph-semantic-ring-bound-gold/v2":
raise ValueError("unexpected gold version")
return gold


def _validated_ref(ref: dict[str, Any]) -> tuple[str, str, int]:
if not isinstance(ref, dict) or set(ref) != {"dataset", "branch", "version"}:
raise ValueError("read reference must pin dataset, branch and version")
dataset, branch, version = ref["dataset"], ref["branch"], ref["version"]
if not isinstance(dataset, str) or not dataset or not isinstance(branch, str) or not branch:
raise ValueError("read reference has unbound dataset/branch")
if type(version) is not int or version < 0:
raise ValueError("read reference version must be an explicit nonnegative integer")
return dataset, branch, version


def evaluate(
case: dict[str, Any],
gold: dict[str, Any],
entry_order: list[int],
*,
facts: list[dict[str, Any]] | None = None,
read_ref: dict[str, Any] | None = None,
) -> dict[str, Any]:
"""A narrow independent reference using pinned versions, never case.expected.

The C-01/C-02 legacy 'external_identity_witness' inputs stay frozen as
originally preregistered. They do NOT control admission. Instead, the
recorded claim at its fixed address becomes visible at its sealed version.
"""
if case["id"] not in ("C-01", "C-02"):
raise ValueError("this oracle covers only C-01/C-02")
if case["input"]["sentences"] != gold["sentences"]:
raise ValueError("sentence/gold binding drift")
if entry_order not in case["input"]["entry_orders"] or sorted(entry_order) != list(range(4)):
raise ValueError("unregistered or incomplete rotation")
if read_ref is None:
read_ref = gold["read_heads"][case["id"]]
dataset, branch, version = _validated_ref(read_ref)

atoms = list(gold["facts"] if facts is None else facts)
if len({a["sentence_index"] for a in atoms}) != len(atoms):
raise ValueError("multiple atoms per sentence exceed this narrow oracle")
if any(a["sentence_index"] not in range(4) for a in atoms):
raise ValueError("unbound source sentence")
selected = sorted(atoms, key=lambda a: entry_order.index(a["sentence_index"]))

redirects: dict[str, str] = {}
witnessed: list[tuple[str, str, str, int]] = []
roots: set[str] = set()
for assertion in gold["attested_identity"]:
lhs, rhs = assertion["lhs"], assertion["rhs"]
# This symbol is a fixed, manually bound *claim* address, never a
# newly minted witness ID. The assertion is located by address.
if assertion["claim_address"] != f"corefers({lhs},{rhs})":
raise ValueError("identity assertion at wrong claim address")
a_dataset, a_branch, a_version = _validated_ref(assertion["sealed_in"])
if (a_dataset, a_branch) != (dataset, branch) or a_version > version:
continue
if lhs in redirects and redirects[lhs] != rhs:
raise ValueError("conflicting visible identity assertions")
redirects[lhs] = rhs
witnessed.append((assertion["claim_address"], a_dataset, a_branch, a_version))
roots.update(assertion["source_roots"])

def canonical(term: str) -> str:
seen: set[str] = set()
while term in redirects:
if term in seen:
raise ValueError("cycle in identity assertions")
seen.add(term)
term = redirects[term]
return term

relations = tuple(
sorted((canonical(a["subject"]), a["predicate"], canonical(a["object"]))
for a in selected)
)
# The test-only traversal checks that the recorded four bound SPO facts
# are a complete cycle, not whether a parser or Datalog join found them.
adjacency: dict[str, list[tuple[str, int]]] = {}
for atom in selected:
adjacency.setdefault(canonical(atom["subject"]), []).append(
(canonical(atom["object"]), atom["sentence_index"])
)
current = canonical("first_sentence.key")
visited: set[int] = set()
for _ in range(4):
options = [edge for edge in adjacency.get(current, []) if edge[1] not in visited]
if len(options) != 1:
break
current, sentence_id = options[0]
visited.add(sentence_id)
closed = len(visited) == 4 and current == canonical("first_sentence.key")
return {
"cycle_closed": closed,
"coreference_status": "VersionWitnessed" if witnessed else "Unresolved",
"relations": relations,
"witness_coordinates": tuple(sorted(witnessed)),
"source_roots": tuple(sorted(roots)),
"source_sentence_indices": tuple(sorted(visited)),
"observed_ref": (dataset, branch, version),
}


def main() -> None:
gold = load_gold()
for case_id in ("C-01", "C-02"):
case = load_case(case_id)
results = [evaluate(case, gold, order) for order in case["input"]["entry_orders"]]
if not all(x == results[0] for x in results):
raise SystemExit(f"{case_id}: rotation changed canonical result")
if results[0]["cycle_closed"] != case["expected"]["cycle_closed"]:
raise SystemExit(f"{case_id}: closure mismatched original pre-registration")
print(f"{case_id}: rotations={len(results)}, closed={results[0]['cycle_closed']}, "
f"ref={results[0]['observed_ref']}, witness={results[0]['witness_coordinates']}")
print("Reference only: no production parser, R2IL joins or evidence gate tested.")


if __name__ == "__main__":
main()
Loading
Loading