feat(governance): RV16 — el override del veto se decide al cerrar la ventana, con 4/5 de quien participa

Cierra L-11 (override real del veto). David aprobó el 4/5 el 2026-10-01: «exigir una mayoría
calificada de 4/5 en el consejo para anular un veto de emergencia es el cerrojo adecuado antes
de retirar la llave Sudo».

Qué cambia
- La ventana de override tiene dos lados: revertir (override_council_veto) y sostener
  (uphold_council_veto, nuevo). Quien votó Nay puede sostener el veto; antes no tenía voz.
- Nada se decide al cruzar un umbral. finalize_override (nuevo) decide UNA vez, al cerrar la
  ventana: se revierte si override >= ceil(4/5 * max(quórum, participación)).
- El quórum se fija al vetar: max(Aye + Nay del voto original, MinOverrideQuorum). Abstain no
  cuenta. Así el bond que el veto devuelve no basta para revertirlo y un upgrade no lo mueve.
- MinOverrideQuorum en el runtime = 100M OGC (el 10% de PILAR_4 §6.6); integrity_test exige
  que supere MIN_PROPOSAL_STAKE.
- El periodo de voto ordinario dura al menos 14 días (MIN = DEFAULT) y el veto solo cabe dentro
  de él. Un voto de peso 0 en la ventana se rechaza.
- spec_version 16. Pesos de uphold_council_veto y finalize_override medidos (frame-omni-bencher
  0.15.0, 50 steps x 20 repeats); los demás re-medidos en el mismo banco.

La revisión adversarial (nivel 1 de la directiva) tumbó la primera versión
- Bloqueante: la regla de quórum no era monótona y votar «sostener» podía REVERTIR el veto.
  Ahora sostener nunca ayuda a revertir (test con la frontera exacta 2250/2251).
- El mínimo de 48 h regalaba la vía de emergencia a cualquiera; ahora 14 días.
- Las abstenciones inflaban el quórum; el quórum podía cambiar con un upgrade; la spec no subía.
- 18 contrafactuales (mutaciones V1..V18), todos en rojo sin su pieza.

Divergencia que queda abierta (D1 del roadmap V2): PILAR_4 §6.6 ratificó «override por 2/3» y
el código implementa 4/5, que es lo que David confirmó. La constitución del DAO lo fija.

Tests: gobernanza 78 (94 con runtime-benchmarks), runtime 44.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
CharonandClaude Opus 5.5 committed 2026-10-07 10:30:18 -05:00
1 parent a694cbd9f0
commit 3771a15165
8 files changed
+1081 -173

No files matched your search

@@ -536,6 +536,11 @@ impl origin_chain_governance::Config for Runtime {
type RuntimeFreezeReason = RuntimeFreezeReason;
// RV15: votos VIVOS simultáneos por cuenta (los vencidos se descartan solos).
type MaxActiveVotes = frame_support::traits::ConstU32<32>;
// RV16: participación mínima para revertir un veto del council: 100M OGC, el 10%
// del suministro que ratifica PILAR_4 §6.6 como quórum («10% quorum»). Es un
// parámetro de la constitución del DAO y lo fija David; mientras, este. Lo pinna
// `quorum_de_override_del_runtime` (genesis_config_presets.rs).
type MinOverrideQuorum = frame_support::traits::ConstU128<{ 100_000_000 * OGC }>;
type TreasuryPalletId = GovernanceTreasuryPalletId;
// Weights INTERIM hand-derived DB-aware (ver weights.rs). Regenerable en HW.
type WeightInfo = origin_chain_governance::weights::SubstrateWeight<Runtime>;
@@ -615,7 +615,7 @@ mod tests {
use crate::{Balances, Governance, RuntimeFreezeReason, RuntimeOrigin, System};
use frame_support::assert_ok;
use frame_support::traits::fungible::{Inspect, InspectFreeze};
use origin_chain_governance::{FreezeReason, MIN_PROPOSAL_STAKE};
use origin_chain_governance::{FreezeReason, MIN_PROPOSAL_STAKE, MIN_VOTING_PERIOD_BLOCKS};
use origin_chain_types::VoteStance;
let json = get_preset(&PresetId::from(PRESET_LOCAL_TESTNET)).expect("preset");
@@ -628,7 +628,7 @@ mod tests {
let bob = account_from_sr25519(BOB_SR25519);
let charlie = account_from_sr25519(CHARLIE_SR25519);
let (titulo, cuerpo, periodo) = ([1u8; 32], [2u8; 32], 100u64);
let (titulo, cuerpo, periodo) = ([1u8; 32], [2u8; 32], MIN_VOTING_PERIOD_BLOCKS);
assert_ok!(Governance::submit_proposal(
RuntimeOrigin::signed(alice.clone()),
titulo,
@@ -676,4 +676,22 @@ mod tests {
));
});
}
/// RV16: el quórum de override del runtime es el 10% del suministro que
/// ratifica PILAR_4 §6.6 (100M OGC). Sin este test, un typo como `{ 100_000_000 }`
/// sin `* OGC` (10^-10 OGC) o `ConstU128<0>` pasaba toda la suite (revisión de
/// RV16). `integrity_test` exige además que supere el bond de una propuesta.
#[test]
fn quorum_de_override_del_runtime() {
use frame_support::traits::Get;
let quorum: u128 =
<<crate::Runtime as origin_chain_governance::Config>::MinOverrideQuorum as Get<u128>>::get();
assert_eq!(quorum, 100_000_000 * crate::configs::OGC);
assert_eq!(
quorum,
crate::configs::OGC * 1_000_000_000 / 10,
"10% del suministro"
);
assert!(quorum > origin_chain_governance::MIN_PROPOSAL_STAKE);
}
}
+2 -2
View File
@@ -1019,7 +1019,7 @@ pub const VERSION: sp_version::RuntimeVersion = sp_version::RuntimeVersion {
spec_name: alloc::borrow::Cow::Borrowed("origin-chain"),
impl_name: alloc::borrow::Cow::Borrowed("origin-chain-charon"),
authoring_version: 1,
spec_version: 15,
spec_version: 16,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
@@ -1087,7 +1087,7 @@ mod tests {
/// mismo commit en que la version subio a 12.
#[test]
fn spec_version_vigente_y_transaction_version_intacta() {
assert_eq!(RUNTIME_VERSION, 15);
assert_eq!(RUNTIME_VERSION, 16);
assert_eq!(TRANSACTION_VERSION, 1);
}