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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Thi
## [Unreleased]

### Added
- Agreement test v0.1 — glossary relations vs the blast-radius graph (task #13, neurosymbolic): `tools/agreement_test.py` projects the glossary's composition relations (`has-a`/`has-member`) onto the estate via `alignment.estateBinding` and compares them to a consumed blast-radius/dependency graph (GBRG owns that graph). Fail-closed on OVERCLAIM (a declared dependency with no observed edge = governance hole); reports DRIFT (an observed edge no relation names) as a remediation candidate (a proposed `has-a` relation), like the vocab-currency loop's candidate terms. `validate-agreement` teeth: aligned agrees; overclaim refused; drift surfaced as candidate.
- Glossary promotion v0.1 — the governed draft→approved alignment pass (task #13, closes the vocabulary lifecycle): `tools/promote_glossary_term.py` promotes a `draft` GlossaryTerm to `approved` ONLY when the 3-method alignment holds as a fail-closed MEET — capture (`ontologyClassRef`) ∧ vector-align (`vectorLink`, pinned to the sovereign nomic/768 space AND reciprocated by the named peer) ∧ implement (`estateBinding` entity/service/action). It recomputes the meet (never trusts a flag), refuses on any missing/non-reciprocal/off-space method (term stays draft, no governance hole), and its approved output PASSES the #250 alignment drift-guard by construction. `validate-glossary-promotion` teeth: promotes on full alignment; refuses missing-implement / non-reciprocal / off-space; promoted output passes the #250 guard.
- Vocab-currency loop — LIVE dogfood on the estate's own vocab (`make vocab-currency-dogfood`): `tools/dogfood_vocab_currency.py` runs the governed loop over this repo's approved `GlossaryTerm`s (fixed/LSA) vs `specs/*.md` (open/LDA). Today only a handful of terms are approved against the full spec corpus, so the governed loop escalates-human (can't reach currency in its bound), writing real draft `GlossaryTerm` proposals to `build/vocab-currency-proposals/` — the artifacts ontogenesis ingests. Reports the outcome (does not gate on it, exit 0); Markdown-cleaned + generic-word stoplisted so domain terms surface. Enforcement stays on fixtures in `validate-vocab-currency-loop`.
- Vocab-currency loop — draft-term emission (closes the currency→proposal lifecycle): each remediation step now emits a conformant status:**draft** `GlossaryTerm` artifact (`result.proposedTerms`) for the token it connects — the loop PROPOSES drafts for ontogenesis's 3-method alignment pass and never self-approves (`partOfSpeech`/`alignment` are left for downstream; status stays `draft`). The CI harness now REFUSES any proposed term that doesn't conform to `GlossaryTerm.json` or that isn't status:draft, so the loop can't push malformed or self-approved vocab into the glossary. 6 teeth total.
Expand Down
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.PHONY: validate validate-resource-contract validate-measurement validate-value-type validate-source-locator validate-sourceos-repo-manifest validate-mesh-action-registry validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples validate-interpretability-examples validate-lifecycle-boundary-examples validate-svf-contracts validate-sync-cycle-receipts validate-onboarding-examples validate-runtime-causality-examples validate-agentic-os-examples validate-triparty-examples validate-labor-market-examples validate-supply-chain-risk-examples validate-reasoning-examples validate-mpcc-event-examples validate-knowledge-nugget-examples validate-semantic-action-examples validate-epistemic-kernel-examples validate-ab-update-examples validate-device-service-examples validate-duplicate-schema-ids validate-lawful-dispatch-receipt validate-architectural-building-block validate-agent-passport-examples validate-seam-definition-examples validate-agent-system-vocabulary validate-genesis-inception-examples validate-measurement validate-world-model-examples validate-eval-item-examples validate-ingestion-pipeline-examples validate-data-acquisition-examples validate-glossary-alignment-examples validate-data-class-examples validate-table-keys validate-dag-loop validate-vocab-currency-loop vocab-currency-dogfood validate-glossary-promotion
.PHONY: validate validate-resource-contract validate-measurement validate-value-type validate-source-locator validate-sourceos-repo-manifest validate-mesh-action-registry validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples validate-interpretability-examples validate-lifecycle-boundary-examples validate-svf-contracts validate-sync-cycle-receipts validate-onboarding-examples validate-runtime-causality-examples validate-agentic-os-examples validate-triparty-examples validate-labor-market-examples validate-supply-chain-risk-examples validate-reasoning-examples validate-mpcc-event-examples validate-knowledge-nugget-examples validate-semantic-action-examples validate-epistemic-kernel-examples validate-ab-update-examples validate-device-service-examples validate-duplicate-schema-ids validate-lawful-dispatch-receipt validate-architectural-building-block validate-agent-passport-examples validate-seam-definition-examples validate-agent-system-vocabulary validate-genesis-inception-examples validate-measurement validate-world-model-examples validate-eval-item-examples validate-ingestion-pipeline-examples validate-data-acquisition-examples validate-glossary-alignment-examples validate-data-class-examples validate-table-keys validate-dag-loop validate-vocab-currency-loop vocab-currency-dogfood validate-glossary-promotion validate-agreement

validate: validate-data-class-examples validate-glossary-alignment-examples validate-data-acquisition-examples validate-ingestion-pipeline-examples validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples validate-interpretability-examples validate-lifecycle-boundary-examples validate-svf-contracts validate-sync-cycle-receipts validate-onboarding-examples validate-runtime-causality-examples validate-agentic-os-examples validate-triparty-examples validate-labor-market-examples validate-supply-chain-risk-examples validate-reasoning-examples validate-mpcc-event-examples validate-knowledge-nugget-examples validate-semantic-action-examples validate-epistemic-kernel-examples validate-ab-update-examples validate-device-service-examples validate-duplicate-schema-ids validate-value-type validate-source-locator validate-sourceos-repo-manifest validate-mesh-action-registry validate-lawful-dispatch-receipt validate-architectural-building-block validate-agent-passport-examples validate-seam-definition-examples validate-agent-system-vocabulary validate-genesis-inception-examples validate-measurement validate-world-model-examples validate-eval-item-examples validate-resource-contract validate-table-keys validate-dag-loop validate-vocab-currency-loop validate-glossary-promotion
validate: validate-data-class-examples validate-glossary-alignment-examples validate-data-acquisition-examples validate-ingestion-pipeline-examples validate-control-plane-examples validate-nlboot-examples validate-lattice-data-governai-examples validate-ops-history-examples validate-runtime-observability-examples validate-interpretability-examples validate-lifecycle-boundary-examples validate-svf-contracts validate-sync-cycle-receipts validate-onboarding-examples validate-runtime-causality-examples validate-agentic-os-examples validate-triparty-examples validate-labor-market-examples validate-supply-chain-risk-examples validate-reasoning-examples validate-mpcc-event-examples validate-knowledge-nugget-examples validate-semantic-action-examples validate-epistemic-kernel-examples validate-ab-update-examples validate-device-service-examples validate-duplicate-schema-ids validate-value-type validate-source-locator validate-sourceos-repo-manifest validate-mesh-action-registry validate-lawful-dispatch-receipt validate-architectural-building-block validate-agent-passport-examples validate-seam-definition-examples validate-agent-system-vocabulary validate-genesis-inception-examples validate-measurement validate-world-model-examples validate-eval-item-examples validate-resource-contract validate-table-keys validate-dag-loop validate-vocab-currency-loop validate-glossary-promotion validate-agreement
@echo "OK: validate"

validate-ingestion-pipeline-examples:
Expand All @@ -19,6 +19,9 @@ validate-glossary-promotion:
python3 -m pip install --user jsonschema >/dev/null
python3 tools/validate_glossary_promotion.py

validate-agreement:
python3 tools/validate_agreement.py

validate-table-keys:
python3 -m pip install --user jsonschema referencing >/dev/null
python3 tools/validate_key_bipartite.py
Expand Down
18 changes: 18 additions & 0 deletions fixtures/agreement/blast_radius_graph.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"nodes": [
"urn:srcos:service:release-gate",
"urn:srcos:service:attestation-signer",
"urn:srcos:service:epistemic-evaluator",
"urn:srcos:service:key-store"
],
"edges": [
{
"from": "urn:srcos:service:release-gate",
"to": "urn:srcos:service:attestation-signer"
},
{
"from": "urn:srcos:service:attestation-signer",
"to": "urn:srcos:service:key-store"
}
]
}
45 changes: 45 additions & 0 deletions fixtures/agreement/glossary.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"terms": [
{
"id": "urn:srcos:glossary:release-gate",
"status": "approved",
"name": "release gate",
"alignment": {
"estateBinding": {
"kind": "service",
"ref": "urn:srcos:service:release-gate"
}
},
"relations": [
{
"predicate": "has-a",
"target": "urn:srcos:glossary:attestation"
}
]
},
{
"id": "urn:srcos:glossary:attestation",
"status": "approved",
"name": "attestation",
"alignment": {
"estateBinding": {
"kind": "service",
"ref": "urn:srcos:service:attestation-signer"
}
},
"relations": []
},
{
"id": "urn:srcos:glossary:epistemic-level",
"status": "approved",
"name": "epistemic level",
"alignment": {
"estateBinding": {
"kind": "service",
"ref": "urn:srcos:service:epistemic-evaluator"
}
},
"relations": []
}
]
}
22 changes: 22 additions & 0 deletions specs/agreement-test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Agreement test (v0.1) — declared relations vs observed dependencies

The neurosymbolic check that the vocabulary tells the truth about structure. Two independent views
of the same estate:

- **symbolic** — the glossary's typed `relations` between terms (composition: `has-a`, `has-member`);
- **observed** — the blast-radius / dependency graph over estate entities (as a governed GBRG
analysis emits it).

Projected onto each other via `alignment.estateBinding`, they must **agree**:

- **overclaim** — a term declares `A has-a B` (both bound) but the graph shows no edge → the
vocabulary asserts a dependency the estate doesn't exhibit. **Fail-closed** (governance hole).
- **drift** — the graph shows an edge between two bound entities that no relation names → the estate
has a dependency the vocabulary hasn't captured. Surfaced as a **remediation candidate** (a
proposed `has-a` relation), not a hard failure — same treatment as the vocab-currency loop's
candidate terms.

`tools/agreement_test.py` CONSUMES a blast-radius graph (GBRG owns that graph; this only compares).
Only composition predicates (`has-a`, `has-member`) imply runtime dependencies; `is-a`/`skos:*` are
subsumption/lexical and excluded. `make validate-agreement` proves: aligned agrees, overclaim is
refused, drift is reported as a candidate.
112 changes: 112 additions & 0 deletions tools/agreement_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""Agreement test — the glossary's DECLARED relations vs the estate's OBSERVED dependencies (task #13).

The neurosymbolic check: the vocabulary is the SYMBOLIC view (typed `relations` between terms, e.g.
`has-a` composition); the blast-radius graph is the OBSERVED view (actual dependency edges between
estate entities, as a governed blast-radius/GBRG analysis emits them). Two independent views of the
same structure must AGREE — if they don't, one is wrong:

* OVERCLAIM — a term declares `A has-a B`, both bound to estate entities, but the blast-radius
graph shows no corresponding edge. The vocabulary asserts a dependency the estate doesn't
exhibit: a governance hole. FAIL-CLOSED.
* DRIFT — the blast-radius graph shows an edge between two bound entities that no declared
relation names. The estate has a dependency the vocabulary hasn't captured: staleness. Reported
as a remediation candidate (a proposed `has-a` relation), not a hard failure — same treatment as
the vocab-currency loop's candidate terms.

This tool CONSUMES a blast-radius graph (it does not define one — GBRG is that graph's authority);
it only projects the glossary's relations onto the estate via `alignment.estateBinding` and compares.

Dependency-implying predicates are the composition/aggregation ones (`has-a`, `has-member`) — the
whole depends on / blast-radiates to its parts. Subsumption (`is-a`) and lexical (`skos:*`) links
are NOT runtime dependencies and are excluded.
"""
from __future__ import annotations

import argparse
import json
from pathlib import Path

DEP_PREDICATES = {"has-a", "has-member"}


def load(p: Path) -> dict:
return json.loads(Path(p).read_text(encoding="utf-8"))


def _binding(term: dict) -> str | None:
return ((term.get("alignment") or {}).get("estateBinding") or {}).get("ref")


def agreement(glossary: dict, graph: dict) -> dict:
terms = {t["id"]: t for t in glossary["terms"]}
# Only APPROVED terms regulate state, so only their relations are held to agreement — a draft
# term hasn't been promoted through the alignment gate and can't overclaim the estate.
bind = {tid: _binding(t) for tid, t in terms.items()
if _binding(t) and t.get("status") == "approved"}

# Fail-closed on ambiguous bindings: if two approved terms bind the SAME estate entity, drift
# attribution is ambiguous (which term should own a proposed relation?). Surface it, don't guess.
ref_owners: dict[str, list[str]] = {}
for tid, ref in bind.items():
ref_owners.setdefault(ref, []).append(tid)
binding_conflicts = [{"estateRef": ref, "terms": sorted(tids)}
for ref, tids in ref_owners.items() if len(tids) > 1]

# Fail-closed on a malformed consumed graph: an edge missing from/to can't be reasoned about.
observed, malformed_edges = set(), []
for e in graph.get("edges", []):
if isinstance(e, dict) and e.get("from") and e.get("to"):
observed.add((e["from"], e["to"]))
else:
malformed_edges.append(e)

# DECLARED estate edges: a dep-implying relation A->B where both A and B are bound entities.
declared: dict[tuple[str, str], tuple[str, str]] = {}
for tid, t in terms.items():
if tid not in bind:
continue
for rel in t.get("relations", []):
if rel.get("predicate") in DEP_PREDICATES and rel.get("target") in bind:
declared[(bind[tid], bind[rel["target"]])] = (tid, rel["target"])

agreements = [{"declaredBy": v, "edge": list(k)} for k, v in declared.items() if k in observed]
overclaims = [{"declaredBy": v, "edge": list(k),
"detail": "vocabulary declares a dependency the blast-radius graph does not show"}
for k, v in declared.items() if k not in observed]

# DRIFT: an observed edge between two bound entities with no declared dep relation -> propose one.
# Only unambiguously-bound entities participate (conflicting refs are surfaced separately above).
ent_to_term = {ref: tids[0] for ref, tids in ref_owners.items() if len(tids) == 1}
drift = []
for (ef, et) in sorted(observed):
if ef in ent_to_term and et in ent_to_term and (ef, et) not in declared:
drift.append({"edge": [ef, et],
"proposedRelation": {"subject": ent_to_term[ef], "predicate": "has-a",
"target": ent_to_term[et]},
"detail": "estate shows a dependency the vocabulary does not name"})

return {
# Fail-closed on overclaims AND on ambiguous bindings / malformed graph edges; drift alone
# is remediation, not failure.
"ok": not overclaims and not binding_conflicts and not malformed_edges,
"agreements": agreements,
"overclaims": overclaims,
"driftCandidates": drift,
"bindingConflicts": binding_conflicts,
"malformedEdges": malformed_edges,
}


def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--glossary", default=str(Path(__file__).resolve().parents[1] / "fixtures" / "agreement" / "glossary.json"))
ap.add_argument("--graph", default=str(Path(__file__).resolve().parents[1] / "fixtures" / "agreement" / "blast_radius_graph.json"))
args = ap.parse_args()
result = agreement(load(Path(args.glossary)), load(Path(args.graph)))
print(json.dumps(result, indent=2))
return 0 if result["ok"] else 1


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