feat(vocab): governed draft->approved glossary promotion — the 3-method alignment pass (task #13) - #258
Merged
Merged
Conversation
…od 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) AND vector-align (vectorLink pinned to the sovereign nomic/768 space AND reciprocated by the named peer) AND implement (estateBinding entity/service/action). It recomputes the meet (never trusts a declared 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 (in make validate) teeth: full alignment promotes; missing-implement, non-reciprocal peer, and off-sovereign-space embedding are each refused; promoted output passes the reused #250 check_alignment. Example: governed-loop <-> operational-dag reciprocal pair.
…d (own review)
promote() now refuses a non-GlossaryTerm input and an already-approved term (no silent mutation),
and uses .get('id') so malformed input can't KeyError. Two teeth added; 7 total.
There was a problem hiding this comment.
Pull request overview
Adds a governed, fail-closed “draft → approved” promotion step for GlossaryTerm, intended to close the vocabulary lifecycle by only approving terms that satisfy the 3-method alignment contract (capture, vector-align, implement) and by gating the output against the existing #250 alignment drift-guard.
Changes:
- Introduces
tools/promote_glossary_term.pyto recompute the 3-method alignment meet and promote/refuse accordingly. - Adds
tools/validate_glossary_promotion.py+validate-glossary-promotionMakefile target to enforce promotion “teeth” in CI using the existing #250 alignment checker. - Adds a worked fixture + spec doc describing the promotion pass, and documents the addition in
CHANGELOG.md.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/validate_glossary_promotion.py | CI validation harness for promotion behavior and #250 drift-guard consistency. |
| tools/promote_glossary_term.py | Implements the fail-closed promotion logic from draft to approved via 3-method alignment checks. |
| specs/glossary-promotion.md | Normative description of the glossary promotion pass (v0.1). |
| Makefile | Adds validate-glossary-promotion target and wires it into make validate. |
| examples/glossary_promotion.governed_loop.json | Fixture bundle used by the promotion validation script. |
| CHANGELOG.md | Documents the new glossary promotion tool + validation gate. |
Comment on lines
+41
to
+47
| G250.FAILURES.clear() | ||
| G250.check_alignment({"approved": approved, "peer": peer}) | ||
| holes = [m for m in G250.FAILURES if approved["id"] in m or "governance hole" in m] | ||
| if holes: | ||
| FAILURES.append(f"promoted term fails #250 alignment drift-guard: {holes}") | ||
| else: | ||
| CHECKS["promoted-output:passes-250-guard"] = True |
Comment on lines
+41
to
+57
| # 2. vector-align — present, const-pinned to the sovereign space, and RECIPROCATED by the peer | ||
| vl = alignment.get("vectorLink") or {} | ||
| if not vl.get("peerRef"): | ||
| fails.append("vector-align (alignment.vectorLink missing)") | ||
| elif vl.get("model") != EMBED_MODEL or vl.get("dimension") != EMBED_DIM: | ||
| fails.append(f"vector-align (vectorLink must pin {EMBED_MODEL} / dim {EMBED_DIM})") | ||
| else: | ||
| back = ((peer or {}).get("alignment") or {}).get("vectorLink") or {} | ||
| if not peer or peer.get("id") != vl["peerRef"]: | ||
| fails.append(f"vector-align (peer {vl['peerRef']} not supplied for reciprocity check)") | ||
| elif back.get("peerRef") != term.get("id"): | ||
| fails.append(f"vector-align (peer {vl['peerRef']} does not link back — one-way link is not an alignment)") | ||
| # 3. implement | ||
| eb = alignment.get("estateBinding") or {} | ||
| if eb.get("kind") not in ESTATE_KINDS or not str(eb.get("ref") or "").strip(): | ||
| fails.append("implement (alignment.estateBinding missing a valid kind+ref)") | ||
| return fails |
…output + guard filter - promote() now requires vectorLink.cosine (number in [-1,1], schema-required) AND validates the minted approved term against GlossaryTerm.json — it can never produce a schema-invalid approved term (fail-closed on the output, not just the alignment content). - the #250-guard teeth keyed the terms dict by 'approved'/'peer', but check_alignment prefixes messages with the dict key, so a real failure (e.g. non-reciprocal) whose message lacked the term id could slip the filter. Now keyed by id; filter catches any failure prefixed with the id. - added teeth: missing-cosine refused + a regression test proving the #250-guard filter catches a holey approved term. 9 teeth total.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Close the vocabulary lifecycle
The vocab-currency loop ingests terms as
draft; a draft term names something but doesn't yet regulate state. This is the governed pass that makes itapproved— and only through the 3-method alignment from the GlossaryTerm contract (#250), as a fail-closed meet (same shape as DAR = governance ∧ IP/legal):alignment.ontologyClassRefalignment.vectorLink, pinned to the sovereignnomic-ai/nomic-embed-text-v1.5/ 768 space and reciprocated by the named peer (a one-way link is not an alignment)alignment.estateBinding(entity / service / action)tools/promote_glossary_term.pyrecomputes the meet (never trusts a declared flag) and promotes ONLY if all three hold; otherwise it refuses and names the unaligned method(s), leaving the termdraft. An approved-but-unaligned term is a governance hole — exactly what #250's drift-guard rejects.Teeth (
make validate-glossary-promotion)estateBinding→ refused (implement)check_alignment(reused, not reimplemented) — promotion is consistent-by-construction with the alignment contract, can never mint a governance holeExample:
governed-loop↔operational-dagreciprocal pair, promoting governed-loop draft→approved.Full lifecycle now closed: currency-detect (#255) → propose draft (#256) → dogfood (#257) → ontogenesis ingest + SHACL gate (ontogenesis#133) → governed alignment promotion → approved (here).