Skip to content

feat(vocab): agreement test — glossary relations vs blast-radius graph (task #13) - #259

Merged
mdheller merged 3 commits into
mainfrom
feat/glossary-agreement-test
Aug 2, 2026
Merged

feat(vocab): agreement test — glossary relations vs blast-radius graph (task #13)#259
mdheller merged 3 commits into
mainfrom
feat/glossary-agreement-test

Conversation

@mdheller

@mdheller mdheller commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Does the vocabulary tell the truth about structure?

The neurosymbolic agreement check. Two independent views of the same estate:

  • symbolic — the glossary's typed relations (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 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 it; this only compares). Only composition predicates imply runtime dependencies; is-a/skos:* are subsumption/lexical and excluded.

Teeth (make validate-agreement)

  • aligned vocabulary agrees with the observed graph
  • an overclaimed dependency (no observed edge) is refused
  • drift (observed-but-undeclared) is reported as a proposed has-a relation, not silently passed and not hard-failed

Completes the last self-contained thread of the vocabulary program: currency (#255), proposal (#256), dogfood (#257), ingest+gate (ontogenesis#133), alignment promotion (#258), and now relations↔structure agreement. The estate's words and its wiring are now held to match.

…h (task #13)

The neurosymbolic check that the vocabulary tells the truth about structure. 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; this only compares). 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), same treatment as the vocab-currency loop's candidate terms.
Only composition predicates imply runtime dependencies; is-a/skos:* excluded.

validate-agreement (in make validate) teeth: aligned agrees; overclaim refused; drift reported as
candidate.
Copilot AI review requested due to automatic review settings August 2, 2026 23:31
A draft term hasn't passed the alignment gate and doesn't regulate state, so its relations must
not overclaim the estate. Restrict declared edges + bindings to status==approved terms.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “agreement test” that cross-checks the glossary’s declared composition relations (has-a / has-member, projected via alignment.estateBinding) against an observed blast-radius dependency graph, with CI “teeth” that fail closed on overclaims and report drift as remediation candidates.

Changes:

  • Introduces tools/agreement_test.py to compute agreements / overclaims / drift candidates between declared relations and observed edges.
  • Adds tools/validate_agreement.py plus make validate-agreement (and wires it into make validate) to enforce the expected fail-closed vs remediation behavior on fixtures.
  • Documents the agreement test in specs/agreement-test.md and records it in CHANGELOG.md, with fixtures under fixtures/agreement/.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/validate_agreement.py CI harness (“teeth”) exercising aligned/overclaim/drift scenarios against fixtures.
tools/agreement_test.py Core comparison logic: projects glossary relations into estate edges and compares to observed graph edges.
specs/agreement-test.md Spec doc describing semantics (overclaim fail-closed; drift reported).
Makefile Adds validate-agreement target and includes it in validate.
fixtures/agreement/glossary.json Minimal glossary bundle fixture used by the validator scenarios.
fixtures/agreement/blast_radius_graph.json Minimal blast-radius graph fixture used by the validator scenarios.
CHANGELOG.md Notes the addition of agreement test v0.1 and its validation gate.

Comment thread tools/agreement_test.py
Comment on lines +60 to +75
# DRIFT: an observed edge between two bound entities with no declared dep relation -> propose one.
ent_to_term = {ref: tid for tid, ref in bind.items()}
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 {
"ok": not overclaims, # fail-closed on overclaims; drift is remediation, not failure
"agreements": agreements,
"overclaims": overclaims,
"driftCandidates": drift,
}
Comment thread tools/agreement_test.py Outdated
def agreement(glossary: dict, graph: dict) -> dict:
terms = {t["id"]: t for t in glossary["terms"]}
bind = {tid: _binding(t) for tid, t in terms.items() if _binding(t)}
observed = {(e["from"], e["to"]) for e in graph.get("edges", [])}
… (Copilot #259)

- two approved terms binding the SAME estate entity made drift attribution ambiguous (dict silently
  dropped a duplicate). Now surfaced as bindingConflicts and fails closed; only unambiguously-bound
  entities participate in drift.
- a graph edge missing from/to would KeyError-crash. Now malformed edges are collected into
  malformedEdges and fail closed instead of crashing.
Two teeth added (ambiguous-binding, malformed-edge); 5 total.
@mdheller
mdheller merged commit f588311 into main Aug 2, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants