ilc-core 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (357) hide show
  1. ilc_core/agent.py +273 -0
  2. ilc_core/analysis/agent_descriptors.py +93 -0
  3. ilc_core/analysis/agent_dossier_export.py +110 -0
  4. ilc_core/analysis/agent_profiles.py +214 -0
  5. ilc_core/analysis/claim_scores.py +99 -0
  6. ilc_core/analysis/competency_kpis.py +173 -0
  7. ilc_core/analysis/econ_accounting.py +77 -0
  8. ilc_core/analysis/econ_kpis.py +238 -0
  9. ilc_core/analysis/embedding_pipeline.py +464 -0
  10. ilc_core/analysis/epistemic_code.py +67 -0
  11. ilc_core/analysis/epoch_report_export.py +104 -0
  12. ilc_core/analysis/fairness_metrics.py +344 -0
  13. ilc_core/analysis/freshness_gate.py +107 -0
  14. ilc_core/analysis/genesis_accrual_governor.py +224 -0
  15. ilc_core/analysis/governance_weight.py +226 -0
  16. ilc_core/analysis/graph_kpis.py +131 -0
  17. ilc_core/analysis/laplacian_analytics.py +407 -0
  18. ilc_core/analysis/light_cone_kpis.py +131 -0
  19. ilc_core/analysis/local_spectral_analytics.py +131 -0
  20. ilc_core/analysis/namespace_health.py +130 -0
  21. ilc_core/analysis/node_value_conformance.py +89 -0
  22. ilc_core/analysis/node_value_extraction.py +90 -0
  23. ilc_core/analysis/node_value_governance_conformance.py +309 -0
  24. ilc_core/analysis/node_value_input_canon.py +317 -0
  25. ilc_core/analysis/node_value_kernel.py +334 -0
  26. ilc_core/analysis/node_value_policy_migration.py +134 -0
  27. ilc_core/analysis/path_lift_counterfactual.py +210 -0
  28. ilc_core/analysis/problem_space_kpis.py +106 -0
  29. ilc_core/analysis/reuse_diversity_invariants.py +156 -0
  30. ilc_core/analysis/routed_tasks.py +190 -0
  31. ilc_core/analysis/routed_tasks_export.py +58 -0
  32. ilc_core/analysis/spectral_trajectory.py +167 -0
  33. ilc_core/analysis/spectral_utils.py +184 -0
  34. ilc_core/analysis/stress_and_cohesion.py +108 -0
  35. ilc_core/analysis/stress_response_kpis.py +137 -0
  36. ilc_core/analysis/task_routing_suggestions.py +190 -0
  37. ilc_core/analysis/utility_flow_rewards.py +403 -0
  38. ilc_core/asgi.py +6 -0
  39. ilc_core/cli/_cli_error.py +38 -0
  40. ilc_core/cli/_key_utils.py +24 -0
  41. ilc_core/cli/canon_bundle_key_registry.py +348 -0
  42. ilc_core/cli/canon_bundle_key_registry_bundle.py +198 -0
  43. ilc_core/cli/canon_bundle_key_registry_channel.py +155 -0
  44. ilc_core/cli/canon_bundle_key_registry_channel_sign.py +82 -0
  45. ilc_core/cli/canon_bundle_key_registry_channel_verify.py +89 -0
  46. ilc_core/cli/canon_bundle_key_registry_fetch.py +144 -0
  47. ilc_core/cli/canon_bundle_key_registry_promotion.py +137 -0
  48. ilc_core/cli/canon_bundle_key_registry_sync.py +325 -0
  49. ilc_core/cli/canon_bundle_pipeline.py +238 -0
  50. ilc_core/cli/canon_bundle_replay.py +109 -0
  51. ilc_core/cli/canon_bundle_sign.py +57 -0
  52. ilc_core/cli/canon_bundle_validate.py +87 -0
  53. ilc_core/cli/canon_cli.py +31 -0
  54. ilc_core/cli/canon_cluster_a_replay_proof.py +616 -0
  55. ilc_core/cli/canon_consumer.py +277 -0
  56. ilc_core/cli/canon_export.py +76 -0
  57. ilc_core/cli/d2e_agent_cli.py +69 -0
  58. ilc_core/cli/d2e_lifecycle_cli.py +138 -0
  59. ilc_core/cli/d2e_query_truth_cli.py +144 -0
  60. ilc_core/cli/d2e_submit_cli.py +179 -0
  61. ilc_core/cli/ep_task_cli.py +189 -0
  62. ilc_core/cli/main.py +1241 -0
  63. ilc_core/cli/mcp_cli.py +140 -0
  64. ilc_core/config.py +83 -0
  65. ilc_core/consensus/__init__.py +107 -0
  66. ilc_core/consensus/circuit_breaker_interface.py +183 -0
  67. ilc_core/consensus/clustering.py +31 -0
  68. ilc_core/consensus/diversity_floor_runtime.py +139 -0
  69. ilc_core/consensus/engine.py +454 -0
  70. ilc_core/consensus/epoch_state_runtime.py +349 -0
  71. ilc_core/consensus/finality_evaluator.py +417 -0
  72. ilc_core/consensus/governance.py +254 -0
  73. ilc_core/consensus/popperian_gate_runtime.py +126 -0
  74. ilc_core/consensus/production_bridge.py +638 -0
  75. ilc_core/consensus/reputation.py +146 -0
  76. ilc_core/crypto/__init__.py +43 -0
  77. ilc_core/crypto/cbor_canonical.py +86 -0
  78. ilc_core/crypto/cose_sign1.py +248 -0
  79. ilc_core/economics/entropy.py +80 -0
  80. ilc_core/economics/epoch_attribution_settle_runtime.py +557 -0
  81. ilc_core/economics/epoch_ledger.py +81 -0
  82. ilc_core/economics/onboarding.py +67 -0
  83. ilc_core/economics/outcome.py +53 -0
  84. ilc_core/economics/passive_ecu_attribution_runtime.py +100 -0
  85. ilc_core/economics/reward.py +76 -0
  86. ilc_core/economics/rl_agents.py +62 -0
  87. ilc_core/economics/telemetry.py +78 -0
  88. ilc_core/encoding/__init__.py +38 -0
  89. ilc_core/encoding/cidv1.py +278 -0
  90. ilc_core/encoding/dag_cbor.py +478 -0
  91. ilc_core/encoding/varint.py +79 -0
  92. ilc_core/epistemic/__init__.py +209 -0
  93. ilc_core/epistemic/aesthetic_panel_runtime.py +98 -0
  94. ilc_core/epistemic/ingestion_shadow_harness.py +498 -0
  95. ilc_core/epistemic/jury_activation_gate.py +432 -0
  96. ilc_core/epistemic/jury_assignment_runtime.py +721 -0
  97. ilc_core/epistemic/jury_incentive_runtime.py +102 -0
  98. ilc_core/epistemic/maintenance_lottery_runtime.py +256 -0
  99. ilc_core/epistemic/node_submission_runtime.py +129 -0
  100. ilc_core/epistemic/novelty_check_runtime.py +50 -0
  101. ilc_core/epistemic/refutation_runtime.py +93 -0
  102. ilc_core/epistemic/reuse_centrality_runtime.py +72 -0
  103. ilc_core/epistemic/review_lane_admission_runtime.py +499 -0
  104. ilc_core/epistemic/truth_primitive_graph_store.py +284 -0
  105. ilc_core/epistemic/truth_primitive_submission_runtime.py +456 -0
  106. ilc_core/epistemic/vrf_proof_verifier.py +346 -0
  107. ilc_core/epoch/__init__.py +345 -0
  108. ilc_core/epoch/allocation_distributor_runtime.py +358 -0
  109. ilc_core/epoch/ecu_price_clamp_runtime.py +230 -0
  110. ilc_core/epoch/epoch_boundary_witness_runtime.py +56 -0
  111. ilc_core/epoch/epoch_emission_runtime.py +194 -0
  112. ilc_core/epoch/epoch_snapshot_runtime.py +421 -0
  113. ilc_core/epoch/fee_burn_split_runtime.py +159 -0
  114. ilc_core/epoch/issuance_economics_integration_gate.py +527 -0
  115. ilc_core/epoch/treasury_governance_runtime.py +225 -0
  116. ilc_core/epoch/validator_reward_pool_routing_runtime.py +293 -0
  117. ilc_core/eve/__init__.py +1 -0
  118. ilc_core/eve/capsule.py +218 -0
  119. ilc_core/eve/capsule_builder.py +125 -0
  120. ilc_core/exceptions.py +187 -0
  121. ilc_core/genesis/__init__.py +99 -0
  122. ilc_core/genesis/admission_control_bootstrap.py +126 -0
  123. ilc_core/genesis/assertion_schema.py +470 -0
  124. ilc_core/genesis/genesis_intervention_runtime.py +466 -0
  125. ilc_core/genesis/genesis_state_bundle_runtime.py +329 -0
  126. ilc_core/genesis/schema.py +24 -0
  127. ilc_core/genesis/validator_bootstrap_runtime.py +178 -0
  128. ilc_core/genesis/work_task.py +80 -0
  129. ilc_core/governance/__init__.py +114 -0
  130. ilc_core/governance/challenge_node_runtime.py +553 -0
  131. ilc_core/governance/fork_legitimacy_runtime.py +533 -0
  132. ilc_core/graph/__init__.py +210 -0
  133. ilc_core/graph/agent_graph_projection_runtime.py +539 -0
  134. ilc_core/graph/sidecar_public_path_preflight.py +614 -0
  135. ilc_core/graph/sidecar_query_runtime.py +531 -0
  136. ilc_core/graph.py +203 -0
  137. ilc_core/hardware.py +119 -0
  138. ilc_core/identity/__init__.py +51 -0
  139. ilc_core/identity/agent_id_runtime.py +150 -0
  140. ilc_core/identity/endorsement_packet_schema.py +350 -0
  141. ilc_core/identity/epoch_endorsement_runtime.py +567 -0
  142. ilc_core/identity/genesis_record_schema.py +316 -0
  143. ilc_core/identity/log_redaction_runtime.py +171 -0
  144. ilc_core/identity/sybil_resistance_runtime.py +149 -0
  145. ilc_core/ledger/__init__.py +29 -0
  146. ilc_core/ledger/backend.py +338 -0
  147. ilc_core/ledger/canon_bundle_audit_artifact.py +158 -0
  148. ilc_core/ledger/canon_bundle_key_registry.py +707 -0
  149. ilc_core/ledger/canon_bundle_key_registry_bundle.py +313 -0
  150. ilc_core/ledger/canon_bundle_key_registry_channel.py +429 -0
  151. ilc_core/ledger/canon_bundle_key_registry_channel_signing.py +272 -0
  152. ilc_core/ledger/canon_bundle_key_registry_fetch.py +374 -0
  153. ilc_core/ledger/canon_bundle_key_registry_promotion.py +272 -0
  154. ilc_core/ledger/canon_bundle_key_registry_sync.py +947 -0
  155. ilc_core/ledger/canon_bundle_key_registry_sync_state.py +114 -0
  156. ilc_core/ledger/canon_bundle_pipeline_report.py +106 -0
  157. ilc_core/ledger/canon_bundle_replay_report.py +148 -0
  158. ilc_core/ledger/canon_bundle_replay_verify.py +169 -0
  159. ilc_core/ledger/canon_bundle_utils.py +65 -0
  160. ilc_core/ledger/canon_export.py +125 -0
  161. ilc_core/ledger/canon_export_bundle.py +134 -0
  162. ilc_core/ledger/canon_export_bundle_report.py +62 -0
  163. ilc_core/ledger/canon_export_bundle_sign.py +124 -0
  164. ilc_core/ledger/canon_export_bundle_validate.py +214 -0
  165. ilc_core/ledger/canon_export_bundle_verify_sig.py +106 -0
  166. ilc_core/ledger/canon_export_format.py +134 -0
  167. ilc_core/ledger/canon_export_validate.py +159 -0
  168. ilc_core/ledger/canon_loader.py +317 -0
  169. ilc_core/ledger/cdl048_conversion_sweeper_runtime.py +1187 -0
  170. ilc_core/ledger/claimability_proof_binding_runtime.py +670 -0
  171. ilc_core/ledger/ecu_active_layer_runtime.py +361 -0
  172. ilc_core/ledger/ecu_ilc_lifecycle_runtime.py +209 -0
  173. ilc_core/ledger/exact_numeric.py +102 -0
  174. ilc_core/ledger/ledger_export.py +221 -0
  175. ilc_core/ledger/lmdb_backend.py +180 -0
  176. ilc_core/ledger/persistent_backend.py +177 -0
  177. ilc_core/ledger/public_economics_admission_firewall.py +278 -0
  178. ilc_core/ledger/settlement_metrics.py +95 -0
  179. ilc_core/ledger/settlement_verification.py +184 -0
  180. ilc_core/ledger/stake_snapshot.py +50 -0
  181. ilc_core/links.py +21 -0
  182. ilc_core/logging_config.py +36 -0
  183. ilc_core/mcp/__init__.py +1 -0
  184. ilc_core/mcp/node_service.py +43 -0
  185. ilc_core/mcp/schema.py +114 -0
  186. ilc_core/mcp/schemas/__init__.py +1 -0
  187. ilc_core/mcp/schemas/ilc_mcp_tools_mvp_schema_v0.1.json +315 -0
  188. ilc_core/mcp/schemas/mcp_tool_call_event_schema_v0.1.json +95 -0
  189. ilc_core/mcp/service.py +396 -0
  190. ilc_core/mining/benchmark.py +304 -0
  191. ilc_core/network/__init__.py +23 -0
  192. ilc_core/network/d2d/__init__.py +31 -0
  193. ilc_core/network/d2d/bootstrap_fetch_runtime.py +244 -0
  194. ilc_core/network/d2d/cdl087_observability.py +228 -0
  195. ilc_core/network/d2d/cdl087_serving_peer_evidence.py +393 -0
  196. ilc_core/network/d2d/centrality_delta_gossip_runtime.py +241 -0
  197. ilc_core/network/d2d/gossip.py +253 -0
  198. ilc_core/network/d2d/gossip_peer_registry.py +161 -0
  199. ilc_core/network/d2d/gossip_transport.py +185 -0
  200. ilc_core/network/d2d/http_fetch_transport_runtime.py +305 -0
  201. ilc_core/network/d2d/http_gossip_transport_runtime.py +447 -0
  202. ilc_core/network/d2d/interface.py +221 -0
  203. ilc_core/network/d2d/peer.py +169 -0
  204. ilc_core/network/d2d/peer_fingerprint_cache.py +235 -0
  205. ilc_core/network/d2d/persistent_fetch_rate_limiter_runtime.py +204 -0
  206. ilc_core/network/d2d/routing_reputation_runtime.py +167 -0
  207. ilc_core/network/d2d/spectral_beacon.py +768 -0
  208. ilc_core/network/d2d/spectral_routing_runtime.py +304 -0
  209. ilc_core/network/d2d/transport_principal_pre_public_path.py +413 -0
  210. ilc_core/network/d2d/transport_principal_public_path_preflight.py +513 -0
  211. ilc_core/network/d2d/truth_primitive_fetch_runtime.py +462 -0
  212. ilc_core/network/d2d/truth_primitive_gossip_runtime.py +206 -0
  213. ilc_core/network/gossip.py +85 -0
  214. ilc_core/network/node_load_export.py +75 -0
  215. ilc_core/network/peer.py +167 -0
  216. ilc_core/network/star_map/__init__.py +0 -0
  217. ilc_core/network/star_map/star_map_route_index_runtime.py +401 -0
  218. ilc_core/network/topology.py +151 -0
  219. ilc_core/network/wire_transport_runtime.py +359 -0
  220. ilc_core/node/__init__.py +103 -0
  221. ilc_core/node/devnet.py +210 -0
  222. ilc_core/node/executable_descriptor_runtime_363.py +409 -0
  223. ilc_core/node/node_dissemination_runtime_362.py +510 -0
  224. ilc_core/node/node_schema_core_runtime_360.py +424 -0
  225. ilc_core/node/node_startup_runtime.py +569 -0
  226. ilc_core/node/node_v0.py +122 -0
  227. ilc_core/node/promotion_continuity_runtime_364.py +438 -0
  228. ilc_core/node/tier3_runtime_linkage_runtime.py +156 -0
  229. ilc_core/node/timed_out_lifecycle_runtime_411.py +52 -0
  230. ilc_core/node/validation_lifecycle_runtime_361.py +353 -0
  231. ilc_core/privacy/__init__.py +69 -0
  232. ilc_core/privacy/lane.py +369 -0
  233. ilc_core/privacy/metrics.py +297 -0
  234. ilc_core/privacy/monitor.py +197 -0
  235. ilc_core/privacy/transfer_mixing_framework.py +81 -0
  236. ilc_core/protocol/__init__.py +128 -0
  237. ilc_core/protocol/bundle_verify.py +120 -0
  238. ilc_core/protocol/commit_epoch_emission_runtime.py +480 -0
  239. ilc_core/protocol/event_export.py +203 -0
  240. ilc_core/protocol/event_log.py +719 -0
  241. ilc_core/protocol/event_log_retention.py +261 -0
  242. ilc_core/protocol/governance_weighted_decision.py +334 -0
  243. ilc_core/protocol/harness_interfaces.py +151 -0
  244. ilc_core/protocol/ilc_cluster_a_acceptance_evidence.py +396 -0
  245. ilc_core/protocol/ilc_cluster_a_clause_binding.py +166 -0
  246. ilc_core/protocol/ilc_cluster_a_conformance.py +346 -0
  247. ilc_core/protocol/ilc_cluster_a_ingest.py +568 -0
  248. ilc_core/protocol/ilc_cluster_a_replay_attestation.py +196 -0
  249. ilc_core/protocol/ilc_cluster_a_replay_proof_batch.py +179 -0
  250. ilc_core/protocol/ilc_cluster_a_replay_proof_batch_compare.py +201 -0
  251. ilc_core/protocol/ilc_cluster_a_replay_proof_batch_ops.py +101 -0
  252. ilc_core/protocol/ilc_cluster_a_replay_proof_ci_gate.py +502 -0
  253. ilc_core/protocol/ilc_cluster_a_replay_proof_package.py +288 -0
  254. ilc_core/protocol/ilc_cluster_a_replay_proof_schemas.py +47 -0
  255. ilc_core/protocol/ilc_governance_record_validate.py +88 -0
  256. ilc_core/protocol/ilc_receipt_validate.py +86 -0
  257. ilc_core/protocol/ilc_transcript_validate.py +124 -0
  258. ilc_core/protocol/ilc_wire_validate.py +107 -0
  259. ilc_core/protocol/known_records_migration.py +114 -0
  260. ilc_core/protocol/mapper.py +112 -0
  261. ilc_core/protocol/ndjson_bundle.py +584 -0
  262. ilc_core/protocol/params.py +168 -0
  263. ilc_core/protocol/primitive_type_registry.py +27 -0
  264. ilc_core/protocol/public_init_admission_runtime.py +141 -0
  265. ilc_core/protocol/public_receipt_runtime.py +208 -0
  266. ilc_core/protocol/public_wallet_runtime.py +214 -0
  267. ilc_core/protocol/schema.py +30 -0
  268. ilc_core/protocol/schemas/canon_bundle_key_registry_bundle_v0.1.json +12 -0
  269. ilc_core/protocol/schemas/canon_bundle_key_registry_channel_v0.1.json +10 -0
  270. ilc_core/protocol/schemas/canon_bundle_key_registry_channel_v0.2.json +37 -0
  271. ilc_core/protocol/schemas/canon_bundle_key_registry_channel_v0.3.json +22 -0
  272. ilc_core/protocol/schemas/canon_bundle_key_registry_channel_v0.4.json +24 -0
  273. ilc_core/protocol/schemas/canon_bundle_key_registry_v0.1.json +57 -0
  274. ilc_core/protocol/schemas/canon_export_format_v0.1.json +92 -0
  275. ilc_core/protocol/schemas/commit_epoch_event_schema_v0.1.json +112 -0
  276. ilc_core/protocol/schemas/ilc_canonical_transcript_schema_v0.1.json +69 -0
  277. ilc_core/protocol/schemas/ilc_cluster_a_replay_proof_batch_compare_v0.1.json +73 -0
  278. ilc_core/protocol/schemas/ilc_cluster_a_replay_proof_batch_ops_contract_v0.1.json +58 -0
  279. ilc_core/protocol/schemas/ilc_cluster_a_replay_proof_batch_report_v0.1.json +120 -0
  280. ilc_core/protocol/schemas/ilc_cluster_a_replay_proof_ci_gate_report_v0.1.json +173 -0
  281. ilc_core/protocol/schemas/ilc_governance_record_schema_v0.1.json +91 -0
  282. ilc_core/protocol/schemas/ilc_protocol_wire_format_v0.1.json +145 -0
  283. ilc_core/protocol/schemas/ilc_receipt_schema_v0.1.json +78 -0
  284. ilc_core/protocol/schemas/mcp_tool_call_event_schema_v0.1.json +95 -0
  285. ilc_core/rc/__init__.py +89 -0
  286. ilc_core/rc/atlas_graph_discipline.py +1425 -0
  287. ilc_core/rc/economic_cycle_runtime.py +766 -0
  288. ilc_core/rc/genesis_v0_2_signing_ceremony_gate.py +841 -0
  289. ilc_core/rc/local_skill_preview.py +223 -0
  290. ilc_core/rc/package_boundary_inventory.py +337 -0
  291. ilc_core/rc/package_profile_ci_gate.py +463 -0
  292. ilc_core/rc/package_profiles.py +435 -0
  293. ilc_core/rc/public_rc_exclude_disposition.py +507 -0
  294. ilc_core/rc/public_rc_publication_claim_gate.py +770 -0
  295. ilc_core/rc/release_artifact_manifest_rehearsal.py +538 -0
  296. ilc_core/rc/release_artifact_production_gate.py +934 -0
  297. ilc_core/rc/release_key_envelope_rehearsal.py +602 -0
  298. ilc_core/rc/release_keys_envelopes_generation_gate.py +897 -0
  299. ilc_core/rc/source_allowlist_export_execution_gate.py +823 -0
  300. ilc_core/rc/source_allowlist_export_rehearsal.py +954 -0
  301. ilc_core/reputation/__init__.py +19 -0
  302. ilc_core/reputation/temporal_decay_runtime.py +128 -0
  303. ilc_core/schema/__init__.py +21 -0
  304. ilc_core/schema/d2_schema_baseline_runtime.py +219 -0
  305. ilc_core/security/__init__.py +69 -0
  306. ilc_core/security/key_compromise_runtime.py +261 -0
  307. ilc_core/security/rollback_resistance_runtime.py +100 -0
  308. ilc_core/security/signer_lineage_runtime.py +350 -0
  309. ilc_core/server.py +380 -0
  310. ilc_core/sidecars/__init__.py +16 -0
  311. ilc_core/sidecars/claim_nullifier_registry_v1.py +542 -0
  312. ilc_core/sidecars/claimability_receipt_verifier.py +1460 -0
  313. ilc_core/sidecars/confidential_coordination_capability.py +1453 -0
  314. ilc_core/sidecars/confidential_coordination_gossip_policy.py +1126 -0
  315. ilc_core/sidecars/confidential_coordination_sealed_sender.py +1351 -0
  316. ilc_core/sidecars/confidential_coordination_shard.py +1272 -0
  317. ilc_core/sidecars/local_graph_memory_projection.py +1084 -0
  318. ilc_core/sidecars/public_fetch_p2p_readiness.py +730 -0
  319. ilc_core/sidecars/registry_manifest.py +1157 -0
  320. ilc_core/sidecars/transport_principal_admission.py +1126 -0
  321. ilc_core/sidecars/value_path_activation_boundary_preflight.py +617 -0
  322. ilc_core/sidecars/wallet_action_semantics_preflight.py +573 -0
  323. ilc_core/sim/devnet_epoch_orchestrator.py +247 -0
  324. ilc_core/sim/devnet_experiments.py +186 -0
  325. ilc_core/sim/devnet_multi_epoch.py +298 -0
  326. ilc_core/sim/devnet_scenarios.py +315 -0
  327. ilc_core/sim/harness_closed_loop_controllers.py +180 -0
  328. ilc_core/sim/harness_econ_scenarios.py +201 -0
  329. ilc_core/sim/harness_param_grid.py +227 -0
  330. ilc_core/sim/sim_fetch_01/__init__.py +0 -0
  331. ilc_core/sim/sim_fetch_01/sim_fetch_01_harness.py +1694 -0
  332. ilc_core/sim/sim_fetch_01/werner_topology_pressure_profile.py +193 -0
  333. ilc_core/star_map/__init__.py +1 -0
  334. ilc_core/star_map/debruijn_harness.py +207 -0
  335. ilc_core/star_map/route_index.py +310 -0
  336. ilc_core/storage/__init__.py +33 -0
  337. ilc_core/storage/interfaces.py +48 -0
  338. ilc_core/storage/lmdb_graph_pruning_runtime.py +212 -0
  339. ilc_core/storage/lmdb_public_runtime.py +236 -0
  340. ilc_core/storage/truth_primitive_graph_lmdb_adapter.py +88 -0
  341. ilc_core/testing/__init__.py +15 -0
  342. ilc_core/testing/phase_commit_manifest.py +65 -0
  343. ilc_core/testing/ratification_mutation_scope_guardrail.py +211 -0
  344. ilc_core/types.py +389 -0
  345. ilc_core/validator/__init__.py +117 -0
  346. ilc_core/validator/admission_ejection_runtime.py +431 -0
  347. ilc_core/validator/re_admission_runtime.py +53 -0
  348. ilc_core/validator/staking_liveness_runtime.py +53 -0
  349. ilc_core/validator/topology_shuffle_runtime.py +323 -0
  350. ilc_core/validator/trust_tier_runtime.py +82 -0
  351. ilc_core/work/task_queue.py +127 -0
  352. ilc_core-0.1.0.dist-info/METADATA +163 -0
  353. ilc_core-0.1.0.dist-info/RECORD +357 -0
  354. ilc_core-0.1.0.dist-info/WHEEL +5 -0
  355. ilc_core-0.1.0.dist-info/entry_points.txt +11 -0
  356. ilc_core-0.1.0.dist-info/licenses/LICENSE +28 -0
  357. ilc_core-0.1.0.dist-info/top_level.txt +1 -0
ilc_core/agent.py ADDED
@@ -0,0 +1,273 @@
1
+ from typing import Optional
2
+ import logging
3
+ from decimal import Decimal, InvalidOperation
4
+ from .types import Node
5
+ from .graph import EpistemicGraph
6
+ from .exceptions import InsufficientStakeError
7
+ from .consensus.engine import ConsensusEngine
8
+ from .mining.benchmark import PoWBenchmark
9
+ from .economics.onboarding import OnboardingVault
10
+ from .identity.log_redaction_runtime import redact_agent_id_for_log
11
+ import time
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ DRAFT_SIGNATURE = "draft_unsigned_mvp"
16
+
17
+ class EveAgent:
18
+ def __init__(self, agent_id: str, graph: EpistemicGraph, consensus: ConsensusEngine, vault: Optional[OnboardingVault] = None):
19
+ self.id = agent_id
20
+ self.graph = graph
21
+ self.consensus = consensus
22
+ self.vault = vault
23
+
24
+ self.wallet_balance = Decimal("0")
25
+ self.credit_balance = Decimal("0")
26
+
27
+ # Onboarding: Request Credit if Vault exists
28
+ if self.vault:
29
+ credit = self.vault.request_starter_credit(self.id)
30
+ self.credit_balance += credit
31
+ self.wallet_balance += credit # Credit is liquid for staking
32
+
33
+ self.benchmark_engine = PoWBenchmark(matrix_size=1000)
34
+ self.trust_vector = {
35
+ "accuracy": 0.5,
36
+ "precision": 0.5,
37
+ "potential": 0.0,
38
+ "tier": "unknown"
39
+ }
40
+
41
+ def decide_stake_for_claim(self, requested_stake: Decimal) -> Decimal:
42
+ """
43
+ Decide how much to stake for a claim.submit task.
44
+
45
+ MVP behavior (backwards-compatible):
46
+ - Query the current ECU fee for "claim.submit" from Governance.
47
+ - Ensure the chosen stake is at least the ECU fee.
48
+ - Never exceed the agent's wallet_balance.
49
+ - If requested_stake >= required_fee and <= wallet_balance, keep it unchanged.
50
+ (This preserves existing tests that pass explicit stakes like 5.0 or 10.0.)
51
+ - If requested_stake < required_fee but wallet can afford required_fee, bump up
52
+ to exactly required_fee.
53
+ - If wallet_balance < required_fee, return 0.0 to signal "cannot stake".
54
+ """
55
+ self._normalize_balances()
56
+ stake_amount = _agent_coerce_decimal(requested_stake, "agent_requested_stake_invalid")
57
+ if stake_amount < Decimal("0"):
58
+ raise InsufficientStakeError(
59
+ self.id,
60
+ stake=stake_amount,
61
+ required=Decimal("0"),
62
+ message="requested_stake cannot be negative",
63
+ )
64
+
65
+ # Ask consensus governance for current ECU fee.
66
+ required_fee = self.consensus.governance.get_task_fee_ecu("claim.submit")
67
+ min_required = required_fee
68
+
69
+ # Wallet cannot afford even the minimum fee → no staking.
70
+ if self.wallet_balance < min_required:
71
+ return Decimal("0")
72
+
73
+ # Preserve explicit stakes from callers if they are sane.
74
+ if stake_amount >= min_required and stake_amount <= self.wallet_balance:
75
+ return stake_amount
76
+
77
+ # If requested stake is below the required fee, bump up to exactly the fee
78
+ # as long as the wallet can pay.
79
+ if stake_amount < min_required and self.wallet_balance >= min_required:
80
+ return min_required
81
+
82
+ # If requested stake is more than wallet_balance, cap at wallet_balance.
83
+ if stake_amount > self.wallet_balance:
84
+ return self.wallet_balance
85
+
86
+ # Fallback: refuse to stake in any weird edge case.
87
+ return Decimal("0")
88
+
89
+ def perform_pow_benchmark(self) -> float:
90
+ """
91
+ Run a hardware / PoW-style benchmark and update this agent's
92
+ trust_vector["potential"].
93
+
94
+ The resulting score ∈ [0, 1] is used as a proxy for hardware capability
95
+ (and, in future phases, can inform how aggressively the agent chooses
96
+ to spend stake on ILC tasks).
97
+ """
98
+ # Preserving Phase H Logic
99
+ result = self.benchmark_engine.run(self.id)
100
+ self.trust_vector["potential"] = result["score"]
101
+ self.trust_vector["tier"] = result["tier"]
102
+ logger.info(
103
+ "agent_hardware_verified agent=%s device=%s tier=%s",
104
+ redact_agent_id_for_log(self.id),
105
+ result["device"],
106
+ result["tier"],
107
+ )
108
+ return result["score"]
109
+
110
+ def mine_thought(self, content: str, parent_id: str, stake: Decimal) -> Optional[Node]:
111
+ """
112
+ Mine a new claim node linked to parent_id, staking some amount of
113
+ the agent's wallet balance.
114
+
115
+ This MVP version now routes the requested stake through
116
+ decide_stake_for_claim(...) to ensure we at least pay the ECU fee
117
+ and never exceed wallet_balance.
118
+ """
119
+ # Decide how much we can/should actually stake given ECU fees + wallet.
120
+ chosen_stake = self.decide_stake_for_claim(stake)
121
+
122
+ # If we cannot stake anything meaningful, abort with the existing message.
123
+ if chosen_stake <= Decimal("0") or self.wallet_balance < chosen_stake:
124
+ logger.warning(
125
+ "agent_stake_insufficient_funds agent=%s wallet=%s chosen=%s",
126
+ redact_agent_id_for_log(self.id),
127
+ self.wallet_balance,
128
+ chosen_stake,
129
+ )
130
+ return None
131
+
132
+ # Construct the node exactly as before.
133
+ node = Node(
134
+ id="",
135
+ type="claim",
136
+ content=content,
137
+ agent_id=self.id,
138
+ signature=DRAFT_SIGNATURE,
139
+ )
140
+ node.id = node.compute_id()
141
+ self.graph.add_node(node)
142
+ self.graph.add_edge_by_ids(node.id, parent_id, "derives_from")
143
+
144
+ # Deduct the chosen stake and register it with the consensus engine.
145
+ self.wallet_balance -= chosen_stake
146
+ success = self.consensus.register_stake(node.id, chosen_stake)
147
+ if not success:
148
+ # In case governance rejects the stake (e.g., ECU fee changed mid-flight),
149
+ # revert the wallet deduction and abort.
150
+ self.wallet_balance += chosen_stake
151
+ logger.warning(
152
+ "agent_stake_rejected_by_consensus agent=%s chosen=%s",
153
+ redact_agent_id_for_log(self.id),
154
+ chosen_stake,
155
+ )
156
+ return None
157
+
158
+ logger.info(
159
+ "agent_claim_minted agent=%s node=%s",
160
+ redact_agent_id_for_log(self.id),
161
+ node.id[:8],
162
+ )
163
+ return node
164
+
165
+ def auto_mine_claim(self, content: str, parent_id: str) -> Optional[Node]:
166
+ """
167
+ Strategy helper: automatically choose a stake for a claim.submit task
168
+ based on:
169
+ - current wallet_balance,
170
+ - the ECU fee for "claim.submit" (via Governance),
171
+ - this agent's hardware potential (trust_vector["potential"]).
172
+
173
+ This is primarily intended for simulations and does not change the
174
+ behavior of mine_thought(...) when called directly with an explicit
175
+ stake.
176
+
177
+ MVP strategy:
178
+ - Ensure we have a hardware potential score (run perform_pow_benchmark()
179
+ if needed).
180
+ - Map potential ∈ [0, 1] to a stake fraction ∈ [f_min, f_max] of the
181
+ current wallet_balance.
182
+ - Compute candidate_stake = fraction * wallet_balance.
183
+ - Route candidate_stake through decide_stake_for_claim(...) to enforce
184
+ ECU fees and wallet caps.
185
+ - If the resulting stake is valid, call mine_thought(...) with that
186
+ chosen stake.
187
+ """
188
+ # Ensure we have a potential measurement.
189
+ self._normalize_balances()
190
+ if self.trust_vector.get("potential", 0.0) <= 0.0:
191
+ self.perform_pow_benchmark()
192
+
193
+ potential = self.trust_vector.get("potential", 0.0)
194
+
195
+ # Low potential → more cautious; high potential → more aggressive.
196
+ # Allow external config (e.g., hardware archetypes) to override.
197
+ f_min = getattr(self, "strategy_stake_fraction_min", 0.05)
198
+ f_max = getattr(self, "strategy_stake_fraction_max", 0.25)
199
+ frac = f_min + (f_max - f_min) * potential
200
+
201
+ # Derive a candidate stake from current wallet.
202
+ candidate_stake = Decimal(str(frac)) * self.wallet_balance
203
+
204
+ # Let the ECU-aware helper adjust or reject.
205
+ chosen = self.decide_stake_for_claim(candidate_stake)
206
+
207
+ if chosen <= Decimal("0") or self.wallet_balance < chosen:
208
+ logger.warning(
209
+ "agent_auto_mine_aborted agent=%s wallet=%s chosen=%s",
210
+ redact_agent_id_for_log(self.id),
211
+ self.wallet_balance,
212
+ chosen,
213
+ )
214
+ return None
215
+
216
+ # Delegate actual minting to the existing mine_thought path.
217
+ return self.mine_thought(content, parent_id, chosen)
218
+
219
+ def receive_reward(self, amount: Decimal):
220
+ """Handle earnings and auto-repayment."""
221
+ self._normalize_balances()
222
+ reward_amount = _agent_coerce_decimal(amount, "agent_reward_invalid")
223
+ if self.vault:
224
+ repayment, net = self.vault.process_repayment(self.id, reward_amount)
225
+ self.wallet_balance += net
226
+ # Credit balance is technically liability, but simplistic tracking here:
227
+ if repayment > Decimal("0"):
228
+ logger.info(
229
+ "agent_reward_repayment agent=%s repaid=%s net=%s",
230
+ redact_agent_id_for_log(self.id),
231
+ repayment,
232
+ net,
233
+ )
234
+ else:
235
+ self.wallet_balance += reward_amount
236
+
237
+ def refute_node(self, target_id: str, stake: Decimal):
238
+ self._normalize_balances()
239
+ stake_amount = _agent_coerce_decimal(stake, "agent_refutation_stake_invalid")
240
+ if self.wallet_balance < stake_amount:
241
+ logger.warning(
242
+ "agent_refute_insufficient_balance agent=%s target=%s wallet=%s stake=%s",
243
+ redact_agent_id_for_log(self.id),
244
+ target_id,
245
+ self.wallet_balance,
246
+ stake_amount,
247
+ )
248
+ return False
249
+ self.wallet_balance -= stake_amount
250
+ self.consensus.process_contradiction(target_id, stake_amount)
251
+ return True
252
+
253
+ def _normalize_balances(self) -> None:
254
+ self.wallet_balance = _agent_coerce_decimal(
255
+ self.wallet_balance, "agent_wallet_balance_invalid"
256
+ )
257
+ self.credit_balance = _agent_coerce_decimal(
258
+ self.credit_balance, "agent_credit_balance_invalid"
259
+ )
260
+
261
+
262
+ def _agent_coerce_decimal(value: object, token: str) -> Decimal:
263
+ if isinstance(value, bool):
264
+ raise ValueError(token)
265
+ if not isinstance(value, (Decimal, int, float, str)):
266
+ raise ValueError(token)
267
+ try:
268
+ amount = value if isinstance(value, Decimal) else Decimal(str(value))
269
+ except (InvalidOperation, ValueError) as exc:
270
+ raise ValueError(token) from exc
271
+ if not amount.is_finite():
272
+ raise ValueError(f"{token}_non_finite")
273
+ return amount
@@ -0,0 +1,93 @@
1
+ from dataclasses import dataclass, field, asdict
2
+ from typing import Dict, List, TypeAlias
3
+ from os import PathLike
4
+ from pathlib import Path
5
+ import csv
6
+
7
+ from ilc_core.analysis.problem_space_kpis import (
8
+ ProblemSpace,
9
+ compute_problem_space_kpis,
10
+ )
11
+
12
+
13
+ AgentDescriptorRow: TypeAlias = Dict[str, str | float]
14
+
15
+ @dataclass
16
+ class AgentDescriptor:
17
+ """
18
+ Lightweight descriptor of an agent's behavior across problem spaces.
19
+ """
20
+ agent_id: str
21
+ problem_space_counts: Dict[str, float] = field(default_factory=dict)
22
+ total_tasks: float = 0.0
23
+ dominant_problem_space: str = "OTHER"
24
+
25
+ def as_dict(self) -> AgentDescriptorRow:
26
+ data = {
27
+ "agent_id": self.agent_id,
28
+ "total_tasks": self.total_tasks,
29
+ "dominant_problem_space": self.dominant_problem_space,
30
+ }
31
+ # Flatten counts with a prefix for clarity.
32
+ for space, count in self.problem_space_counts.items():
33
+ data[f"space_{space}"] = count
34
+ return data
35
+
36
+
37
+ def build_agent_descriptors_from_task_rows(
38
+ task_rows: List[Dict[str, str]],
39
+ ) -> Dict[str, AgentDescriptor]:
40
+ """
41
+ Build AgentDescriptors from in-memory task rows.
42
+ """
43
+ space_kpis = compute_problem_space_kpis(task_rows)
44
+ descriptors: Dict[str, AgentDescriptor] = {}
45
+
46
+ for agent_id, stats in space_kpis.items():
47
+ total_tasks = stats.get("total_tasks", 0.0)
48
+
49
+ # Extract per-space counts (excluding total_tasks).
50
+ problem_space_counts = {
51
+ k: v for k, v in stats.items() if k != "total_tasks"
52
+ }
53
+
54
+ # Identify dominant problem space (ties broken arbitrarily by max).
55
+ if problem_space_counts:
56
+ dominant_space = max(problem_space_counts.items(), key=lambda kv: kv[1])[0]
57
+ else:
58
+ dominant_space = "OTHER"
59
+
60
+ descriptors[agent_id] = AgentDescriptor(
61
+ agent_id=agent_id,
62
+ problem_space_counts=problem_space_counts,
63
+ total_tasks=total_tasks,
64
+ dominant_problem_space=dominant_space,
65
+ )
66
+
67
+ return descriptors
68
+
69
+
70
+ def load_tasks_csv(path: PathLike) -> List[Dict[str, str]]:
71
+ """
72
+ Convenience loader for tasks.csv to feed into descriptor builders.
73
+ """
74
+ p = Path(path)
75
+ rows: List[Dict[str, str]] = []
76
+ if not p.exists():
77
+ return rows
78
+
79
+ with p.open("r", encoding="utf-8", newline="") as f:
80
+ reader = csv.DictReader(f)
81
+ for row in reader:
82
+ rows.append(dict(row))
83
+ return rows
84
+
85
+
86
+ def build_agent_descriptors_from_tasks_csv(
87
+ tasks_csv_path: PathLike,
88
+ ) -> Dict[str, AgentDescriptor]:
89
+ """
90
+ High-level helper: load tasks.csv and build AgentDescriptors.
91
+ """
92
+ rows = load_tasks_csv(tasks_csv_path)
93
+ return build_agent_descriptors_from_task_rows(rows)
@@ -0,0 +1,110 @@
1
+ from os import PathLike
2
+ from pathlib import Path
3
+ from typing import Dict, List, TypeAlias
4
+ import csv
5
+ import json
6
+
7
+ from ilc_core.analysis.agent_profiles import AgentProfile
8
+
9
+ ScalarValue: TypeAlias = str | int | float | bool | None
10
+ AgentDossierRow: TypeAlias = Dict[str, ScalarValue]
11
+ AgentDossierRows: TypeAlias = List[AgentDossierRow]
12
+
13
+
14
+ def flatten_profile_for_csv(profile: AgentProfile) -> AgentDossierRow:
15
+ """
16
+ Flatten an AgentProfile into a 1-level dict suitable for CSV export.
17
+
18
+ Strategy:
19
+ - Use profile.as_dict() as the base.
20
+ - Keep simple scalar fields (agent_id, econ_*, claim_*, influence_*, etc.).
21
+ - For nested dicts (competency, stress_response, light_cone), extract
22
+ a small number of top-level summary scalars, and also include a
23
+ *_json column with the full nested data as JSON.
24
+ """
25
+ base = profile.as_dict()
26
+ row: AgentDossierRow = {}
27
+
28
+ # Always include agent_id
29
+ row["agent_id"] = base.get("agent_id")
30
+
31
+ # Pass through flat econ/claim/influence keys
32
+ for k, v in base.items():
33
+ if k == "agent_id":
34
+ continue
35
+ if k in ("competency", "stress_response", "light_cone"):
36
+ continue
37
+ # Only include scalars; skip any unexpected nested structures
38
+ if not isinstance(v, (dict, list)):
39
+ row[k] = v
40
+
41
+ # Competency summary
42
+ comp = base.get("competency") or {}
43
+ if comp:
44
+ global_c = comp.get("global") or {}
45
+ row["competency_total_tasks"] = global_c.get("total_tasks", 0)
46
+ row["competency_avg_success_rate"] = global_c.get("avg_success_rate", 0.0)
47
+ row["competency_json"] = json.dumps(comp)
48
+
49
+ # Stress-response summary
50
+ stress = base.get("stress_response") or {}
51
+ if stress:
52
+ # Expect shape like {"by_stress_bucket": {...}, "preference": "..."} for example.
53
+ row["stress_preference"] = stress.get("preference", "")
54
+ row["stress_response_json"] = json.dumps(stress)
55
+
56
+ # Light-cone summary
57
+ lc = base.get("light_cone") or {}
58
+ if lc:
59
+ row["light_cone_score"] = lc.get("light_cone_score", 0.0)
60
+ row["light_cone_reach"] = lc.get("reach_score", 0.0)
61
+ row["light_cone_horizon"] = lc.get("horizon_score", 0.0)
62
+ row["light_cone_domain_span"] = lc.get("domain_span", 0)
63
+ row["light_cone_json"] = json.dumps(lc)
64
+
65
+ return row
66
+
67
+ def export_agent_dossiers_to_csv(
68
+ profiles: Dict[str, AgentProfile],
69
+ path: PathLike,
70
+ ) -> None:
71
+ """
72
+ Export all AgentProfiles to a CSV file.
73
+
74
+ - Unions all keys across flattened rows to form the header.
75
+ - Writes one row per agent.
76
+ """
77
+ p = Path(path)
78
+ p.parent.mkdir(parents=True, exist_ok=True)
79
+
80
+ rows: AgentDossierRows = [flatten_profile_for_csv(pf) for pf in profiles.values()]
81
+
82
+ # Handle empty gracefully
83
+ if not rows:
84
+ fieldnames = ["agent_id"]
85
+ else:
86
+ # Union of all keys
87
+ keys = set()
88
+ for r in rows:
89
+ keys.update(r.keys())
90
+ fieldnames = sorted(keys)
91
+
92
+ with p.open("w", newline="", encoding="utf-8") as f:
93
+ writer = csv.DictWriter(f, fieldnames=fieldnames)
94
+ writer.writeheader()
95
+ for r in rows:
96
+ writer.writerow(r)
97
+
98
+ def export_agent_dossiers_to_json(
99
+ profiles: Dict[str, AgentProfile],
100
+ path: PathLike,
101
+ ) -> None:
102
+ """
103
+ Export all AgentProfiles to a JSON file as a list of flattened rows.
104
+ """
105
+ p = Path(path)
106
+ p.parent.mkdir(parents=True, exist_ok=True)
107
+
108
+ rows = [flatten_profile_for_csv(pf) for pf in profiles.values()]
109
+ with p.open("w", encoding="utf-8") as f:
110
+ json.dump(rows, f, indent=2)
@@ -0,0 +1,214 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Dict, List, Optional, TypedDict, TypeAlias
3
+ from ilc_core.analysis.claim_scores import ClaimInfluenceRow
4
+ from ilc_core.analysis.competency_kpis import AgentCompetencySummaryMap
5
+ from ilc_core.analysis.light_cone_kpis import AgentLightConeRow
6
+ from os import PathLike
7
+
8
+ from ilc_core.analysis.econ_kpis import (
9
+ load_tasks_csv,
10
+ load_epochs_csv,
11
+ load_claims_csv,
12
+ compute_agent_econ_kpis,
13
+ compute_claim_kpis,
14
+ )
15
+
16
+
17
+ class AgentInfluenceRow(TypedDict):
18
+ total_influence: float
19
+ num_influenced_claims: float
20
+ total_net_support: float
21
+ avg_influence: float
22
+
23
+
24
+ AgentInfluenceMap: TypeAlias = Dict[str, AgentInfluenceRow]
25
+ AgentLightConeMap: TypeAlias = Dict[str, AgentLightConeRow]
26
+ ProfileNestedMap: TypeAlias = Dict[str, object]
27
+ ProfileValue: TypeAlias = str | int | float | bool | None | ProfileNestedMap
28
+ AgentProfileDict: TypeAlias = Dict[str, ProfileValue]
29
+
30
+ @dataclass
31
+ class AgentProfile:
32
+ agent_id: str
33
+ node_id: Optional[str] = None
34
+
35
+ # Econ KPIs (from compute_basic_kpis)
36
+ econ: Dict[str, float] = field(default_factory=dict)
37
+
38
+ # Claim KPIs (from compute_claim_kpis)
39
+ claims: Dict[str, float] = field(default_factory=dict)
40
+
41
+ # Influence KPIs (from compute_agent_influence_kpis)
42
+ influence: Dict[str, float] = field(default_factory=dict)
43
+
44
+ # Competency (from attach_competency_to_profiles)
45
+ competency: ProfileNestedMap = field(default_factory=dict)
46
+
47
+ # Stress response patterns (from stress_response_kpis)
48
+ stress_response: ProfileNestedMap = field(default_factory=dict)
49
+
50
+ # Light Cone KPIs (from light_cone_kpis)
51
+ light_cone: ProfileNestedMap = field(default_factory=dict)
52
+
53
+ def as_dict(self) -> AgentProfileDict:
54
+ """
55
+ Flatten into a single dict for printing/JSON export.
56
+ Namespaces econ_* and claim_* keys to avoid collisions.
57
+ """
58
+ data: AgentProfileDict = {"agent_id": self.agent_id}
59
+ if self.node_id is not None:
60
+ data["node_id"] = self.node_id
61
+
62
+ for k, v in self.econ.items():
63
+ data[f"econ_{k}"] = v
64
+ for k, v in self.claims.items():
65
+ data[f"claim_{k}"] = v
66
+ for k, v in self.influence.items():
67
+ data[f"influence_{k}"] = v
68
+ if self.competency:
69
+ data["competency"] = self.competency
70
+ if self.stress_response:
71
+ data["stress_response"] = self.stress_response
72
+ if self.light_cone:
73
+ data["light_cone"] = self.light_cone
74
+ return data
75
+
76
+ def build_agent_profiles(
77
+ tasks_csv_path: PathLike,
78
+ epochs_csv_path: PathLike,
79
+ claims_csv_path: PathLike,
80
+ ) -> Dict[str, AgentProfile]:
81
+ """
82
+ Load CSVs, compute econ + claim KPIs, and join them into AgentProfile objects.
83
+
84
+ Returns:
85
+ {agent_id: AgentProfile}
86
+ """
87
+ tasks = load_tasks_csv(tasks_csv_path)
88
+ epochs = load_epochs_csv(epochs_csv_path)
89
+ claims = load_claims_csv(claims_csv_path)
90
+
91
+ econ_kpis = compute_agent_econ_kpis(tasks)
92
+ claim_kpis = compute_claim_kpis(claims)
93
+
94
+ profiles: Dict[str, AgentProfile] = {}
95
+
96
+ # Seed profiles from econ
97
+ for agent_id, econ_stats in econ_kpis.items():
98
+ profiles.setdefault(agent_id, AgentProfile(agent_id=agent_id)).econ = dict(econ_stats)
99
+
100
+ # Merge claim stats
101
+ for agent_id, claim_stats in claim_kpis.items():
102
+ profile = profiles.setdefault(agent_id, AgentProfile(agent_id=agent_id))
103
+ profile.claims = dict(claim_stats)
104
+
105
+ return profiles
106
+
107
+
108
+ def attach_competency_to_profiles(
109
+ profiles: Dict[str, AgentProfile],
110
+ competency_summary: AgentCompetencySummaryMap,
111
+ ) -> Dict[str, AgentProfile]:
112
+ """
113
+ Attach competency summaries to existing AgentProfile objects.
114
+ If an agent_id in competency_summary is not present in profiles,
115
+ create a new profile shell for it.
116
+ """
117
+ for agent_id, comp in competency_summary.items():
118
+ profile = profiles.get(agent_id)
119
+ if profile is None:
120
+ profile = AgentProfile(agent_id=agent_id)
121
+ profiles[agent_id] = profile
122
+ profile.competency = comp
123
+ return profiles
124
+
125
+ def compute_agent_influence_kpis(
126
+ claim_influence_rows: List[ClaimInfluenceRow],
127
+ claims_csv_path: PathLike,
128
+ ) -> AgentInfluenceMap:
129
+ """
130
+ Aggregate claim influence metrics per agent.
131
+
132
+ Uses claims.csv to map claim_id -> agent_id, then aggregates:
133
+
134
+ total_influence
135
+ avg_influence
136
+ num_influenced_claims
137
+ total_net_support
138
+
139
+ Returns:
140
+ {agent_id: {metric_name: value}}
141
+ """
142
+ claim_rows = load_claims_csv(claims_csv_path)
143
+ claim_to_agent: Dict[str, str] = {}
144
+ for row in claim_rows:
145
+ cid = row.get("id")
146
+ aid = row.get("agent_id") or "unknown"
147
+ if cid:
148
+ claim_to_agent[cid] = aid
149
+
150
+ per_agent: AgentInfluenceMap = {}
151
+
152
+ for row in claim_influence_rows:
153
+ claim_id = row.claim_id
154
+ agent_id = claim_to_agent.get(claim_id, "unknown")
155
+
156
+ stats = per_agent.setdefault(
157
+ agent_id,
158
+ {
159
+ "total_influence": 0.0,
160
+ "num_influenced_claims": 0.0,
161
+ "total_net_support": 0.0,
162
+ },
163
+ )
164
+
165
+ stats["total_influence"] += row.influence_score
166
+ stats["num_influenced_claims"] += 1.0
167
+ stats["total_net_support"] += float(row.net_support)
168
+
169
+ # Compute derived metrics like avg_influence
170
+ for stats in per_agent.values():
171
+ n = stats["num_influenced_claims"]
172
+ stats["avg_influence"] = stats["total_influence"] / n if n > 0 else 0.0
173
+
174
+ return per_agent
175
+
176
+ def attach_influence_to_profiles(
177
+ profiles: Dict[str, AgentProfile],
178
+ claim_influence_rows: List[ClaimInfluenceRow],
179
+ claims_csv_path: PathLike,
180
+ ) -> None:
181
+ """
182
+ Compute per-agent influence KPIs and merge them into existing profiles in-place.
183
+ """
184
+ influence_kpis = compute_agent_influence_kpis(
185
+ claim_influence_rows,
186
+ claims_csv_path,
187
+ )
188
+
189
+ for agent_id, stats in influence_kpis.items():
190
+ profile = profiles.setdefault(agent_id, AgentProfile(agent_id=agent_id))
191
+ profile.influence = dict(stats)
192
+
193
+ def attach_light_cone_to_profiles(
194
+ profiles: Dict[str, AgentProfile],
195
+ light_cone_rows: AgentLightConeMap,
196
+ ) -> Dict[str, AgentProfile]:
197
+ """
198
+ Attach light-cone metrics to existing AgentProfile objects.
199
+ If an agent_id appears only in light_cone_rows, create a shell profile.
200
+ """
201
+ # Note: light_cone_rows is expected to be Dict[str, AgentLightConeRow]
202
+ # We iterate and access attributes.
203
+ for agent_id, row in light_cone_rows.items():
204
+ profile = profiles.get(agent_id)
205
+ if profile is None:
206
+ profile = AgentProfile(agent_id=agent_id)
207
+ profiles[agent_id] = profile
208
+ profile.light_cone = {
209
+ "reach_score": row.reach_score,
210
+ "horizon_score": row.horizon_score,
211
+ "domain_span": row.domain_span,
212
+ "light_cone_score": row.light_cone_score,
213
+ }
214
+ return profiles