"Quorum" names three unrelated concepts across the codebase, and identity's own "I3" has two non-reconciled definitions
Background
Companion to #225 (dead-code cleanup) and #226 (I2 hard-error question), surfaced during the same identity-domain invariant investigation.
The collision, concretely
Definition A — musehub/graph/push_validator.py's "I3" (actually enforced, hard error):
Per-edge signature authorization. Does the right party's Ed25519
signature appear in authorized_by for a spawns/member_of edge, with
a bootstrap exception for an org's founding member and a
min(quorum, prior_members) signature-count rule for subsequent joins.
This is real, wired into wire_push, and actually rejects bad pushes.
# push_validator.py module docstring
I3 Authorization — hard error (push rejected)
Definition B — musehub/graph/quorum.py's "I3" (real, tested, never called):
Recursive weighted-vote tallying. An org's vote inside a parent org counts only if that org's own quorum is independently satisfied, terminating because I1 guarantees no circular org membership.
# quorum.py module docstring
"""I3 — Quorum soundness invariant.
An org's vote in a parent org counts only if that org's own quorum is
independently satisfied. Recursive."""
QuorumEngine from this file is never invoked by push_validator.py, or
by anything else outside its own module/tests (see #225).
Definition C — musehub/services/musehub_governance.py::check_quorum (unrelated domain entirely):
Counts approved code-review approvals against a repo's proposal
governance config (governance["quorum"]["threshold"]/["members"]) to
decide whether a proposal has enough sign-off to merge. Nothing to do
with the identity graph, org membership, or spawns/member_of edges —
this is repo/proposal governance, a completely different subsystem that
happens to reuse the word "quorum."
Why this matters
- Two different pieces of code both claim to be "I3" for the identity domain and describe genuinely different mechanisms (signature-count authorization vs. recursive vote-weight tallying). Anyone implementing a third caller, or auditing "is I3 satisfied," has to know which of the two definitions is meant — the name alone doesn't disambiguate.
- A third, entirely unrelated "quorum" (proposal-review approval count) shares no code, no data model, and no invariant number with the other two, but shares the English word — searching the codebase for "quorum" surfaces all three undifferentiated.
- This is exactly the kind of naming collision that produces a confidently-wrong fix later: someone "fixing quorum enforcement" could plausibly patch the wrong one of the three without realizing three exist.
Scope
- Decide canonical terminology. Suggestion (not a mandate — a product/naming call): keep "I3" reserved for whichever of A/B is actually the identity domain's intended invariant (likely A, since it's the one actually enforced), rename B to something distinct (e.g. "org vote quorum" or "recursive governance quorum") if it's kept at all per #225's dead-code decision, and leave C's "proposal review quorum" as its own clearly-scoped term with no shared number.
- Update both
push_validator.py's andquorum.py's module docstrings (andmuse/plugins/identity/plugin.py's "I3 Quorum soundness" line) to match the resolved terminology — right now the plugin's own docstring uses the label "Quorum soundness," which reads as pointing at Definition B (the thing nothing calls), not Definition A (the thing that's actually enforced at push time), which is itself confusing independent of the dead-code question. - Cross-reference #225's resolution — if
QuorumEngine/quorum.pygets deleted as dead code, this issue mostly resolves by removing Definition B entirely, leaving only A and C (which don't collide once B is gone, since they were never actually confused with each other, only both with the "I3" label).
Acceptance criteria
- No two pieces of enforced-or-intended-to-be-enforced logic in this codebase share the name "I3" (or "quorum" more broadly) while meaning different things.
muse/plugins/identity/plugin.py's docstring accurately names whichever mechanism is real.