Add crypto protocol cohort V0.3

This commit is contained in:
Daniel Maddern 2026-08-16 21:00:15 +07:00
parent 783ad66f4e
commit d4ccc2e6f0
6 changed files with 320 additions and 86 deletions

View file

@ -58,19 +58,22 @@ from research.searxng import SearxngSearchClient, WebPageFetcher
CRYPTO_TERRITORIES = [ CRYPTO_TERRITORIES = [
"DECENTRALIZED_AI_COMPUTE", "MACHINE_ECONOMIC_IDENTITY",
"AGENT_TO_AGENT_PAYMENTS", "DECENTRALIZED_SECURITY_MARKETS",
"PROOF_ATTESTATION_MARKETS", "VERIFIABLE_AI_INFERENCE",
"DECENTRALIZED_DATA_MARKETS", "PROOF_MARKETS",
"SECURITY_STAKING_PROTOCOLS", "DECENTRALIZED_COMPUTE",
"MACHINE_REPUTATION", "DATA_PROVENANCE_NETWORKS",
"AUTONOMOUS_API_MARKETPLACES", "MACHINE_TO_MACHINE_SETTLEMENT",
"DEPIN_COORDINATION", "AUTONOMOUS_ESCROW",
"ONCHAIN_AGENT_COMMERCE", "DECENTRALIZED_INSURANCE_RISK_POOLS",
"DECENTRALIZED_MODEL_SERVICES", "RESOURCE_RIGHTS_MARKETS",
"ONCHAIN_CREDENTIALS", "DECENTRALIZED_BANDWIDTH_STORAGE",
"PROTOCOLIZED_ESCROW", "AI_MODEL_CONTRIBUTION_NETWORKS",
"CRYPTO_NATIVE_INTELLIGENCE", "SOFTWARE_SECURITY_BONDS",
"AUTONOMOUS_AGENT_COORDINATION",
"DECENTRALIZED_ORACLE_ATTESTATION",
"CRYPTO_NATIVE_CREATOR_DATA_RIGHTS",
"OPEN_CRYPTO_CATEGORY", "OPEN_CRYPTO_CATEGORY",
] ]
@ -108,7 +111,10 @@ CRYPTO_SCORE_DIMENSIONS = [
"PRE_TOKEN_MONETIZATION_POTENTIAL", "PRE_TOKEN_MONETIZATION_POTENTIAL",
] ]
CRYPTO_RESEARCH_CATEGORIES = ["USER_PAIN", "EXISTING_PROTOCOLS", "FAILED_PRECEDENTS", "PAYMENT_MODELS", "TOKEN_MODELS", "STAKING_MODELS", "SLASHING_PRECEDENTS", "PROVIDER_ECONOMICS", "NETWORK_BOOTSTRAP", "ONCHAIN_ALTERNATIVES", "OFFCHAIN_ALTERNATIVES", "SECURITY_INCIDENTS", "LEGAL_REGULATORY", "TOKEN_LAUNCH_PRECEDENTS", "PRE_TOKEN_MONETIZATION_PRECEDENTS"] CRYPTO_RESEARCH_CATEGORIES = ["USER_PAIN", "EXISTING_PROTOCOLS", "FAILED_PRECEDENTS", "ONCHAIN_ALTERNATIVES", "OFFCHAIN_ALTERNATIVES", "TOKEN_MODELS", "COLLATERAL_MODELS", "STAKING_SLASHING_PRECEDENTS", "PROVIDER_ECONOMICS", "NETWORK_BOOTSTRAP", "SECURITY_FAILURES", "LEGAL_REGULATORY", "PRE_TOKEN_MONETIZATION", "TOKEN_LAUNCH_PRECEDENTS"]
PRIMARY_SOURCE_DOMAINS = ["ethereum.org", "eips.ethereum.org", "github.com", "arxiv.org", "sec.gov", "cftc.gov", "immunefi.com"]
SOURCE_REJECTION_REASONS = {"IRRELEVANT": "SOURCE_REJECT_IRRELEVANT", "WRONG_CATEGORY": "SOURCE_REJECT_WRONG_CATEGORY", "LOW_AUTHORITY": "SOURCE_REJECT_LOW_AUTHORITY", "SEARCH_NOISE": "SOURCE_REJECT_SEARCH_NOISE", "UNPARSABLE": "SOURCE_REJECT_UNPARSABLE", "DUPLICATE": "SOURCE_REJECT_DUPLICATE"}
CRYPTO_SCENARIOS = [ CRYPTO_SCENARIOS = [
"oracle failure", "oracle failure",
@ -142,37 +148,56 @@ class CryptoVentureService:
objective="Generate crypto-native protocol ventures where onchain execution and a native token are genuinely necessary for real usage, not speculation.", objective="Generate crypto-native protocol ventures where onchain execution and a native token are genuinely necessary for real usage, not speculation.",
constraints={"venture_track": VentureTrack.CRYPTO_PROTOCOL, "no_token_sale": True, "no_fundraising": True, "no_mainnet_issuance": True, "no_us_targeted_activity": True, "testnet_or_local_only": True, "no_real_spend": True, "no_user_contact": True}, constraints={"venture_track": VentureTrack.CRYPTO_PROTOCOL, "no_token_sale": True, "no_fundraising": True, "no_mainnet_issuance": True, "no_us_targeted_activity": True, "testnet_or_local_only": True, "no_real_spend": True, "no_user_contact": True},
optimization_targets=["token necessity", "onchain necessity", "real usage demand", "value accrual", "sustainable tokenomics", "autonomous operability", "regulatory manageability", "security model quality"], optimization_targets=["token necessity", "onchain necessity", "real usage demand", "value accrual", "sustainable tokenomics", "autonomous operability", "regulatory manageability", "security model quality"],
metadata={"milestone": "CRYPTO_PROTOCOL_VENTURE_COHORT_V0.2", "venture_track": VentureTrack.CRYPTO_PROTOCOL}, metadata={"milestone": "CRYPTO_PROTOCOL_VENTURE_COHORT_V0.3", "venture_track": VentureTrack.CRYPTO_PROTOCOL},
) )
self._artifact(None, mandate, "CRYPTO_VENTURE_MANDATE", "Crypto Protocol Venture V0.1 Mandate", mandate.constraints, "Crypto protocol cohort mandate. No token sale, fundraising, or mainnet issuance.", "crypto_venture") self._artifact(None, mandate, "CRYPTO_VENTURE_MANDATE", "Crypto Protocol Venture V0.3 Mandate", mandate.constraints, "Crypto protocol cohort mandate. Sol-native problem-first ideation. No token sale, fundraising, or mainnet issuance.", "crypto_venture")
return mandate return mandate
def prepare_cohort(self, *, size: int, graph_run=None, concurrency: int = 2) -> VentureCohort: def prepare_cohort(self, *, size: int, graph_run=None, concurrency: int = 2) -> VentureCohort:
mandate = self.create_crypto_mandate() mandate = self.create_crypto_mandate()
cohort_id = f"CPV02-{time.strftime('%Y%m%d%H%M%S')}-{uuid.uuid4().hex[:8]}" cohort_id = f"CPV03-{time.strftime('%Y%m%d%H%M%S')}-{uuid.uuid4().hex[:8]}"
return VentureCohort.objects.create(cohort_id=cohort_id, mandate=mandate, cohort_size=size, graph_run=graph_run, concurrency=concurrency, status="PREPARING_CRYPTO", graph_versions={"cohort": "crypto_venture_cohort v2"}, research_policy={"source_linked_evidence_required": True, "research_insufficient_if_no_sources": True, "crypto_categories": CRYPTO_RESEARCH_CATEGORIES}, scoring_policy={"dimensions": CRYPTO_SCORE_DIMENSIONS, "strict_token_filter": True}, metadata={"milestone": "CRYPTO_PROTOCOL_VENTURE_COHORT_V0.2", "venture_track": VentureTrack.CRYPTO_PROTOCOL, "real_spend": 0, "real_customer_outreach": False, "token_sale": False, "fundraising": False, "mainnet_issuance": False}) return VentureCohort.objects.create(cohort_id=cohort_id, mandate=mandate, cohort_size=size, graph_run=graph_run, concurrency=concurrency, status="PREPARING_CRYPTO", graph_versions={"cohort": "crypto_venture_cohort v3"}, research_policy={"source_linked_evidence_required": True, "research_insufficient_if_no_sources": True, "primary_source_first": True, "minimum_category_evidence": "one strong source or multiple independent medium sources", "crypto_categories": CRYPTO_RESEARCH_CATEGORIES}, scoring_policy={"dimensions": CRYPTO_SCORE_DIMENSIONS, "strict_token_filter": True, "sol_problem_first_generation": True}, metadata={"milestone": "CRYPTO_PROTOCOL_VENTURE_COHORT_V0.3", "venture_track": VentureTrack.CRYPTO_PROTOCOL, "real_spend": 0, "real_customer_outreach": False, "token_sale": False, "fundraising": False, "mainnet_issuance": False, "v02_baseline": {"raw": 30, "crypto_survivors": 2, "autonomous_survivors": 0, "finalists": 0}})
def generate_protocols(self, cohort: VentureCohort) -> list[CompanyProposal]: def generate_protocols(self, cohort: VentureCohort) -> list[CompanyProposal]:
accepted = [] accepted = []
attempts = 0 attempts = 0
model_usage = Counter(cohort.metrics.get("model_usage", {}))
for index in range(cohort.cohort_size): for index in range(cohort.cohort_size):
territory = CRYPTO_TERRITORIES[index % len(CRYPTO_TERRITORIES)] territory = CRYPTO_TERRITORIES[index % len(CRYPTO_TERRITORIES)]
payload, source = self._protocol_payload(index, territory) payload, source, usage = self._protocol_payload(index, territory)
model_usage.update(usage)
attempts += 1 attempts += 1
proposal = self._create_proposal(cohort.mandate, payload, source, territory) proposal = self._create_proposal(cohort.mandate, payload, source, territory)
VentureCohortMember.objects.create(cohort=cohort, proposal=proposal, metadata={"territory": territory}) VentureCohortMember.objects.create(cohort=cohort, proposal=proposal, metadata={"territory": territory})
accepted.append(proposal) accepted.append(proposal)
cohort.metrics = {**cohort.metrics, "raw_target": cohort.cohort_size, "raw_generated": len(accepted), "requested_protocol_count": cohort.cohort_size, "generation_attempts": attempts, "accepted_protocols": len(accepted), "duplicate_rejections": 0, "token_necessity_rejections": 0, "generation_sources": sorted({p.metadata.get("generation_source", "unknown") for p in accepted})} cohort.metrics = {**cohort.metrics, "raw_target": cohort.cohort_size, "raw_generated": len(accepted), "raw_sol_theses": len([p for p in accepted if p.metadata.get("generation_source") == "sol"]), "requested_protocol_count": cohort.cohort_size, "generation_attempts": attempts, "accepted_protocols": len(accepted), "duplicate_rejections": 0, "token_necessity_rejections": 0, "generation_sources": sorted({p.metadata.get("generation_source", "unknown") for p in accepted}), "model_usage": dict(model_usage), "sol_generation_requests": model_usage.get("sol_generation_requests", 0), "fallback_count": model_usage.get("fallback_count", 0)}
cohort.status = "PROTOCOLS_GENERATED" cohort.status = "PROTOCOLS_GENERATED"
cohort.save(update_fields=["metrics", "status", "updated_at"]) cohort.save(update_fields=["metrics", "status", "updated_at"])
return accepted return accepted
def onchain_necessity_gate(self, cohort: VentureCohort) -> list[OnchainNecessityAssessment]:
assessments = [self.onchain_assessment(member.proposal) for member in cohort.members.select_related("proposal")]
removed = 0
for member in list(cohort.members.select_related("proposal")):
proposal = member.proposal
assessment = getattr(proposal, "onchain_assessment", None) or self.onchain_assessment(proposal)
if assessment.onchain_necessity_score < 70:
VentureGenerationRejection.objects.create(cohort=cohort, slot_index=member.rank or 0, attempt=1, decision=NoveltyGateDecision.REGENERATE_HARD_EXCLUSION, reason="removed by onchain necessity gate", candidate={"title": proposal.title, "onchain_necessity_score": assessment.onchain_necessity_score}, similarity_score=0.0)
member.delete()
proposal.status = CompanyProposalStatus.REJECTED
proposal.metadata = {**proposal.metadata, "routed_to_saas": True, "onchain_rejected": True}
proposal.save(update_fields=["status", "metadata", "updated_at"])
removed += 1
cohort.metrics = {**cohort.metrics, "onchain_rejected": cohort.metrics.get("onchain_rejected", 0) + removed, "crypto_survivors_after_onchain": cohort.members.count()}
cohort.save(update_fields=["metrics", "updated_at"])
return assessments
def novelty_gate(self, cohort: VentureCohort) -> dict[str, int]: def novelty_gate(self, cohort: VentureCohort) -> dict[str, int]:
removed = 0 removed = 0
hard_excluded = 0 hard_excluded = 0
duplicate = 0 duplicate = 0
seen: dict[str, CompanyProposal] = {} seen: dict[str, CompanyProposal] = {}
hard_terms = ["meme", "generic dex", "generic l1", "generic l2", "nft collection", "yield farm", "ponzi", "copycat launchpad", "speculative asset"] hard_terms = ["meme", "generic dex", "generic l1", "generic l2", "nft collection", "yield farm", "ponzi", "copycat launchpad", "speculative asset", "api reliability slashing pool", "verimesh", "verichain"]
for member in list(cohort.members.select_related("proposal").order_by("created_at")): for member in list(cohort.members.select_related("proposal").order_by("created_at")):
proposal = member.proposal proposal = member.proposal
text = self._proposal_text(proposal) text = self._proposal_text(proposal)
@ -201,7 +226,7 @@ class CryptoVentureService:
def regenerate_rejected_slots(self, cohort: VentureCohort, *, reason: str = "novelty_or_token_gate") -> list[CompanyProposal]: def regenerate_rejected_slots(self, cohort: VentureCohort, *, reason: str = "novelty_or_token_gate") -> list[CompanyProposal]:
created: list[CompanyProposal] = [] created: list[CompanyProposal] = []
cohort.metrics = {**cohort.metrics, "regeneration_attempts": cohort.metrics.get("regeneration_attempts", 0), "regenerated_protocols": cohort.metrics.get("regenerated_protocols", 0), "crypto_survivors": cohort.members.count(), "unfilled_slots_after_regeneration": max(0, cohort.cohort_size - cohort.members.count()), "regeneration_policy": "V0.2 uses total raw generation budget; weak token ideas are not force-filled."} cohort.metrics = {**cohort.metrics, "regeneration_attempts": cohort.metrics.get("regeneration_attempts", 0), "regenerated_protocols": cohort.metrics.get("regenerated_protocols", 0), "crypto_survivors": cohort.members.count(), "unfilled_slots_after_regeneration": max(0, cohort.cohort_size - cohort.members.count()), "regeneration_policy": "V0.3 uses total raw Sol generation budget; offchain or weak token ideas are not force-filled."}
cohort.save(update_fields=["metrics", "updated_at"]) cohort.save(update_fields=["metrics", "updated_at"])
return created return created
@ -255,10 +280,18 @@ class CryptoVentureService:
all_sources = [] all_sources = []
query_results = [] query_results = []
engine_health = Counter() engine_health = Counter()
circuit_breaker = Counter()
avoided = 0
primary_sources = self.primary_source_seed_sources(proposal, query_plan)
all_sources.extend(primary_sources)
if client is not None: if client is not None:
per_query_limit = 4 if depth == "deep" else 2 per_query_limit = 4 if depth == "deep" else 2
for category, queries in query_plan.items(): for category, queries in query_plan.items():
for query in queries: for query in queries:
if sum(circuit_breaker.values()) >= (8 if depth == "deep" else 4):
avoided += 1
query_results.append({"category": category, "query": query, "status": "AVOIDED_DUE_TO_CIRCUIT_BREAKER", "result_count": 0})
continue
try: try:
payload = client.search_payload(query) payload = client.search_payload(query)
except Exception as exc: except Exception as exc:
@ -266,13 +299,16 @@ class CryptoVentureService:
continue continue
unresponsive = payload.get("unresponsive_engines", []) if isinstance(payload, dict) else [] unresponsive = payload.get("unresponsive_engines", []) if isinstance(payload, dict) else []
for engine, reason in unresponsive: for engine, reason in unresponsive:
engine_health[self._engine_status(str(reason))] += 1 status = self._engine_status(str(reason))
engine_health[status] += 1
if status in {"CAPTCHA", "RATE_LIMITED", "ACCESS_DENIED"}:
circuit_breaker[status] += 1
raw_results = payload.get("results", []) if isinstance(payload, dict) else [] raw_results = payload.get("results", []) if isinstance(payload, dict) else []
query_results.append({"category": category, "query": query, "status": "SEARCH_WORKED", "result_count": len(raw_results), "unresponsive_engines": unresponsive}) query_results.append({"category": category, "query": query, "status": "SEARCH_WORKED", "result_count": len(raw_results), "unresponsive_engines": unresponsive})
for item in raw_results[:per_query_limit]: for item in raw_results[:per_query_limit]:
if isinstance(item, dict) and item.get("url"): if isinstance(item, dict) and item.get("url"):
all_sources.append({"type": "public_web", "source": "searxng", "url": str(item["url"]), "title": str(item.get("title", "")), "category": category, "summary": str(item.get("content", item.get("snippet", ""))), "fallback_evidence": False}) all_sources.append({"type": "public_web", "source": "searxng", "url": str(item["url"]), "title": str(item.get("title", "")), "category": category, "summary": str(item.get("content", item.get("snippet", ""))), "fallback_evidence": False})
pages = fetcher.fetch_many(all_sources, max_pages=10 if depth == "deep" else 5) if all_sources else [] pages = fetcher.fetch_many(all_sources, max_pages=14 if depth == "deep" else 7) if all_sources else []
page_by_url = {page["url"]: page for page in pages} page_by_url = {page["url"]: page for page in pages}
accepted = [] accepted = []
rejected = [] rejected = []
@ -280,21 +316,24 @@ class CryptoVentureService:
for source in all_sources: for source in all_sources:
url = source.get("url") url = source.get("url")
if not url or url in seen: if not url or url in seen:
if url in seen:
rejected.append({**source, "quality": "reject", "rejection_reason": SOURCE_REJECTION_REASONS["DUPLICATE"], "relevance_score": 0})
continue continue
seen.add(url) seen.add(url)
enriched = {**source, **({"content": page_by_url[url].get("text", "")} if url in page_by_url else {})} enriched = {**source, **({"content": page_by_url[url].get("text", "")} if url in page_by_url else {})}
quality = self.crypto_source_quality(proposal, enriched) quality = self.crypto_source_quality(proposal, enriched)
if quality["accepted"]: if quality["accepted"]:
accepted.append({**enriched, "quality": quality["quality"], "relevance_score": quality["score"], "acceptance_reason": quality["reason"]}) accepted.append({**enriched, "quality": quality["quality"], "source_tier": quality.get("source_tier", source.get("source_tier", "TIER_C")), "relevance_score": quality["score"], "acceptance_reason": quality["reason"]})
else: else:
rejected.append({**enriched, "quality": "weak", "rejection_reason": quality["reason"], "relevance_score": quality["score"]}) rejected.append({**enriched, "quality": "weak", "rejection_reason": quality["reason"], "relevance_score": quality["score"]})
covered = {source["category"] for source in accepted if source.get("quality") == "strong"} category_sources = {category: [source for source in accepted if source.get("category") == category] for category in CRYPTO_RESEARCH_CATEGORIES}
coverage = {category: category in covered for category in CRYPTO_RESEARCH_CATEGORIES} coverage_confidence = {category: self.category_coverage_confidence(sources) for category, sources in category_sources.items()}
coverage = {category: confidence in {"MEDIUM", "HIGH"} for category, confidence in coverage_confidence.items()}
coverage_ratio = round(sum(1 for value in coverage.values() if value) / len(CRYPTO_RESEARCH_CATEGORIES), 2) coverage_ratio = round(sum(1 for value in coverage.values() if value) / len(CRYPTO_RESEARCH_CATEGORIES), 2)
acceptance_rate = round(len(accepted) / max(1, len(accepted) + len(rejected)), 2) acceptance_rate = round(len(accepted) / max(1, len(accepted) + len(rejected)), 2)
search_infrastructure_failed = client is None or (not all_sources and any(item.get("status") == "SEARCH_INFRASTRUCTURE_FAILED" for item in query_results)) search_infrastructure_failed = client is None or (not all_sources and any(item.get("status") == "SEARCH_INFRASTRUCTURE_FAILED" for item in query_results))
evidence_insufficient = len(accepted) == 0 or coverage_ratio < (0.7 if depth == "deep" else 0.25) evidence_insufficient = len(accepted) == 0 or coverage_ratio < (0.7 if depth == "deep" else 0.25)
research = {"query_plan": query_plan, "query_results": query_results, "sources": accepted, "source_rejections": rejected, "source_count": len(accepted), "source_rejection_count": len(rejected), "page_fetch_count": len(pages), "search_result_count": len(all_sources), "coverage": coverage, "coverage_ratio": coverage_ratio, "category_coverage_confidence": {category: "HIGH" if coverage[category] else "LOW" for category in CRYPTO_RESEARCH_CATEGORIES}, "source_provider_health": "SEARCH_INFRASTRUCTURE_FAILED" if search_infrastructure_failed else "SEARCH_WORKED", "search_engine_health": dict(engine_health), "source_acceptance_rate": acceptance_rate, "research_status": "SEARCH_INFRASTRUCTURE_FAILED" if search_infrastructure_failed else "EVIDENCE_INSUFFICIENT" if evidence_insufficient else "SOURCE_LINKED", "unverified_categories": [category for category, ok in coverage.items() if not ok]} research = {"query_plan": query_plan, "query_results": query_results, "sources": accepted, "source_rejections": rejected, "source_count": len(accepted), "source_rejection_count": len(rejected), "page_fetch_count": len(pages), "search_result_count": len(all_sources), "primary_source_count": len([s for s in accepted if s.get("source_tier") == "TIER_A"]), "coverage": coverage, "coverage_ratio": coverage_ratio, "coverage_confidence": coverage_confidence, "category_coverage_confidence": coverage_confidence, "source_provider_health": "SEARCH_INFRASTRUCTURE_FAILED" if search_infrastructure_failed else "SEARCH_WORKED", "search_engine_health": dict(engine_health), "queries_avoided_due_to_circuit_breaker": avoided, "source_acceptance_rate": acceptance_rate, "research_status": "SEARCH_INFRASTRUCTURE_FAILED" if search_infrastructure_failed else "EVIDENCE_INSUFFICIENT" if evidence_insufficient else "SOURCE_LINKED", "unverified_categories": [category for category, ok in coverage.items() if not ok]}
proposal.market_evidence = [*self._as_list(proposal.market_evidence), *accepted, {"type": "crypto_research_coverage", "source": "crypto_research", "summary": f"{depth} crypto research coverage {coverage_ratio}", "coverage": coverage, "depth": depth}] proposal.market_evidence = [*self._as_list(proposal.market_evidence), *accepted, {"type": "crypto_research_coverage", "source": "crypto_research", "summary": f"{depth} crypto research coverage {coverage_ratio}", "coverage": coverage, "depth": depth}]
proposal.metadata = {**proposal.metadata, "research": research} proposal.metadata = {**proposal.metadata, "research": research}
proposal.save(update_fields=["market_evidence", "metadata", "updated_at"]) proposal.save(update_fields=["market_evidence", "metadata", "updated_at"])
@ -303,43 +342,88 @@ class CryptoVentureService:
def crypto_query_plan(self, proposal: CompanyProposal, *, depth: str) -> dict[str, list[str]]: def crypto_query_plan(self, proposal: CompanyProposal, *, depth: str) -> dict[str, list[str]]:
crypto = proposal.metadata.get("crypto", {}) crypto = proposal.metadata.get("crypto", {})
seed = " ".join([proposal.title, str(crypto.get("territory", "")), proposal.protocol_thesis.protocol_category]).replace("_", " ") seed = " ".join([str(crypto.get("territory", "")), proposal.protocol_thesis.protocol_category, proposal.problem, proposal.target_customer]).replace("_", " ")
seed = seed.replace(proposal.title, "")
seed = re.sub(r"\s+", " ", seed).strip()[:120]
terms = { terms = {
"USER_PAIN": [f"{seed} user pain", f"{proposal.target_customer} decentralized protocol pain"], "USER_PAIN": [f"{seed} user pain", f"{proposal.target_customer} coordination failure"],
"EXISTING_PROTOCOLS": [f"{seed} protocol", f"{seed} competitors crypto"], "EXISTING_PROTOCOLS": [f"{seed} protocol documentation", f"{seed} crypto protocol precedent"],
"FAILED_PRECEDENTS": [f"{seed} failed crypto project", f"{seed} postmortem"], "FAILED_PRECEDENTS": [f"{seed} failed crypto project", f"{seed} postmortem"],
"PAYMENT_MODELS": [f"{seed} stablecoin payments", f"{seed} usage fees"], "ONCHAIN_ALTERNATIVES": [f"{seed} onchain escrow settlement attestation"],
"OFFCHAIN_ALTERNATIVES": [f"{seed} SaaS alternative", f"{seed} offchain alternative"],
"TOKEN_MODELS": [f"{seed} token model staking", f"{seed} tokenomics"], "TOKEN_MODELS": [f"{seed} token model staking", f"{seed} tokenomics"],
"STAKING_MODELS": [f"{seed} staking slashing", f"provider staking protocol slashing"], "COLLATERAL_MODELS": [f"{seed} collateral model stablecoin ETH staking"],
"SLASHING_PRECEDENTS": [f"{seed} slashing precedent", f"objective slashing crypto protocol"], "STAKING_SLASHING_PRECEDENTS": [f"{seed} staking slashing", f"objective slashing crypto protocol"],
"PROVIDER_ECONOMICS": [f"{seed} provider economics", f"decentralized provider marketplace economics"], "PROVIDER_ECONOMICS": [f"{seed} provider economics", f"decentralized provider marketplace economics"],
"NETWORK_BOOTSTRAP": [f"{seed} network bootstrap", f"crypto protocol bootstrap supply demand"], "NETWORK_BOOTSTRAP": [f"{seed} network bootstrap", f"crypto protocol bootstrap supply demand"],
"ONCHAIN_ALTERNATIVES": [f"{seed} onchain alternatives"], "SECURITY_FAILURES": [f"{seed} security incident", f"{seed} exploit postmortem"],
"OFFCHAIN_ALTERNATIVES": [f"{seed} SaaS alternative", f"{seed} offchain alternative"],
"SECURITY_INCIDENTS": [f"{seed} security incident", f"{seed} exploit"],
"LEGAL_REGULATORY": [f"{seed} token regulatory risk", f"crypto staking slashing regulatory"], "LEGAL_REGULATORY": [f"{seed} token regulatory risk", f"crypto staking slashing regulatory"],
"TOKEN_LAUNCH_PRECEDENTS": [f"{seed} token launch", f"protocol token launch precedent"], "TOKEN_LAUNCH_PRECEDENTS": [f"{seed} token launch", f"protocol token launch precedent"],
"PRE_TOKEN_MONETIZATION_PRECEDENTS": [f"{seed} paid beta", f"crypto protocol pre token revenue"], "PRE_TOKEN_MONETIZATION": [f"{seed} paid beta", f"crypto protocol pre token revenue"],
} }
limit = 4 if depth == "deep" else 2 limit = 4 if depth == "deep" else 2
return {category: queries[:limit] for category, queries in terms.items()} return {category: queries[:limit] for category, queries in terms.items()}
def primary_source_seed_sources(self, proposal: CompanyProposal, query_plan: dict[str, list[str]]) -> list[dict[str, Any]]:
seeds = []
category_urls = {
"ONCHAIN_ALTERNATIVES": ["https://ethereum.org/en/developers/docs/", "https://eips.ethereum.org/"],
"TOKEN_MODELS": ["https://ethereum.org/en/developers/docs/standards/tokens/"],
"COLLATERAL_MODELS": ["https://ethereum.org/en/defi/"],
"STAKING_SLASHING_PRECEDENTS": ["https://ethereum.org/en/staking/"],
"SECURITY_FAILURES": ["https://immunefi.com/blog/", "https://github.com/pcaversaccio/reentrancy-attacks"],
"LEGAL_REGULATORY": ["https://www.sec.gov/newsroom", "https://www.cftc.gov/PressRoom/PressReleases"],
}
for category in query_plan:
for url in category_urls.get(category, [])[:2]:
seeds.append({"type": "primary_seed", "source": "primary_source_seed", "source_tier": "TIER_A", "url": url, "title": url, "category": category, "summary": "Primary-source seed for crypto protocol research.", "fallback_evidence": False})
return seeds
def crypto_source_quality(self, proposal: CompanyProposal, source: dict[str, Any]) -> dict[str, Any]: def crypto_source_quality(self, proposal: CompanyProposal, source: dict[str, Any]) -> dict[str, Any]:
text = " ".join(str(source.get(key, "")) for key in ["title", "summary", "content", "url"]).lower() text = " ".join(str(source.get(key, "")) for key in ["title", "summary", "content", "url"]).lower()
url = str(source.get("url", "")).lower() url = str(source.get("url", "")).lower()
category = str(source.get("category", ""))
if any(noisy in url for noisy in ["pinterest", "facebook.com", "instagram.com", "x.com/intent", "webcache", "archive.org", "youtube.com/watch"]): if any(noisy in url for noisy in ["pinterest", "facebook.com", "instagram.com", "x.com/intent", "webcache", "archive.org", "youtube.com/watch"]):
return {"accepted": False, "score": 0.0, "reason": "noisy or low-evidence source"} return {"accepted": False, "score": 0.0, "reason": SOURCE_REJECTION_REASONS["SEARCH_NOISE"]}
preferred = ["docs", "github", "whitepaper", "paper", "postmortem", "audit", "security", "research", "protocol", "token", "staking", "slashing", "stablecoin", "attestation", "oracle"] source_tier = self.source_tier(url, text)
preferred = ["docs", "github", "whitepaper", "paper", "postmortem", "audit", "security", "research", "protocol", "token", "staking", "slashing", "stablecoin", "collateral", "attestation", "oracle", "eip"]
proposal_terms = set(re.findall(r"[a-z0-9]{5,}", self._proposal_text(proposal))) proposal_terms = set(re.findall(r"[a-z0-9]{5,}", self._proposal_text(proposal)))
source_terms = set(re.findall(r"[a-z0-9]{5,}", text)) source_terms = set(re.findall(r"[a-z0-9]{5,}", text))
overlap = len(proposal_terms & source_terms) overlap = len(proposal_terms & source_terms)
category_terms = set(re.findall(r"[a-z0-9]{5,}", category.lower().replace("_", " ")))
category_overlap = len(category_terms & source_terms)
preferred_hits = sum(1 for term in preferred if term in text or term in url) preferred_hits = sum(1 for term in preferred if term in text or term in url)
score = round(min(1.0, overlap * 0.03 + preferred_hits * 0.08), 2) score = round(min(1.0, overlap * 0.025 + category_overlap * 0.12 + preferred_hits * 0.08 + (0.18 if source_tier == "TIER_A" else 0.08 if source_tier == "TIER_B" else 0)), 2)
if score >= 0.22: if category and category_overlap == 0 and source_tier == "TIER_C" and score < 0.45:
return {"accepted": True, "quality": "strong", "score": score, "reason": "crypto-relevant technical/protocol evidence"} return {"accepted": False, "score": score, "reason": SOURCE_REJECTION_REASONS["WRONG_CATEGORY"]}
if score >= 0.12: if score >= 0.45:
return {"accepted": True, "quality": "weak", "score": score, "reason": "accepted as weak crypto context"} return {"accepted": True, "quality": "strong", "source_tier": source_tier, "score": score, "reason": "strong category-relevant crypto primary/technical evidence"}
return {"accepted": False, "score": score, "reason": "insufficient crypto/protocol relevance"} if score >= 0.28 and source_tier in {"TIER_A", "TIER_B"}:
return {"accepted": True, "quality": "medium", "source_tier": source_tier, "score": score, "reason": "medium authority category evidence"}
if source_tier == "TIER_C":
return {"accepted": False, "score": score, "reason": SOURCE_REJECTION_REASONS["LOW_AUTHORITY"]}
return {"accepted": False, "score": score, "reason": SOURCE_REJECTION_REASONS["IRRELEVANT"]}
def source_tier(self, url: str, text: str) -> str:
if any(domain in url for domain in PRIMARY_SOURCE_DOMAINS) or any(marker in url for marker in ["/docs", "docs.", "whitepaper", "forum.", "research."]):
return "TIER_A"
if any(marker in text or marker in url for marker in ["audit", "postmortem", "technical research", "security research", "protocol research"]):
return "TIER_B"
return "TIER_C"
def category_coverage_confidence(self, sources: list[dict[str, Any]]) -> str:
strong = len([source for source in sources if source.get("quality") == "strong"])
medium_domains = {self._domain(str(source.get("url", ""))) for source in sources if source.get("quality") == "medium"}
if strong >= 2:
return "HIGH"
if strong >= 1 or len(medium_domains) >= 2:
return "MEDIUM"
if sources:
return "LOW"
return "NONE"
def _domain(self, url: str) -> str:
return re.sub(r"^https?://(www\.)?", "", url.lower()).split("/")[0]
def _engine_status(self, reason: str) -> str: def _engine_status(self, reason: str) -> str:
lowered = reason.lower() lowered = reason.lower()
@ -407,7 +491,11 @@ class CryptoVentureService:
member.save(update_fields=["rank", "portfolio_score", "is_top_3", "updated_at"]) member.save(update_fields=["rank", "portfolio_score", "is_top_3", "updated_at"])
row["rank"] = rank row["rank"] = rank
review, _ = PortfolioICReview.objects.update_or_create(cohort=cohort, defaults={"rankings": rows, "top_3": top_3, "concentration": self._crypto_concentration(rows), "metadata": {"finalist_shortfall": max(0, 3 - len(top_3)), "no_fund_decision_v01": True}}) review, _ = PortfolioICReview.objects.update_or_create(cohort=cohort, defaults={"rankings": rows, "top_3": top_3, "concentration": self._crypto_concentration(rows), "metadata": {"finalist_shortfall": max(0, 3 - len(top_3)), "no_fund_decision_v01": True}})
cohort.metrics = {**cohort.metrics, "crypto_ranked_count": len(rows), "crypto_top_3_count": len(top_3), "autonomous_crypto_survivors": len([row for row in rows if row.get("crypto_autonomy_class") == CryptoAutonomyClass.AUTONOMOUS_CRYPTO]), "finalists": len(top_3)} model_usage = Counter(cohort.metrics.get("model_usage", {}))
model_usage["sol_judge_requests"] += len([row for row in rows if row.get("token_necessity_classification")])
model_usage["sol_final_ic_requests"] += len(rows) if self.final_ic_model_hint == "sol" else 0
model_usage["qwen_requests"] += 0
cohort.metrics = {**cohort.metrics, "crypto_ranked_count": len(rows), "crypto_top_3_count": len(top_3), "autonomous_crypto_survivors": len([row for row in rows if row.get("crypto_autonomy_class") == CryptoAutonomyClass.AUTONOMOUS_CRYPTO]), "finalists": len(top_3), "model_usage": dict(model_usage), "sol_judge_requests": model_usage.get("sol_judge_requests", 0), "sol_final_ic_requests": model_usage.get("sol_final_ic_requests", 0), "qwen_requests": model_usage.get("qwen_requests", 0)}
cohort.status = "CRYPTO_IC_COMPLETE" cohort.status = "CRYPTO_IC_COMPLETE"
cohort.save(update_fields=["metrics", "status", "updated_at"]) cohort.save(update_fields=["metrics", "status", "updated_at"])
return review return review
@ -497,8 +585,10 @@ class CryptoVentureService:
review = cohort.portfolio_review review = cohort.portfolio_review
rows = review.rankings rows = review.rankings
research_health = self._research_health(rows) research_health = self._research_health(rows)
content = {"title": "CRYPTO VENTURE COHORT V0.2", "cohort_id": cohort.cohort_id, "graph_run": str(cohort.graph_run_id or ""), "runtime": cohort.metrics, "raw_protocols_generated": cohort.metrics.get("raw_generated", 0), "token_unnecessary": cohort.metrics.get("token_unnecessary_rejections", 0), "token_optional_routed_saas": cohort.metrics.get("token_optional_route_to_saas", 0), "duplicates": cohort.metrics.get("duplicate_rejections", 0), "crypto_survivors": cohort.members.count(), "autonomous_crypto_survivors": cohort.metrics.get("autonomous_crypto_survivors", 0), "security_blocked": cohort.metrics.get("protocol_security_gate_blocked", 0), "finalists": len(review.top_3), "generation_attempts": cohort.metrics.get("generation_attempts", 0), "ranking": rows, "top_3": review.top_3, "token_red_team_failures": cohort.metrics.get("token_red_team_failures", {}), "token_utility_distribution": dict(Counter(util for row in rows for util in row.get("token_utility", []))), "crypto_thesis_saturation": review.concentration, "capability_gaps": review.capability_demand, "research_health": research_health, "system_metrics": cohort.metrics, "stop_conditions": {"token_sale": False, "nft_sale": False, "founding_membership_sale": False, "fundraising": False, "mainnet_issuance": False, "user_contact": False, "real_spend": 0}} routed_to_saas = [{"company": proposal.title, "token_classification": getattr(proposal, "token_utility_assessment", None).classification if hasattr(proposal, "token_utility_assessment") else None, "product_thesis": proposal.description, "reason": "Failed onchain/token counterfactual but may be valuable as pre-token/SaaS product."} for proposal in cohort.mandate.company_proposals.filter(status=CompanyProposalStatus.REJECTED, metadata__routed_to_saas=True).order_by("created_at")[:10]]
artifact = self._artifact(None, cohort.mandate, "CRYPTO_VENTURE_COHORT_REPORT", "Crypto Venture Cohort V0.2 Report", content, self._readable_report(content), "crypto_portfolio_ic", graph_run=cohort.graph_run) comparison = {"v02": cohort.metadata.get("v02_baseline", {"raw": 30, "crypto_survivors": 2, "autonomous_survivors": 0, "finalists": 0}), "v03": {"raw": cohort.metrics.get("raw_generated", 0), "crypto_survivors": cohort.members.count(), "autonomous_survivors": cohort.metrics.get("autonomous_crypto_survivors", 0), "finalists": len(review.top_3), "average_research_quality": research_health.get("average_coverage", 0), "idea_diversity": review.concentration}}
content = {"title": "CRYPTO VENTURE COHORT V0.3", "cohort_id": cohort.cohort_id, "graph_run": str(cohort.graph_run_id or ""), "runtime": cohort.metrics, "raw_protocols_generated": cohort.metrics.get("raw_generated", 0), "raw_sol_theses": cohort.metrics.get("raw_sol_theses", 0), "onchain_rejected": cohort.metrics.get("onchain_rejected", 0), "token_unnecessary": cohort.metrics.get("token_unnecessary_rejections", 0), "token_optional_routed_saas": cohort.metrics.get("token_optional_route_to_saas", 0), "duplicates": cohort.metrics.get("duplicate_rejections", 0), "crypto_survivors": cohort.members.count(), "autonomous_crypto_survivors": cohort.metrics.get("autonomous_crypto_survivors", 0), "security_blocked": cohort.metrics.get("protocol_security_gate_blocked", 0), "finalists": len(review.top_3), "generation_attempts": cohort.metrics.get("generation_attempts", 0), "model_usage": cohort.metrics.get("model_usage", {}), "ranking": rows, "top_3": review.top_3, "routed_to_saas": routed_to_saas, "v02_comparison": comparison, "token_red_team_failures": cohort.metrics.get("token_red_team_failures", {}), "token_utility_distribution": dict(Counter(util for row in rows for util in row.get("token_utility", []))), "crypto_thesis_saturation": review.concentration, "capability_gaps": review.capability_demand, "research_health": research_health, "system_metrics": cohort.metrics, "stop_conditions": {"token_sale": False, "nft_sale": False, "founding_membership_sale": False, "fundraising": False, "mainnet_issuance": False, "user_contact": False, "real_spend": 0}}
artifact = self._artifact(None, cohort.mandate, "CRYPTO_VENTURE_COHORT_REPORT", "Crypto Venture Cohort V0.3 Report", content, self._readable_report(content), "crypto_portfolio_ic", graph_run=cohort.graph_run)
cohort.status = "COMPLETE" cohort.status = "COMPLETE"
cohort.save(update_fields=["status", "updated_at"]) cohort.save(update_fields=["status", "updated_at"])
return artifact return artifact
@ -507,14 +597,18 @@ class CryptoVentureService:
engine_counts = Counter() engine_counts = Counter()
accepted = 0 accepted = 0
rejected = 0 rejected = 0
primary = 0
avoided = 0
coverage = [] coverage = []
for row in rows: for row in rows:
research = row.get("research", {}) research = row.get("research", {})
accepted += int(research.get("source_count", 0) or 0) accepted += int(research.get("source_count", 0) or 0)
rejected += int(research.get("source_rejection_count", 0) or 0) rejected += int(research.get("source_rejection_count", 0) or 0)
primary += int(research.get("primary_source_count", 0) or 0)
avoided += int(research.get("queries_avoided_due_to_circuit_breaker", 0) or 0)
coverage.append(float(research.get("coverage_ratio", 0) or 0)) coverage.append(float(research.get("coverage_ratio", 0) or 0))
engine_counts.update(research.get("search_engine_health", {})) engine_counts.update(research.get("search_engine_health", {}))
return {"search_engines": dict(engine_counts), "sources_accepted": accepted, "sources_rejected": rejected, "average_coverage": round(sum(coverage) / max(1, len(coverage)), 2), "healthy": engine_counts.get("AVAILABLE", 0), "rate_limited": engine_counts.get("RATE_LIMITED", 0), "captcha": engine_counts.get("CAPTCHA", 0), "denied": engine_counts.get("ACCESS_DENIED", 0), "timed_out": engine_counts.get("TIMEOUT", 0)} return {"search_engines": dict(engine_counts), "sources_accepted": accepted, "sources_rejected": rejected, "primary_sources_accepted": primary, "queries_avoided_due_to_circuit_breaker": avoided, "average_coverage": round(sum(coverage) / max(1, len(coverage)), 2), "healthy": engine_counts.get("AVAILABLE", 0), "rate_limited": engine_counts.get("RATE_LIMITED", 0), "captcha": engine_counts.get("CAPTCHA", 0), "denied": engine_counts.get("ACCESS_DENIED", 0), "timed_out": engine_counts.get("TIMEOUT", 0)}
def assess_token_necessity(self, proposal: CompanyProposal) -> TokenUtilityAssessment: def assess_token_necessity(self, proposal: CompanyProposal) -> TokenUtilityAssessment:
crypto = proposal.metadata.get("crypto", {}) crypto = proposal.metadata.get("crypto", {})
@ -526,7 +620,9 @@ class CryptoVentureService:
weak = any(term in text for term in ["meme", "speculative", "community token", "marketing token", "community marketing", "governance token only", "token gated subscription"]) weak = any(term in text for term in ["meme", "speculative", "community token", "marketing token", "community marketing", "governance token only", "token gated subscription"])
payment_only = roles.required_roles == ["PAYMENT"] payment_only = roles.required_roles == ["PAYMENT"]
score = min(100, 25 + strong_count * 10 + required_roles * 16 + (12 if "slash" in text or "slashing" in text else 0) + (8 if "provider" in text else 0) - (25 if payment_only else 0) - (35 if weak else 0)) score = min(100, 25 + strong_count * 10 + required_roles * 16 + (12 if "slash" in text or "slashing" in text else 0) + (8 if "provider" in text else 0) - (25 if payment_only else 0) - (35 if weak else 0))
sol_review = self._sol_json("Counterfactual token necessity review. If the token were removed and replaced with fiat/stablecoin/database credits, would the product materially degrade? Return JSON with classification TOKEN_ESSENTIAL, TOKEN_STRONGLY_JUSTIFIED, TOKEN_OPTIONAL, or TOKEN_UNNECESSARY; score 0-100; rationale; fiat_or_database_substitution. Proposal: " + json.dumps(proposal.pitch, default=str) + " Crypto: " + json.dumps(crypto, default=str)) economic_model = getattr(proposal, "token_economic_model", None)
model_payload = {"unit_of_service": economic_model.unit_of_service, "payment_asset": economic_model.payment_asset, "stake_requirement": economic_model.stake_requirement, "collateral_requirement": economic_model.collateral_requirement} if economic_model else {}
sol_review = self._sol_json("CRYPTO_TOKEN_COUNTERFACTUAL_JUDGE V0.3. Independent from generation. Compare MODEL A native token, MODEL B stablecoin payments + native bond token, MODEL C stablecoin payments + ETH/stablecoin collateral, MODEL D onchain system with no proprietary token, MODEL E centralized SaaS/database. Return JSON with classification TOKEN_ESSENTIAL, TOKEN_STRONGLY_JUSTIFIED, TOKEN_OPTIONAL, or TOKEN_UNNECESSARY; score 0-100; rationale; fiat_or_database_substitution; model_a_native_token; model_b_stablecoin_payment_native_bond; model_c_external_collateral; model_d_no_proprietary_token; model_e_centralized_saas; minimum_required_crypto_architecture; minimum_required_token_architecture; native_token_removed_breaks list; native_token_removed_degradation NONE/MINOR/MODERATE/MATERIAL/CRITICAL; native_token_removed_explanation. Product/protocol/token/economic model: " + json.dumps({"pitch": proposal.pitch, "crypto": crypto, "economic_model": model_payload}, default=str))
if sol_review: if sol_review:
score = float(sol_review.get("score", score)) score = float(sol_review.get("score", score))
sol_classification = str(sol_review.get("classification", "")) sol_classification = str(sol_review.get("classification", ""))
@ -542,7 +638,13 @@ class CryptoVentureService:
classification = TokenNecessityClassification.TOKEN_UNNECESSARY classification = TokenNecessityClassification.TOKEN_UNNECESSARY
if sol_classification in TokenNecessityClassification.values: if sol_classification in TokenNecessityClassification.values:
classification = sol_classification classification = sol_classification
return TokenUtilityAssessment.objects.update_or_create(proposal=proposal, defaults={"classification": classification, "token_necessity_score": score, "utility_categories": utilities, "fiat_or_database_substitution": str((sol_review or {}).get("fiat_or_database_substitution", "If normal fiat/stablecoin/database credits preserve the core coordination, route to SaaS.")), "rationale": str((sol_review or {}).get("rationale", "Token score derives from genuinely required roles, not native payment currency alone.")), "metadata": {"weak_utility_terms": INSUFFICIENT_TOKEN_UTILITIES, "sol_counterfactual_review": sol_review or {}, "required_roles": roles.required_roles, "payment_only_penalty": payment_only}})[0] degradation = str((sol_review or {}).get("native_token_removed_degradation", "MATERIAL" if required_roles else "MINOR"))
if degradation in {"NONE", "MINOR"} and classification in {TokenNecessityClassification.TOKEN_ESSENTIAL, TokenNecessityClassification.TOKEN_STRONGLY_JUSTIFIED}:
classification = TokenNecessityClassification.TOKEN_OPTIONAL
score = min(score, 62)
proposal.metadata = {**proposal.metadata, "crypto": {**crypto, "counterfactual_judge_model": self.final_ic_model_hint, "counterfactual_judge_used": bool(sol_review)}}
proposal.save(update_fields=["metadata", "updated_at"])
return TokenUtilityAssessment.objects.update_or_create(proposal=proposal, defaults={"classification": classification, "token_necessity_score": score, "utility_categories": utilities, "fiat_or_database_substitution": str((sol_review or {}).get("fiat_or_database_substitution", "If normal fiat/stablecoin/database credits preserve the core coordination, route to SaaS.")), "rationale": str((sol_review or {}).get("rationale", "Token score derives from genuinely required roles, not native payment currency alone.")), "metadata": {"weak_utility_terms": INSUFFICIENT_TOKEN_UTILITIES, "sol_counterfactual_review": sol_review or {}, "independent_counterfactual_judge": "CRYPTO_TOKEN_COUNTERFACTUAL_JUDGE", "required_roles": roles.required_roles, "payment_only_penalty": payment_only, "model_a_native_token": (sol_review or {}).get("model_a_native_token", {}), "model_b_stablecoin_payment_native_bond": (sol_review or {}).get("model_b_stablecoin_payment_native_bond", {}), "model_c_external_collateral": (sol_review or {}).get("model_c_external_collateral", {}), "model_d_no_proprietary_token": (sol_review or {}).get("model_d_no_proprietary_token", {}), "model_e_centralized_saas": (sol_review or {}).get("model_e_centralized_saas", {}), "minimum_required_crypto_architecture": (sol_review or {}).get("minimum_required_crypto_architecture", ""), "minimum_required_token_architecture": (sol_review or {}).get("minimum_required_token_architecture", ""), "native_token_removed_breaks": self._as_list((sol_review or {}).get("native_token_removed_breaks")), "native_token_removed_degradation": degradation, "native_token_removed_explanation": (sol_review or {}).get("native_token_removed_explanation", "")}})[0]
def token_role_decomposition(self, proposal: CompanyProposal) -> TokenRoleDecomposition: def token_role_decomposition(self, proposal: CompanyProposal) -> TokenRoleDecomposition:
text = self._proposal_text(proposal) text = self._proposal_text(proposal)
@ -569,7 +671,7 @@ class CryptoVentureService:
value = TokenRoleRequirement.UNNECESSARY value = TokenRoleRequirement.UNNECESSARY
roles[role] = value roles[role] = value
required = [role for role, value in roles.items() if value == TokenRoleRequirement.REQUIRED] required = [role for role, value in roles.items() if value == TokenRoleRequirement.REQUIRED]
stablecoin = {"MODEL_A_native_payment_staking_governance": "baseline proposal", "MODEL_B_stablecoin_payment_native_bond": "preferred if payment utility is separable", "MODEL_C_stablecoin_payment_stablecoin_collateral": "valid if slashing/collateral does not need protocol-native exposure", "MODEL_D_centralized_database_credits": "routes to SaaS if verification/settlement/reputation do not degrade", "usdc_identical_payment_utility": roles.get("PAYMENT") != TokenRoleRequirement.REQUIRED, "stable_collateral_identical_security": "SLASHABLE_COLLATERAL" not in required and "SECURITY_BOND" not in required} stablecoin = {"MODEL_A_native_payment_staking_governance": "baseline proposal", "MODEL_B_stablecoin_payment_native_bond": "preferred if payment utility is separable", "MODEL_C_stablecoin_payment_stablecoin_collateral": "valid if slashing/collateral does not need protocol-native exposure", "MODEL_D_onchain_no_proprietary_token": "valid if proprietary token adds no security/resource allocation advantage", "MODEL_E_centralized_saas_database": "routes to SaaS if verification/settlement/reputation do not degrade", "usdc_identical_payment_utility": roles.get("PAYMENT") != TokenRoleRequirement.REQUIRED, "external_collateral_identical_security": "SLASHABLE_COLLATERAL" not in required and "SECURITY_BOND" not in required, "stable_collateral_identical_security": "SLASHABLE_COLLATERAL" not in required and "SECURITY_BOND" not in required}
outcome = "Native token removal materially degrades security/reputation if required roles remain." if required else "Native token removed changes little; route to SaaS or revise token model." outcome = "Native token removal materially degrades security/reputation if required roles remain." if required else "Native token removed changes little; route to SaaS or revise token model."
return TokenRoleDecomposition.objects.update_or_create(proposal=proposal, defaults={"roles": roles, "required_roles": required, "stablecoin_counterfactual": stablecoin, "native_token_removed_outcome": outcome})[0] return TokenRoleDecomposition.objects.update_or_create(proposal=proposal, defaults={"roles": roles, "required_roles": required, "stablecoin_counterfactual": stablecoin, "native_token_removed_outcome": outcome})[0]
@ -582,8 +684,11 @@ class CryptoVentureService:
score = 0 if mode == PreTokenMonetizationMode.NONE else 55 + (15 if "api" in text else 0) + (10 if "automated" in text or "machine" in text else 0) score = 0 if mode == PreTokenMonetizationMode.NONE else 55 + (15 if "api" in text else 0) + (10 if "automated" in text or "machine" in text else 0)
score = min(100, score) score = min(100, score)
membership_policy = {"no_investment_return": True, "no_equity": True, "no_profit_or_revenue_share": True, "no_guaranteed_native_token_allocation": True, "no_appreciation_promise": True, "useful_without_future_token": True, "public_sale_requires_human_legal_gate": True, "v02_sale_executed": False} membership_policy = {"no_investment_return": True, "no_equity": True, "no_profit_or_revenue_share": True, "no_guaranteed_native_token_allocation": True, "no_appreciation_promise": True, "useful_without_future_token": True, "public_sale_requires_human_legal_gate": True, "v02_sale_executed": False}
ladder = {"1000": "sell small paid beta/API-credit packages", "5000": "10-25 paid beta users or usage-credit customers", "10000": "recurring API/subscription/service-credit revenue without native token"} customer = proposal.target_customer[:180]
return PreTokenMonetizationAssessment.objects.update_or_create(proposal=proposal, defaults={"mode": mode, "pre_token_monetization_potential": score, "pre_token_business": "Sell useful product/network access before native token issuance using fiat/stablecoin credits, paid beta, subscription, or legally reviewed membership. No native token required.", "immediate_utility": immediate, "revenue_ladder": ladder, "founding_membership_policy": membership_policy, "autonomous_fulfillment_notes": "Prefer self-service API/testnet onboarding, automated usage metering, and docs-first support."})[0] product = proposal.description[:220]
payment = proposal.pricing_hypothesis or "Stripe/stablecoin usage credits"
ladder = {"1000": f"Sell 10 x $100 paid beta/API-credit packages to {customer} for {product}; fulfill with hosted testnet/API access and automated reports.", "5000": f"Sell 20 x $250 monthly usage-credit packages via {payment}; buyers get measurable protocol simulations, SDK/API access, and evidence dashboards before any token.", "10000": f"Sell 20 x $500 subscription/service-credit plans; fulfillment is self-service onboarding, usage metering, testnet jobs, and downloadable verification evidence."}
return PreTokenMonetizationAssessment.objects.update_or_create(proposal=proposal, defaults={"mode": mode, "pre_token_monetization_potential": score, "pre_token_business": "Sell useful product/network access before native token issuance using fiat/stablecoin credits, paid beta, subscription, service credits, or legally reviewed membership. No native token required.", "immediate_utility": immediate, "revenue_ladder": ladder, "founding_membership_policy": membership_policy, "autonomous_fulfillment_notes": "Prefer self-service API/testnet onboarding, automated usage metering, and docs-first support."})[0]
def token_economic_model(self, proposal: CompanyProposal) -> TokenEconomicModel: def token_economic_model(self, proposal: CompanyProposal) -> TokenEconomicModel:
text = self._proposal_text(proposal) text = self._proposal_text(proposal)
@ -592,7 +697,7 @@ class CryptoVentureService:
providers = max(3, 4 + len(token.utility_categories) * 2) providers = max(3, 4 + len(token.utility_categories) * 2)
users = max(25, int(float(proposal.confidence) * 220)) users = max(25, int(float(proposal.confidence) * 220))
stake = 200 + int(token.token_necessity_score * 10) stake = 200 + int(token.token_necessity_score * 10)
return TokenEconomicModel.objects.update_or_create(proposal=proposal, defaults={"unit_of_service": "verified job/API call/attestation", "expected_usage_frequency": "weekly to daily machine/API usage", "transaction_fee_model": "Per-use stablecoin or credit fee pre-token; protocol fees may later settle through approved token design.", "average_fee": avg_fee, "payment_asset": "stablecoin_or_fiat_pre_token", "stake_requirement": stake, "collateral_requirement": stake, "provider_count": providers, "demand_side_users": users, "slash_event_assumptions": {"baseline_rate": 0.01, "high_slash_rate": 0.08, "objective_conditions_required": True}, "slash_amount": round(stake * 0.15, 2), "emission_schedule": {"v02": "none", "post_launch_model_only": "declining bootstrap subsidies"}, "bootstrap_subsidies": 0.0, "treasury_share": 0.2, "provider_reward_share": 0.8, "token_sinks": ["provider bonds", "slashing penalties", "protocol fees", "security budget"], "unlock_assumptions": {"v02": "none"}, "circulation_assumptions": {"v02_native_token_supply": 0}})[0] return TokenEconomicModel.objects.update_or_create(proposal=proposal, defaults={"unit_of_service": "verified job/API call/attestation", "expected_usage_frequency": "weekly to daily machine/API usage", "transaction_fee_model": "Per-use stablecoin or credit fee pre-token; protocol fees may later settle through approved token design.", "average_fee": avg_fee, "payment_asset": "stablecoin_or_fiat_pre_token", "stake_requirement": stake, "collateral_requirement": stake, "provider_count": providers, "demand_side_users": users, "slash_event_assumptions": {"baseline_rate": 0.01, "high_slash_rate": 0.08, "objective_conditions_required": True, "conditions": ["missed settlement", "invalid proof", "fraudulent attestation"]}, "slash_amount": round(stake * 0.15, 2), "emission_schedule": {"v03": "none", "post_launch_model_only": "declining bootstrap subsidies"}, "bootstrap_subsidies": 0.0, "treasury_share": 0.2, "provider_reward_share": 0.8, "token_sinks": ["provider bonds", "slashing penalties", "protocol fees", "security budget"], "unlock_assumptions": {"v03": "none"}, "circulation_assumptions": {"v03_native_token_supply": 0}, "metadata": {"buyer": proposal.target_customer, "provider": "permissionless providers/operators serving the unit of service", "average_transaction": avg_fee, "protocol_fee": round(avg_fee * 0.2, 3), "provider_reward": round(avg_fee * 0.8, 3), "token_sources": ["earned service fees", "bond acquisition only after legal gate"], "provider_economics": {"gross_fee_share": 0.8, "slash_risk": 0.01, "stake_requirement": stake}}})[0]
def _create_proposal(self, mandate: CompanyMandate, payload: dict[str, Any], source: str, territory: str) -> CompanyProposal: def _create_proposal(self, mandate: CompanyMandate, payload: dict[str, Any], source: str, territory: str) -> CompanyProposal:
title = str(payload.get("name") or payload.get("title") or f"Protocol {territory.title()}")[:255] title = str(payload.get("name") or payload.get("title") or f"Protocol {territory.title()}")[:255]
@ -603,22 +708,34 @@ class CryptoVentureService:
validation = str(payload.get("validation_experiment") or "Run a testnet/local-chain pilot with fake credits and simulated token accounting; no sale, fundraising, or mainnet issuance.") validation = str(payload.get("validation_experiment") or "Run a testnet/local-chain pilot with fake credits and simulated token accounting; no sale, fundraising, or mainnet issuance.")
thesis = VentureThesis.objects.create(mandate=mandate, title=title, thesis=protocol, similarity_fingerprint=self._fingerprint(title + protocol), metadata={"source": source, "venture_track": VentureTrack.CRYPTO_PROTOCOL, "territory": territory}, evidence_tier="TIER_0_THESIS") thesis = VentureThesis.objects.create(mandate=mandate, title=title, thesis=protocol, similarity_fingerprint=self._fingerprint(title + protocol), metadata={"source": source, "venture_track": VentureTrack.CRYPTO_PROTOCOL, "territory": territory}, evidence_tier="TIER_0_THESIS")
proposal = CompanyProposal.objects.create(mandate=mandate, thesis=thesis, title=title, description=product, problem=str(payload.get("user_pain") or payload.get("problem") or "Users lack trustworthy decentralized coordination."), target_customer=str(payload.get("user") or payload.get("target_user") or "Developers and network participants"), proposed_solution=str(payload.get("product") or product), business_model=str(payload.get("business_model") or "Protocol fees on real usage; no V0 token sale."), pricing_hypothesis=str(payload.get("fee_model") or "Testnet/free validation, later usage fees."), acquisition_strategy=str(payload.get("bootstrap_plan") or "Developer adoption through testnet docs and public artifacts only after approval."), validation_plan=validation, capital_requested=Decimal("0"), time_to_first_dollar_estimate="No V0 revenue target; validate protocol usage without token sale.", expected_margin="Protocol fee margin depends on provider economics.", build_complexity=str(payload.get("build_complexity") or "MEDIUM-HIGH"), market_evidence=[], differentiation=str(payload.get("differentiation") or protocol), major_risks=self._as_list(payload.get("major_risks") or ["Token not required", "Regulatory uncertainty", "Security model weak"]), confidence=self._confidence(payload.get("confidence", 0.55)), status=CompanyProposalStatus.SUBMITTED, pitch={"Company name": title, "One-line thesis": str(payload.get("one_line_thesis") or protocol), "Product thesis": product, "Protocol thesis": protocol, "Token thesis": token, "Token demand loop": demand_loop, "Validation experiment": validation}, metadata={"generation_source": source, "venture_track": VentureTrack.CRYPTO_PROTOCOL, "crypto": {"territory": territory, "product_thesis": product, "protocol_thesis": protocol, "token_thesis": token, "token_demand_loop": demand_loop, "token_utility": self._as_list(payload.get("token_utility") or self._infer_utilities(token + " " + protocol)), "value_capture": str(payload.get("value_capture") or "Usage fees accrue to providers, security budget, and protocol treasury."), "network_effect": str(payload.get("network_effect") or "More users attract more providers, improving liquidity/reliability."), "bootstrap_plan": str(payload.get("bootstrap_plan") or "Run without a live token using test credits and provider simulations."), "security_model": str(payload.get("security_model") or "Contracts, staking, slashing, oracle controls, admin keys, and treasury controls require review."), "regulatory_policy": "US_EXCLUDED; TOKEN_SALE_DISABLED; MAINNET_TOKEN_ISSUANCE_DISABLED; FUNDRAISING_DISABLED"}, "real_spend": 0, "real_customer_outreach": False, "token_sale": False, "mainnet_issuance": False}, evidence_tier="TIER_0_THESIS") proposal = CompanyProposal.objects.create(mandate=mandate, thesis=thesis, title=title, description=product, problem=str(payload.get("user_pain") or payload.get("problem") or "Users lack trustworthy decentralized coordination."), target_customer=str(payload.get("user") or payload.get("target_user") or "Developers and network participants"), proposed_solution=str(payload.get("product") or product), business_model=str(payload.get("business_model") or "Protocol fees on real usage; no V0 token sale."), pricing_hypothesis=str(payload.get("fee_model") or "Testnet/free validation, later usage fees."), acquisition_strategy=str(payload.get("bootstrap_plan") or "Developer adoption through testnet docs and public artifacts only after approval."), validation_plan=validation, capital_requested=Decimal("0"), time_to_first_dollar_estimate="No V0 revenue target; validate protocol usage without token sale.", expected_margin="Protocol fee margin depends on provider economics.", build_complexity=str(payload.get("build_complexity") or "MEDIUM-HIGH"), market_evidence=[], differentiation=str(payload.get("differentiation") or protocol), major_risks=self._as_list(payload.get("major_risks") or ["Token not required", "Regulatory uncertainty", "Security model weak"]), confidence=self._confidence(payload.get("confidence", 0.55)), status=CompanyProposalStatus.SUBMITTED, pitch={"Company name": title, "One-line thesis": str(payload.get("one_line_thesis") or protocol), "Product thesis": product, "Protocol thesis": protocol, "Token thesis": token, "Token demand loop": demand_loop, "Validation experiment": validation}, metadata={"generation_source": source, "venture_track": VentureTrack.CRYPTO_PROTOCOL, "crypto": {"territory": territory, "product_thesis": product, "protocol_thesis": protocol, "token_thesis": token, "token_demand_loop": demand_loop, "token_utility": self._as_list(payload.get("token_utility") or self._infer_utilities(token + " " + protocol)), "value_capture": str(payload.get("value_capture") or "Usage fees accrue to providers, security budget, and protocol treasury."), "network_effect": str(payload.get("network_effect") or "More users attract more providers, improving liquidity/reliability."), "bootstrap_plan": str(payload.get("bootstrap_plan") or "Run without a live token using test credits and provider simulations."), "security_model": str(payload.get("security_model") or "Contracts, staking, slashing, oracle controls, admin keys, and treasury controls require review."), "regulatory_policy": "US_EXCLUDED; TOKEN_SALE_DISABLED; MAINNET_TOKEN_ISSUANCE_DISABLED; FUNDRAISING_DISABLED"}, "real_spend": 0, "real_customer_outreach": False, "token_sale": False, "mainnet_issuance": False}, evidence_tier="TIER_0_THESIS")
if payload.get("generation_passes"):
proposal.metadata = {**proposal.metadata, "crypto": {**proposal.metadata["crypto"], "generation_passes": payload.get("generation_passes", {}), "problem_first_pass_a_had_no_token": True, "pre_token_product": payload.get("pre_token_product"), "pre_token_customer": payload.get("pre_token_customer"), "pre_token_value": payload.get("pre_token_value"), "pre_token_payment_method": payload.get("pre_token_payment_method"), "pre_token_revenue_model": payload.get("pre_token_revenue_model")}}
proposal.save(update_fields=["metadata", "updated_at"])
ProtocolThesis.objects.create(proposal=proposal, product_thesis=product, protocol_thesis=protocol, token_thesis=token, network_effect=proposal.metadata["crypto"]["network_effect"], bootstrap_plan=proposal.metadata["crypto"]["bootstrap_plan"], autonomous_operability="Artifex can build/testnet deploy/monitor only; mainnet and issuance stop at human legal gate.", utility_categories=proposal.metadata["crypto"]["token_utility"], protocol_category=territory) ProtocolThesis.objects.create(proposal=proposal, product_thesis=product, protocol_thesis=protocol, token_thesis=token, network_effect=proposal.metadata["crypto"]["network_effect"], bootstrap_plan=proposal.metadata["crypto"]["bootstrap_plan"], autonomous_operability="Artifex can build/testnet deploy/monitor only; mainnet and issuance stop at human legal gate.", utility_categories=proposal.metadata["crypto"]["token_utility"], protocol_category=territory)
TokenDemandLoop.objects.create(proposal=proposal, loop=demand_loop, real_usage_driver=str(payload.get("real_usage_driver") or product), non_speculative_demand=not any(term in " ".join(demand_loop).lower() for term in ["speculation", "price go up"]), bootstrap_without_token=proposal.metadata["crypto"]["bootstrap_plan"]) TokenDemandLoop.objects.create(proposal=proposal, loop=demand_loop, real_usage_driver=str(payload.get("real_usage_driver") or product), non_speculative_demand=not any(term in " ".join(demand_loop).lower() for term in ["speculation", "price go up"]), bootstrap_without_token=proposal.metadata["crypto"]["bootstrap_plan"])
self.bus.publish("CRYPTO_PROTOCOL_PROPOSED", payload={"proposal_id": str(proposal.id), "territory": territory, "source": source}) self.bus.publish("CRYPTO_PROTOCOL_PROPOSED", payload={"proposal_id": str(proposal.id), "territory": territory, "source": source})
return proposal return proposal
def _protocol_payload(self, index: int, territory: str) -> tuple[dict[str, Any], str]: def _protocol_payload(self, index: int, territory: str) -> tuple[dict[str, Any], str, Counter]:
usage = Counter()
if self.router is not None: if self.router is not None:
try: try:
prompt = "Generate exactly one crypto-native protocol venture as JSON. Do not propose meme coins, generic DEX/L1/NFT/yield farms, or SaaS plus token. Include name, one_line_thesis, product_thesis, user, protocol_thesis, why_onchain, token_thesis, token_utility list, token_demand_loop list, value_capture, network_effect, bootstrap_plan, validation_experiment, security_model, regulatory_risks, major_risks, confidence. Territory: " + territory + ". Rules: no token sale, no fundraising, no mainnet issuance, no US-targeted activity; validation must use testnet/local/fake credits/stablecoin-only simulation." pass_a_prompt = "PASS A - PROBLEM / NETWORK DESIGN. Generate exactly one protocol venture problem thesis as JSON with NO token fields and no token design. Include name, one_line_thesis, product_thesis, user, participants, supply_side, demand_side, coordination_failure, trust_problem, why_neutral_network_may_help, pre_token_product, pre_token_customer, pre_token_value, pre_token_payment_method, pre_token_revenue_model, revenue_1000_path, revenue_5000_path, revenue_10000_path, autonomous_operating_loop, bootstrap_plan, validation_experiment, major_risks, confidence. Do not include token_thesis, token_utility, tokenomics, governance token, rewards token, or staking token. Territory: " + territory + ". Exclude API Reliability Slashing Pool, VeriMesh, VeriChain, and simple staking/slashing agent marketplaces unless the core coordination mechanism is materially different."
response = self.router.complete(ModelRequestContract(purpose=ModelCapability.PLANNING, model_hint=self.generation_model_hint, prompt=prompt)) pass_a = self._model_json(self.generation_model_hint, pass_a_prompt, purpose=ModelCapability.PLANNING)
parsed = extract_json_object(response.content) usage["sol_generation_requests" if self.generation_model_hint == "sol" else "qwen_requests"] += 1
if parsed: if pass_a and not any(key in pass_a for key in ["token_thesis", "token_utility", "tokenomics"]):
return parsed, self.generation_model_hint pass_b_prompt = "PASS B - ONCHAIN NECESSITY. Independently evaluate whether this coordination system materially improves when implemented onchain. Return JSON with onchain_necessity_score 0-100, why_onchain, offchain_substitute, pass_onchain boolean, reasons list. Consider trust minimization, neutral settlement, permissionless participation, verifiable state, escrow, programmable guarantees, censorship resistance, cross-organization coordination, and machine-to-machine interaction. Thesis: " + json.dumps(pass_a, default=str)
pass_b = self._model_json(self.generation_model_hint, pass_b_prompt, purpose=ModelCapability.REASONING)
usage["sol_onchain_judge_requests" if self.generation_model_hint == "sol" else "qwen_requests"] += 1
pass_c_prompt = "PASS C - DERIVED TOKEN DESIGN. Only if the onchain system needs a native token, derive the minimum token architecture from required protocol functions. Prefer native security bond, endogenous slashable collateral, scarce resource allocation, decentralized security budget, permissionless provider admission, machine economic identity, protocol-specific risk collateral, or required decentralized supply incentives. Weak roles such as payment, discounts, governance, rewards, community, token-gating, treasury, marketing, or alignment cannot justify the token alone. Return JSON with token_thesis, token_utility list, token_demand_loop list, value_capture, security_model, regulatory_risks, required_token_functions, weak_token_functions. If no native token is needed, return token_thesis='No native token required' and token_utility=[]. Problem thesis: " + json.dumps(pass_a, default=str) + " Onchain review: " + json.dumps(pass_b, default=str)
pass_c = self._model_json(self.generation_model_hint, pass_c_prompt, purpose=ModelCapability.REASONING)
usage["sol_token_design_requests" if self.generation_model_hint == "sol" else "qwen_requests"] += 1
payload = {**pass_a, **pass_b, **pass_c, "user_pain": pass_a.get("coordination_failure") or pass_a.get("trust_problem"), "protocol_thesis": pass_a.get("why_neutral_network_may_help") or pass_a.get("product_thesis"), "business_model": pass_a.get("pre_token_revenue_model"), "fee_model": pass_a.get("pre_token_payment_method"), "generation_passes": {"pass_a_problem_network": pass_a, "pass_b_onchain": pass_b, "pass_c_token": pass_c}}
return payload, self.generation_model_hint, usage
except Exception: except Exception:
pass pass
return self._fallback_payload(index, territory), "deterministic_crypto_fallback" usage["fallback_count"] += 1
return self._fallback_payload(index, territory), "deterministic_crypto_fallback", usage
def _fallback_payload(self, index: int, territory: str) -> dict[str, Any]: def _fallback_payload(self, index: int, territory: str) -> dict[str, Any]:
names = { names = {
@ -634,7 +751,7 @@ class CryptoVentureService:
def _update_protocol_research_flags(self, proposal: CompanyProposal) -> None: def _update_protocol_research_flags(self, proposal: CompanyProposal) -> None:
research = proposal.metadata.get("research", {}) research = proposal.metadata.get("research", {})
crypto = proposal.metadata.get("crypto", {}) crypto = proposal.metadata.get("crypto", {})
proposal.metadata = {**proposal.metadata, "crypto": {**crypto, "research_status": "RESEARCH_INSUFFICIENT" if research.get("source_count", 0) == 0 else "SOURCE_LINKED", "research_categories": RESEARCH_CATEGORIES}} proposal.metadata = {**proposal.metadata, "crypto": {**crypto, "research_status": "RESEARCH_INSUFFICIENT" if research.get("source_count", 0) == 0 else "SOURCE_LINKED", "research_categories": CRYPTO_RESEARCH_CATEGORIES}}
proposal.save(update_fields=["metadata", "updated_at"]) proposal.save(update_fields=["metadata", "updated_at"])
def red_team_proposal(self, proposal: CompanyProposal) -> CryptoRedTeamAssessment: def red_team_proposal(self, proposal: CompanyProposal) -> CryptoRedTeamAssessment:
@ -696,7 +813,11 @@ class CryptoVentureService:
text = self._proposal_text(proposal) text = self._proposal_text(proposal)
terms = ["escrow", "slashing", "stake", "attestation", "settlement", "collateral", "trust", "reputation", "marketplace", "machine"] terms = ["escrow", "slashing", "stake", "attestation", "settlement", "collateral", "trust", "reputation", "marketplace", "machine"]
score = min(100, 35 + sum(8 for term in terms if term in text)) score = min(100, 35 + sum(8 for term in terms if term in text))
return OnchainNecessityAssessment.objects.update_or_create(proposal=proposal, defaults={"onchain_necessity_score": score, "reasons": [term for term in terms if term in text], "offchain_substitute": "Private SaaS/database is acceptable only if escrow, slashing, public reputation, and neutral settlement are not material.", "rationale": "Scores onchain necessity from trust-minimized coordination, settlement, staking/slashing, attestations, and multi-party neutrality."})[0] generation_passes = proposal.metadata.get("crypto", {}).get("generation_passes", {})
pass_b = generation_passes.get("pass_b_onchain", {}) if isinstance(generation_passes, dict) else {}
if pass_b:
score = float(pass_b.get("onchain_necessity_score", score))
return OnchainNecessityAssessment.objects.update_or_create(proposal=proposal, defaults={"onchain_necessity_score": score, "reasons": self._as_list(pass_b.get("reasons")) or [term for term in terms if term in text], "offchain_substitute": str(pass_b.get("offchain_substitute") or "Private SaaS/database is acceptable only if escrow, slashing, public reputation, and neutral settlement are not material."), "rationale": str(pass_b.get("why_onchain") or "Scores onchain necessity from trust-minimized coordination, settlement, staking/slashing, attestations, and multi-party neutrality."), "metadata": {"generation_pass_b": pass_b}})[0]
def value_capture_assessment(self, proposal: CompanyProposal) -> ProtocolValueCapture: def value_capture_assessment(self, proposal: CompanyProposal) -> ProtocolValueCapture:
text = self._proposal_text(proposal) text = self._proposal_text(proposal)
@ -720,7 +841,7 @@ class CryptoVentureService:
roles = getattr(proposal, "token_role_decomposition", None) or self.token_role_decomposition(proposal) roles = getattr(proposal, "token_role_decomposition", None) or self.token_role_decomposition(proposal)
base_users = max(1, model.demand_side_users) base_users = max(1, model.demand_side_users)
tx_per_user = 10 tx_per_user = 10
scenario_multipliers = {"LOW_USAGE": 0.3, "EXPECTED_USAGE": 1.0, "HIGH_USAGE": 3.0, "NO_SUBSIDY": 0.9, "TOKEN_PRICE_DOWN_80_PERCENT": 0.8, "TOKEN_PRICE_UP_10X": 1.2, "PROVIDER_CHURN_50_PERCENT": 0.7, "USER_GROWTH_10X": 10.0, "HIGH_SLASH_RATE": 1.0, "ZERO_NEW_TOKEN_EMISSIONS": 1.0, "STABLECOIN_PAYMENT_SUBSTITUTE": 1.0, "NATIVE_TOKEN_REMOVED": 1.0} scenario_multipliers = {"LOW_USAGE": 0.3, "EXPECTED": 1.0, "HIGH_USAGE": 3.0, "NO_SUBSIDY": 0.9, "TOKEN_PRICE_DOWN_80": 0.8, "TOKEN_PRICE_UP_10X": 1.2, "PROVIDER_CHURN_50": 0.7, "USER_GROWTH_10X": 10.0, "HIGH_SLASH_RATE": 1.0, "ZERO_EMISSIONS": 1.0, "STABLECOIN_PAYMENT": 1.0, "EXTERNAL_COLLATERAL": 1.0, "NATIVE_TOKEN_REMOVED": 1.0}
for name, multiplier in scenario_multipliers.items(): for name, multiplier in scenario_multipliers.items():
users = int(base_users * multiplier) users = int(base_users * multiplier)
tx = int(users * tx_per_user) tx = int(users * tx_per_user)
@ -728,15 +849,15 @@ class CryptoVentureService:
provider_supply = max(1, int(model.provider_count * multiplier)) provider_supply = max(1, int(model.provider_count * multiplier))
staking = provider_supply * model.stake_requirement staking = provider_supply * model.stake_requirement
slash_rate = float(model.slash_event_assumptions.get("high_slash_rate" if name == "HIGH_SLASH_RATE" else "baseline_rate", 0.01)) slash_rate = float(model.slash_event_assumptions.get("high_slash_rate" if name == "HIGH_SLASH_RATE" else "baseline_rate", 0.01))
emissions = 0 if name in {"NO_SUBSIDY", "ZERO_NEW_TOKEN_EMISSIONS"} else round(max(0, tx * 0.01), 2) emissions = 0 if name in {"NO_SUBSIDY", "ZERO_EMISSIONS"} else round(max(0, tx * 0.01), 2)
if name == "PROVIDER_CHURN_50_PERCENT": if name == "PROVIDER_CHURN_50":
provider_supply = max(1, int(provider_supply * 0.45)) provider_supply = max(1, int(provider_supply * 0.45))
staking = max(100, int(staking * 0.45)) staking = max(100, int(staking * 0.45))
native_removed = name == "NATIVE_TOKEN_REMOVED" native_removed = name == "NATIVE_TOKEN_REMOVED"
stablecoin_substitute = name == "STABLECOIN_PAYMENT_SUBSTITUTE" stablecoin_substitute = name in {"STABLECOIN_PAYMENT", "EXTERNAL_COLLATERAL"}
protocol_degrades = native_removed and bool(roles.required_roles) protocol_degrades = native_removed and bool(roles.required_roles)
scenarios[name] = {"users": users, "transactions": tx, "fees": fees, "token_demand": 0 if native_removed else fees + staking * 0.01, "provider_supply": provider_supply, "staking": 0 if native_removed else staking, "emissions": emissions, "treasury": round(1000 + fees * model.treasury_share - emissions * 0.1, 2), "circulating_supply": 0 if native_removed else 1_000_000 + emissions, "token_velocity": 0 if native_removed else round(tx / max(1, fees + staking * 0.01), 2), "reward_coverage": round(fees / max(1, emissions), 2), "network_security_budget": 0 if native_removed else staking, "slash_events": round(tx * slash_rate, 2), "stablecoin_payment_works": stablecoin_substitute or roles.roles.get("PAYMENT") != TokenRoleRequirement.REQUIRED, "protocol_degrades_without_native_token": protocol_degrades} scenarios[name] = {"users": users, "transactions": tx, "fees": fees, "token_demand": 0 if native_removed else fees + staking * 0.01, "provider_supply": provider_supply, "staking": 0 if native_removed else staking, "emissions": emissions, "treasury": round(1000 + fees * model.treasury_share - emissions * 0.1, 2), "circulating_supply": 0 if native_removed else 1_000_000 + emissions, "token_velocity": 0 if native_removed else round(tx / max(1, fees + staking * 0.01), 2), "reward_coverage": round(fees / max(1, emissions), 2), "network_security_budget": 0 if native_removed else staking, "slash_events": round(tx * slash_rate, 2), "stablecoin_payment_works": stablecoin_substitute or roles.roles.get("PAYMENT") != TokenRoleRequirement.REQUIRED, "external_collateral_works": name == "EXTERNAL_COLLATERAL" and roles.stablecoin_counterfactual.get("external_collateral_identical_security"), "protocol_degrades_without_native_token": protocol_degrades}
summary = {"token_price_appreciation_primary_success_variable": False, "subsidy_removed_survives": scenarios["NO_SUBSIDY"]["reward_coverage"] >= 1.0, "native_token_removed_degrades_protocol": scenarios["NATIVE_TOKEN_REMOVED"]["protocol_degrades_without_native_token"], "stablecoin_payment_substitute_works": scenarios["STABLECOIN_PAYMENT_SUBSTITUTE"]["stablecoin_payment_works"], "stress_notes": ["Price appreciation is not a success variable.", "If native-token-removed changes little, reduce token necessity."]} summary = {"token_price_appreciation_primary_success_variable": False, "subsidy_removed_survives": scenarios["NO_SUBSIDY"]["reward_coverage"] >= 1.0, "native_token_removed_degrades_protocol": scenarios["NATIVE_TOKEN_REMOVED"]["protocol_degrades_without_native_token"], "stablecoin_payment_substitute_works": scenarios["STABLECOIN_PAYMENT"]["stablecoin_payment_works"], "external_collateral_substitute_works": scenarios["EXTERNAL_COLLATERAL"]["external_collateral_works"], "stress_notes": ["Price appreciation is not a success variable.", "If native-token-removed changes little, reduce token necessity."]}
return TokenomicsSimulation.objects.update_or_create(proposal=proposal, defaults={"scenarios": scenarios, "summary": summary})[0] return TokenomicsSimulation.objects.update_or_create(proposal=proposal, defaults={"scenarios": scenarios, "summary": summary})[0]
def crypto_scenario_lab(self, cohort: VentureCohort) -> list[CryptoScenarioLab]: def crypto_scenario_lab(self, cohort: VentureCohort) -> list[CryptoScenarioLab]:
@ -774,7 +895,8 @@ class CryptoVentureService:
score = round(sum(component_scores.values()) / len(component_scores) - len(red.flags) * 4, 1) score = round(sum(component_scores.values()) / len(component_scores) - len(red.flags) * 4, 1)
decision = self._crypto_decision(component_scores, token.classification, red.flags) decision = self._crypto_decision(component_scores, token.classification, red.flags)
autonomy_class = CryptoAutonomyClass.AUTONOMOUS_CRYPTO if autonomy.autonomous_operability_score >= 70 else CryptoAutonomyClass.ASSISTED_CRYPTO autonomy_class = CryptoAutonomyClass.AUTONOMOUS_CRYPTO if autonomy.autonomous_operability_score >= 70 else CryptoAutonomyClass.ASSISTED_CRYPTO
return {"proposal_id": str(proposal.id), "company": proposal.title, "one_line_thesis": proposal.pitch.get("One-line thesis", ""), "product_thesis": proposal.protocol_thesis.product_thesis, "protocol_thesis": proposal.protocol_thesis.protocol_thesis, "user": proposal.target_customer, "pre_token_business": pretoken.pre_token_business, "pre_token_monetization": pretoken.mode, "pre_token_monetization_potential": pretoken.pre_token_monetization_potential, "pre_token_revenue_ladder": pretoken.revenue_ladder, "protocol_economy": proposal.protocol_thesis.protocol_thesis, "token_economy": proposal.protocol_thesis.token_thesis, "token_thesis": proposal.protocol_thesis.token_thesis, "why_onchain": proposal.onchain_assessment.rationale, "why_token": token.rationale, "token_role_decomposition": roles.roles, "stablecoin_counterfactual": roles.stablecoin_counterfactual, "native_token_removed_outcome": roles.native_token_removed_outcome, "native_token_removed_degrades_protocol": sim.summary.get("native_token_removed_degrades_protocol", False), "token_utility": token.utility_categories, "token_necessity_classification": token.classification, "token_demand_loop": proposal.token_demand_loop.loop, "value_capture": proposal.value_capture.value_accrual, "network_effect": proposal.protocol_thesis.network_effect, "bootstrap_plan": proposal.protocol_thesis.bootstrap_plan, "autonomous_operability": autonomy.autonomous_operability_score, "crypto_autonomy_class": autonomy_class, "regulatory_manageability": policy.regulatory_manageability_score, "security_assessment": security.decision if security else "NOT_ASSESSED", "security_risk": component_scores["SECURITY_MODEL_QUALITY"], "validation_experiment": proposal.validation_plan, "component_scores": component_scores, "value_accrual_quality": value.value_accrual_quality_score, "tokenomics_sustainability": value.tokenomics_sustainability_score, "simulation_summary": sim.summary, "token_red_team_flags": red.flags, "crypto_ic_decision": decision, "crypto_ic_score": score, "research": proposal.metadata.get("research", {}), "research_confidence": proposal.metadata.get("research", {}).get("research_status", "RESEARCH_INSUFFICIENT"), "legal_review_required": policy.legal_review_required} counterfactual = token.metadata.get("sol_counterfactual_review", {}) if isinstance(token.metadata, dict) else {}
return {"proposal_id": str(proposal.id), "company": proposal.title, "one_line_thesis": proposal.pitch.get("One-line thesis", ""), "product_thesis": proposal.protocol_thesis.product_thesis, "protocol_thesis": proposal.protocol_thesis.protocol_thesis, "user": proposal.target_customer, "pre_token_business": pretoken.pre_token_business, "pre_token_monetization": pretoken.mode, "pre_token_monetization_potential": pretoken.pre_token_monetization_potential, "pre_token_revenue_ladder": pretoken.revenue_ladder, "protocol_economy": proposal.protocol_thesis.protocol_thesis, "token_economy": proposal.protocol_thesis.token_thesis, "token_thesis": proposal.protocol_thesis.token_thesis, "why_onchain": proposal.onchain_assessment.rationale, "why_token": token.rationale, "token_role_decomposition": roles.roles, "stablecoin_counterfactual": roles.stablecoin_counterfactual, "external_collateral_counterfactual": counterfactual.get("model_c_external_collateral", {}), "native_token_removed_breaks": token.metadata.get("native_token_removed_breaks", []), "native_token_removed_degradation": token.metadata.get("native_token_removed_degradation", ""), "native_token_removed_explanation": token.metadata.get("native_token_removed_explanation", ""), "native_token_removed_outcome": roles.native_token_removed_outcome, "native_token_removed_degrades_protocol": sim.summary.get("native_token_removed_degrades_protocol", False), "token_utility": token.utility_categories, "token_necessity_classification": token.classification, "token_demand_loop": proposal.token_demand_loop.loop, "value_capture": proposal.value_capture.value_accrual, "network_effect": proposal.protocol_thesis.network_effect, "bootstrap_plan": proposal.protocol_thesis.bootstrap_plan, "autonomous_operability": autonomy.autonomous_operability_score, "crypto_autonomy_class": autonomy_class, "regulatory_manageability": policy.regulatory_manageability_score, "security_assessment": security.decision if security else "NOT_ASSESSED", "security_risk": component_scores["SECURITY_MODEL_QUALITY"], "guard_verdict": security.decision if security else "NOT_ASSESSED", "validation_experiment": proposal.validation_plan, "component_scores": component_scores, "value_accrual_quality": value.value_accrual_quality_score, "tokenomics_sustainability": value.tokenomics_sustainability_score, "simulation_summary": sim.summary, "token_red_team_flags": red.flags, "crypto_ic_decision": decision, "crypto_ic_score": score, "research": proposal.metadata.get("research", {}), "research_confidence": proposal.metadata.get("research", {}).get("research_status", "RESEARCH_INSUFFICIENT"), "legal_review_required": policy.legal_review_required}
def assess_autonomy(self, proposal: CompanyProposal) -> AutonomousOperabilityAssessment: def assess_autonomy(self, proposal: CompanyProposal) -> AutonomousOperabilityAssessment:
assessment = self.venture.assess_autonomous_operability(proposal) assessment = self.venture.assess_autonomous_operability(proposal)
@ -812,7 +934,7 @@ class CryptoVentureService:
return CryptoICDecisionType.PROTOCOL_VALIDATE return CryptoICDecisionType.PROTOCOL_VALIDATE
def _sol_final_ic_adjustment(self, row: dict[str, Any]) -> dict[str, Any]: def _sol_final_ic_adjustment(self, row: dict[str, Any]) -> dict[str, Any]:
review = self._sol_json("Final Crypto IC review. Return JSON with optional score_adjustment -20..20, decision from PROTOCOL_VALIDATE, TESTNET_PILOT, REVISE_TOKEN_MODEL, ROUTE_TO_SAAS, WATCHLIST, REJECT_TOKEN_NOT_NEEDED, REJECT_SPECULATIVE, REJECT_REGULATORY_RISK, REJECT_ECONOMIC_MODEL, REJECT_OPERABILITY, and rationale. Do not promote weak token ideas. Row: " + json.dumps(row, default=str)) review = self._sol_json("Final Crypto IC review V0.3. Consider original Sol thesis, independent token counterfactual, Token Red Team, Guard assessment, token economic model, simulation, primary-source evidence, autonomy analysis, pre-token revenue model, and uncertainty. Return JSON with optional score_adjustment -20..20, decision from TESTNET_PILOT, PROTOCOL_VALIDATE, REVISE_TOKEN_MODEL, ROUTE_TO_SAAS, ROUTE_TO_ASSISTED_CRYPTO, WATCHLIST, REJECT_TOKEN_NOT_NEEDED, REJECT_ONCHAIN_NOT_NEEDED, REJECT_SECURITY_MODEL, REJECT_ECONOMIC_MODEL, REJECT_AUTONOMY, REJECT_NO_DEMAND, and rationale. Do not promote weak token ideas or fill finalist slots. Row: " + json.dumps(row, default=str))
if not review: if not review:
return row return row
adjustment = max(-20, min(20, float(review.get("score_adjustment", 0)))) adjustment = max(-20, min(20, float(review.get("score_adjustment", 0))))
@ -861,8 +983,13 @@ class CryptoVentureService:
def _sol_json(self, prompt: str) -> dict[str, Any]: def _sol_json(self, prompt: str) -> dict[str, Any]:
if self.router is None or self.final_ic_model_hint not in self.router.providers: if self.router is None or self.final_ic_model_hint not in self.router.providers:
return {} return {}
return self._model_json(self.final_ic_model_hint, prompt, purpose=ModelCapability.REASONING)
def _model_json(self, model_hint: str, prompt: str, *, purpose: str) -> dict[str, Any]:
if self.router is None or model_hint not in self.router.providers:
return {}
try: try:
response = self.router.complete(ModelRequestContract(purpose=ModelCapability.REASONING, model_hint=self.final_ic_model_hint, prompt=prompt)) response = self.router.complete(ModelRequestContract(purpose=purpose, model_hint=model_hint, prompt=prompt))
parsed = extract_json_object(response.content) parsed = extract_json_object(response.content)
return parsed if isinstance(parsed, dict) else {} return parsed if isinstance(parsed, dict) else {}
except Exception: except Exception:
@ -894,7 +1021,7 @@ class CryptoVentureService:
return VentureArtifact.objects.create(proposal=proposal, mandate=mandate, graph_run=graph_run, artifact_type=artifact_type, name=name, content=content, readable=readable, generated_by=generated_by) return VentureArtifact.objects.create(proposal=proposal, mandate=mandate, graph_run=graph_run, artifact_type=artifact_type, name=name, content=content, readable=readable, generated_by=generated_by)
def _readable_report(self, content: dict[str, Any]) -> str: def _readable_report(self, content: dict[str, Any]) -> str:
lines = ["# CRYPTO VENTURE COHORT V0.2", "", f"Cohort ID: {content['cohort_id']}", f"Crypto survivors: {content['crypto_survivors']}", "", "## Ranking"] lines = ["# CRYPTO VENTURE COHORT V0.3", "", f"Cohort ID: {content['cohort_id']}", f"Crypto survivors: {content['crypto_survivors']}", "", "## Ranking"]
for row in content["ranking"]: for row in content["ranking"]:
lines.append(f"- Rank {row.get('rank')}: {row['company']} | {row['crypto_ic_decision']} | score {row['crypto_ic_score']} | token {row['token_necessity_classification']}") lines.append(f"- Rank {row.get('rank')}: {row['company']} | {row['crypto_ic_decision']} | score {row['crypto_ic_score']} | token {row['token_necessity_classification']}")
lines.append("") lines.append("")

View file

@ -11,7 +11,7 @@ from control_plane.ventures.models import VentureCohort
class Command(BaseCommand): class Command(BaseCommand):
help = "Export a Crypto Venture Cohort V0.2 report." help = "Export a Crypto Venture Cohort V0.3 report."
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument("--cohort", help="Cohort stable ID or primary key. Defaults to latest crypto cohort.") parser.add_argument("--cohort", help="Cohort stable ID or primary key. Defaults to latest crypto cohort.")
@ -64,6 +64,7 @@ class Command(BaseCommand):
f"Cohort ID: `{payload['cohort_id']}`", f"Cohort ID: `{payload['cohort_id']}`",
f"GraphRun: `{payload['graph_run']}`", f"GraphRun: `{payload['graph_run']}`",
f"Raw protocols generated: `{payload.get('raw_protocols_generated', payload.get('accepted_protocols', 0))}`", f"Raw protocols generated: `{payload.get('raw_protocols_generated', payload.get('accepted_protocols', 0))}`",
f"Raw Sol theses: `{payload.get('raw_sol_theses', payload.get('raw_protocols_generated', 0))}`. Onchain rejected: `{payload.get('onchain_rejected', 0)}`.",
f"Token unnecessary: `{payload.get('token_unnecessary', 0)}`. Token optional/routed SaaS: `{payload.get('token_optional_routed_saas', 0)}`. Duplicates: `{payload.get('duplicates', 0)}`.", f"Token unnecessary: `{payload.get('token_unnecessary', 0)}`. Token optional/routed SaaS: `{payload.get('token_optional_routed_saas', 0)}`. Duplicates: `{payload.get('duplicates', 0)}`.",
f"Crypto survivors: `{payload.get('crypto_survivors', 0)}`. Autonomous crypto survivors: `{payload.get('autonomous_crypto_survivors', 0)}`. Security-blocked: `{payload.get('security_blocked', 0)}`. Finalists: `{payload.get('finalists', len(payload.get('top_3', [])))}`.", f"Crypto survivors: `{payload.get('crypto_survivors', 0)}`. Autonomous crypto survivors: `{payload.get('autonomous_crypto_survivors', 0)}`. Security-blocked: `{payload.get('security_blocked', 0)}`. Finalists: `{payload.get('finalists', len(payload.get('top_3', [])))}`.",
f"Generation attempts: `{payload['generation_attempts']}`", f"Generation attempts: `{payload['generation_attempts']}`",
@ -74,6 +75,12 @@ class Command(BaseCommand):
json.dumps(payload.get("runtime", {}), indent=2, default=str), json.dumps(payload.get("runtime", {}), indent=2, default=str),
"```", "```",
"", "",
"## Model Usage",
"",
"```json",
json.dumps(payload.get("model_usage", {}), indent=2, default=str),
"```",
"",
"## Ranking", "## Ranking",
"", "",
] ]
@ -98,6 +105,8 @@ class Command(BaseCommand):
"", "",
f"Stablecoin counterfactual: {json.dumps(row.get('stablecoin_counterfactual', {}), default=str)}", f"Stablecoin counterfactual: {json.dumps(row.get('stablecoin_counterfactual', {}), default=str)}",
"", "",
f"External collateral counterfactual: {json.dumps(row.get('external_collateral_counterfactual', {}), default=str)}",
"",
f"Native token removed outcome: {row.get('native_token_removed_outcome', '')}", f"Native token removed outcome: {row.get('native_token_removed_outcome', '')}",
"", "",
f"Token demand loop: {self.inline(row['token_demand_loop'])}", f"Token demand loop: {self.inline(row['token_demand_loop'])}",

View file

@ -16,12 +16,13 @@ from model_router.router import ModelRouter
class Command(BaseCommand): class Command(BaseCommand):
help = "Run a Crypto / Protocol Venture Cohort V0.2." help = "Run a Crypto / Protocol Venture Cohort V0.3."
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument("--size", type=int, default=30) parser.add_argument("--size", type=int, default=20)
parser.add_argument("--concurrency", type=int, default=2) parser.add_argument("--concurrency", type=int, default=2)
parser.add_argument("--qwen-only", action="store_true", help="Use Qwen for all crypto cohort model roles.") parser.add_argument("--qwen-only", action="store_true", help="Use Qwen for all crypto cohort model roles.")
parser.add_argument("--sol-generation", action="store_true", default=True, help="Use Sol for original V0.3 problem-first protocol ideation.")
parser.add_argument("--sol-final-ic", action="store_true", help="Use Sol for final IC role when configured.") parser.add_argument("--sol-final-ic", action="store_true", help="Use Sol for final IC role when configured.")
parser.add_argument("--no-web-research", action="store_true") parser.add_argument("--no-web-research", action="store_true")
parser.add_argument("--persist-requests", action="store_true") parser.add_argument("--persist-requests", action="store_true")
@ -36,6 +37,9 @@ class Command(BaseCommand):
os.environ["ARTIFEX_VENTURE_IDEATION_MODEL"] = "qwen" os.environ["ARTIFEX_VENTURE_IDEATION_MODEL"] = "qwen"
os.environ["ARTIFEX_VENTURE_RESEARCH_MODEL"] = "qwen" os.environ["ARTIFEX_VENTURE_RESEARCH_MODEL"] = "qwen"
os.environ["ARTIFEX_VENTURE_PORTFOLIO_IC_MODEL"] = "qwen" os.environ["ARTIFEX_VENTURE_PORTFOLIO_IC_MODEL"] = "qwen"
elif options["sol_generation"]:
os.environ["ARTIFEX_VENTURE_IDEATION_MODEL"] = "sol"
os.environ["ARTIFEX_VENTURE_RESEARCH_MODEL"] = "qwen"
if options["sol_final_ic"]: if options["sol_final_ic"]:
os.environ["ARTIFEX_VENTURE_PORTFOLIO_IC_MODEL"] = "sol" os.environ["ARTIFEX_VENTURE_PORTFOLIO_IC_MODEL"] = "sol"
try: try:
@ -44,6 +48,8 @@ class Command(BaseCommand):
raise CommandError("No model providers configured. Run seed_spark_resources first.") raise CommandError("No model providers configured. Run seed_spark_resources first.")
if options["qwen_only"] and "qwen" not in providers: if options["qwen_only"] and "qwen" not in providers:
raise CommandError("--qwen-only requested but no Qwen provider is configured.") raise CommandError("--qwen-only requested but no Qwen provider is configured.")
if not options["qwen_only"] and options["sol_generation"] and "sol" not in providers:
raise CommandError("--sol-generation requested but no Sol provider is configured.")
if options["sol_final_ic"] and "sol" not in providers: if options["sol_final_ic"] and "sol" not in providers:
raise CommandError("--sol-final-ic requested but no Sol provider is configured.") raise CommandError("--sol-final-ic requested but no Sol provider is configured.")
version = champion_crypto_venture_cohort_graph_v1() version = champion_crypto_venture_cohort_graph_v1()
@ -82,12 +88,15 @@ class Command(BaseCommand):
"concurrency": cohort.concurrency, "concurrency": cohort.concurrency,
"metrics": cohort.metrics, "metrics": cohort.metrics,
"raw_protocols_generated": cohort.metrics.get("raw_generated", 0), "raw_protocols_generated": cohort.metrics.get("raw_generated", 0),
"raw_sol_theses": cohort.metrics.get("raw_sol_theses", 0),
"onchain_rejected": cohort.metrics.get("onchain_rejected", 0),
"token_unnecessary": cohort.metrics.get("token_unnecessary_rejections", 0), "token_unnecessary": cohort.metrics.get("token_unnecessary_rejections", 0),
"token_optional_routed_saas": cohort.metrics.get("token_optional_route_to_saas", 0), "token_optional_routed_saas": cohort.metrics.get("token_optional_route_to_saas", 0),
"crypto_survivors": cohort.members.count(), "crypto_survivors": cohort.members.count(),
"autonomous_crypto_survivors": cohort.metrics.get("autonomous_crypto_survivors", 0), "autonomous_crypto_survivors": cohort.metrics.get("autonomous_crypto_survivors", 0),
"security_blocked": cohort.metrics.get("protocol_security_gate_blocked", 0), "security_blocked": cohort.metrics.get("protocol_security_gate_blocked", 0),
"finalists": cohort.metrics.get("finalists", 0), "finalists": cohort.metrics.get("finalists", 0),
"model_usage": cohort.metrics.get("model_usage", {}),
"generation_sources": sorted({str(member.proposal.metadata.get("generation_source", "unknown")) for member in members}), "generation_sources": sorted({str(member.proposal.metadata.get("generation_source", "unknown")) for member in members}),
"top_3": review.top_3 if review else [], "top_3": review.top_3 if review else [],
"rejected_token_not_needed": [row for row in (review.rankings if review else []) if row.get("crypto_ic_decision") == "REJECT_TOKEN_NOT_NEEDED"], "rejected_token_not_needed": [row for row in (review.rankings if review else []) if row.get("crypto_ic_decision") == "REJECT_TOKEN_NOT_NEEDED"],

View file

@ -109,11 +109,16 @@ class CryptoICDecisionType(models.TextChoices):
TESTNET_PILOT = "TESTNET_PILOT" TESTNET_PILOT = "TESTNET_PILOT"
REVISE_TOKEN_MODEL = "REVISE_TOKEN_MODEL" REVISE_TOKEN_MODEL = "REVISE_TOKEN_MODEL"
ROUTE_TO_SAAS = "ROUTE_TO_SAAS" ROUTE_TO_SAAS = "ROUTE_TO_SAAS"
ROUTE_TO_ASSISTED_CRYPTO = "ROUTE_TO_ASSISTED_CRYPTO"
WATCHLIST = "WATCHLIST" WATCHLIST = "WATCHLIST"
REJECT_TOKEN_NOT_NEEDED = "REJECT_TOKEN_NOT_NEEDED" REJECT_TOKEN_NOT_NEEDED = "REJECT_TOKEN_NOT_NEEDED"
REJECT_ONCHAIN_NOT_NEEDED = "REJECT_ONCHAIN_NOT_NEEDED"
REJECT_SECURITY_MODEL = "REJECT_SECURITY_MODEL"
REJECT_SPECULATIVE = "REJECT_SPECULATIVE" REJECT_SPECULATIVE = "REJECT_SPECULATIVE"
REJECT_REGULATORY_RISK = "REJECT_REGULATORY_RISK" REJECT_REGULATORY_RISK = "REJECT_REGULATORY_RISK"
REJECT_ECONOMIC_MODEL = "REJECT_ECONOMIC_MODEL" REJECT_ECONOMIC_MODEL = "REJECT_ECONOMIC_MODEL"
REJECT_AUTONOMY = "REJECT_AUTONOMY"
REJECT_NO_DEMAND = "REJECT_NO_DEMAND"
REJECT_OPERABILITY = "REJECT_OPERABILITY" REJECT_OPERABILITY = "REJECT_OPERABILITY"

View file

@ -12,6 +12,7 @@ def crypto_venture_cohort_graph_v1() -> ExecutionGraphSpec:
"prepare_crypto_mandate", "prepare_crypto_mandate",
"portfolio_crypto_thesis_review", "portfolio_crypto_thesis_review",
"generate_independent_protocols", "generate_independent_protocols",
"onchain_necessity_gate",
"token_necessity_gate", "token_necessity_gate",
"novelty_gate", "novelty_gate",
"regenerate_rejected_slots", "regenerate_rejected_slots",
@ -30,7 +31,7 @@ def crypto_venture_cohort_graph_v1() -> ExecutionGraphSpec:
"produce_crypto_cohort_report", "produce_crypto_cohort_report",
"complete", "complete",
] ]
spec = ExecutionGraphSpec(name="crypto_venture_cohort", version=2, graph_type="CRYPTO_VENTURE_COHORT", entry="prepare_crypto_mandate", nodes={node: GraphNodeSpec(node, node if node == "complete" else f"crypto_venture_{node}") for node in nodes}, edges=[GraphEdgeSpec(nodes[index], nodes[index + 1], "success") for index in range(len(nodes) - 1)], terminal_nodes=["complete"], metadata={"description": "Crypto / Protocol Venture Cohort V0.2: 30-raw strict funnel, pre-token monetization, hardened research, token economic model, Guard-style security design review."}) spec = ExecutionGraphSpec(name="crypto_venture_cohort", version=3, graph_type="CRYPTO_VENTURE_COHORT", entry="prepare_crypto_mandate", nodes={node: GraphNodeSpec(node, node if node == "complete" else f"crypto_venture_{node}") for node in nodes}, edges=[GraphEdgeSpec(nodes[index], nodes[index + 1], "success") for index in range(len(nodes) - 1)], terminal_nodes=["complete"], metadata={"description": "Crypto / Protocol Venture Cohort V0.3: Sol-native problem-first ideation, explicit onchain gate, independent token counterfactual judge, primary-source-first research."})
spec.validate() spec.validate()
return spec return spec
@ -70,6 +71,12 @@ class TokenNecessityNode(CryptoCohortNode):
return NodeResult("COMPLETE", "success", {"assessment_count": len(assessments)}) return NodeResult("COMPLETE", "success", {"assessment_count": len(assessments)})
class OnchainNecessityNode(CryptoCohortNode):
def run(self, context: GraphExecutionContext) -> NodeResult:
assessments = self.service.onchain_necessity_gate(self.cohort(context))
return NodeResult("COMPLETE", "success", {"assessment_count": len(assessments)})
class NoveltyGateNode(CryptoCohortNode): class NoveltyGateNode(CryptoCohortNode):
def run(self, context: GraphExecutionContext) -> NodeResult: def run(self, context: GraphExecutionContext) -> NodeResult:
return NodeResult("COMPLETE", "success", self.service.novelty_gate(self.cohort(context))) return NodeResult("COMPLETE", "success", self.service.novelty_gate(self.cohort(context)))
@ -164,6 +171,7 @@ def crypto_venture_cohort_registry(service: CryptoVentureService, *, cohort_size
PrepareCryptoMandateNode(service, "crypto_venture_prepare_crypto_mandate", cohort_size=cohort_size, concurrency=concurrency), PrepareCryptoMandateNode(service, "crypto_venture_prepare_crypto_mandate", cohort_size=cohort_size, concurrency=concurrency),
NoopNode(service, "crypto_venture_portfolio_crypto_thesis_review"), NoopNode(service, "crypto_venture_portfolio_crypto_thesis_review"),
GenerateProtocolsNode(service, "crypto_venture_generate_independent_protocols"), GenerateProtocolsNode(service, "crypto_venture_generate_independent_protocols"),
OnchainNecessityNode(service, "crypto_venture_onchain_necessity_gate"),
TokenNecessityNode(service, "crypto_venture_token_necessity_gate"), TokenNecessityNode(service, "crypto_venture_token_necessity_gate"),
NoveltyGateNode(service, "crypto_venture_novelty_gate"), NoveltyGateNode(service, "crypto_venture_novelty_gate"),
RegenerateRejectedSlotsNode(service, "crypto_venture_regenerate_rejected_slots"), RegenerateRejectedSlotsNode(service, "crypto_venture_regenerate_rejected_slots"),

View file

@ -16,8 +16,14 @@ class SolCryptoReviewProvider(ModelProvider):
def complete(self, request: ModelRequestContract) -> ModelResponseContract: def complete(self, request: ModelRequestContract) -> ModelResponseContract:
prompt = request.prompt prompt = request.prompt
if "Counterfactual token necessity" in prompt: if "PASS A - PROBLEM" in prompt:
payload = {"classification": "TOKEN_STRONGLY_JUSTIFIED", "score": 78, "rationale": "Provider staking and slashing materially degrade with database credits.", "fiat_or_database_substitution": "Stablecoin can pay fees but cannot replace bonded slashing and public reputation."} payload = {"name": "Machine Identity Credit Union", "one_line_thesis": "Autonomous agents build portable credit histories for machine services.", "product_thesis": "A pre-token API for machine credit and settlement risk scoring.", "user": "AI agent operators", "participants": ["agents", "service providers", "risk observers"], "supply_side": "providers publish measurable service outcomes", "demand_side": "agents need counterparties", "coordination_failure": "portable machine credit is not trusted across operators", "trust_problem": "operators can rewrite private reputation", "why_neutral_network_may_help": "neutral verifiable state lets agents carry economic identity across providers", "pre_token_product": "risk scoring API", "pre_token_customer": "agent operators", "pre_token_value": "lower counterparty default risk", "pre_token_payment_method": "Stripe or stablecoin usage credits", "pre_token_revenue_model": "$100 beta packages", "revenue_1000_path": "10 beta packages", "revenue_5000_path": "20 risk API packages", "revenue_10000_path": "20 subscriptions", "autonomous_operating_loop": "machine demand -> discovery -> settlement -> verification -> reputation update", "bootstrap_plan": "local/testnet simulation", "validation_experiment": "simulate 100 agent jobs", "major_risks": ["token not required"], "confidence": 0.7}
elif "PASS B - ONCHAIN" in prompt:
payload = {"onchain_necessity_score": 86, "why_onchain": "public reputation and escrow materially degrade in private databases", "offchain_substitute": "single operator SaaS", "pass_onchain": True, "reasons": ["verifiable state", "neutral settlement", "machine-to-machine interaction"]}
elif "PASS C - DERIVED TOKEN" in prompt:
payload = {"token_thesis": "Native bond represents protocol-specific machine default risk and is slashed for objective settlement failures.", "token_utility": ["protocol-specific collateral", "machine reputation-backed economic participation"], "token_demand_loop": ["agents earn credit", "providers require bond", "defaults slash bond", "reliable agents receive access"], "value_capture": "risk fees and slashing penalties", "security_model": "escrow, timelock, pause, oracle checks, key controls", "regulatory_risks": ["legal review required"], "required_token_functions": ["protocol-specific risk collateral"], "weak_token_functions": ["payment"]}
elif "CRYPTO_TOKEN_COUNTERFACTUAL_JUDGE" in prompt or "Counterfactual token necessity" in prompt:
payload = {"classification": "TOKEN_STRONGLY_JUSTIFIED", "score": 78, "rationale": "Provider staking and slashing materially degrade with database credits.", "fiat_or_database_substitution": "Stablecoin can pay fees but cannot replace bonded slashing and public reputation.", "model_c_external_collateral": {"breaks": "external collateral misses protocol-specific risk"}, "native_token_removed_breaks": ["machine economic identity weakens"], "native_token_removed_degradation": "MATERIAL", "native_token_removed_explanation": "USDC payments work, but protocol-specific bond/reputation does not."}
elif "Independent Token Red Team" in prompt: elif "Independent Token Red Team" in prompt:
payload = {"flags": ["BOOTSTRAP_PROBLEM"], "severity": "MEDIUM", "critique": "Provider supply bootstrap remains the main risk."} payload = {"flags": ["BOOTSTRAP_PROBLEM"], "severity": "MEDIUM", "critique": "Provider supply bootstrap remains the main risk."}
elif "Final Crypto IC" in prompt: elif "Final Crypto IC" in prompt:
@ -34,6 +40,10 @@ def service(router: ModelRouter | None = None) -> CryptoVentureService:
return CryptoVentureService(router, web_research_available=False, generation_model_hint="qwen", research_model_hint="qwen", final_ic_model_hint="sol") return CryptoVentureService(router, web_research_available=False, generation_model_hint="qwen", research_model_hint="qwen", final_ic_model_hint="sol")
def sol_service(router: ModelRouter | None = None) -> CryptoVentureService:
return CryptoVentureService(router, web_research_available=False, generation_model_hint="sol", research_model_hint="qwen", final_ic_model_hint="sol")
def test_crypto_track_and_token_gate_remove_weak_then_regenerate() -> None: def test_crypto_track_and_token_gate_remove_weak_then_regenerate() -> None:
svc = service() svc = service()
cohort = svc.prepare_cohort(size=2, concurrency=1) cohort = svc.prepare_cohort(size=2, concurrency=1)
@ -52,7 +62,23 @@ def test_crypto_track_and_token_gate_remove_weak_then_regenerate() -> None:
assert weak.status == CompanyProposalStatus.REJECTED assert weak.status == CompanyProposalStatus.REJECTED
assert weak.metadata["routed_to_saas"] is False assert weak.metadata["routed_to_saas"] is False
assert cohort.members.count() == 0 assert cohort.members.count() == 0
assert cohort.metrics["regeneration_policy"].startswith("V0.2") assert cohort.metrics["regeneration_policy"].startswith("V0.3")
def test_v03_sol_problem_first_generation_no_token_in_pass_a_and_onchain_gate() -> None:
svc = sol_service(ModelRouter({"sol": SolCryptoReviewProvider()}))
cohort = svc.prepare_cohort(size=1, concurrency=1)
proposals = svc.generate_protocols(cohort)
proposal = proposals[0]
assessment = svc.onchain_necessity_gate(cohort)[0]
passes = proposal.metadata["crypto"]["generation_passes"]
assert proposal.metadata["generation_source"] == "sol"
assert "token_thesis" not in passes["pass_a_problem_network"]
assert proposal.metadata["crypto"]["problem_first_pass_a_had_no_token"] is True
assert assessment.onchain_necessity_score == 86
assert cohort.metrics["raw_sol_theses"] == 1
def test_sol_counterfactual_red_team_and_final_ic_are_used() -> None: def test_sol_counterfactual_red_team_and_final_ic_are_used() -> None:
@ -93,9 +119,46 @@ def test_tokenomics_simulation_is_proposal_specific() -> None:
low_sim = svc.simulate_tokenomics(low) low_sim = svc.simulate_tokenomics(low)
high_sim = svc.simulate_tokenomics(high) high_sim = svc.simulate_tokenomics(high)
assert low_sim.scenarios["EXPECTED_USAGE"]["users"] != high_sim.scenarios["EXPECTED_USAGE"]["users"] assert low_sim.scenarios["EXPECTED"]["users"] != high_sim.scenarios["EXPECTED"]["users"]
assert low_sim.scenarios["EXPECTED_USAGE"]["token_demand"] != high_sim.scenarios["EXPECTED_USAGE"]["token_demand"] assert low_sim.scenarios["EXPECTED"]["token_demand"] != high_sim.scenarios["EXPECTED"]["token_demand"]
assert "NATIVE_TOKEN_REMOVED" in low_sim.scenarios assert "NATIVE_TOKEN_REMOVED" in low_sim.scenarios
assert "EXTERNAL_COLLATERAL" in low_sim.scenarios
def test_v03_primary_source_quality_wrong_category_and_weak_coverage() -> None:
svc = service()
cohort = svc.prepare_cohort(size=1, concurrency=1)
proposal = svc._create_proposal(cohort.mandate, svc._fallback_payload(0, "MACHINE_ECONOMIC_IDENTITY"), "test", "MACHINE_ECONOMIC_IDENTITY")
good = svc.crypto_source_quality(proposal, {"url": "https://ethereum.org/en/developers/docs/", "title": "Ethereum developer docs", "summary": "onchain escrow settlement staking protocol", "category": "ONCHAIN_ALTERNATIVES"})
wrong = svc.crypto_source_quality(proposal, {"url": "https://www.microsoft.com/windows", "title": "Windows help", "summary": "generic software", "category": "TOKEN_MODELS"})
assert good["accepted"] is True
assert good["source_tier"] == "TIER_A"
assert wrong["accepted"] is False
assert wrong["reason"].startswith("SOURCE_REJECT_")
assert svc.category_coverage_confidence([{"quality": "weak", "url": "https://example.com/a"}]) == "LOW"
def test_v03_search_engine_circuit_breaker(monkeypatch) -> None:
class CaptchaClient:
def search_payload(self, query: str) -> dict:
return {"unresponsive_engines": [("google", "CAPTCHA required")], "results": []}
class EmptyFetcher:
def fetch_many(self, sources, max_pages=5):
return []
monkeypatch.setattr("agents.crypto_venture.SearxngSearchClient.from_resources", staticmethod(lambda: CaptchaClient()))
monkeypatch.setattr("agents.crypto_venture.WebPageFetcher", lambda: EmptyFetcher())
svc = service()
cohort = svc.prepare_cohort(size=1, concurrency=1)
proposal = svc._create_proposal(cohort.mandate, svc._fallback_payload(0, "MACHINE_ECONOMIC_IDENTITY"), "test", "MACHINE_ECONOMIC_IDENTITY")
research = svc.crypto_research(proposal, depth="light")
assert research["queries_avoided_due_to_circuit_breaker"] > 0
assert research["search_engine_health"]["CAPTCHA"] >= 4
def test_v02_pretoken_roles_security_and_launch_readiness() -> None: def test_v02_pretoken_roles_security_and_launch_readiness() -> None:
@ -117,6 +180,18 @@ def test_v02_pretoken_roles_security_and_launch_readiness() -> None:
assert readiness.launch_readiness_status != "READY_FOR_HUMAN_LAUNCH_REVIEW" assert readiness.launch_readiness_status != "READY_FOR_HUMAN_LAUNCH_REVIEW"
def test_v03_independent_counterfactual_native_removed_fields() -> None:
svc = service(ModelRouter({"sol": SolCryptoReviewProvider()}))
cohort = svc.prepare_cohort(size=1, concurrency=1)
proposal = svc._create_proposal(cohort.mandate, svc._fallback_payload(0, "PROOF_MARKETS"), "test", "PROOF_MARKETS")
token = svc.assess_token_necessity(proposal)
assert token.metadata["independent_counterfactual_judge"] == "CRYPTO_TOKEN_COUNTERFACTUAL_JUDGE"
assert token.metadata["native_token_removed_degradation"] == "MATERIAL"
assert token.metadata["model_c_external_collateral"]
def test_crypto_cohort_graph_real_gates_and_report() -> None: def test_crypto_cohort_graph_real_gates_and_report() -> None:
svc = service() svc = service()
version = champion_crypto_venture_cohort_graph_v1() version = champion_crypto_venture_cohort_graph_v1()
@ -130,7 +205,8 @@ def test_crypto_cohort_graph_real_gates_and_report() -> None:
assert graph_run.status == GraphRunStatus.COMPLETE assert graph_run.status == GraphRunStatus.COMPLETE
assert cohort.metadata["venture_track"] == VentureTrack.CRYPTO_PROTOCOL assert cohort.metadata["venture_track"] == VentureTrack.CRYPTO_PROTOCOL
assert cohort.members.count() == 3 assert cohort.members.count() <= 3
assert cohort.metrics["protocol_security_gate_passed"] >= 1 assert cohort.metrics["protocol_security_gate_passed"] >= 1
assert len(report.content["ranking"]) == 3 assert len(report.content["ranking"]) <= 3
assert len(report.content["top_3"]) <= 3 assert len(report.content["top_3"]) <= 3
assert report.content["title"] == "CRYPTO VENTURE COHORT V0.3"