Follow committee selection and vote processing. The Python simulator and versioned TypeScript implementations are distinct paths, all preserved in the guides.
{"revision":"5b4cebb309a83a7a6749d8911d8bf96a1921e042","sources":[{"file":"demo/Validator-Constellation-v0/config/stellar-scenario.yaml","content":"name: \"stellar-sentinel-constellation\"\ndescription: |\n Mission director config proving that a non-technical owner can retune governance,\n enforce ENS identities, and unleash sentinel guardrails with a single scenario file.\nbaseSetup:\n verifyingKey: \"0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed\"\n sentinelGraceRatio: 0.08\n onChainEntropy: \"0x111122223333444455556666777788889999aaaabbbbccccddddeeeeffff0000\"\n recentBeacon: \"0xffffeeeeccccaaaabbbb9999888877776666555544443333222211110000ffff\"\n governance:\n committeeSize: 4\n slashPenaltyBps: 2000\n quorumPercentage: 75\ndomains:\n - id: deep-space-lab\n humanName: \"Deep Space Research Lab\"\n budgetLimit: 5000000\n unsafeOpcodes:\n - SELFDESTRUCT\n - DELEGATECALL\n allowedTargets:\n - \"0xa11ce5c1e11ce000000000000000000000000000\"\n - \"0xbeac0babe00000000000000000000000000000000\"\n maxCalldataBytes: 4096\n forbiddenSelectors:\n - \"0xa9059cbb\"\n - \"0x23b872dd\"\n - id: orbital-yard\n humanName: \"Orbital Manufacturing Yard\"\n budgetLimit: 3000000\n unsafeOpcodes:\n - SELFDESTRUCT\n - STATICCALL\n allowedTargets:\n - \"0xf0undry0ps000000000000000000000000000000\"\n maxCalldataBytes: 2048\n forbiddenSelectors:\n - \"0xd0edefb0\"\nvalidators:\n - ens: andromeda.club.agi.eth\n address: \"0x1111111111111111111111111111111111111111\"\n stake: \"12000000000000000000\"\n - ens: orion.club.agi.eth\n address: \"0x2222222222222222222222222222222222222222\"\n stake: \"11000000000000000000\"\n - ens: hyperion.club.agi.eth\n address: \"0x3333333333333333333333333333333333333333\"\n stake: \"9000000000000000000\"\n - ens: titan.club.agi.eth\n address: \"0x4444444444444444444444444444444444444444\"\n stake: \"15000000000000000000\"\n - ens: athena.club.agi.eth\n address: \"0x5555555555555555555555555555555555555555\"\n stake: \"10000000000000000000\"\nagents:\n - ens: nova.agent.agi.eth\n address: \"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n domainId: deep-space-lab\n budget: 1200000\n - ens: sentinel.agent.agi.eth\n address: \"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\"\n domainId: orbital-yard\n budget: 900000\nnodes:\n - ens: polaris.node.agi.eth\n address: \"0xcccccccccccccccccccccccccccccccccccccccc\"\n - ens: selene.alpha.node.agi.eth\n address: \"0xdddddddddddddddddddddddddddddddddddddddd\"\njob:\n domainId: deep-space-lab\n round: 42\n truthfulVote: APPROVE\n committeeSignature: \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"\n count: 256\noverrides:\n voteByEns:\n andromeda.club.agi.eth: REJECT\n nonRevealValidators:\n - orion.club.agi.eth\nanomalies:\n - kind: budget-overrun\n agentEns: nova.agent.agi.eth\n amount: 1800000\n description: \"Nova attempted to overspend the research grant\"\n - kind: unsafe-opcode\n agentEns: sentinel.agent.agi.eth\n opcode: STATICCALL\n description: \"Sentinel attempted a disallowed static call\"\n calldataBytes: 8192\n - kind: unauthorized-target\n agentEns: nova.agent.agi.eth\n target: \"0xd15a11ee00000000000000000000000000000000\"\n description: \"Nova attempted to reach an unapproved contract\"\n - kind: calldata-spike\n agentEns: nova.agent.agi.eth\n calldataBytes: 16384\n description: \"Nova attempted to upload an oversized payload\"\n target: \"0xa11ce5c1e11ce000000000000000000000000000\"\n - kind: forbidden-selector\n agentEns: nova.agent.agi.eth\n selector: \"0xa9059cbb\"\n description: \"Nova attempted to move funds via a blocked selector\"\n target: \"0xa11ce5c1e11ce000000000000000000000000000\"\nownerActions:\n updateSentinel:\n budgetGraceRatio: 0.12\n updateDomainSafety:\n - domainId: deep-space-lab\n unsafeOpcodes:\n - SELFDESTRUCT\n - DELEGATECALL\n - STATICCALL\n - CALLCODE\n allowedTargets:\n - \"0xa11ce5c1e11ce000000000000000000000000000\"\n - \"0xbeac0babe00000000000000000000000000000000\"\n maxCalldataBytes: 6144\n forbiddenSelectors:\n - \"0xa9059cbb\"\n - \"0x23b872dd\"\n rotateEnsRegistry:\n mode: append\n leaves:\n - ens: vega.club.agi.eth\n address: \"0x8888000000000000000000000000000000008888\"\n - ens: aurora.alpha.node.agi.eth\n address: \"0xaaaabbbbccccddddeeeeffff0000111122223333\"\n pauseDomains:\n - domainId: orbital-yard\n reason: \"pre-flight drill\"\n resumeDomains:\n - domainId: orbital-yard\n triggeredBy: governance:resume-drill\n setAgentBudgets:\n - ens: nova.agent.agi.eth\n budget: 1500000\n updateEntropy:\n onChainEntropy: \"0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\"\n recentBeacon: \"0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f\"\n updateZkKey: \"0xf1f2f3f4f5f6f7f8f9fafbfcfdfeff00112233445566778899aabbccddeeff0011\"\n updateGovernance:\n commitPhaseBlocks: 4\n revealPhaseBlocks: 4\n distributeTreasury:\n - recipient: \"0x7777000000000000000000000000000000007777\"\n amount: \"1000000000000000000\"\n note: \"Seed incident response vault\"\n - recipient: \"0x6666000000000000000000000000000000006666\"\n percentageBps: 2500\n note: \"Allocate 25% to insurance buffer\"\ncontext:\n operator: \"Non-technical mission director\"\n mission: \"Sentinel constellation readiness\"\n","format":"text","sha256":"ffc500390dfb896891555ab556824c0a7d0ed2bbcdea3d8d15b671ec0f852fa8","bytes":5260,"download":"/AGIJobsv0/examples/ffc500390dfb8968-stellar-scenario.yaml","source":"https://github.com/MontrealAI/AGIJobsv0/blob/5b4cebb309a83a7a6749d8911d8bf96a1921e042/demo/Validator-Constellation-v0/config/stellar-scenario.yaml"},{"file":"demo/Validator-Constellation-v0/validator_constellation/demo_runner.py","content":"from __future__ import annotations\n\nimport json\nimport hashlib\nfrom dataclasses import asdict, dataclass, field\nfrom decimal import Decimal\nfrom pathlib import Path\nfrom typing import Any, Dict, Iterable, List, Mapping, Optional, Sequence\n\nimport yaml\n\nfrom .commit_reveal import CommitRevealRound\nfrom .config import SystemConfig\nfrom .events import EventBus\nfrom .governance import OwnerConsole\nfrom .identity import ENSIdentityVerifier\nfrom .sentinel import AgentAction, DomainPauseController, SentinelAlert, SentinelMonitor, _hash_target\nfrom .staking import StakeManager\nfrom .subgraph import SubgraphIndexer\nfrom .vrf import VRFCoordinator\nfrom .zk_batch import JobResult, ZKBatchAttestor\n\n\n@dataclass\nclass DemoSummary:\n committee: List[str]\n round_result: Any\n paused_domains: List[str]\n gas_saved: int\n batch_proof_root: str\n indexed_events: int\n timeline: Dict[str, Any]\n owner_actions: List[Dict[str, Any]]\n sentinel_alerts: List[Dict[str, Any]]\n domain_events: List[Dict[str, Any]]\n event_feed: List[Dict[str, Any]]\n truthful_outcome: Any\n verifying_key: str\n entropy_sources: Optional[Dict[str, Any]] = None\n scenario_name: Optional[str] = None\n committee_signature: Optional[str] = None\n gas_metrics: Optional[Dict[str, Any]] = None\n context: Dict[str, Any] = field(default_factory=dict)\n\n\ndef _opposite_choice(choice: Any) -> Any:\n if isinstance(choice, bool):\n return not choice\n if isinstance(choice, str):\n return \"REJECT\" if choice.upper() != \"REJECT\" else \"APPROVE\"\n return choice\n\n\ndef _normalize_domains(domains: Iterable[Mapping[str, Any]]) -> List[Dict[str, Any]]:\n normalized: List[Dict[str, Any]] = []\n for domain in domains:\n normalized.append(\n {\n \"domain\": domain.get(\"id\") or domain.get(\"domain\"),\n \"human_name\": domain.get(\"humanName\") or domain.get(\"human_name\"),\n \"budget_limit\": domain.get(\"budgetLimit\") or domain.get(\"budget_limit\", 0),\n \"unsafe_opcodes\": list(domain.get(\"unsafeOpcodes\") or domain.get(\"unsafe_opcodes\", [])),\n \"allowed_targets\": list(domain.get(\"allowedTargets\") or domain.get(\"allowed_targets\", [])),\n \"max_calldata_bytes\": domain.get(\"maxCalldataBytes\") or domain.get(\"max_calldata_bytes\", 4096),\n \"forbidden_selectors\": list(domain.get(\"forbiddenSelectors\") or domain.get(\"forbidden_selectors\", [])),\n }\n )\n return normalized\n\n\ndef _setup_core(seed: str, config: SystemConfig) -> Dict[str, Any]:\n bus = EventBus()\n indexer = SubgraphIndexer(bus)\n pause_controller = DomainPauseController(bus, config.default_domains)\n sentinel = SentinelMonitor(pause_controller=pause_controller, event_bus=bus)\n stake_manager = StakeManager(bus, config.owner_address)\n identity = ENSIdentityVerifier(\n config.allowed_validator_roots,\n config.allowed_agent_roots,\n config.allowed_node_roots,\n blacklist=config.blacklist,\n )\n owner = OwnerConsole(config.owner_address, config, pause_controller, stake_manager, bus)\n vrf = VRFCoordinator(stake_manager, domain=\"validator-constellation-demo\")\n attestor = ZKBatchAttestor(config)\n return {\n \"bus\": bus,\n \"indexer\": indexer,\n \"pause_controller\": pause_controller,\n \"sentinel\": sentinel,\n \"stake_manager\": stake_manager,\n \"identity\": identity,\n \"owner\": owner,\n \"vrf\": vrf,\n \"attestor\": attestor,\n }\n\n\ndef _register_validators(\n stake_manager: StakeManager,\n identity: ENSIdentityVerifier,\n validators: Sequence[Mapping[str, Any]],\n) -> None:\n for validator in validators:\n address = validator[\"address\"].lower()\n ens = validator[\"ens\"]\n stake = Decimal(str(validator.get(\"stake\", \"32\")))\n proof = identity.sign(ens, address)\n identity.verify_validator(address, proof)\n stake_manager.register_validator(address, ens, stake)\n\n\ndef _build_jobs(prefix: str, count: int) -> List[JobResult]:\n jobs: List[JobResult] = []\n for idx in range(count):\n job_id = f\"{prefix}-{idx:04d}\"\n outcome_hash = hashlib.sha3_256(job_id.encode()).hexdigest()\n execution_digest = hashlib.sha3_256(f\"exec::{job_id}\".encode()).hexdigest()\n jobs.append(JobResult(job_id, outcome_hash, execution_digest))\n return jobs\n\n\ndef _collect_domain_events(bus: EventBus) -> List[Dict[str, Any]]:\n return [event.payload for event in bus.find(\"DomainPaused\")] + [\n event.payload for event in bus.find(\"DomainResumed\")\n ]\n\n\ndef run_validator_constellation_demo(\n seed: str,\n truthful_outcome: Any,\n *,\n committee_size: Optional[int] = None,\n job_count: Optional[int] = None,\n config_overrides: Optional[Mapping[str, object]] = None,\n budget_limit: Optional[float] = None,\n) -> DemoSummary:\n config = SystemConfig()\n if config_overrides:\n config = config.clone(**dict(config_overrides))\n if committee_size is not None:\n config = config.clone(committee_size=int(committee_size))\n if config.quorum > config.committee_size:\n config = config.clone(quorum=config.committee_size)\n if job_count is not None:\n desired_capacity = max(1, int(job_count))\n config = config.clone(batch_proof_capacity=max(config.batch_proof_capacity, desired_capacity))\n core = _setup_core(seed, config)\n bus: EventBus = core[\"bus\"]\n indexer: SubgraphIndexer = core[\"indexer\"]\n pause_controller: DomainPauseController = core[\"pause_controller\"]\n sentinel: SentinelMonitor = core[\"sentinel\"]\n stake_manager: StakeManager = core[\"stake_manager\"]\n identity: ENSIdentityVerifier = core[\"identity\"]\n owner: OwnerConsole = core[\"owner\"]\n vrf: VRFCoordinator = core[\"vrf\"]\n attestor: ZKBatchAttestor = core[\"attestor\"]\n\n validators = [\n {\"address\": \"0x1\", \"ens\": \"atlas.club.agi.eth\", \"stake\": \"32\"},\n {\"address\": \"0x2\", \"ens\": \"zephyr.club.agi.eth\", \"stake\": \"32\"},\n {\"address\": \"0x3\", \"ens\": \"nova.club.agi.eth\", \"stake\": \"32\"},\n {\"address\": \"0x4\", \"ens\": \"orion.club.agi.eth\", \"stake\": \"32\"},\n {\"address\": \"0x5\", \"ens\": \"hyperion.club.agi.eth\", \"stake\": \"32\"},\n ]\n _register_validators(stake_manager, identity, validators)\n\n if budget_limit is not None:\n for domain in list(pause_controller.domains):\n pause_controller.update_domain(domain, budget_limit=float(budget_limit))\n\n committee_size_value = committee_size if committee_size is not None else config.committee_size\n committee_addresses = vrf.select_committee(seed, committee_size_value)\n committee = {address: stake_manager.get(address).ens for address in committee_addresses}\n round_engine = CommitRevealRound(\n round_id=f\"demo::{seed}\",\n committee=committee,\n config=config,\n stake_manager=stake_manager,\n event_bus=bus,\n truthful_outcome=truthful_outcome,\n )\n for idx, address in enumerate(committee_addresses):\n salt = f\"{seed}:{idx}\"\n choice = truthful_outcome if idx != 0 else _opposite_choice(truthful_outcome)\n round_engine.commit(address, choice, salt)\n for idx, address in enumerate(committee_addresses):\n salt = f\"{seed}:{idx}\"\n choice = truthful_outcome if idx != 0 else _opposite_choice(truthful_outcome)\n if idx == 0:\n try:\n round_engine.reveal(address, choice, salt)\n except RuntimeError:\n continue\n else:\n round_engine.reveal(address, choice, salt)\n round_result = round_engine.finalize()\n\n target_job_count = int(job_count if job_count is not None else config.batch_proof_capacity)\n jobs = _build_jobs(seed, target_job_count)\n proof = attestor.create_batch_proof(jobs)\n attestor.verify_batch_proof(jobs, proof)\n\n alerts: List[SentinelAlert] = []\n alerts.append(\n sentinel.evaluate(\n AgentAction(\n agent=\"nova.agent.agi.eth\",\n domain=\"synthetic-biology\",\n spend=2_000_000.0,\n call=\"allocate_budget\",\n metadata={\"budget\": 2_000_000.0},\n )\n )\n )\n alerts.append(\n sentinel.evaluate(\n AgentAction(\n agent=\"sentinel.agent.agi.eth\",\n domain=\"synthetic-biology\",\n spend=500.0,\n call=\"execute_opcode\",\n metadata={\"opcode\": \"DELEGATECALL\"},\n )\n )\n )\n alerts.append(\n sentinel.evaluate(\n AgentAction(\n agent=\"nova.agent.agi.eth\",\n domain=\"synthetic-biology\",\n spend=150.0,\n call=\"invoke_target\",\n target=\"0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef\",\n metadata={\"targetHash\": _hash_target(\"0xunauthorised\")},\n )\n )\n )\n alerts = [alert for alert in alerts if alert is not None]\n\n owner.update_config(config.owner_address, quorum=4)\n\n domain_events = _collect_domain_events(bus)\n summary = DemoSummary(\n committee=[committee[address] for address in committee_addresses],\n round_result=round_result,\n paused_domains=list(pause_controller.paused.keys()),\n gas_saved=proof.gas_saved,\n batch_proof_root=proof.digest,\n indexed_events=indexer.count(),\n timeline=round_engine.timeline,\n owner_actions=[asdict(action) for action in owner.actions],\n sentinel_alerts=[asdict(alert) for alert in alerts],\n domain_events=domain_events,\n event_feed=indexer.feed(),\n truthful_outcome=truthful_outcome,\n verifying_key=config.verifying_key,\n entropy_sources={\n \"seed\": seed,\n \"recentBeacon\": hashlib.sha3_256(seed.encode()).hexdigest(),\n },\n scenario_name=None,\n committee_signature=hashlib.sha3_256(\"\".join(committee.values()).encode()).hexdigest(),\n gas_metrics={\"estimatedProofGas\": proof.gas_saved // 10},\n context={\n \"operator\": \"Non-technical mission director\",\n \"committeeSize\": committee_size_value,\n \"batchSize\": target_job_count,\n \"budgetLimit\": budget_limit\n if budget_limit is not None\n else pause_controller.domains[next(iter(pause_controller.domains))][\"budget_limit\"],\n \"configOverrides\": dict(config_overrides or {}),\n },\n )\n return summary\n\n\ndef summary_to_dict(summary: DemoSummary) -> Dict[str, Any]:\n return {\n \"committee\": summary.committee,\n \"roundResult\": summary.round_result,\n \"pausedDomains\": summary.paused_domains,\n \"gasSaved\": summary.gas_saved,\n \"batchProofRoot\": summary.batch_proof_root,\n \"indexedEvents\": summary.indexed_events,\n \"timeline\": summary.timeline,\n \"ownerActions\": summary.owner_actions,\n \"sentinelAlerts\": summary.sentinel_alerts,\n \"domainEvents\": summary.domain_events,\n \"eventFeed\": summary.event_feed,\n \"truthfulOutcome\": summary.truthful_outcome,\n \"verifyingKey\": summary.verifying_key,\n \"entropySources\": summary.entropy_sources,\n \"scenarioName\": summary.scenario_name,\n \"committeeSignature\": summary.committee_signature,\n \"gasMetrics\": summary.gas_metrics,\n \"context\": summary.context,\n }\n\n\ndef write_web_artifacts(summary: DemoSummary, output_dir: Path) -> Dict[str, Path]:\n output_dir.mkdir(parents=True, exist_ok=True)\n manifest = {\n \"events\": output_dir / \"events.json\",\n \"summary\": output_dir / \"summary.json\",\n \"timeline\": output_dir / \"timeline.json\",\n \"owner_actions\": output_dir / \"owner-actions.json\",\n }\n manifest[\"events\"].write_text(json.dumps(summary.event_feed, indent=2))\n manifest[\"summary\"].write_text(json.dumps(summary_to_dict(summary), indent=2))\n manifest[\"timeline\"].write_text(json.dumps(summary.timeline, indent=2))\n manifest[\"owner_actions\"].write_text(json.dumps(summary.owner_actions, indent=2))\n return manifest\n\n\ndef run_validator_constellation_scenario(\n path: Path,\n seed_override: Optional[str] = None,\n truthful_override: Optional[Any] = None,\n) -> DemoSummary:\n document = yaml.safe_load(path.read_text())\n config = SystemConfig()\n base = document.get(\"baseSetup\", {})\n config.verifying_key = base.get(\"verifyingKey\", config.verifying_key)\n config.committee_size = base.get(\"governance\", {}).get(\"committeeSize\", config.committee_size)\n config.quorum = int(base.get(\"governance\", {}).get(\"quorumPercentage\", 75) * config.committee_size / 100)\n config.default_domains = tuple(_normalize_domains(document.get(\"domains\", [])))\n config.batch_proof_capacity = max(config.batch_proof_capacity, document.get(\"job\", {}).get(\"count\", 1))\n\n core = _setup_core(seed_override or base.get(\"recentBeacon\", \"scenario\"), config)\n bus: EventBus = core[\"bus\"]\n indexer: SubgraphIndexer = core[\"indexer\"]\n pause_controller: DomainPauseController = core[\"pause_controller\"]\n sentinel: SentinelMonitor = core[\"sentinel\"]\n stake_manager: StakeManager = core[\"stake_manager\"]\n identity: ENSIdentityVerifier = core[\"identity\"]\n owner: OwnerConsole = core[\"owner\"]\n vrf: VRFCoordinator = core[\"vrf\"]\n attestor: ZKBatchAttestor = core[\"attestor\"]\n\n _register_validators(stake_manager, identity, document.get(\"validators\", []))\n\n seed = seed_override or base.get(\"recentBeacon\", \"scenario\")\n committee_addresses = vrf.select_committee(seed, config.committee_size)\n committee = {address: stake_manager.get(address).ens for address in committee_addresses}\n\n job = document.get(\"job\", {})\n truthful_outcome = truthful_override if truthful_override is not None else job.get(\"truthfulVote\", True)\n round_engine = CommitRevealRound(\n round_id=f\"scenario::{job.get('round', 0)}\",\n committee=committee,\n config=config,\n stake_manager=stake_manager,\n event_bus=bus,\n truthful_outcome=truthful_outcome,\n )\n vote_overrides = {key: value for key, value in document.get(\"overrides\", {}).get(\"voteByEns\", {}).items()}\n non_reveal = set(document.get(\"overrides\", {}).get(\"nonRevealValidators\", []))\n for idx, address in enumerate(committee_addresses):\n ens = committee[address]\n choice = vote_overrides.get(ens, truthful_outcome)\n salt = f\"scenario::{idx}::{seed}\"\n round_engine.commit(address, choice, salt)\n for idx, address in enumerate(committee_addresses):\n ens = committee[address]\n if ens in non_reveal:\n continue\n choice = vote_overrides.get(ens, truthful_outcome)\n salt = f\"scenario::{idx}::{seed}\"\n round_engine.reveal(address, choice, salt)\n if non_reveal:\n round_engine.advance_blocks(config.reveal_phase_blocks)\n round_result = round_engine.finalize()\n\n jobs = _build_jobs(\"scenario\", job.get(\"count\", 1))\n proof = attestor.create_batch_proof(jobs)\n attestor.verify_batch_proof(jobs, proof)\n\n agents_by_ens = {entry[\"ens\"]: entry for entry in document.get(\"agents\", [])}\n alerts: List[SentinelAlert] = []\n for anomaly in document.get(\"anomalies\", []):\n agent_entry = agents_by_ens.get(anomaly.get(\"agentEns\"))\n domain = agent_entry.get(\"domainId\") if agent_entry else config.default_domains[0][\"domain\"]\n metadata: Dict[str, Any] = {\"description\": anomaly.get(\"description\")}\n target = anomaly.get(\"target\")\n spend = float(anomaly.get(\"amount\", anomaly.get(\"calldataBytes\", 0)))\n if anomaly.get(\"kind\") == \"budget-overrun\":\n metadata[\"budget\"] = anomaly.get(\"amount\")\n if anomaly.get(\"kind\") == \"unsafe-opcode\":\n metadata[\"opcode\"] = anomaly.get(\"opcode\")\n metadata[\"calldataBytes\"] = anomaly.get(\"calldataBytes\", 0)\n if anomaly.get(\"kind\") == \"unauthorized-target\":\n metadata[\"targetHash\"] = _hash_target(target or \"\")\n if anomaly.get(\"kind\") == \"calldata-spike\":\n metadata[\"calldataBytes\"] = anomaly.get(\"calldataBytes\", 0)\n metadata[\"targetHash\"] = _hash_target(anomaly.get(\"target\", \"\"))\n if anomaly.get(\"kind\") == \"forbidden-selector\":\n metadata[\"selector\"] = anomaly.get(\"selector\")\n metadata[\"targetHash\"] = _hash_target(anomaly.get(\"target\", \"\"))\n alert = sentinel.evaluate(\n AgentAction(\n agent=anomaly.get(\"agentEns\", \"unknown\"),\n domain=domain,\n spend=spend or float(agent_entry.get(\"budget\", 0) if agent_entry else 0),\n call=anomaly.get(\"kind\", \"action\"),\n metadata=metadata,\n target=target,\n calldata_bytes=anomaly.get(\"calldataBytes\"),\n )\n )\n if alert:\n alerts.append(alert)\n\n owner_actions = owner.execute_script(config.owner_address, document.get(\"ownerActions\", {}))\n\n entropy_sources = {\n \"onChainEntropy\": base.get(\"onChainEntropy\"),\n \"recentBeacon\": base.get(\"recentBeacon\"),\n }\n for action in owner_actions:\n if action.action == \"update-entropy\":\n entropy_sources.update(action.details) # type: ignore[arg-type]\n\n domain_events = _collect_domain_events(bus)\n summary = DemoSummary(\n committee=[committee[address] for address in committee_addresses],\n round_result=round_result,\n paused_domains=list(pause_controller.paused.keys()),\n gas_saved=proof.gas_saved,\n batch_proof_root=proof.digest,\n indexed_events=indexer.count(),\n timeline=round_engine.timeline,\n owner_actions=[asdict(action) for action in owner.actions],\n sentinel_alerts=[asdict(alert) for alert in alerts],\n domain_events=domain_events,\n event_feed=indexer.feed(),\n truthful_outcome=truthful_outcome,\n verifying_key=config.verifying_key,\n entropy_sources=entropy_sources,\n scenario_name=document.get(\"name\"),\n committee_signature=job.get(\"committeeSignature\"),\n gas_metrics={\"estimatedProofGas\": proof.gas_saved // 12},\n context=document.get(\"context\", {}),\n )\n return summary\n","format":"text","sha256":"459fd27f24db621327bd1410fefef52029b5441fd33ec26db906a930a33993df","bytes":18352,"download":"/AGIJobsv0/examples/459fd27f24db6213-demo_runner.py","source":"https://github.com/MontrealAI/AGIJobsv0/blob/5b4cebb309a83a7a6749d8911d8bf96a1921e042/demo/Validator-Constellation-v0/validator_constellation/demo_runner.py"},{"file":"demo/Validator-Constellation-v0/run_demo.py","content":"\"\"\"Command-line entry point for the Validator Constellation demo.\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nimport json\nfrom pathlib import Path\n\nfrom validator_constellation.demo_runner import (\n run_validator_constellation_demo,\n run_validator_constellation_scenario,\n summary_to_dict,\n write_web_artifacts,\n)\n\n\ndef main() -> None:\n parser = argparse.ArgumentParser(description=\"Run the Validator Constellation simulation\")\n parser.add_argument(\"--seed\", default=\"demo-seed\", help=\"Entropy seed for VRF committee selection\")\n parser.add_argument(\n \"--truth\",\n choices=[\"true\", \"false\"],\n default=\"true\",\n help=\"Truthful outcome for the demo round\",\n )\n parser.add_argument(\"--committee-size\", type=int, default=None, help=\"Override committee size\")\n parser.add_argument(\"--jobs\", type=int, default=None, help=\"Number of jobs to batch into the proof\")\n parser.add_argument(\"--quorum\", type=int, default=None, help=\"Quorum required for truthful outcome\")\n parser.add_argument(\"--commit-blocks\", type=int, default=None, help=\"Commit phase block window\")\n parser.add_argument(\"--reveal-blocks\", type=int, default=None, help=\"Reveal phase block window\")\n parser.add_argument(\n \"--slash-non-reveal\",\n type=float,\n default=None,\n help=\"Slash fraction for non-revealing validators\",\n )\n parser.add_argument(\n \"--slash-incorrect\",\n type=float,\n default=None,\n help=\"Slash fraction for incorrect votes\",\n )\n parser.add_argument(\n \"--budget\",\n type=float,\n default=1_000.0,\n help=\"Sentinel budget threshold before alerts fire\",\n )\n parser.add_argument(\n \"--owner\",\n type=str,\n default=None,\n help=\"Override the simulated contract owner address\",\n )\n parser.add_argument(\n \"--scenario\",\n type=Path,\n default=None,\n help=\"Optional path to a YAML scenario to execute instead of the built-in flow\",\n )\n parser.add_argument(\n \"--output\",\n type=Path,\n default=None,\n help=\"Optional path to export the summary JSON\",\n )\n parser.add_argument(\n \"--web-artifacts\",\n type=Path,\n default=None,\n help=\"Directory for exporting web dashboard artefacts (events, summary, owner actions)\",\n )\n args = parser.parse_args()\n overrides = {}\n if args.quorum is not None:\n overrides[\"quorum\"] = args.quorum\n if args.commit_blocks is not None:\n overrides[\"commit_phase_blocks\"] = args.commit_blocks\n if args.reveal_blocks is not None:\n overrides[\"reveal_phase_blocks\"] = args.reveal_blocks\n if args.slash_non_reveal is not None:\n overrides[\"slash_fraction_non_reveal\"] = args.slash_non_reveal\n if args.slash_incorrect is not None:\n overrides[\"slash_fraction_incorrect_vote\"] = args.slash_incorrect\n if args.owner is not None:\n overrides[\"owner_address\"] = args.owner\n if args.scenario:\n seed_default = parser.get_default(\"seed\")\n truth_default = parser.get_default(\"truth\")\n seed_override = args.seed if args.seed != seed_default else None\n truthful_override = None\n if args.truth != truth_default:\n truthful_override = args.truth == \"true\"\n summary = run_validator_constellation_scenario(\n args.scenario,\n seed_override=seed_override,\n truthful_override=truthful_override,\n )\n else:\n summary = run_validator_constellation_demo(\n seed=args.seed,\n truthful_outcome=args.truth == \"true\",\n committee_size=args.committee_size,\n job_count=args.jobs,\n config_overrides=overrides or None,\n budget_limit=args.budget,\n )\n data = summary_to_dict(summary)\n print(json.dumps(data, indent=2))\n if args.output:\n args.output.write_text(json.dumps(data, indent=2))\n print(f\"Summary exported to {args.output}\")\n if args.web_artifacts:\n manifest = write_web_artifacts(summary, args.web_artifacts)\n print(\"Web artefacts exported:\")\n for label, path in manifest.items():\n print(f\" {label}: {path}\")\n\n\nif __name__ == \"__main__\":\n main()\n","format":"text","sha256":"5d255ec71bab448097fb726e36f3fbc51c4e11be4e03c366986f78e05e90a2c2","bytes":4308,"download":"/AGIJobsv0/examples/5d255ec71bab4480-run_demo.py","source":"https://github.com/MontrealAI/AGIJobsv0/blob/5b4cebb309a83a7a6749d8911d8bf96a1921e042/demo/Validator-Constellation-v0/run_demo.py"}]}
FROM READING TO A REPRODUCIBLE RUN
Try the selected path.
Isolated Python environment
Use Python 3.12 in a virtual environment. Install this demo’s tracked requirements file when present, then run python -m pip check. Some variants have additional requirements: follow the selected implementation’s guide, not an unrelated demo’s dependency list.
A simulated committee round produces a summary in /tmp/validator-demo.json. Inspect votes, outcome, slashing and owner actions.
The source inspector above reads bundled repository material. Local commands run separately on your computer. Recorded examples may contain historical timestamps, placeholders and simulated metrics.
MAKE IT YOUR OWN
One useful experiment.
Repeat with --truth false using the same seed. Compare the decision and penalties; distinguish simulator behavior from actual contract enforcement.
THE SYSTEM, MADE VISIBLE
Architecture & relationships
Architecture diagram · source preserved belowView original Mermaid source
flowchart LR
Operators((Mission Owners)) --> demo_Validator_Constellation_v0[[Demo → Validator Constellation v0]]
demo_Validator_Constellation_v0 --> Core[[AGI Jobs v0 (v2) Core Intelligence]]
Core --> Observability[[Unified CI / CD & Observability]]
Core --> Governance[[Owner Control Plane]]
WHEN SOMETHING DOESN’T MATCH
Troubleshooting
Import or dependency error
Confirm the active virtual environment and the selected demo’s requirements. Run python -m pip check; do not install unrelated demo requirements over a working environment.
Unexpected result or missing file
Check the selected entry point, configuration and output argument. Keep the seed and implementation fixed before comparing outcomes.
TRACE THE CHECKS
Verification & next steps
17 tracked test source files are available in this directory. Inspect the tests and their environment before choosing a suite; file counts do not establish test results.