Skip to content

Commit b71eeb9

Browse files
committed
fix(vocab): guard promotion input — refuse non-term + already-approved (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.
1 parent 8f71add commit b71eeb9

2 files changed

Lines changed: 23 additions & 1 deletion

File tree

tools/promote_glossary_term.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,19 @@ def _method_failures(term: dict, alignment: dict, peer: dict | None) -> list[str
5858

5959

6060
def promote(term: dict, alignment: dict, peer: dict | None = None) -> dict:
61+
# Input guard — only promote an actual draft GlossaryTerm; a non-term or an already-approved
62+
# term is not a valid promotion target (don't silently mutate it).
63+
if term.get("type") != "GlossaryTerm" or not str(term.get("id") or "").strip():
64+
return {"promoted": False, "term": term.get("id"), "status": term.get("status"),
65+
"refused": "not-a-draft-glossary-term",
66+
"detail": "promotion input must be a GlossaryTerm with an id"}
67+
if term.get("status") == "approved":
68+
return {"promoted": False, "term": term["id"], "status": "approved",
69+
"refused": "already-approved", "detail": "term is already approved — nothing to promote"}
70+
6171
fails = _method_failures(term, alignment, peer)
6272
if fails:
63-
return {"promoted": False, "term": term.get("id"), "status": "draft",
73+
return {"promoted": False, "term": term["id"], "status": "draft",
6474
"refused": "incomplete-alignment", "unaligned": fails,
6575
"detail": "draft->approved refused: an approved term must be captured + vector-aligned "
6676
"+ implemented (fail-closed meet); it stays draft"}

tools/validate_glossary_promotion.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@ def main() -> int:
7171
else:
7272
CHECKS["off-space-embedding:refused"] = True
7373

74+
# 5. Input guards — a non-term and an already-approved term are both refused (no silent mutation).
75+
r = P.promote({"id": "x", "type": "NotATerm"}, alignment, peer)
76+
if r.get("promoted") or r.get("refused") != "not-a-draft-glossary-term":
77+
FAILURES.append("a non-GlossaryTerm input must be refused")
78+
else:
79+
CHECKS["non-term-input:refused"] = True
80+
r = P.promote({**draft, "status": "approved"}, alignment, peer)
81+
if r.get("promoted") or r.get("refused") != "already-approved":
82+
FAILURES.append("an already-approved term must be refused (nothing to promote)")
83+
else:
84+
CHECKS["already-approved:refused"] = True
85+
7486
for m in FAILURES:
7587
print(f"FAIL: {m}", file=sys.stderr)
7688
ok = not FAILURES and all(CHECKS.values())

0 commit comments

Comments
 (0)