Add crypto protocol cohort V0.3
This commit is contained in:
parent
783ad66f4e
commit
d4ccc2e6f0
6 changed files with 320 additions and 86 deletions
|
|
@ -58,19 +58,22 @@ from research.searxng import SearxngSearchClient, WebPageFetcher
|
|||
|
||||
|
||||
CRYPTO_TERRITORIES = [
|
||||
"DECENTRALIZED_AI_COMPUTE",
|
||||
"AGENT_TO_AGENT_PAYMENTS",
|
||||
"PROOF_ATTESTATION_MARKETS",
|
||||
"DECENTRALIZED_DATA_MARKETS",
|
||||
"SECURITY_STAKING_PROTOCOLS",
|
||||
"MACHINE_REPUTATION",
|
||||
"AUTONOMOUS_API_MARKETPLACES",
|
||||
"DEPIN_COORDINATION",
|
||||
"ONCHAIN_AGENT_COMMERCE",
|
||||
"DECENTRALIZED_MODEL_SERVICES",
|
||||
"ONCHAIN_CREDENTIALS",
|
||||
"PROTOCOLIZED_ESCROW",
|
||||
"CRYPTO_NATIVE_INTELLIGENCE",
|
||||
"MACHINE_ECONOMIC_IDENTITY",
|
||||
"DECENTRALIZED_SECURITY_MARKETS",
|
||||
"VERIFIABLE_AI_INFERENCE",
|
||||
"PROOF_MARKETS",
|
||||
"DECENTRALIZED_COMPUTE",
|
||||
"DATA_PROVENANCE_NETWORKS",
|
||||
"MACHINE_TO_MACHINE_SETTLEMENT",
|
||||
"AUTONOMOUS_ESCROW",
|
||||
"DECENTRALIZED_INSURANCE_RISK_POOLS",
|
||||
"RESOURCE_RIGHTS_MARKETS",
|
||||
"DECENTRALIZED_BANDWIDTH_STORAGE",
|
||||
"AI_MODEL_CONTRIBUTION_NETWORKS",
|
||||
"SOFTWARE_SECURITY_BONDS",
|
||||
"AUTONOMOUS_AGENT_COORDINATION",
|
||||
"DECENTRALIZED_ORACLE_ATTESTATION",
|
||||
"CRYPTO_NATIVE_CREATOR_DATA_RIGHTS",
|
||||
"OPEN_CRYPTO_CATEGORY",
|
||||
]
|
||||
|
||||
|
|
@ -108,7 +111,10 @@ CRYPTO_SCORE_DIMENSIONS = [
|
|||
"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 = [
|
||||
"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.",
|
||||
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"],
|
||||
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
|
||||
|
||||
def prepare_cohort(self, *, size: int, graph_run=None, concurrency: int = 2) -> VentureCohort:
|
||||
mandate = self.create_crypto_mandate()
|
||||
cohort_id = f"CPV02-{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})
|
||||
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 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]:
|
||||
accepted = []
|
||||
attempts = 0
|
||||
model_usage = Counter(cohort.metrics.get("model_usage", {}))
|
||||
for index in range(cohort.cohort_size):
|
||||
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
|
||||
proposal = self._create_proposal(cohort.mandate, payload, source, territory)
|
||||
VentureCohortMember.objects.create(cohort=cohort, proposal=proposal, metadata={"territory": territory})
|
||||
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.save(update_fields=["metrics", "status", "updated_at"])
|
||||
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]:
|
||||
removed = 0
|
||||
hard_excluded = 0
|
||||
duplicate = 0
|
||||
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")):
|
||||
proposal = member.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]:
|
||||
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"])
|
||||
return created
|
||||
|
||||
|
|
@ -255,10 +280,18 @@ class CryptoVentureService:
|
|||
all_sources = []
|
||||
query_results = []
|
||||
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:
|
||||
per_query_limit = 4 if depth == "deep" else 2
|
||||
for category, queries in query_plan.items():
|
||||
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:
|
||||
payload = client.search_payload(query)
|
||||
except Exception as exc:
|
||||
|
|
@ -266,13 +299,16 @@ class CryptoVentureService:
|
|||
continue
|
||||
unresponsive = payload.get("unresponsive_engines", []) if isinstance(payload, dict) else []
|
||||
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 []
|
||||
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]:
|
||||
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})
|
||||
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}
|
||||
accepted = []
|
||||
rejected = []
|
||||
|
|
@ -280,21 +316,24 @@ class CryptoVentureService:
|
|||
for source in all_sources:
|
||||
url = source.get("url")
|
||||
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
|
||||
seen.add(url)
|
||||
enriched = {**source, **({"content": page_by_url[url].get("text", "")} if url in page_by_url else {})}
|
||||
quality = self.crypto_source_quality(proposal, enriched)
|
||||
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:
|
||||
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"}
|
||||
coverage = {category: category in covered for category in CRYPTO_RESEARCH_CATEGORIES}
|
||||
category_sources = {category: [source for source in accepted if source.get("category") == category] 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)
|
||||
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))
|
||||
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.metadata = {**proposal.metadata, "research": research}
|
||||
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]]:
|
||||
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 = {
|
||||
"USER_PAIN": [f"{seed} user pain", f"{proposal.target_customer} decentralized protocol pain"],
|
||||
"EXISTING_PROTOCOLS": [f"{seed} protocol", f"{seed} competitors crypto"],
|
||||
"USER_PAIN": [f"{seed} user pain", f"{proposal.target_customer} coordination failure"],
|
||||
"EXISTING_PROTOCOLS": [f"{seed} protocol documentation", f"{seed} crypto protocol precedent"],
|
||||
"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"],
|
||||
"STAKING_MODELS": [f"{seed} staking slashing", f"provider staking protocol slashing"],
|
||||
"SLASHING_PRECEDENTS": [f"{seed} slashing precedent", f"objective slashing crypto protocol"],
|
||||
"COLLATERAL_MODELS": [f"{seed} collateral model stablecoin ETH staking"],
|
||||
"STAKING_SLASHING_PRECEDENTS": [f"{seed} staking slashing", f"objective slashing crypto protocol"],
|
||||
"PROVIDER_ECONOMICS": [f"{seed} provider economics", f"decentralized provider marketplace economics"],
|
||||
"NETWORK_BOOTSTRAP": [f"{seed} network bootstrap", f"crypto protocol bootstrap supply demand"],
|
||||
"ONCHAIN_ALTERNATIVES": [f"{seed} onchain alternatives"],
|
||||
"OFFCHAIN_ALTERNATIVES": [f"{seed} SaaS alternative", f"{seed} offchain alternative"],
|
||||
"SECURITY_INCIDENTS": [f"{seed} security incident", f"{seed} exploit"],
|
||||
"SECURITY_FAILURES": [f"{seed} security incident", f"{seed} exploit postmortem"],
|
||||
"LEGAL_REGULATORY": [f"{seed} token regulatory risk", f"crypto staking slashing regulatory"],
|
||||
"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
|
||||
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]:
|
||||
text = " ".join(str(source.get(key, "")) for key in ["title", "summary", "content", "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"]):
|
||||
return {"accepted": False, "score": 0.0, "reason": "noisy or low-evidence source"}
|
||||
preferred = ["docs", "github", "whitepaper", "paper", "postmortem", "audit", "security", "research", "protocol", "token", "staking", "slashing", "stablecoin", "attestation", "oracle"]
|
||||
return {"accepted": False, "score": 0.0, "reason": SOURCE_REJECTION_REASONS["SEARCH_NOISE"]}
|
||||
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)))
|
||||
source_terms = set(re.findall(r"[a-z0-9]{5,}", text))
|
||||
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)
|
||||
score = round(min(1.0, overlap * 0.03 + preferred_hits * 0.08), 2)
|
||||
if score >= 0.22:
|
||||
return {"accepted": True, "quality": "strong", "score": score, "reason": "crypto-relevant technical/protocol evidence"}
|
||||
if score >= 0.12:
|
||||
return {"accepted": True, "quality": "weak", "score": score, "reason": "accepted as weak crypto context"}
|
||||
return {"accepted": False, "score": score, "reason": "insufficient crypto/protocol relevance"}
|
||||
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 category and category_overlap == 0 and source_tier == "TIER_C" and score < 0.45:
|
||||
return {"accepted": False, "score": score, "reason": SOURCE_REJECTION_REASONS["WRONG_CATEGORY"]}
|
||||
if score >= 0.45:
|
||||
return {"accepted": True, "quality": "strong", "source_tier": source_tier, "score": score, "reason": "strong category-relevant crypto primary/technical evidence"}
|
||||
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:
|
||||
lowered = reason.lower()
|
||||
|
|
@ -407,7 +491,11 @@ class CryptoVentureService:
|
|||
member.save(update_fields=["rank", "portfolio_score", "is_top_3", "updated_at"])
|
||||
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}})
|
||||
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.save(update_fields=["metrics", "status", "updated_at"])
|
||||
return review
|
||||
|
|
@ -497,8 +585,10 @@ class CryptoVentureService:
|
|||
review = cohort.portfolio_review
|
||||
rows = review.rankings
|
||||
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}}
|
||||
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)
|
||||
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]]
|
||||
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.save(update_fields=["status", "updated_at"])
|
||||
return artifact
|
||||
|
|
@ -507,14 +597,18 @@ class CryptoVentureService:
|
|||
engine_counts = Counter()
|
||||
accepted = 0
|
||||
rejected = 0
|
||||
primary = 0
|
||||
avoided = 0
|
||||
coverage = []
|
||||
for row in rows:
|
||||
research = row.get("research", {})
|
||||
accepted += int(research.get("source_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))
|
||||
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:
|
||||
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"])
|
||||
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))
|
||||
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:
|
||||
score = float(sol_review.get("score", score))
|
||||
sol_classification = str(sol_review.get("classification", ""))
|
||||
|
|
@ -542,7 +638,13 @@ class CryptoVentureService:
|
|||
classification = TokenNecessityClassification.TOKEN_UNNECESSARY
|
||||
if sol_classification in TokenNecessityClassification.values:
|
||||
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:
|
||||
text = self._proposal_text(proposal)
|
||||
|
|
@ -569,7 +671,7 @@ class CryptoVentureService:
|
|||
value = TokenRoleRequirement.UNNECESSARY
|
||||
roles[role] = value
|
||||
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."
|
||||
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 = 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}
|
||||
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"}
|
||||
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]
|
||||
customer = proposal.target_customer[:180]
|
||||
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:
|
||||
text = self._proposal_text(proposal)
|
||||
|
|
@ -592,7 +697,7 @@ class CryptoVentureService:
|
|||
providers = max(3, 4 + len(token.utility_categories) * 2)
|
||||
users = max(25, int(float(proposal.confidence) * 220))
|
||||
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:
|
||||
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.")
|
||||
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")
|
||||
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)
|
||||
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})
|
||||
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:
|
||||
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."
|
||||
response = self.router.complete(ModelRequestContract(purpose=ModelCapability.PLANNING, model_hint=self.generation_model_hint, prompt=prompt))
|
||||
parsed = extract_json_object(response.content)
|
||||
if parsed:
|
||||
return parsed, self.generation_model_hint
|
||||
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."
|
||||
pass_a = self._model_json(self.generation_model_hint, pass_a_prompt, purpose=ModelCapability.PLANNING)
|
||||
usage["sol_generation_requests" if self.generation_model_hint == "sol" else "qwen_requests"] += 1
|
||||
if pass_a and not any(key in pass_a for key in ["token_thesis", "token_utility", "tokenomics"]):
|
||||
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:
|
||||
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]:
|
||||
names = {
|
||||
|
|
@ -634,7 +751,7 @@ class CryptoVentureService:
|
|||
def _update_protocol_research_flags(self, proposal: CompanyProposal) -> None:
|
||||
research = proposal.metadata.get("research", {})
|
||||
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"])
|
||||
|
||||
def red_team_proposal(self, proposal: CompanyProposal) -> CryptoRedTeamAssessment:
|
||||
|
|
@ -696,7 +813,11 @@ class CryptoVentureService:
|
|||
text = self._proposal_text(proposal)
|
||||
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))
|
||||
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:
|
||||
text = self._proposal_text(proposal)
|
||||
|
|
@ -720,7 +841,7 @@ class CryptoVentureService:
|
|||
roles = getattr(proposal, "token_role_decomposition", None) or self.token_role_decomposition(proposal)
|
||||
base_users = max(1, model.demand_side_users)
|
||||
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():
|
||||
users = int(base_users * multiplier)
|
||||
tx = int(users * tx_per_user)
|
||||
|
|
@ -728,15 +849,15 @@ class CryptoVentureService:
|
|||
provider_supply = max(1, int(model.provider_count * multiplier))
|
||||
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))
|
||||
emissions = 0 if name in {"NO_SUBSIDY", "ZERO_NEW_TOKEN_EMISSIONS"} else round(max(0, tx * 0.01), 2)
|
||||
if name == "PROVIDER_CHURN_50_PERCENT":
|
||||
emissions = 0 if name in {"NO_SUBSIDY", "ZERO_EMISSIONS"} else round(max(0, tx * 0.01), 2)
|
||||
if name == "PROVIDER_CHURN_50":
|
||||
provider_supply = max(1, int(provider_supply * 0.45))
|
||||
staking = max(100, int(staking * 0.45))
|
||||
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)
|
||||
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}
|
||||
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."]}
|
||||
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"]["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]
|
||||
|
||||
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)
|
||||
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
|
||||
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:
|
||||
assessment = self.venture.assess_autonomous_operability(proposal)
|
||||
|
|
@ -812,7 +934,7 @@ class CryptoVentureService:
|
|||
return CryptoICDecisionType.PROTOCOL_VALIDATE
|
||||
|
||||
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:
|
||||
return row
|
||||
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]:
|
||||
if self.router is None or self.final_ic_model_hint not in self.router.providers:
|
||||
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:
|
||||
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)
|
||||
return parsed if isinstance(parsed, dict) else {}
|
||||
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)
|
||||
|
||||
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"]:
|
||||
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("")
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from control_plane.ventures.models import VentureCohort
|
|||
|
||||
|
||||
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):
|
||||
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"GraphRun: `{payload['graph_run']}`",
|
||||
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"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']}`",
|
||||
|
|
@ -74,6 +75,12 @@ class Command(BaseCommand):
|
|||
json.dumps(payload.get("runtime", {}), indent=2, default=str),
|
||||
"```",
|
||||
"",
|
||||
"## Model Usage",
|
||||
"",
|
||||
"```json",
|
||||
json.dumps(payload.get("model_usage", {}), indent=2, default=str),
|
||||
"```",
|
||||
"",
|
||||
"## Ranking",
|
||||
"",
|
||||
]
|
||||
|
|
@ -98,6 +105,8 @@ class Command(BaseCommand):
|
|||
"",
|
||||
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"Token demand loop: {self.inline(row['token_demand_loop'])}",
|
||||
|
|
|
|||
|
|
@ -16,12 +16,13 @@ from model_router.router import ModelRouter
|
|||
|
||||
|
||||
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):
|
||||
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("--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("--no-web-research", 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_RESEARCH_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"]:
|
||||
os.environ["ARTIFEX_VENTURE_PORTFOLIO_IC_MODEL"] = "sol"
|
||||
try:
|
||||
|
|
@ -44,6 +48,8 @@ class Command(BaseCommand):
|
|||
raise CommandError("No model providers configured. Run seed_spark_resources first.")
|
||||
if options["qwen_only"] and "qwen" not in providers:
|
||||
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:
|
||||
raise CommandError("--sol-final-ic requested but no Sol provider is configured.")
|
||||
version = champion_crypto_venture_cohort_graph_v1()
|
||||
|
|
@ -82,12 +88,15 @@ class Command(BaseCommand):
|
|||
"concurrency": cohort.concurrency,
|
||||
"metrics": 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),
|
||||
"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": 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}),
|
||||
"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"],
|
||||
|
|
|
|||
|
|
@ -109,11 +109,16 @@ class CryptoICDecisionType(models.TextChoices):
|
|||
TESTNET_PILOT = "TESTNET_PILOT"
|
||||
REVISE_TOKEN_MODEL = "REVISE_TOKEN_MODEL"
|
||||
ROUTE_TO_SAAS = "ROUTE_TO_SAAS"
|
||||
ROUTE_TO_ASSISTED_CRYPTO = "ROUTE_TO_ASSISTED_CRYPTO"
|
||||
WATCHLIST = "WATCHLIST"
|
||||
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_REGULATORY_RISK = "REJECT_REGULATORY_RISK"
|
||||
REJECT_ECONOMIC_MODEL = "REJECT_ECONOMIC_MODEL"
|
||||
REJECT_AUTONOMY = "REJECT_AUTONOMY"
|
||||
REJECT_NO_DEMAND = "REJECT_NO_DEMAND"
|
||||
REJECT_OPERABILITY = "REJECT_OPERABILITY"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ def crypto_venture_cohort_graph_v1() -> ExecutionGraphSpec:
|
|||
"prepare_crypto_mandate",
|
||||
"portfolio_crypto_thesis_review",
|
||||
"generate_independent_protocols",
|
||||
"onchain_necessity_gate",
|
||||
"token_necessity_gate",
|
||||
"novelty_gate",
|
||||
"regenerate_rejected_slots",
|
||||
|
|
@ -30,7 +31,7 @@ def crypto_venture_cohort_graph_v1() -> ExecutionGraphSpec:
|
|||
"produce_crypto_cohort_report",
|
||||
"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()
|
||||
return spec
|
||||
|
||||
|
|
@ -70,6 +71,12 @@ class TokenNecessityNode(CryptoCohortNode):
|
|||
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):
|
||||
def run(self, context: GraphExecutionContext) -> NodeResult:
|
||||
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),
|
||||
NoopNode(service, "crypto_venture_portfolio_crypto_thesis_review"),
|
||||
GenerateProtocolsNode(service, "crypto_venture_generate_independent_protocols"),
|
||||
OnchainNecessityNode(service, "crypto_venture_onchain_necessity_gate"),
|
||||
TokenNecessityNode(service, "crypto_venture_token_necessity_gate"),
|
||||
NoveltyGateNode(service, "crypto_venture_novelty_gate"),
|
||||
RegenerateRejectedSlotsNode(service, "crypto_venture_regenerate_rejected_slots"),
|
||||
|
|
|
|||
|
|
@ -16,8 +16,14 @@ class SolCryptoReviewProvider(ModelProvider):
|
|||
|
||||
def complete(self, request: ModelRequestContract) -> ModelResponseContract:
|
||||
prompt = request.prompt
|
||||
if "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."}
|
||||
if "PASS A - PROBLEM" in prompt:
|
||||
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:
|
||||
payload = {"flags": ["BOOTSTRAP_PROBLEM"], "severity": "MEDIUM", "critique": "Provider supply bootstrap remains the main risk."}
|
||||
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")
|
||||
|
||||
|
||||
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:
|
||||
svc = service()
|
||||
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.metadata["routed_to_saas"] is False
|
||||
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:
|
||||
|
|
@ -93,9 +119,46 @@ def test_tokenomics_simulation_is_proposal_specific() -> None:
|
|||
low_sim = svc.simulate_tokenomics(low)
|
||||
high_sim = svc.simulate_tokenomics(high)
|
||||
|
||||
assert low_sim.scenarios["EXPECTED_USAGE"]["users"] != high_sim.scenarios["EXPECTED_USAGE"]["users"]
|
||||
assert low_sim.scenarios["EXPECTED_USAGE"]["token_demand"] != high_sim.scenarios["EXPECTED_USAGE"]["token_demand"]
|
||||
assert low_sim.scenarios["EXPECTED"]["users"] != high_sim.scenarios["EXPECTED"]["users"]
|
||||
assert low_sim.scenarios["EXPECTED"]["token_demand"] != high_sim.scenarios["EXPECTED"]["token_demand"]
|
||||
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:
|
||||
|
|
@ -117,6 +180,18 @@ def test_v02_pretoken_roles_security_and_launch_readiness() -> None:
|
|||
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:
|
||||
svc = service()
|
||||
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 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 len(report.content["ranking"]) == 3
|
||||
assert len(report.content["ranking"]) <= 3
|
||||
assert len(report.content["top_3"]) <= 3
|
||||
assert report.content["title"] == "CRYPTO VENTURE COHORT V0.3"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue