2026-08-15 20:56:56 +07:00
from __future__ import annotations
import hashlib
import json
import re
2026-08-16 15:08:32 +07:00
import threading
2026-08-15 21:42:46 +07:00
import time
2026-08-16 15:08:32 +07:00
from concurrent . futures import ThreadPoolExecutor , as_completed
2026-08-15 21:42:46 +07:00
from itertools import combinations
2026-08-15 20:56:56 +07:00
from decimal import Decimal
from typing import Any
2026-08-15 21:42:46 +07:00
from collections import Counter , defaultdict
2026-08-16 16:39:46 +07:00
from django . core . exceptions import ObjectDoesNotExist
2026-08-16 15:08:32 +07:00
from django . db import close_old_connections , connection
2026-08-15 20:56:56 +07:00
from django . utils import timezone
from control_plane . events . bus import EventBus
2026-08-16 17:48:04 +07:00
from control_plane . ventures . models import AutonomousCandidateStatus , AutonomousGateResult , AutonomousOperabilityAssessment , CapabilityPriority , CapabilityStatus , CohortIdeationMandate , CompanyBoardReview , CompanyCapabilityRequirement , CompanyMandate , CompanyProposal , CompanyProposalStatus , EvidenceTier , FounderDependencyLevel , ICDecision , ICDecisionType , ICDiligence , ICQuestion , ICResponse , NoveltyGateDecision , OpportunityTerritory , OverlapClassification , PortfolioCapabilityGap , PortfolioICReview , PortfolioSaturationAnalysis , PortfolioThesis , PortfolioThesisCluster , PortfolioThesisStatus , ThesisMatchClassification , VentureArtifact , VentureCapabilityDemand , VentureCohort , VentureCohortMember , VentureCollision , VentureGenerationRejection , VentureThesis , VentureThesisFingerprint , VentureTrack
2026-08-15 21:42:46 +07:00
from graph . models import GraphRun , GraphRunStatus
from model_router . providers import extract_json_object
2026-08-16 15:35:55 +07:00
from model_router . policy import model_for_role
2026-08-15 20:56:56 +07:00
from model_router . router import ModelCapability , ModelRequestContract , ModelRouter
2026-08-16 15:35:55 +07:00
from research . searxng import SearxngSearchClient , WebPageFetcher
2026-08-15 20:56:56 +07:00
PITCH_SECTIONS = [ " Company name " , " One-line thesis " , " Problem " , " ICP " , " Why now " , " Product / service " , " Business model " , " Pricing " , " Route to first customer " , " Validation plan " , " $50 capital allocation proposal " , " Time to first dollar " , " Path to $500 net cash " , " Competition " , " Differentiation " , " Build requirements " , " Distribution requirements " , " Risks " , " What would falsify the thesis " , " Confidence " ]
2026-08-16 17:48:04 +07:00
SCORE_DIMENSIONS = [ " Demand Evidence " , " Time-to-First-Dollar Attractiveness " , " Capital Efficiency " , " Validation Affordability " , " Gross Margin Potential " , " Distribution Feasibility " , " Build Simplicity " , " Defensibility " , " Market Opportunity " , " Competitive Position " , " Risk Manageability " , " AI Leverage " , " Platformization Potential " , " Autonomous Operability " , " Probability of Reaching $500 " ]
2026-08-15 21:42:46 +07:00
SCORE_DEFINITIONS = { dimension : " 100 = highly attractive; 0 = highly unattractive " for dimension in SCORE_DIMENSIONS }
EVIDENCE_CEILINGS = { EvidenceTier . TIER_0_THESIS : 35 , EvidenceTier . TIER_1_PUBLIC_EVIDENCE : 55 , EvidenceTier . TIER_2_CUSTOMER_SIGNAL : 70 , EvidenceTier . TIER_3_WILLINGNESS_TO_PAY : 85 , EvidenceTier . TIER_4_PAID_CUSTOMER : 95 , EvidenceTier . TIER_5_REPEATABLE_TRACTION : 100 }
2026-08-16 14:30:40 +07:00
AI_NATIVE_POLICY = { " preference " : " Prefer AI-native opportunities where Artifex can build or deliver most value with existing agents, local inference, and owned workflow capabilities. " , " preferred_classes " : [ " AI wrapper platforms " , " AI-enabled services " , " AI infrastructure / developer tools " , " AI research / intelligence products " , " AI automation for SMB / enterprise workflows " ] , " soft_portfolio_targets " : { " ai_wrapper_platforms " : " 40-50 % " , " ai_enabled_services " : " 20-30 % " , " developer_infrastructure_intelligence " : " 10-20 % " , " non_ai_economic_outliers " : " 10-20 % " } , " penalize_concentration " : [ " Shopify/ecommerce " , " generic audits " , " emergency fix services " , " one-off consulting " , " identical outbound-led service models " ] , " do_not " : " Do not let AI novelty outweigh customer demand. " }
2026-08-16 16:39:46 +07:00
DEFAULT_HARD_EXCLUSIONS = [ " AI RFP response drafting / proposal automation " , " SaaS churn prediction / retention playbook generation " ]
DEFAULT_SOFT_EXCLUSIONS = [ " generic contract scanners " , " Shopify audit products " , " generic AI content generators " , " generic productized consulting " , " generic one-off audits " , " generic chatbot wrappers " ]
DEFAULT_TERRITORY_ALLOCATION = [ OpportunityTerritory . VERTICAL_AI_WRAPPERS , OpportunityTerritory . VERTICAL_AI_WRAPPERS , OpportunityTerritory . ENTERPRISE_WORKFLOW_AUTOMATION , OpportunityTerritory . SMB_AUTOMATION , OpportunityTerritory . DEVELOPER_AI_INFRASTRUCTURE , OpportunityTerritory . INTELLIGENCE_MONITORING , OpportunityTerritory . DATA_DOCUMENT_AUTOMATION , OpportunityTerritory . AI_ENABLED_SERVICE_TO_PLATFORM , OpportunityTerritory . OPEN_CATEGORY , OpportunityTerritory . OPEN_CATEGORY ]
2026-08-16 17:10:34 +07:00
DEFAULT_DUPLICATE_POLICY = { " max_near_duplicate_per_thesis_per_cohort " : 1 , " max_competitive_per_thesis_per_cohort " : 2 , " max_attempts_per_slot " : 3 , " cohort_attempt_budget_multiplier " : 4 }
RESEARCH_CATEGORIES = [ " competitors " , " pricing " , " customer_pain " , " market_alternatives " , " regulatory_platform_risks " ]
2026-08-16 17:48:04 +07:00
AUTONOMOUS_ARCHETYPES = [ " SELF_SERVICE_AI_TOOL " , " AUTOMATED_MONITORING_PRODUCT " , " DIGITAL_ANALYSIS_SERVICE " , " AUTOMATED_TRANSFORMATION_SERVICE " , " MICRO_SAAS " , " DATA_INTELLIGENCE_SUBSCRIPTION " , " DEVELOPER_TOOL " , " AUTONOMOUS_DIGITAL_PRODUCT " , " MARKETPLACE_DELIVERED_SERVICE " , " AGENT_AS_A_SERVICE " ]
2026-08-15 20:56:56 +07:00
class VentureDiscoveryService :
2026-08-16 15:35:55 +07:00
def __init__ ( self , router : ModelRouter | None = None , bus : EventBus | None = None , web_research_available : bool = False , research_model_hint : str | None = None , ideation_model_hint : str | None = None , search_client : Any | None = None , page_fetcher : Any | None = None ) - > None :
2026-08-15 20:56:56 +07:00
self . router = router
self . bus = bus or EventBus ( )
self . web_research_available = web_research_available
2026-08-16 15:35:55 +07:00
self . research_model_hint = research_model_hint or model_for_role ( " venture_research " )
self . ideation_model_hint = ideation_model_hint or model_for_role ( " venture_ideation " )
self . search_client = search_client
self . page_fetcher = page_fetcher or WebPageFetcher ( )
2026-08-16 15:08:32 +07:00
self . _last_bounded_map_peak = 0
2026-08-15 20:56:56 +07:00
def create_v0_mandate ( self ) - > CompanyMandate :
mandate = CompanyMandate . objects . create (
2026-08-16 17:48:04 +07:00
objective = " Design a business that Artifex itself could plausibly operate and turn at most $50 of external validation capital into at least $500 net new cash within 30 days, with no more than 30 minutes/week of routine human intervention. " ,
constraints = { " external_validation_capital_max " : 50 , " target_net_new_cash " : 500 , " target_window_days " : 30 , " max_human_routine_minutes_per_week " : 30 , " default_venture_track " : VentureTrack . AUTONOMOUS , " routine_human_involvement_not_allowed " : [ " founder-led sales calls " , " manual prospecting " , " routine support " , " manual fulfillment " , " bespoke consulting " , " routine QA " , " manual payment chasing " , " routine onboarding " ] , " allowed_human_involvement " : [ " approve account/legal setup " , " approve initial spend " , " approve material legal commitments " , " exceptional safety/security escalation " , " irreversible capital decisions " ] , " no_equity_raise " : True , " no_debt " : True , " no_illegal_or_deceptive_activity " : True , " no_spam " : True , " no_fake_traction " : True , " no_fabricated_customer_evidence " : True , " no_real_spend_in_v0 " : True , " no_real_customer_outreach_in_v0 " : True , " existing_artifex_compute_sunk_available " : True , " validation_deployment " : " SUBDOMAIN_DEPLOYMENT " } ,
optimization_targets = [ " autonomous fulfillment " , " autonomous customer acquisition " , " self-service onboarding " , " standardized digital delivery " , " AI leverage " , " platformization " , " recurring revenue " , " simple payment flow " , " low support burden " , " low regulatory burden " , " low integration burden " , " rapid validation " ] ,
metadata = { " milestone " : " VENTURE_DISCOVERY_V0_AUTONOMOUS " , " venture_track " : VentureTrack . AUTONOMOUS , " spend_authorized " : False , " customer_outreach_authorized " : False , " ai_native_policy " : AI_NATIVE_POLICY , " autonomous_archetypes " : AUTONOMOUS_ARCHETYPES } ,
2026-08-15 20:56:56 +07:00
)
self . _artifact ( None , mandate , " VENTURE_MANDATE " , " Venture Discovery V0 Mandate " , { " objective " : mandate . objective , " constraints " : mandate . constraints , " optimization_targets " : mandate . optimization_targets } , self . _readable_mandate ( mandate ) , " venture_discovery " )
return mandate
2026-08-16 16:39:46 +07:00
def generate_single_company ( self , mandate : CompanyMandate , * , graph_run = None , ideation_index : int | None = None , ideation_context : dict [ str , Any ] | None = None , payload : dict [ str , Any ] | None = None , source : str | None = None ) - > CompanyProposal :
if payload is None or source is None :
payload , source = self . _company_payload ( mandate , ideation_index = ideation_index , ideation_context = ideation_context )
return self . _create_company_from_payload ( mandate , payload , source , graph_run = graph_run )
def _create_company_from_payload ( self , mandate : CompanyMandate , payload : dict [ str , Any ] , source : str , * , graph_run = None ) - > CompanyProposal :
2026-08-16 15:35:55 +07:00
pitch = self . _pitch ( payload , fallback = source == " deterministic_fallback " )
2026-08-15 21:42:46 +07:00
confidence = self . _confidence ( payload . get ( " confidence " , 0.55 ) )
2026-08-15 20:56:56 +07:00
evidence = self . _as_list ( payload . get ( " market_evidence " , [ ] ) )
if not self . web_research_available :
evidence . append ( { " type " : " capability_gap " , " source " : " internal " , " summary " : " Public web market research is not configured; demand/competitor evidence is unverified. " , " fallback_evidence " : True } )
confidence = min ( confidence , 0.58 )
2026-08-15 21:42:46 +07:00
thesis = VentureThesis . objects . create ( mandate = mandate , title = str ( payload [ " title " ] ) , thesis = str ( payload [ " one_line_thesis " ] ) , similarity_fingerprint = self . _fingerprint ( payload ) , metadata = { " source " : source , " exactly_one_company_generated " : True } , evidence_tier = EvidenceTier . TIER_0_THESIS )
2026-08-15 20:56:56 +07:00
proposal = CompanyProposal . objects . create (
mandate = mandate ,
thesis = thesis ,
title = str ( payload [ " title " ] ) ,
description = str ( payload [ " description " ] ) ,
problem = str ( payload [ " problem " ] ) ,
target_customer = str ( payload [ " target_customer " ] ) ,
proposed_solution = str ( payload [ " proposed_solution " ] ) ,
business_model = str ( payload [ " business_model " ] ) ,
pricing_hypothesis = str ( payload [ " pricing_hypothesis " ] ) ,
acquisition_strategy = str ( payload [ " acquisition_strategy " ] ) ,
validation_plan = str ( payload [ " validation_plan " ] ) ,
capital_requested = self . _money ( payload . get ( " capital_requested " , " 50 " ) ) ,
time_to_first_dollar_estimate = str ( payload [ " time_to_first_dollar_estimate " ] ) ,
expected_margin = str ( payload [ " expected_margin " ] ) ,
build_complexity = str ( payload [ " build_complexity " ] ) ,
market_evidence = evidence ,
differentiation = str ( payload [ " differentiation " ] ) ,
major_risks = self . _as_list ( payload [ " major_risks " ] ) ,
confidence = confidence ,
status = CompanyProposalStatus . SUBMITTED ,
pitch = pitch ,
2026-08-16 17:48:04 +07:00
metadata = { " generation_source " : source , " fallback_evidence " : source == " deterministic_fallback " , " web_research_available " : self . web_research_available , " real_spend " : 0 , " real_customer_outreach " : False , " validation_offer " : payload . get ( " validation_offer " , { } ) , " end_state_business_model " : payload . get ( " end_state_business_model " , { } ) , " fulfillment_contract " : payload . get ( " fulfillment_contract " , { } ) , " minutes_per_week_human " : payload . get ( " minutes_per_week_human " , None ) , " human_actions_required " : self . _as_list ( payload . get ( " human_actions_required " , [ ] ) ) } ,
2026-08-15 21:42:46 +07:00
evidence_tier = EvidenceTier . TIER_0_THESIS ,
2026-08-15 20:56:56 +07:00
)
2026-08-16 15:35:55 +07:00
self . _artifact ( proposal , mandate , " STANDARDIZED_COMPANY_PITCH " , " Standardized Company Pitch " , pitch , self . readable_pitch ( pitch ) , f " Company Brain/ { source } " if source != " deterministic_fallback " else " deterministic_fallback " , graph_run = graph_run )
2026-08-15 20:56:56 +07:00
self . bus . publish ( " VENTURE_COMPANY_PROPOSED " , payload = { " proposal_id " : str ( proposal . id ) , " source " : source } )
return proposal
2026-08-16 17:10:34 +07:00
def conduct_market_research ( self , proposal : CompanyProposal , * , graph_run = None , depth : str = " light " ) - > dict [ str , Any ] :
required = RESEARCH_CATEGORIES
2026-08-15 21:42:46 +07:00
research = { " coverage " : { category : False for category in required } , " sources " : [ ] , " findings " : { } , " unverified_categories " : required , " research_available " : False }
2026-08-16 17:10:34 +07:00
search_sources = self . _searxng_sources ( proposal , required , depth = depth ) if self . web_research_available else [ ]
2026-08-16 15:35:55 +07:00
page_corpus = self . _page_corpus ( search_sources ) if search_sources else [ ]
2026-08-16 17:48:04 +07:00
diagnostics = { " search_result_count " : len ( search_sources ) , " page_fetch_count " : len ( page_corpus ) , " model_provider " : self . research_model_hint if self . router is not None else " none " , " search_provider " : " searxng " if search_sources else " none " }
search_diagnostics = getattr ( self , " _last_search_diagnostics " , { } )
if search_diagnostics :
diagnostics [ " search_diagnostics " ] = search_diagnostics
2026-08-15 21:42:46 +07:00
if self . web_research_available and self . router is not None :
try :
2026-08-16 17:10:34 +07:00
response = self . router . complete ( ModelRequestContract ( purpose = ModelCapability . REASONING , model_hint = self . research_model_hint , prompt = " Bounded public web market research for exactly one startup pitch. Return JSON with keys: sources (list of { url,title,category,summary}), findings (object keyed by competitors, pricing, customer_pain, market_alternatives, regulatory_platform_risks), and coverage (object with each required key true/false). Use only the provided SearXNG search context and fetched page excerpts for source URLs; do not fabricate URLs. If a category has no strong relevant source, mark coverage false. Research depth: " + depth + " . Search context: " + json . dumps ( search_sources , default = str ) + " Page excerpts: " + json . dumps ( page_corpus , default = str ) + " Pitch: " + json . dumps ( proposal . pitch , default = str ) ) )
2026-08-15 21:42:46 +07:00
parsed = extract_json_object ( response . content )
if isinstance ( parsed , dict ) :
research = self . _normalize_research ( parsed , required )
research [ " research_available " ] = True
research [ " provider " ] = self . research_model_hint
except Exception as exc :
research [ " failure " ] = str ( exc )
2026-08-16 15:35:55 +07:00
if search_sources :
research = self . _merge_search_sources ( research , search_sources , required )
if page_corpus :
research = { * * research , " page_corpus " : page_corpus , " page_fetch_count " : len ( page_corpus ) }
2026-08-15 21:42:46 +07:00
if not research . get ( " sources " ) :
2026-08-16 17:48:04 +07:00
research = { * * self . _missing_research ( required , research . get ( " failure " , " public web research unavailable or returned no source-linked evidence " ) ) , * * diagnostics }
2026-08-16 17:10:34 +07:00
quality = self . filter_research_sources ( proposal , research . get ( " sources " , [ ] ) , required )
research [ " sources " ] = quality [ " accepted_sources " ]
research [ " source_rejections " ] = quality [ " rejected_sources " ]
research [ " source_rejection_count " ] = len ( quality [ " rejected_sources " ] )
source_categories = { str ( source . get ( " category " , " " ) ) for source in research . get ( " sources " , [ ] ) if source . get ( " url " ) and source . get ( " quality " ) == " strong " }
2026-08-15 21:42:46 +07:00
coverage = dict ( research . get ( " coverage " , { } ) )
for category in required :
coverage [ category ] = bool ( coverage . get ( category ) ) and category in source_categories
research [ " coverage " ] = coverage
research [ " unverified_categories " ] = [ category for category in required if not coverage . get ( category ) ]
coverage_ratio = ( len ( required ) - len ( research [ " unverified_categories " ] ) ) / len ( required )
proposal . confidence = round ( min ( float ( proposal . confidence ) , 0.45 + 0.35 * coverage_ratio ) , 2 )
2026-08-16 17:10:34 +07:00
existing_research = proposal . metadata . get ( " research " , { } ) if isinstance ( proposal . metadata , dict ) else { }
proposal . market_evidence = [ * self . _as_list ( proposal . market_evidence ) , * research . get ( " sources " , [ ] ) , { " type " : " research_coverage " , " source " : " venture_research " , " summary " : f " { depth . title ( ) } source-linked research coverage: { round ( coverage_ratio * 100 ) } % " , " coverage " : coverage , " unverified_categories " : research [ " unverified_categories " ] , " depth " : depth } ]
2026-08-16 17:48:04 +07:00
explicit_failure = " " if research . get ( " sources " ) else research . get ( " failure " , " public web research unavailable or returned no source-linked evidence " )
proposal . metadata = { * * proposal . metadata , " research " : { * * existing_research , " coverage_ratio " : coverage_ratio , " coverage " : coverage , " unverified_categories " : research [ " unverified_categories " ] , " source_count " : len ( research . get ( " sources " , [ ] ) ) , " source_rejection_count " : len ( quality [ " rejected_sources " ] ) , " search_result_count " : research . get ( " search_result_count " , len ( search_sources ) ) , " page_fetch_count " : research . get ( " page_fetch_count " , len ( page_corpus ) ) , " provider " : research . get ( " provider " , diagnostics . get ( " model_provider " , " none " ) ) , " search_provider " : research . get ( " search_provider " , diagnostics [ " search_provider " ] ) , " search_diagnostics " : research . get ( " search_diagnostics " , diagnostics . get ( " search_diagnostics " , { } ) ) , " model_research_failure " : research . get ( " failure " , " " ) if research . get ( " sources " ) else " " , " depth " : depth , " explicit_research_failure " : explicit_failure , " before_deep_coverage_ratio " : existing_research . get ( " coverage_ratio " ) if depth == " deep " else existing_research . get ( " before_deep_coverage_ratio " ) } }
2026-08-15 21:42:46 +07:00
proposal . evidence_tier = EvidenceTier . TIER_1_PUBLIC_EVIDENCE if coverage_ratio > 0 else EvidenceTier . TIER_0_THESIS
if proposal . thesis :
proposal . thesis . evidence_tier = proposal . evidence_tier
proposal . thesis . save ( update_fields = [ " evidence_tier " , " updated_at " ] )
proposal . pitch = { * * proposal . pitch , " Research evidence " : research }
proposal . save ( update_fields = [ " confidence " , " market_evidence " , " metadata " , " pitch " , " evidence_tier " , " updated_at " ] )
2026-08-16 17:10:34 +07:00
self . _artifact ( proposal , proposal . mandate , " MARKET_RESEARCH " , f " Bounded Public Market Research ( { depth } ) " , research , self . _readable_research ( research ) , f " { research . get ( ' provider ' , ' search ' ) } /web research " if research . get ( " sources " ) else " research_gap " , graph_run = graph_run )
2026-08-15 21:42:46 +07:00
return research
2026-08-15 20:56:56 +07:00
def board_review ( self , proposal : CompanyProposal , * , graph_run = None ) - > CompanyBoardReview :
observations = {
" CEO " : [ " Mandate fit is strongest if validation sells a narrow paid audit before product build. " , " Keep the first dollar path service-led, not SaaS-led. " ] ,
" CTO " : [ " Build can use existing Artifex analysis, reporting, and frontend capabilities. " , " Avoid integrations until willingness-to-pay evidence exists. " ] ,
" CFO " : [ " $50 cap is adequate only for lightweight landing page/listing tests, not paid acquisition learning. " , " High margin is plausible because delivery is mostly labor/compute already available. " ] ,
" CRO " : [ " Founder/operator communities are reachable manually, but V0 cannot contact customers. " , " Pricing must start as a paid diagnostic to avoid long SaaS evaluation cycles. " ] ,
" Independent Director " : [ " Demand evidence is weak without public research or customer conversations. " , " The company must prove urgency before building automation. " ] ,
}
weaknesses = [ " No customer outreach or paid test has occurred. " , " Public web research is unavailable, so market evidence remains partial. " , " First customers may require trust and examples before paying. " ]
revisions = [ " Frame offer as a productized validation audit with optional Artifex-assisted build plan. " , " Make falsification criteria explicit before spend. " ]
pitch = dict ( proposal . pitch )
pitch [ " What would falsify the thesis " ] = " Fewer than 5 credible target-customer responses or zero willingness-to-pay signals after a compliant validation test. "
proposal . pitch = pitch
proposal . metadata = { * * proposal . metadata , " board_revised_pitch " : True }
proposal . save ( update_fields = [ " pitch " , " metadata " , " updated_at " ] )
2026-08-15 21:42:46 +07:00
identity = self . validate_identity_content ( proposal , pitch )
review = CompanyBoardReview . objects . create ( proposal = proposal , observations = observations , strengths = [ " Service-led revenue path can precede product build. " , " Uses current Artifex planning, engineering, and review capabilities. " , " Small validation budget aligns with a narrow paid offer. " ] , weaknesses = weaknesses , key_assumptions = [ " Target customers feel enough urgency to pay for validation clarity. " , " Manual outbound or community posting can generate credible responses without spam. " , " Artifex can produce a differentiated audit faster than generic consultants. " ] , required_revisions = revisions , recommendation = " PROCEED_TO_IC_WITH_REVISIONS " , revised_pitch = pitch , metadata = { " roles " : list ( observations ) , " company_does_not_grade_itself " : True , " identity_validation " : identity } )
self . _artifact ( proposal , proposal . mandate , " COMPANY_BOARD_REVIEW " , " Company Board Review " , { " observations " : observations , " strengths " : review . strengths , " weaknesses " : weaknesses , " required_revisions " : revisions , " recommendation " : review . recommendation , " revised_pitch " : pitch , " identity_validation " : identity } , self . _readable_board ( review ) , " Company Board " , graph_run = graph_run )
2026-08-15 20:56:56 +07:00
return review
def start_ic_diligence ( self , proposal : CompanyProposal , * , graph_run = None ) - > ICDiligence :
proposal . status = CompanyProposalStatus . UNDER_DILIGENCE
proposal . save ( update_fields = [ " status " , " updated_at " ] )
diligence = ICDiligence . objects . create ( proposal = proposal , status = " FIRST_PASS " , rounds = [ " Initial Pitch " , " Diligence Round 1 " , " Final Challenge " , " Decision " ] , metadata = { " bounded_rounds " : True , " self_grading " : False } )
self . _artifact ( proposal , proposal . mandate , " IC_FIRST_PASS " , " IC First-Pass Review " , { " status " : diligence . status , " initial_concerns " : [ " Demand evidence is unverified. " , " Distribution assumptions need evidence. " , " Need validation gate before any spend. " ] } , " IC first pass: proceed to evidence-seeking questions; do not score final decision yet. " , " Independent IC " , graph_run = graph_run )
return diligence
def generate_ic_questions ( self , diligence : ICDiligence , * , graph_run = None ) - > list [ ICQuestion ] :
pitch = diligence . proposal . pitch
raw_questions = self . _questions_from_pitch ( pitch )
questions = [ ICQuestion . objects . create ( diligence = diligence , question = item [ " question " ] , category = item [ " category " ] , evidence_required = True ) for item in raw_questions [ : 8 ] ]
self . _artifact ( diligence . proposal , diligence . proposal . mandate , " IC_QUESTIONS " , " IC Diligence Questions " , { " questions " : [ q . question for q in questions ] } , " \n " . join ( f " - { q . question } " for q in questions ) , " Independent IC " , graph_run = graph_run )
return questions
def answer_questions ( self , diligence : ICDiligence , * , graph_run = None ) - > list [ ICResponse ] :
responses = [ ]
for question in diligence . questions . all ( ) :
answer = self . _answer_question ( question )
responses . append ( ICResponse . objects . create ( question = question , answer = answer [ " answer " ] , evidence = answer [ " evidence " ] , uncertainty = answer [ " uncertainty " ] , pitch_changes = answer . get ( " pitch_changes " , { } ) , metadata = { " no_customer_outreach " : True , " no_spend " : True } ) )
diligence . status = " COMPANY_RESPONSE "
diligence . save ( update_fields = [ " status " , " updated_at " ] )
2026-08-15 21:42:46 +07:00
identity = self . validate_identity_content ( diligence . proposal , { " responses " : [ r . answer for r in responses ] } )
self . _artifact ( diligence . proposal , diligence . proposal . mandate , " IC_RESPONSES " , " Company Responses to IC " , { " responses " : [ { " question " : r . question . question , " answer " : r . answer , " evidence " : r . evidence , " uncertainty " : r . uncertainty , " pitch_changes " : r . pitch_changes } for r in responses ] , " identity_validation " : identity } , self . _readable_responses ( responses ) , " Company reasoning roles " , graph_run = graph_run )
2026-08-15 20:56:56 +07:00
return responses
def red_team ( self , diligence : ICDiligence , * , graph_run = None ) - > dict [ str , object ] :
challenge = { " concerns " : [ " The offer may be perceived as generic consulting unless anchored to a painful, immediate decision. " , " Without public research or customer contact, demand remains a hypothesis. " , " Manual distribution could fail if the target audience distrusts AI-generated audits. " ] , " required_final_response " : [ " Narrow ICP further. " , " Specify willingness-to-pay proof. " , " Define hard kill criteria. " ] , " recommendation " : " continue_to_final_response " }
diligence . red_team_challenge = challenge
diligence . status = " FINAL_CHALLENGE "
diligence . save ( update_fields = [ " red_team_challenge " , " status " , " updated_at " ] )
2026-08-15 21:42:46 +07:00
challenge [ " identity_validation " ] = self . validate_identity_content ( diligence . proposal , challenge )
2026-08-15 20:56:56 +07:00
self . _artifact ( diligence . proposal , diligence . proposal . mandate , " IC_RED_TEAM " , " IC Red-Team Challenge " , challenge , " Red-team concerns: \n " + " \n " . join ( f " - { c } " for c in challenge [ " concerns " ] ) , " Independent IC Red Team " , graph_run = graph_run )
return challenge
def final_company_response ( self , diligence : ICDiligence , * , graph_run = None ) - > dict [ str , object ] :
2026-08-15 21:42:46 +07:00
response = { " narrowed_icp " : diligence . proposal . target_customer , " revised_validation_gate " : " Before any spend, collect 5 credible target-customer responses or 1 explicit willingness-to-pay signal through compliant non-spam channels. " , " kill_criteria " : [ " No credible responses after 10 targeted, compliant conversations/posts once outreach is approved. " , " No willingness-to-pay signal at $49-$99. " , " Customers only want free advice, not a paid report. " ] , " pitch_changes " : { " ICP " : " Preserve canonical proposal ICP. " , " Validation plan " : " Gate spend behind credible response/willingness-to-pay evidence. " } }
response [ " identity_validation " ] = self . validate_identity_content ( diligence . proposal , response )
2026-08-15 20:56:56 +07:00
diligence . final_response = response
diligence . status = " FINAL_RESPONSE "
diligence . save ( update_fields = [ " final_response " , " status " , " updated_at " ] )
self . _artifact ( diligence . proposal , diligence . proposal . mandate , " IC_FINAL_RESPONSE " , " Final Company Response " , response , json . dumps ( response , indent = 2 ) , " Company reasoning roles " , graph_run = graph_run )
return response
def score_and_decide ( self , diligence : ICDiligence , * , graph_run = None ) - > ICDecision :
2026-08-16 17:48:04 +07:00
try :
assessment = diligence . proposal . autonomous_assessment
except ObjectDoesNotExist :
assessment = None
2026-08-15 21:42:46 +07:00
scores = self . _evidence_scores ( diligence . proposal )
2026-08-16 17:48:04 +07:00
if assessment is not None :
scores [ " Autonomous Operability " ] = int ( assessment . autonomous_operability_score )
2026-08-15 21:42:46 +07:00
composite = round ( sum ( scores . values ( ) ) / len ( scores ) , 1 )
calibration = self . calibrate_probability ( diligence . proposal , raw_probability = float ( scores [ " Probability of Reaching $500 " ] ) )
scores [ " Probability of Reaching $500 " ] = int ( calibration [ " evidence_adjusted_probability " ] )
2026-08-15 20:56:56 +07:00
composite = round ( sum ( scores . values ( ) ) / len ( scores ) , 1 )
2026-08-15 21:42:46 +07:00
decision_type = ICDecisionType . REVISE_AND_RESUBMIT if scores [ " Demand Evidence " ] < 35 or scores [ " Risk Manageability " ] < 35 else ICDecisionType . CONDITIONAL_FUND if composite > = 55 else ICDecisionType . REVISE_AND_RESUBMIT
tranche = Decimal ( " 20.00 " ) if decision_type == ICDecisionType . CONDITIONAL_FUND and composite > = 70 else Decimal ( " 10.00 " ) if decision_type == ICDecisionType . CONDITIONAL_FUND else None
2026-08-16 17:10:34 +07:00
decision , _ = ICDecision . objects . update_or_create ( diligence = diligence , defaults = { " decision " : decision_type , " component_scores " : scores , " composite_score " : composite , " probability_500_within_30_days " : scores [ " Probability of Reaching $500 " ] , " initial_tranche " : tranche , " validation_condition " : " Obtain 5 credible target-customer responses or 1 explicit willingness-to-pay signal before any build or further spend. " , " evidence_required " : [ " response transcripts or public thread URLs " , " proof of willingness-to-pay signal " , " no-spam/no-fabrication compliance note " ] , " recommended_allocation " : { " initial_tranche " : float ( tranche or 0 ) , " remaining_reserved " : 50 - float ( tranche or 0 ) , " no_spend_in_v0 " : True } , " kill_criteria " : diligence . final_response . get ( " kill_criteria " , [ ] ) , " next_decision_point " : " After validation evidence is collected and before any real spend or customer delivery. " , " metadata " : { " decision_vocabulary " : [ item . value for item in ICDecisionType ] , " no_actual_funding " : True , " score_basis " : " source_linked_research_and_pitch_attributes " , " probability_calibration " : calibration } , " evidence_tier " : diligence . proposal . evidence_tier , " raw_probability_500_within_30_days " : calibration [ " raw_probability " ] , " evidence_ceiling " : calibration [ " evidence_ceiling " ] , " probability_explanation " : calibration [ " explanation " ] , " score_definitions " : SCORE_DEFINITIONS } )
2026-08-15 20:56:56 +07:00
diligence . status = " DECISION "
diligence . save ( update_fields = [ " status " , " updated_at " ] )
diligence . proposal . status = CompanyProposalStatus . FUNDED_RECOMMENDED if decision . decision == ICDecisionType . CONDITIONAL_FUND else CompanyProposalStatus . REVISE
diligence . proposal . save ( update_fields = [ " status " , " updated_at " ] )
2026-08-15 21:42:46 +07:00
self . _artifact ( diligence . proposal , diligence . proposal . mandate , " IC_FINAL_SCORE " , " IC Final Scoring and Decision " , { " scores " : scores , " score_definitions " : SCORE_DEFINITIONS , " decision " : decision . decision , " composite_score " : composite , " conditional_funding " : { " initial_tranche " : str ( decision . initial_tranche ) , " condition " : decision . validation_condition } , " score_basis " : decision . metadata [ " score_basis " ] , " probability_calibration " : calibration } , self . _readable_score ( decision ) , " Independent IC " , graph_run = graph_run )
2026-08-15 20:56:56 +07:00
return decision
def capability_analysis ( self , proposal : CompanyProposal , * , graph_run = None ) - > PortfolioCapabilityGap :
requirements = self . _capability_requirements ( proposal )
for item in requirements :
CompanyCapabilityRequirement . objects . create ( proposal = proposal , * * item )
available = [ r [ " category " ] for r in requirements if r [ " status " ] == CapabilityStatus . AVAILABLE ]
partial = [ r [ " category " ] for r in requirements if r [ " status " ] == CapabilityStatus . PARTIAL ]
missing = [ r [ " category " ] for r in requirements if r [ " status " ] == CapabilityStatus . MISSING ]
ranked = [ { " category " : r [ " category " ] , " priority " : r [ " priority " ] , " rationale " : r [ " rationale " ] } for r in requirements if r [ " status " ] == CapabilityStatus . MISSING ]
priority_order = { CapabilityPriority . BEFORE_VALIDATION : 0 , CapabilityPriority . BEFORE_FIRST_CUSTOMER : 1 , CapabilityPriority . BEFORE_SCALING : 2 }
ranked . sort ( key = lambda item : priority_order [ item [ " priority " ] ] )
report = self . _readable_capability_gap ( available , partial , missing , ranked )
2026-08-15 21:42:46 +07:00
web_requirement = next ( ( item for item in requirements if item [ " category " ] == " WEB_MARKET_RESEARCH " ) , None )
gap = PortfolioCapabilityGap . objects . create ( proposal = proposal , available = available , partial = partial , missing = missing , ranked_missing = ranked , report = report , metadata = { " web_market_research_status " : web_requirement [ " status " ] if web_requirement else " MISSING " } )
2026-08-15 20:56:56 +07:00
self . _artifact ( proposal , proposal . mandate , " CAPABILITY_GAP_REPORT " , " Capability Gap Report " , { " available " : available , " partial " : partial , " missing " : missing , " ranked_missing " : ranked } , report , " Venture Discovery Capability Analysis " , graph_run = graph_run )
return gap
def produce_investment_memo ( self , diligence : ICDiligence , gap : PortfolioCapabilityGap , * , graph_run = None ) - > VentureArtifact :
decision = diligence . decision
proposal = diligence . proposal
2026-08-16 14:30:40 +07:00
memo = { " Company " : proposal . title , " Thesis " : proposal . pitch [ " One-line thesis " ] , " Mandate " : proposal . mandate . objective , " Requested capital " : str ( proposal . capital_requested ) , " Recommended allocation " : decision . recommended_allocation , " IC decision " : decision . decision , " Key metrics " : { " P($500 within 30 days) " : decision . probability_500_within_30_days , " raw P($500 within 30 days) " : decision . raw_probability_500_within_30_days , " evidence ceiling " : decision . evidence_ceiling , " evidence tier " : decision . evidence_tier , " AI leverage " : decision . component_scores . get ( " AI Leverage " ) , " platformization potential " : decision . component_scores . get ( " Platformization Potential " ) , " estimated time to first dollar " : proposal . time_to_first_dollar_estimate , " expected gross margin " : proposal . expected_margin , " validation cost " : " $10-$20 initial tranche; $50 maximum after approval " , " build effort " : proposal . build_complexity , " distribution feasibility " : decision . component_scores [ " Distribution Feasibility " ] } , " Research evidence " : proposal . pitch . get ( " Research evidence " , { } ) , " Why it may work " : [ " Revenue path starts with a paid diagnostic, not a full SaaS build. " , " Artifex has planning, engineering, frontend, review, graph, and agent-control capabilities already. " , " Validation budget can be gated behind evidence. " ] , " Why it may fail " : diligence . red_team_challenge . get ( " concerns " , [ ] ) , " Diligence questions " : [ q . question for q in diligence . questions . all ( ) ] , " Company responses " : [ r . answer for r in ICResponse . objects . filter ( question__diligence = diligence ) ] , " Red-team concerns " : diligence . red_team_challenge . get ( " concerns " , [ ] ) , " IC scoring " : decision . component_scores , " Score definitions " : decision . score_definitions , " Capital recommendation " : decision . recommended_allocation , " Validation gates " : [ decision . validation_condition ] , " Kill criteria " : decision . kill_criteria , " Next decision point " : decision . next_decision_point , " Capability gap " : { " available " : gap . available , " partial " : gap . partial , " missing " : gap . missing } }
2026-08-15 21:42:46 +07:00
identity = self . validate_identity_content ( proposal , memo )
memo [ " Identity validation " ] = identity
2026-08-15 20:56:56 +07:00
return self . _artifact ( proposal , proposal . mandate , " FINAL_INVESTMENT_MEMO " , " Final IC Investment Memo " , memo , self . _readable_memo ( memo ) , " Independent IC " , graph_run = graph_run )
def request_human_approval ( self , decision : ICDecision , action : str ) - > ICDecision :
if action not in { " approve_for_validation " , " reject " , " request_more_diligence " } :
raise ValueError ( " Unsupported venture approval action " )
decision . metadata = { * * decision . metadata , " human_approval_action " : action , " real_spend_still_blocked " : True }
decision . save ( update_fields = [ " metadata " , " updated_at " ] )
return decision
2026-08-15 21:42:46 +07:00
def prepare_cohort ( self , * , size : int = 10 , graph_run = None , concurrency : int = 1 ) - > VentureCohort :
2026-08-16 16:39:46 +07:00
self . seed_dogfood_thesis_registry ( )
2026-08-15 21:42:46 +07:00
mandate = self . create_v0_mandate ( )
2026-08-16 17:10:34 +07:00
cohort_id = f " VDV04- { timezone . now ( ) . strftime ( ' % Y % m %d % H % M % S ' ) } - { hashlib . sha1 ( str ( mandate . id ) . encode ( ) ) . hexdigest ( ) [ : 8 ] } "
return VentureCohort . objects . create ( cohort_id = cohort_id , mandate = mandate , cohort_size = size , graph_run = graph_run , concurrency = concurrency , status = " PREPARING " , graph_versions = { " cohort " : " venture_discovery_cohort v3 " , " company " : " venture_discovery v1 " } , research_policy = { " stage_a " : " lightweight_all " , " finalist_deeper_research " : " top_5 " , " target_finalist_coverage " : 0.8 , " no_spend " : True , " no_customer_outreach " : True } , scoring_policy = { " dimensions " : SCORE_DEFINITIONS , " duplicate_policy " : DEFAULT_DUPLICATE_POLICY } , evidence_calibration_policy = { tier : ceiling for tier , ceiling in EVIDENCE_CEILINGS . items ( ) } , metadata = { " real_spend " : 0 , " real_customer_outreach " : False , " milestone " : " VENTURE_DISCOVERY_V0.4 " } )
2026-08-16 16:39:46 +07:00
def portfolio_thesis_review ( self , cohort : VentureCohort ) - > dict [ str , Any ] :
self . seed_dogfood_thesis_registry ( )
registry = self . registry_snapshot ( )
review = { " saturated_thesis_areas " : [ row for row in registry if row [ " status " ] == PortfolioThesisStatus . SATURATED ] , " active_candidates " : [ row for row in registry if row [ " status " ] == PortfolioThesisStatus . ACTIVE_CANDIDATE ] , " registry_size " : len ( registry ) , " policy " : DEFAULT_DUPLICATE_POLICY }
cohort . metadata = { * * cohort . metadata , " portfolio_thesis_review " : review }
cohort . save ( update_fields = [ " metadata " , " updated_at " ] )
return review
2026-08-15 21:42:46 +07:00
2026-08-16 16:39:46 +07:00
def create_ideation_mandate ( self , cohort : VentureCohort ) - > CohortIdeationMandate :
territories = self . allocate_territories ( cohort . cohort_size )
review = cohort . metadata . get ( " portfolio_thesis_review " , { } ) if isinstance ( cohort . metadata , dict ) else { }
objective = " Intentionally search new venture territory while preserving independent ideation and avoiding known saturated thesis areas. "
mandate , _ = CohortIdeationMandate . objects . update_or_create (
cohort = cohort ,
defaults = {
" objective " : objective ,
" desired_opportunity_classes " : AI_NATIVE_POLICY [ " preferred_classes " ] ,
" hard_exclusions " : DEFAULT_HARD_EXCLUSIONS ,
" soft_exclusions " : DEFAULT_SOFT_EXCLUSIONS ,
" opportunity_territories " : territories ,
" portfolio_gaps " : [ " non-RFP enterprise workflows " , " non-churn SaaS intelligence " , " developer infrastructure " , " document/data automation " ] ,
" diversity_preferences " : { " avoid_repeating_saturated_theses " : True , " max_near_duplicate_per_thesis_per_cohort " : 1 , " max_competitive_per_thesis_per_cohort " : 2 , " ai_native_preferred_not_required " : True } ,
" registry_snapshot " : { " version " : timezone . now ( ) . isoformat ( ) , " theses " : self . registry_snapshot ( ) } ,
" metadata " : { " portfolio_thesis_review " : review , " cohort_ideation_brief " : self . ideation_brief_text ( review , territories ) } ,
} ,
)
self . _artifact ( None , cohort . mandate , " COHORT_IDEATION_MANDATE " , " Cohort Ideation Mandate " , self . ideation_mandate_payload ( mandate ) , mandate . metadata [ " cohort_ideation_brief " ] , " Portfolio IC " , graph_run = cohort . graph_run )
return mandate
2026-08-16 15:08:32 +07:00
2026-08-16 16:39:46 +07:00
def allocate_search_territories ( self , cohort : VentureCohort ) - > list [ str ] :
mandate = getattr ( cohort , " ideation_mandate " , None ) or self . create_ideation_mandate ( cohort )
territories = self . allocate_territories ( cohort . cohort_size )
mandate . opportunity_territories = territories
mandate . save ( update_fields = [ " opportunity_territories " , " updated_at " ] )
return territories
2026-08-16 15:08:32 +07:00
2026-08-16 16:39:46 +07:00
def generate_independent_proposals ( self , cohort : VentureCohort ) - > list [ CompanyProposal ] :
started = time . monotonic ( )
accepted_payloads : list [ dict [ str , Any ] ] = [ ]
2026-08-16 15:08:32 +07:00
proposals = [ ]
2026-08-16 16:39:46 +07:00
mandate = getattr ( cohort , " ideation_mandate " , None ) or self . create_ideation_mandate ( cohort )
2026-08-16 17:10:34 +07:00
policy = cohort . scoring_policy . get ( " duplicate_policy " , DEFAULT_DUPLICATE_POLICY )
total_budget = max ( cohort . cohort_size , int ( cohort . cohort_size * int ( policy . get ( " cohort_attempt_budget_multiplier " , 4 ) ) ) )
attempts = hard_rejections = duplicate_rejections = soft_reviews = model_requests = replacement_attempts = 0
workers = self . _effective_concurrency ( cohort . concurrency )
peak_concurrency = 0
generation_records : list [ dict [ str , Any ] ] = [ ]
while len ( proposals ) < cohort . cohort_size and attempts < total_budget :
batch_size = min ( workers , total_budget - attempts , cohort . cohort_size - len ( proposals ) + workers - 1 )
batch_inputs = [ ]
for _ in range ( batch_size ) :
2026-08-16 16:39:46 +07:00
attempts + = 1
model_requests + = 1
2026-08-16 17:10:34 +07:00
replacement_attempts + = 1 if attempts > cohort . cohort_size else 0
slot_index = len ( proposals ) + 1
territory = mandate . opportunity_territories [ ( attempts - 1 ) % len ( mandate . opportunity_territories ) ] if mandate . opportunity_territories else OpportunityTerritory . OPEN_CATEGORY
batch_inputs . append ( { " attempt " : attempts , " slot_index " : slot_index , " territory " : str ( territory ) } )
def generate_attempt ( item : dict [ str , Any ] ) - > dict [ str , Any ] :
context = { " hard_exclusions " : mandate . hard_exclusions , " soft_exclusions " : mandate . soft_exclusions , " territory " : item [ " territory " ] , " brief " : self . ideation_mandate_payload ( mandate ) }
payload , source = self . _company_payload ( cohort . mandate , ideation_index = item [ " attempt " ] , ideation_context = context )
return { * * item , " payload " : payload , " source " : source }
for result in self . _bounded_map ( batch_inputs , generate_attempt , cohort . concurrency ) :
peak_concurrency = max ( peak_concurrency , self . _last_bounded_map_peak )
gate = self . novelty_gate ( result [ " payload " ] , cohort , accepted_payloads , mandate , territory = result [ " territory " ] )
generation_records . append ( { " attempt " : result [ " attempt " ] , " decision " : gate [ " decision " ] , " title " : result [ " payload " ] . get ( " title " , " " ) , " territory " : result [ " territory " ] } )
if gate [ " decision " ] == NoveltyGateDecision . ACCEPT and len ( proposals ) < cohort . cohort_size :
proposal = self . generate_single_company ( cohort . mandate , ideation_index = len ( proposals ) + 1 , payload = result [ " payload " ] , source = result [ " source " ] )
generation_index = len ( proposals ) + 1
proposal . metadata = { * * proposal . metadata , " cohort_id " : cohort . cohort_id , " independent_generation_index " : generation_index , " prior_ideas_visible " : False , " search_territory " : result [ " territory " ] , " novelty_gate " : { k : v for k , v in gate . items ( ) if k != " matched_thesis " } }
proposal . save ( update_fields = [ " metadata " , " updated_at " ] )
VentureCohortMember . objects . create ( cohort = cohort , proposal = proposal , metadata = { " generation_index " : generation_index , " source_attempt " : result [ " attempt " ] } )
proposals . append ( proposal )
accepted_payloads . append ( result [ " payload " ] )
continue
2026-08-16 16:39:46 +07:00
if gate [ " decision " ] == NoveltyGateDecision . REGENERATE_HARD_EXCLUSION :
hard_rejections + = 1
elif gate [ " decision " ] == NoveltyGateDecision . REGENERATE_DUPLICATE :
duplicate_rejections + = 1
elif gate [ " decision " ] == NoveltyGateDecision . REVIEW_SOFT_EXCLUSION :
soft_reviews + = 1
2026-08-16 17:10:34 +07:00
VentureGenerationRejection . objects . create ( cohort = cohort , slot_index = result [ " slot_index " ] , attempt = result [ " attempt " ] , decision = gate [ " decision " ] , reason = gate [ " explanation " ] , candidate = result [ " payload " ] , matched_thesis = gate . get ( " matched_thesis " ) , similarity_score = float ( gate . get ( " similarity_score " , 0.0 ) ) , metadata = { " territory " : result [ " territory " ] , " classification " : gate . get ( " classification " ) , " semantic_cluster_key " : gate . get ( " semantic_cluster_key " , " " ) } )
if len ( proposals ) > = cohort . cohort_size :
break
failed_slots = max ( 0 , cohort . cohort_size - len ( proposals ) )
if failed_slots :
for missing in range ( len ( proposals ) + 1 , cohort . cohort_size + 1 ) :
VentureGenerationRejection . objects . create ( cohort = cohort , slot_index = missing , attempt = attempts , decision = NoveltyGateDecision . FAILED_IDEATION , reason = " cohort-level attempt budget exhausted " , metadata = { " attempt_budget " : total_budget } )
cohort . status = " PROPOSALS_GENERATED " if len ( proposals ) == cohort . cohort_size else " INSUFFICIENT_ACCEPTED_PROPOSALS "
cohort . metrics = { * * cohort . metrics , " requested_company_count " : cohort . cohort_size , " generation_attempts " : attempts , " cohort_attempt_budget " : total_budget , " accepted_proposals " : len ( proposals ) , " replacement_attempts " : replacement_attempts , " hard_exclusion_rejections " : hard_rejections , " duplicate_rejections " : duplicate_rejections , " soft_exclusion_reviews " : soft_reviews , " failed_slots " : failed_slots , " average_attempts_per_company " : round ( attempts / max ( 1 , len ( proposals ) ) , 2 ) , " generation_model_requests " : model_requests , " proposal_generation_runtime_seconds " : round ( time . monotonic ( ) - started , 2 ) , " proposal_generation_peak_concurrency " : peak_concurrency , " peak_concurrency " : max ( cohort . metrics . get ( " peak_concurrency " , 0 ) , peak_concurrency ) , " generation_records " : generation_records [ - 50 : ] }
2026-08-16 15:08:32 +07:00
cohort . save ( update_fields = [ " metrics " , " status " , " updated_at " ] )
2026-08-15 21:42:46 +07:00
return proposals
def research_cohort ( self , cohort : VentureCohort ) - > None :
started = time . monotonic ( )
2026-08-16 15:08:32 +07:00
def research ( member_id : str ) - > int :
member = VentureCohortMember . objects . select_related ( " proposal " ) . get ( id = member_id )
2026-08-16 17:10:34 +07:00
return len ( self . conduct_market_research ( member . proposal , depth = " light " ) . get ( " sources " , [ ] ) )
2026-08-16 15:08:32 +07:00
member_ids = [ str ( member . id ) for member in cohort . members . order_by ( " created_at " ) ]
source_counts = self . _bounded_map ( member_ids , research , cohort . concurrency )
peak_concurrency = self . _last_bounded_map_peak
2026-08-16 17:10:34 +07:00
source_rejections = sum ( ( member . proposal . metadata . get ( " research " , { } ) if isinstance ( member . proposal . metadata , dict ) else { } ) . get ( " source_rejection_count " , 0 ) for member in cohort . members . select_related ( " proposal " ) )
cohort . metrics = { * * cohort . metrics , " research_runtime_seconds " : round ( time . monotonic ( ) - started , 2 ) , " total_sources " : sum ( source_counts ) , " source_rejection_count " : source_rejections , " public_research_queries " : len ( member_ids ) , " research_peak_concurrency " : peak_concurrency , " peak_concurrency " : max ( cohort . metrics . get ( " peak_concurrency " , 0 ) , peak_concurrency ) }
2026-08-15 21:42:46 +07:00
cohort . status = " RESEARCHED "
cohort . save ( update_fields = [ " metrics " , " status " , " updated_at " ] )
def fingerprint_cohort ( self , cohort : VentureCohort ) - > list [ VentureThesisFingerprint ] :
return [ self . fingerprint_proposal ( member . proposal ) for member in cohort . members . select_related ( " proposal " ) ]
2026-08-16 16:39:46 +07:00
def cluster_theses ( self , cohort : VentureCohort ) - > list [ PortfolioThesisCluster ] :
clusters = [ ]
for member in cohort . members . select_related ( " proposal " ) :
proposal = member . proposal
self . fingerprint_proposal ( proposal )
match = self . match_portfolio_thesis ( proposal )
thesis = match . get ( " matched_thesis " )
if thesis is None :
thesis = self . create_portfolio_thesis_from_proposal ( proposal , cohort )
match = { * * match , " matched_thesis " : thesis , " classification " : ThesisMatchClassification . NEW_THESIS , " similarity_score " : 1.0 , " explanation " : " Created a new portfolio thesis for this opportunity area. " }
cluster , _ = PortfolioThesisCluster . objects . update_or_create (
cohort = cohort ,
proposal = proposal ,
defaults = {
" portfolio_thesis " : thesis ,
" similarity_score " : float ( match [ " similarity_score " ] ) ,
" classification " : match [ " classification " ] ,
" explanation " : match [ " explanation " ] ,
" metadata " : { " canonical_name " : thesis . canonical_name } ,
} ,
)
clusters . append ( cluster )
proposal . metadata = { * * proposal . metadata , " portfolio_thesis_id " : str ( thesis . id ) , " portfolio_thesis " : thesis . canonical_name , " thesis_match " : { " classification " : cluster . classification , " similarity_score " : cluster . similarity_score , " explanation " : cluster . explanation } }
proposal . save ( update_fields = [ " metadata " , " updated_at " ] )
return clusters
2026-08-15 21:42:46 +07:00
def analyze_collisions ( self , cohort : VentureCohort ) - > list [ VentureCollision ] :
collisions = [ ]
proposals = [ member . proposal for member in cohort . members . select_related ( " proposal " ) ]
for a , b in combinations ( proposals , 2 ) :
result = self . classify_overlap ( a , b )
collisions . append ( VentureCollision . objects . create ( cohort = cohort , company_a = a , company_b = b , classification = result [ " classification " ] , similarity_score = result [ " similarity_score " ] , explanation = result [ " explanation " ] , overlapping_dimensions = result [ " overlapping_dimensions " ] ) )
return collisions
def run_individual_diligence_for_cohort ( self , cohort : VentureCohort ) - > None :
started = time . monotonic ( )
2026-08-16 15:08:32 +07:00
def diligence ( member_id : str ) - > None :
member = VentureCohortMember . objects . select_related ( " proposal " ) . get ( id = member_id )
2026-08-15 21:42:46 +07:00
proposal = member . proposal
self . board_review ( proposal )
diligence = self . start_ic_diligence ( proposal )
self . generate_ic_questions ( diligence )
self . answer_questions ( diligence )
self . red_team ( diligence )
self . final_company_response ( diligence )
self . score_and_decide ( diligence )
2026-08-16 17:48:04 +07:00
self . assess_autonomous_operability ( proposal )
2026-08-15 21:42:46 +07:00
gap = self . capability_analysis ( proposal )
self . produce_investment_memo ( diligence , gap )
member . child_graph_run = GraphRun . objects . create ( execution_graph_version = cohort . graph_run . execution_graph_version if cohort . graph_run else None , status = GraphRunStatus . COMPLETE , metadata = { " logical_child_company_run " : True , " proposal_id " : str ( proposal . id ) , " cohort_id " : cohort . cohort_id } ) if cohort . graph_run else None
member . save ( update_fields = [ " child_graph_run " , " updated_at " ] )
2026-08-16 15:08:32 +07:00
member_ids = [ str ( member . id ) for member in cohort . members . order_by ( " created_at " ) ]
self . _bounded_map ( member_ids , diligence , cohort . concurrency )
peak_concurrency = self . _last_bounded_map_peak
cohort . metrics = { * * cohort . metrics , " individual_diligence_runtime_seconds " : round ( time . monotonic ( ) - started , 2 ) , " individual_diligence_peak_concurrency " : peak_concurrency , " peak_concurrency " : max ( cohort . metrics . get ( " peak_concurrency " , 0 ) , peak_concurrency ) }
2026-08-15 21:42:46 +07:00
cohort . status = " INDIVIDUAL_DILIGENCE_COMPLETE "
cohort . save ( update_fields = [ " metrics " , " status " , " updated_at " ] )
2026-08-16 17:48:04 +07:00
def assess_autonomous_operability_for_cohort ( self , cohort : VentureCohort ) - > list [ AutonomousOperabilityAssessment ] :
assessments = [ self . assess_autonomous_operability ( member . proposal ) for member in cohort . members . select_related ( " proposal " ) ]
cohort . metrics = { * * cohort . metrics , " autonomous_eligible_count " : sum ( 1 for item in assessments if item . gate_result == AutonomousGateResult . AUTONOMOUS_ELIGIBLE ) , " assisted_only_count " : sum ( 1 for item in assessments if item . gate_result == AutonomousGateResult . ASSISTED_ONLY ) }
cohort . save ( update_fields = [ " metrics " , " updated_at " ] )
return assessments
def assess_autonomous_operability ( self , proposal : CompanyProposal ) - > AutonomousOperabilityAssessment :
text = " " . join ( [ proposal . title , proposal . description , proposal . problem , proposal . target_customer , proposal . proposed_solution , proposal . business_model , proposal . pricing_hypothesis , proposal . acquisition_strategy , proposal . validation_plan , proposal . differentiation ] ) . lower ( )
positive_terms = [ " self-service " , " automated " , " monitor " , " subscription " , " api " , " dashboard " , " report " , " digital " , " crawl " , " alert " , " standardized " , " recurring " , " checkout " , " plugin " , " cli " , " no-code " ]
negative_causes = {
" SALES " : [ " sales call " , " enterprise sales " , " procurement " , " relationship " ] ,
" DOMAIN_EXPERTISE " : [ " expert " , " clinical " , " medical " , " licensed " , " engineer review " ] ,
" CUSTOMER_TRUST " : [ " trust " , " advisor " , " consultant " ] ,
" RELATIONSHIP_MANAGEMENT " : [ " account management " , " relationship " ] ,
" MANUAL_QA " : [ " manual qa " , " manual review " , " human review " ] ,
" REGULATORY_SIGNOFF " : [ " regulatory sign-off " , " compliance signoff " , " approval " ] ,
" CUSTOM_IMPLEMENTATION " : [ " custom implementation " , " integration " , " bespoke " ] ,
" SUPPORT_ESCALATION " : [ " high-touch support " , " white glove " ] ,
" OFFLINE_ACTIVITY " : [ " onsite " , " offline " , " in person " ] ,
" LEGAL_JUDGMENT " : [ " legal judgment " , " legal advice " , " lawyer " ] ,
" NEGOTIATION " : [ " negotiation " ] ,
" PERSONAL_BRAND " : [ " personal brand " , " founder credibility " ] ,
}
causes = [ cause for cause , terms in negative_causes . items ( ) if any ( term in text for term in terms ) ]
score = 45 + min ( 30 , sum ( 5 for term in positive_terms if term in text ) ) - min ( 45 , len ( causes ) * 8 )
ai = self . ai_leverage_score ( proposal )
platform = self . platformization_potential ( proposal )
score + = 10 if ai > = 70 else 0
score + = 10 if platform > = 70 else 0
score = max ( 0 , min ( 100 , score ) )
minutes = 5 if score > = 90 else 15 if score > = 80 else 30 if score > = 70 else 90 if score > = 50 else 240
dependency = FounderDependencyLevel . NONE if minutes < = 5 and not causes else FounderDependencyLevel . LOW if minutes < = 30 and len ( causes ) < = 1 else FounderDependencyLevel . MEDIUM if minutes < = 90 else FounderDependencyLevel . HIGH if minutes < = 240 else FounderDependencyLevel . CRITICAL
if score > = 80 and dependency in { FounderDependencyLevel . NONE , FounderDependencyLevel . LOW } and ai > = 70 and platform > = 70 :
gate = AutonomousGateResult . AUTONOMOUS_ELIGIBLE
track = VentureTrack . AUTONOMOUS
elif score > = 70 and dependency in { FounderDependencyLevel . LOW , FounderDependencyLevel . MEDIUM } :
gate = AutonomousGateResult . AUTONOMOUS_BORDERLINE
track = VentureTrack . AUTONOMOUS
elif score > = 45 :
gate = AutonomousGateResult . ASSISTED_ONLY
track = VentureTrack . ASSISTED
else :
gate = AutonomousGateResult . REJECT_OPERABILITY
track = VentureTrack . ASSISTED
loop = self . autonomous_operating_loop ( proposal , gate )
contract = self . autonomous_fulfillment_contract ( proposal )
assessment , _ = AutonomousOperabilityAssessment . objects . update_or_create (
proposal = proposal ,
defaults = { " venture_track " : track , " gate_result " : gate , " autonomous_operability_score " : score , " commercial_score " : self . commercial_score ( proposal ) , " founder_dependency " : dependency , " dependency_causes " : causes , " minutes_per_week_human " : minutes , " human_actions_required " : contract [ " escalation " ] , " human_action_categories " : [ " LEGAL_SETUP " , " SPEND_APPROVAL " , " EXCEPTION_ESCALATION " ] if minutes < = 30 else causes , " operating_loop " : loop , " fulfillment_contract " : contract , " validation_offer " : self . validation_offer ( proposal ) , " end_state_business_model " : self . end_state_business_model ( proposal ) , " structural_blockers " : causes , " platform_blockers " : self . platform_blockers ( proposal ) , " component_scores " : { " customer_acquisition_autonomy " : loop [ " discover " ] [ " score " ] , " onboarding_autonomy " : loop [ " onboard " ] [ " score " ] , " fulfillment_autonomy " : loop [ " fulfill " ] [ " score " ] , " support_autonomy " : loop [ " support " ] [ " score " ] , " billing_payment_autonomy " : loop [ " checkout " ] [ " score " ] , " quality_verification " : loop [ " verify " ] [ " score " ] , " repeatability " : 90 if " recurring " in text or " subscription " in text else 65 } , " rationale " : f " { gate } : score { score } , dependency { dependency } , causes { ' , ' . join ( causes ) or ' none ' } . " } ,
)
proposal . metadata = { * * proposal . metadata , " venture_track " : track , " autonomous_operability_score " : score , " founder_dependency " : dependency , " autonomous_gate_result " : gate , " minutes_per_week_human " : minutes , " validation_offer " : assessment . validation_offer , " end_state_business_model " : assessment . end_state_business_model }
proposal . save ( update_fields = [ " metadata " , " updated_at " ] )
self . _artifact ( proposal , proposal . mandate , " AUTONOMOUS_OPERABILITY_REPORT " , " Autonomous Operability Report " , { " score " : score , " gate_result " : gate , " founder_dependency " : dependency , " dependency_causes " : causes , " operating_loop " : loop , " fulfillment_contract " : contract , " validation_offer " : assessment . validation_offer , " end_state_business_model " : assessment . end_state_business_model , " platform_blockers " : assessment . platform_blockers , " structural_blockers " : assessment . structural_blockers } , self . _readable_memo ( { " score " : score , " gate_result " : gate , " founder_dependency " : dependency , " dependency_causes " : causes , " operating_loop " : loop } ) , " Autonomous Operability IC " )
return assessment
def commercial_score ( self , proposal : CompanyProposal ) - > float :
diligence = proposal . ic_diligence . order_by ( " -created_at " ) . first ( )
return float ( diligence . decision . composite_score ) if diligence and hasattr ( diligence , " decision " ) else 0.0
def autonomous_operating_loop ( self , proposal : CompanyProposal , gate : str ) - > dict [ str , dict [ str , Any ] ] :
automated = gate in { AutonomousGateResult . AUTONOMOUS_ELIGIBLE , AutonomousGateResult . AUTONOMOUS_BORDERLINE }
state = " AUTONOMOUS " if automated else " HUMAN_ROUTINE_REQUIRED "
score = 90 if automated else 35
return { stage : { " capability " : " AVAILABLE " if stage in { " discover " , " checkout " , " deliver " , " measure " } else " PARTIAL " , " operation " : state , " score " : score } for stage in [ " discover " , " qualify " , " acquire " , " checkout " , " onboard " , " fulfill " , " verify " , " deliver " , " support " , " measure " , " retain_upsell " ] }
def autonomous_fulfillment_contract ( self , proposal : CompanyProposal ) - > dict [ str , Any ] :
return { " customer_input " : " Customer supplies a URL, file, repository, document, dataset, or narrow workflow input through a self-service intake. " , " artifex_process " : " Artifex agents run retrieval, analysis/transformation, structured QA, report generation, and delivery workflows. " , " customer_output " : proposal . proposed_solution , " quality_verification " : " Automated checks validate schema completeness, source links, threshold scores, and known failure conditions before delivery. " , " failure_recovery " : " If verification fails, rerun once with stricter constraints; unresolved failures create exceptional human escalation. " , " billing_event " : " Payment is collected at checkout before one-off validation delivery or at subscription activation. " , " support_model " : " Agent-authored help, status updates, and retry guidance; human handles only exceptional safety/legal/account issues. " , " escalation " : [ " material legal commitment " , " irreversible spend " , " security/safety exception " , " verification repeatedly fails " ] }
def validation_offer ( self , proposal : CompanyProposal ) - > dict [ str , Any ] :
return { " offer " : " $49-$99 fixed-scope automated validation output " , " price " : " $49-$99 " , " proof " : " willingness-to-pay for a standardized digital result before full product build " , " aligned_with_end_state " : True }
def end_state_business_model ( self , proposal : CompanyProposal ) - > dict [ str , Any ] :
return { " model " : proposal . business_model , " likely_pricing " : proposal . pricing_hypothesis , " recurring_path " : " subscription, usage, or repeat purchase if validation demand repeats " }
def platform_blockers ( self , proposal : CompanyProposal ) - > list [ str ] :
return [ " STRIPE_IMPLEMENTATION_MISSING " , " SUBDOMAIN_DEPLOYMENT " , " EMAIL_DELIVERY " , " SUPPORT_INBOX " ]
2026-08-15 21:42:46 +07:00
def portfolio_ic ( self , cohort : VentureCohort ) - > PortfolioICReview :
2026-08-16 17:48:04 +07:00
self . assess_autonomous_operability_for_cohort ( cohort )
2026-08-16 17:10:34 +07:00
preliminary_rows = self . portfolio_ranking_rows ( cohort )
preliminary_top_5 = preliminary_rows [ : 5 ]
before_coverage = { }
for row in preliminary_top_5 :
proposal = CompanyProposal . objects . get ( id = row [ " proposal_id " ] )
before_coverage [ str ( proposal . id ) ] = ( proposal . metadata . get ( " research " , { } ) if isinstance ( proposal . metadata , dict ) else { } ) . get ( " coverage_ratio " , 0.0 )
deep_started = time . monotonic ( )
for row in preliminary_top_5 :
proposal = CompanyProposal . objects . get ( id = row [ " proposal_id " ] )
self . conduct_market_research ( proposal , depth = " deep " )
diligence = proposal . ic_diligence . order_by ( " -created_at " ) . first ( )
if diligence :
self . score_and_decide ( diligence )
after_coverage = { }
for row in preliminary_top_5 :
proposal = CompanyProposal . objects . get ( id = row [ " proposal_id " ] )
after_coverage [ str ( proposal . id ) ] = ( proposal . metadata . get ( " research " , { } ) if isinstance ( proposal . metadata , dict ) else { } ) . get ( " coverage_ratio " , 0.0 )
rows = self . portfolio_ranking_rows ( cohort )
preliminary_rank_by_id = { row [ " proposal_id " ] : index + 1 for index , row in enumerate ( preliminary_rows ) }
ranking_changes = [ { " proposal_id " : row [ " proposal_id " ] , " company " : row [ " company " ] , " preliminary_rank " : preliminary_rank_by_id . get ( row [ " proposal_id " ] ) , " final_rank " : index + 1 } for index , row in enumerate ( rows ) if preliminary_rank_by_id . get ( row [ " proposal_id " ] ) != index + 1 ]
2026-08-16 17:48:04 +07:00
top_3 = [ row for row in rows if row . get ( " autonomous_gate_result " ) == AutonomousGateResult . AUTONOMOUS_ELIGIBLE ] [ : 3 ]
top_3_ids = { row [ " proposal_id " ] for row in top_3 }
2026-08-16 17:10:34 +07:00
for rank , row in enumerate ( rows , start = 1 ) :
member = cohort . members . get ( proposal_id = row [ " proposal_id " ] )
member . rank = rank
2026-08-16 17:48:04 +07:00
member . is_top_3 = row [ " proposal_id " ] in top_3_ids
2026-08-16 17:10:34 +07:00
member . portfolio_score = row [ " portfolio_score " ]
member . save ( update_fields = [ " rank " , " is_top_3 " , " portfolio_score " , " updated_at " ] )
row [ " rank " ] = rank
concentration = self . _portfolio_concentration ( cohort )
2026-08-16 17:48:04 +07:00
review , _ = PortfolioICReview . objects . update_or_create ( cohort = cohort , defaults = { " rankings " : rows , " top_3 " : top_3 , " concentration " : concentration , " metadata " : { " no_funding " : True , " preliminary_rankings " : preliminary_rows , " preliminary_top_5 " : preliminary_top_5 , " finalist_research_coverage_before " : before_coverage , " finalist_research_coverage_after " : after_coverage , " ranking_changes_after_deep_research " : ranking_changes , " autonomous_top_3_shortfall " : max ( 0 , 3 - len ( top_3 ) ) } } )
2026-08-16 17:10:34 +07:00
cohort . metrics = { * * cohort . metrics , " finalist_deep_research_count " : len ( preliminary_top_5 ) , " finalist_deep_research_runtime_seconds " : round ( time . monotonic ( ) - deep_started , 2 ) , " finalist_research_coverage_before " : before_coverage , " finalist_research_coverage_after " : after_coverage , " ranking_changes_after_deep_research " : ranking_changes }
cohort . status = " PORTFOLIO_IC_COMPLETE "
cohort . save ( update_fields = [ " metrics " , " status " , " updated_at " ] )
return review
def portfolio_ranking_rows ( self , cohort : VentureCohort ) - > list [ dict [ str , Any ] ] :
2026-08-15 21:42:46 +07:00
rows = [ ]
collision_risk = defaultdict ( int )
for collision in cohort . collisions . exclude ( classification = OverlapClassification . NONE ) :
collision_risk [ str ( collision . company_a_id ) ] + = 1
collision_risk [ str ( collision . company_b_id ) ] + = 1
for member in cohort . members . select_related ( " proposal " ) :
proposal = member . proposal
decision = proposal . ic_diligence . order_by ( " -created_at " ) . first ( ) . decision
2026-08-16 17:48:04 +07:00
assessment = self . assess_autonomous_operability ( proposal )
2026-08-15 21:42:46 +07:00
capability_burden = proposal . capability_requirements . filter ( status = CapabilityStatus . MISSING ) . count ( )
2026-08-16 14:30:40 +07:00
concentration_penalty = self . _generic_concentration_penalty ( proposal )
ai_bonus = ( decision . component_scores . get ( " AI Leverage " , 0 ) * 0.08 ) + ( decision . component_scores . get ( " Platformization Potential " , 0 ) * 0.06 )
2026-08-16 17:48:04 +07:00
autonomy_penalty = 35 if assessment . gate_result == AutonomousGateResult . ASSISTED_ONLY else 70 if assessment . gate_result == AutonomousGateResult . REJECT_OPERABILITY else 12 if assessment . gate_result == AutonomousGateResult . AUTONOMOUS_BORDERLINE else 0
score = round ( decision . composite_score * 0.45 + assessment . autonomous_operability_score * 0.45 + decision . probability_500_within_30_days * 0.12 + ai_bonus - capability_burden * 1.5 - collision_risk [ str ( proposal . id ) ] * 2 - concentration_penalty - autonomy_penalty , 1 )
rows . append ( { " proposal_id " : str ( proposal . id ) , " company " : proposal . title , " thesis " : proposal . pitch . get ( " One-line thesis " , proposal . description ) , " ic_score " : decision . composite_score , " commercial_score " : assessment . commercial_score , " autonomy_score " : assessment . autonomous_operability_score , " autonomous_gate_result " : assessment . gate_result , " founder_dependency " : assessment . founder_dependency , " minutes_per_week_human " : assessment . minutes_per_week_human , " probability " : decision . probability_500_within_30_days , " decision " : decision . decision , " evidence_tier " : decision . evidence_tier , " initial_tranche " : str ( decision . initial_tranche or " 0 " ) , " ai_leverage " : decision . component_scores . get ( " AI Leverage " , 0 ) , " platformization_potential " : decision . component_scores . get ( " Platformization Potential " , 0 ) , " portfolio_score " : score , " capability_burden " : capability_burden , " collision_risk " : collision_risk [ str ( proposal . id ) ] , " concentration_penalty " : concentration_penalty } )
2026-08-15 21:42:46 +07:00
rows . sort ( key = lambda item : item [ " portfolio_score " ] , reverse = True )
2026-08-16 17:10:34 +07:00
return rows
2026-08-15 21:42:46 +07:00
2026-08-16 16:39:46 +07:00
def saturation_analysis ( self , cohort : VentureCohort ) - > list [ dict [ str , Any ] ] :
rows = [ ]
by_thesis : dict [ str , list [ PortfolioThesisCluster ] ] = defaultdict ( list )
for cluster in cohort . thesis_clusters . select_related ( " portfolio_thesis " , " proposal " ) :
if cluster . portfolio_thesis_id :
by_thesis [ str ( cluster . portfolio_thesis_id ) ] . append ( cluster )
for clusters in by_thesis . values ( ) :
thesis = clusters [ 0 ] . portfolio_thesis
scores = [ ]
classifications = Counter ( cluster . classification for cluster in clusters )
best_proposal = None
for cluster in clusters :
diligence = cluster . proposal . ic_diligence . order_by ( " -created_at " ) . first ( )
if diligence and hasattr ( diligence , " decision " ) :
scores . append ( float ( diligence . decision . composite_score ) )
if best_proposal is None or diligence . decision . composite_score > best_proposal [ 1 ] :
best_proposal = ( cluster . proposal , float ( diligence . decision . composite_score ) )
best_score = max ( scores ) if scores else 0.0
spread = round ( max ( scores ) - min ( scores ) , 1 ) if len ( scores ) > 1 else 0.0
proposal_count = len ( clusters )
near_count = classifications . get ( ThesisMatchClassification . NEAR_DUPLICATE , 0 ) + classifications . get ( ThesisMatchClassification . DUPLICATE , 0 )
if proposal_count > = 3 and near_count > = 2 and spread < = 15 :
recommendation = PortfolioThesisStatus . SATURATED
rationale = " Multiple semantically similar proposals with limited score spread; more ideation is unlikely to add information without a new wedge. "
elif proposal_count > = 2 and best_score > = 60 and spread < = 10 :
recommendation = PortfolioThesisStatus . SATURATED
rationale = " A differentiated winner appears to have emerged; additional near-term ideation should move elsewhere. "
elif best_score > = 55 :
recommendation = PortfolioThesisStatus . ACTIVE_CANDIDATE
rationale = " Strong enough to remain active but not yet saturated. "
else :
recommendation = PortfolioThesisStatus . EXPLORED
rationale = " Explored without enough evidence or score strength to prioritize. "
analysis , _ = PortfolioSaturationAnalysis . objects . update_or_create (
cohort = cohort ,
portfolio_thesis = thesis ,
defaults = { " proposal_count " : proposal_count , " best_ic_score " : best_score , " score_spread " : spread , " status_recommendation " : recommendation , " rationale " : rationale , " metadata " : { " classification_counts " : dict ( classifications ) , " best_company " : best_proposal [ 0 ] . title if best_proposal else " " } } ,
)
rows . append ( { " thesis " : thesis . canonical_name , " proposal_count " : analysis . proposal_count , " best_ic_score " : analysis . best_ic_score , " score_spread " : analysis . score_spread , " status_recommendation " : analysis . status_recommendation , " rationale " : analysis . rationale } )
if hasattr ( cohort , " portfolio_review " ) :
review = cohort . portfolio_review
review . metadata = { * * review . metadata , " saturation_analysis " : rows }
review . save ( update_fields = [ " metadata " , " updated_at " ] )
cohort . metadata = { * * cohort . metadata , " saturation_analysis " : rows }
cohort . save ( update_fields = [ " metadata " , " updated_at " ] )
return rows
def update_thesis_registry ( self , cohort : VentureCohort ) - > list [ dict [ str , Any ] ] :
updates = [ ]
for analysis in cohort . saturation_analyses . select_related ( " portfolio_thesis " ) :
thesis = analysis . portfolio_thesis
clusters = list ( cohort . thesis_clusters . filter ( portfolio_thesis = thesis ) . select_related ( " proposal " ) )
best = None
for cluster in clusters :
diligence = cluster . proposal . ic_diligence . order_by ( " -created_at " ) . first ( )
if diligence and hasattr ( diligence , " decision " ) and ( best is None or diligence . decision . composite_score > best [ 1 ] ) :
best = ( cluster . proposal , float ( diligence . decision . composite_score ) )
thesis . proposal_count = thesis . proposal_clusters . count ( )
thesis . best_ic_score = max ( float ( thesis . best_ic_score or 0.0 ) , analysis . best_ic_score )
if best and ( thesis . best_company is None or best [ 1 ] > = thesis . best_ic_score ) :
thesis . best_company = best [ 0 ]
thesis . first_seen_cohort = thesis . first_seen_cohort or cohort
thesis . last_seen_cohort = cohort
thesis . status = analysis . status_recommendation
2026-08-16 17:48:04 +07:00
best_assessment = self . assess_autonomous_operability ( best [ 0 ] ) if best else None
if best_assessment :
thesis . venture_track = best_assessment . venture_track
thesis . best_autonomous_operability_score = max ( float ( thesis . best_autonomous_operability_score or 0.0 ) , best_assessment . autonomous_operability_score )
thesis . best_founder_dependency = best_assessment . founder_dependency
thesis . autonomous_candidate_status = AutonomousCandidateStatus . AUTONOMOUS_CANDIDATE if best_assessment . gate_result == AutonomousGateResult . AUTONOMOUS_ELIGIBLE else AutonomousCandidateStatus . ASSISTED_CANDIDATE if best_assessment . gate_result == AutonomousGateResult . ASSISTED_ONLY else AutonomousCandidateStatus . AUTONOMOUS_REJECTED if best_assessment . gate_result == AutonomousGateResult . REJECT_OPERABILITY else AutonomousCandidateStatus . NOT_ASSESSED
2026-08-16 16:39:46 +07:00
thesis . metadata = { * * thesis . metadata , " last_saturation_rationale " : analysis . rationale , " last_reopen_reason " : thesis . metadata . get ( " last_reopen_reason " , " " ) }
2026-08-16 17:48:04 +07:00
thesis . save ( update_fields = [ " proposal_count " , " best_ic_score " , " best_company " , " first_seen_cohort " , " last_seen_cohort " , " status " , " venture_track " , " best_autonomous_operability_score " , " best_founder_dependency " , " autonomous_candidate_status " , " metadata " , " updated_at " ] )
updates . append ( { " thesis " : thesis . canonical_name , " status " : thesis . status , " proposal_count " : thesis . proposal_count , " best_ic_score " : thesis . best_ic_score , " venture_track " : thesis . venture_track , " best_autonomous_operability_score " : thesis . best_autonomous_operability_score , " best_founder_dependency " : thesis . best_founder_dependency , " autonomous_candidate_status " : thesis . autonomous_candidate_status } )
2026-08-16 16:39:46 +07:00
cohort . metadata = { * * cohort . metadata , " thesis_registry_after " : self . registry_snapshot ( ) }
cohort . save ( update_fields = [ " metadata " , " updated_at " ] )
return updates
2026-08-15 21:42:46 +07:00
def aggregate_capability_demand ( self , cohort : VentureCohort , * , top_3_only : bool = False ) - > list [ dict [ str , Any ] ] :
members = cohort . members . filter ( is_top_3 = True ) if top_3_only else cohort . members . all ( )
proposals = [ member . proposal for member in members . select_related ( " proposal " ) ]
by_capability : dict [ str , list [ CompanyCapabilityRequirement ] ] = defaultdict ( list )
for proposal in proposals :
for req in proposal . capability_requirements . all ( ) :
by_capability [ req . category ] . append ( req )
stage_order = { CapabilityPriority . BEFORE_VALIDATION : 0 , CapabilityPriority . BEFORE_FIRST_CUSTOMER : 1 , CapabilityPriority . BEFORE_SCALING : 2 }
results = [ ]
for capability , reqs in by_capability . items ( ) :
earliest = sorted ( [ req . priority for req in reqs ] , key = lambda item : stage_order [ item ] ) [ 0 ]
statuses = Counter ( req . status for req in reqs )
top_3_count = sum ( 1 for req in reqs if req . proposal . cohort_memberships . filter ( cohort = cohort , is_top_3 = True ) . exists ( ) )
priority_score = len ( reqs ) * 10 + top_3_count * 8 + ( 12 if earliest == CapabilityPriority . BEFORE_VALIDATION else 6 if earliest == CapabilityPriority . BEFORE_FIRST_CUSTOMER else 2 ) + statuses . get ( CapabilityStatus . MISSING , 0 ) * 4
row = { " capability " : capability , " count " : len ( reqs ) , " percentage " : round ( ( len ( reqs ) / max ( 1 , len ( proposals ) ) ) * 100 , 1 ) , " earliest_stage " : earliest , " companies " : [ req . proposal . title for req in reqs ] , " status_distribution " : dict ( statuses ) , " top_3_count " : top_3_count , " priority_score " : priority_score }
if not top_3_only :
VentureCapabilityDemand . objects . update_or_create ( cohort = cohort , capability = capability , defaults = { k : v for k , v in row . items ( ) if k != " capability " } )
results . append ( row )
results . sort ( key = lambda item : item [ " priority_score " ] , reverse = True )
if hasattr ( cohort , " portfolio_review " ) :
review = cohort . portfolio_review
if top_3_only :
review . top_3_capability_gaps = results [ : 10 ]
else :
review . capability_demand = results
review . recommended_build_priorities = [ item [ " capability " ] for item in results if CapabilityStatus . MISSING in item [ " status_distribution " ] ] [ : 5 ]
review . save ( update_fields = [ " capability_demand " , " top_3_capability_gaps " , " recommended_build_priorities " , " updated_at " ] )
return results
def produce_cohort_report ( self , cohort : VentureCohort ) - > VentureArtifact :
review = cohort . portfolio_review
2026-08-16 16:39:46 +07:00
ideation_mandate = getattr ( cohort , " ideation_mandate " , None )
clusters = [ { " company " : cluster . proposal . title , " portfolio_thesis " : cluster . portfolio_thesis . canonical_name if cluster . portfolio_thesis else " " , " classification " : cluster . classification , " similarity_score " : cluster . similarity_score , " explanation " : cluster . explanation } for cluster in cohort . thesis_clusters . select_related ( " proposal " , " portfolio_thesis " ) . order_by ( " created_at " ) ]
2026-08-16 17:48:04 +07:00
autonomy = [ self . autonomy_report_row ( member . proposal ) for member in cohort . members . select_related ( " proposal " ) . order_by ( " rank " , " created_at " ) ]
2026-08-16 16:39:46 +07:00
generation_rejections = [ { " slot_index " : rejection . slot_index , " attempt " : rejection . attempt , " decision " : rejection . decision , " reason " : rejection . reason , " candidate_title " : rejection . candidate . get ( " title " , " " ) if isinstance ( rejection . candidate , dict ) else " " , " similarity_score " : rejection . similarity_score } for rejection in cohort . generation_rejections . order_by ( " slot_index " , " attempt " ) ]
saturation = [ { " thesis " : analysis . portfolio_thesis . canonical_name , " proposal_count " : analysis . proposal_count , " best_ic_score " : analysis . best_ic_score , " score_spread " : analysis . score_spread , " status_recommendation " : analysis . status_recommendation , " rationale " : analysis . rationale } for analysis in cohort . saturation_analyses . select_related ( " portfolio_thesis " ) . order_by ( " -proposal_count " ) ]
2026-08-16 17:10:34 +07:00
accepted_count = cohort . members . count ( )
2026-08-16 17:48:04 +07:00
content = { " cohort_id " : cohort . cohort_id , " mandate " : cohort . mandate . objective , " accepted_count " : accepted_count , " requested_count " : cohort . cohort_size , " venture_track " : VentureTrack . AUTONOMOUS , " autonomous_operability_report " : autonomy , " assisted_only_companies " : [ row for row in autonomy if row . get ( " gate_result " ) == AutonomousGateResult . ASSISTED_ONLY ] , " ideation_mandate " : self . ideation_mandate_payload ( ideation_mandate ) if ideation_mandate else { } , " hard_exclusions " : ideation_mandate . hard_exclusions if ideation_mandate else [ ] , " soft_exclusions " : ideation_mandate . soft_exclusions if ideation_mandate else [ ] , " search_territories " : ideation_mandate . opportunity_territories if ideation_mandate else [ ] , " generation_rejections " : generation_rejections , " thesis_registry_before " : ( ideation_mandate . registry_snapshot . get ( " theses " , [ ] ) if ideation_mandate else [ ] ) , " thesis_registry_after " : cohort . metadata . get ( " thesis_registry_after " , [ ] ) , " thesis_clusters " : clusters , " saturation_analysis " : saturation , " idea_diversity_metrics " : self . diversity_metrics ( cohort ) , " runtime " : cohort . metrics , " total_spend " : 0 , " customer_outreach " : " none " , " rankings " : review . rankings , " top_3 " : review . top_3 , " collisions " : self . _collision_summary ( cohort ) , " portfolio_concentration " : review . concentration , " capability_demand " : review . capability_demand , " top_3_capability_gaps " : review . top_3_capability_gaps , " recommended_build_priorities " : review . recommended_build_priorities }
2026-08-15 21:42:46 +07:00
readable = self . _readable_cohort_report ( content )
2026-08-16 17:10:34 +07:00
cohort . status = " COMPLETE " if accepted_count == cohort . cohort_size else " PARTIAL_COMPLETE "
2026-08-15 21:42:46 +07:00
cohort . save ( update_fields = [ " status " , " updated_at " ] )
return VentureArtifact . objects . create ( mandate = cohort . mandate , graph_run = cohort . graph_run , artifact_type = " VENTURE_DISCOVERY_COHORT_REPORT " , name = " Venture Discovery Cohort Report " , content = content , readable = readable , generated_by = " Portfolio IC " )
2026-08-16 17:48:04 +07:00
def autonomy_report_row ( self , proposal : CompanyProposal ) - > dict [ str , Any ] :
assessment = self . assess_autonomous_operability ( proposal )
return { " company " : proposal . title , " venture_track " : assessment . venture_track , " gate_result " : assessment . gate_result , " commercial_score " : assessment . commercial_score , " autonomous_operability_score " : assessment . autonomous_operability_score , " founder_dependency " : assessment . founder_dependency , " dependency_causes " : assessment . dependency_causes , " minutes_per_week_human " : assessment . minutes_per_week_human , " human_actions_required " : assessment . human_actions_required , " operating_loop " : assessment . operating_loop , " fulfillment_contract " : assessment . fulfillment_contract , " validation_offer " : assessment . validation_offer , " end_state_business_model " : assessment . end_state_business_model , " structural_blockers " : assessment . structural_blockers , " platform_blockers " : assessment . platform_blockers , " component_scores " : assessment . component_scores , " rationale " : assessment . rationale }
2026-08-15 21:42:46 +07:00
def calibrate_probability ( self , proposal : CompanyProposal , * , raw_probability : float ) - > dict [ str , Any ] :
tier = proposal . evidence_tier or EvidenceTier . TIER_0_THESIS
ceiling = float ( EVIDENCE_CEILINGS . get ( tier , 35 ) )
adjusted = min ( float ( raw_probability ) , ceiling )
return { " raw_probability " : float ( raw_probability ) , " evidence_adjusted_probability " : adjusted , " evidence_ceiling " : ceiling , " evidence_tier " : tier , " explanation " : f " { tier } caps P($500/30d) at { ceiling } %; IC uses { adjusted } %. " }
2026-08-16 15:08:32 +07:00
def _bounded_map ( self , items : list [ Any ] | range , worker : Any , concurrency : int ) - > list [ Any ] :
self . _last_bounded_map_peak = 0
workers = self . _effective_concurrency ( concurrency )
if workers < = 1 :
results = [ worker ( item ) for item in items ]
self . _last_bounded_map_peak = 1 if results else 0
return results
results : list [ tuple [ int , Any ] ] = [ ]
active = 0
active_lock = threading . Lock ( )
def tracked_worker ( item : Any ) - > Any :
nonlocal active
with active_lock :
active + = 1
self . _last_bounded_map_peak = max ( self . _last_bounded_map_peak , active )
try :
return self . _threaded_worker ( worker , item )
finally :
with active_lock :
active - = 1
with ThreadPoolExecutor ( max_workers = workers ) as executor :
futures = { executor . submit ( tracked_worker , item ) : index for index , item in enumerate ( items ) }
for future in as_completed ( futures ) :
results . append ( ( futures [ future ] , future . result ( ) ) )
results . sort ( key = lambda item : item [ 0 ] )
return [ result for _ , result in results ]
def _threaded_worker ( self , worker : Any , item : Any ) - > Any :
close_old_connections ( )
try :
return worker ( item )
finally :
close_old_connections ( )
def _effective_concurrency ( self , concurrency : int ) - > int :
requested = max ( 1 , int ( concurrency or 1 ) )
if connection . vendor == " sqlite " and connection . settings_dict . get ( " NAME " ) == " :memory: " :
return 1
return requested
2026-08-15 21:42:46 +07:00
def validate_identity_content ( self , proposal : CompanyProposal , content : Any ) - > dict [ str , Any ] :
text = json . dumps ( content , default = str ) . lower ( )
checks = {
" company_identity " : self . _token_overlap ( proposal . title , text ) > 0 ,
" icp " : self . _token_overlap ( proposal . target_customer , text ) > = 1 ,
" problem " : self . _token_overlap ( proposal . problem , text ) > = 1 ,
" offer " : self . _token_overlap ( proposal . proposed_solution , text ) > = 1 ,
" business_model " : self . _token_overlap ( proposal . business_model , text ) > = 1 ,
}
passed = sum ( 1 for value in checks . values ( ) if value ) > = 3 and checks [ " company_identity " ]
result = { " passed " : passed , " checks " : checks , " warning " : " " if passed else " identity_consistency_warning " }
return result
def fingerprint_proposal ( self , proposal : CompanyProposal ) - > VentureThesisFingerprint :
2026-08-16 16:39:46 +07:00
try :
existing = proposal . fingerprint
except ObjectDoesNotExist :
existing = None
2026-08-15 21:42:46 +07:00
data = self . _fingerprint_data ( proposal )
digest = hashlib . sha256 ( json . dumps ( data , sort_keys = True ) . encode ( ) ) . hexdigest ( )
fields = { * * data , " fingerprint_hash " : digest , " metadata " : { " deterministic " : True } }
if existing :
for key , value in fields . items ( ) :
setattr ( existing , key , value )
existing . save ( update_fields = [ * fields . keys ( ) , " updated_at " ] )
return existing
return VentureThesisFingerprint . objects . create ( proposal = proposal , * * fields )
def classify_overlap ( self , a : CompanyProposal , b : CompanyProposal ) - > dict [ str , Any ] :
fa = self . fingerprint_proposal ( a )
fb = self . fingerprint_proposal ( b )
dimensions = [ " industry " , " business_model " , " primary_distribution_channel " , " price_band " , " time_to_first_cash_band " , " service_software_hybrid " , " regulatory_dependency " ]
overlap = [ dim for dim in dimensions if getattr ( fa , dim ) and getattr ( fa , dim ) == getattr ( fb , dim ) ]
text_score = self . _jaccard ( f " { fa . icp } { fa . problem } { fa . offer } " , f " { fb . icp } { fb . problem } { fb . offer } " )
score = round ( ( len ( overlap ) / len ( dimensions ) ) * 0.55 + text_score * 0.45 , 2 )
if score > = 0.82 :
classification = OverlapClassification . DUPLICATE
elif score > = 0.62 :
classification = OverlapClassification . NEAR_DUPLICATE
elif getattr ( fa , " industry " ) == getattr ( fb , " industry " ) and getattr ( fa , " icp " ) == getattr ( fb , " icp " ) :
classification = OverlapClassification . COMPETITIVE
elif getattr ( fa , " industry " ) == getattr ( fb , " industry " ) or getattr ( fa , " business_model " ) == getattr ( fb , " business_model " ) :
classification = OverlapClassification . ADJACENT
else :
classification = OverlapClassification . NONE
return { " classification " : classification , " similarity_score " : score , " overlapping_dimensions " : overlap , " explanation " : f " Overlap { overlap } ; text similarity { text_score : .2f } . " }
2026-08-16 16:39:46 +07:00
def seed_dogfood_thesis_registry ( self ) - > None :
seeds = [
( " AI RFP response automation for B2B SaaS " , " AI-assisted RFP/proposal drafting and response automation for B2B SaaS sales teams. " , " AI wrapper platforms " , PortfolioThesisStatus . SATURATED ) ,
( " AI churn/retention intelligence for SaaS " , " AI analysis of SaaS customer health data to predict churn and generate retention playbooks. " , " AI-enabled services " , PortfolioThesisStatus . SATURATED ) ,
( " Shopify compliance/audit " , " Shopify or ecommerce audit/compliance products and services. " , " SMB automation " , PortfolioThesisStatus . EXPLORED ) ,
( " Freelancer contract risk scanning " , " Contract risk review and clause scanning for freelancers and solo operators. " , " Data/document automation " , PortfolioThesisStatus . ACTIVE_CANDIDATE ) ,
]
for name , thesis , category , status in seeds :
existing = PortfolioThesis . objects . filter ( canonical_name = name ) . first ( )
historical = self . historical_thesis_stats ( name )
2026-08-16 16:43:27 +07:00
historical_metadata = { key : value for key , value in historical . items ( ) if key != " best_company " }
historical_metadata [ " best_company " ] = historical [ " best_company " ] . title if historical [ " best_company " ] else " "
defaults = { " concise_thesis " : thesis , " category " : category , " status " : status , " metadata " : { " dogfood_seed " : True , " historical " : historical_metadata } }
2026-08-16 16:39:46 +07:00
if historical [ " proposal_count " ] :
defaults . update ( { " proposal_count " : historical [ " proposal_count " ] , " best_ic_score " : historical [ " best_ic_score " ] , " best_company " : historical [ " best_company " ] } )
if existing is None :
PortfolioThesis . objects . create ( canonical_name = name , fingerprint = hashlib . sha256 ( name . lower ( ) . encode ( ) ) . hexdigest ( ) , * * defaults )
else :
if existing . metadata . get ( " last_reopen_reason " ) :
defaults . pop ( " status " , None )
for key , value in defaults . items ( ) :
setattr ( existing , key , value )
existing . save ( update_fields = [ * defaults . keys ( ) , " updated_at " ] )
def historical_thesis_stats ( self , canonical_name : str ) - > dict [ str , Any ] :
proposals = [ proposal for proposal in CompanyProposal . objects . all ( ) if self . canonical_thesis_name ( self . payload_from_proposal ( proposal ) ) == canonical_name ]
best_score = 0.0
best_company = None
for proposal in proposals :
diligence = proposal . ic_diligence . order_by ( " -created_at " ) . first ( )
if diligence and hasattr ( diligence , " decision " ) and diligence . decision . composite_score > best_score :
best_score = float ( diligence . decision . composite_score )
best_company = proposal
return { " proposal_count " : len ( proposals ) , " best_ic_score " : best_score , " best_company " : best_company }
def registry_snapshot ( self ) - > list [ dict [ str , Any ] ] :
return [ { " id " : str ( thesis . id ) , " canonical_name " : thesis . canonical_name , " status " : thesis . status , " proposal_count " : thesis . proposal_count , " best_ic_score " : thesis . best_ic_score , " best_company " : thesis . best_company . title if thesis . best_company else " " , " last_seen_cohort " : thesis . last_seen_cohort . cohort_id if thesis . last_seen_cohort else " " , " category " : thesis . category } for thesis in PortfolioThesis . objects . order_by ( " canonical_name " ) ]
def reopen_portfolio_thesis ( self , thesis : PortfolioThesis , * , reason : str , status : str = PortfolioThesisStatus . ACTIVE_CANDIDATE ) - > PortfolioThesis :
thesis . status = status
thesis . metadata = { * * thesis . metadata , " last_reopen_reason " : reason , " reopened_at " : timezone . now ( ) . isoformat ( ) }
thesis . save ( update_fields = [ " status " , " metadata " , " updated_at " ] )
return thesis
def allocate_territories ( self , size : int ) - > list [ str ] :
base = [ item . value for item in DEFAULT_TERRITORY_ALLOCATION ]
return [ base [ index % len ( base ) ] for index in range ( max ( 1 , size ) ) ]
def ideation_mandate_payload ( self , mandate : CohortIdeationMandate ) - > dict [ str , Any ] :
return { " objective " : mandate . objective , " desired_opportunity_classes " : mandate . desired_opportunity_classes , " hard_exclusions " : mandate . hard_exclusions , " soft_exclusions " : mandate . soft_exclusions , " opportunity_territories " : mandate . opportunity_territories , " portfolio_gaps " : mandate . portfolio_gaps , " diversity_preferences " : mandate . diversity_preferences , " registry_snapshot " : mandate . registry_snapshot }
def ideation_brief_text ( self , review : dict [ str , Any ] , territories : list [ str ] ) - > str :
return " \n " . join ( [
" COHORT IDEATION BRIEF " ,
" Mandate: intentionally search new venture territory while preserving independent ideation. " ,
" Desired opportunity classes: AI wrapper platforms; agentic workflow products; vertical copilots; AI-enabled services; developer / AI infrastructure; intelligence products; automation products; service -> platform paths; recurring revenue; owned-compute leverage. " ,
" HARD EXCLUSIONS: " + " ; " . join ( DEFAULT_HARD_EXCLUSIONS ) ,
" SOFT EXCLUSIONS: " + " ; " . join ( DEFAULT_SOFT_EXCLUSIONS ) ,
" SATURATED THESIS AREAS: " + " ; " . join ( row [ " canonical_name " ] for row in review . get ( " saturated_thesis_areas " , [ ] ) ) ,
" ACTIVE CANDIDATES: " + " ; " . join ( row [ " canonical_name " ] for row in review . get ( " active_candidates " , [ ] ) ) ,
" UNDEREXPLORED TERRITORIES: developer infrastructure; intelligence monitoring; data/document automation; non-RFP enterprise workflows; non-churn SaaS intelligence. " ,
" SEARCH TERRITORY ALLOCATION: " + " ; " . join ( territories ) ,
] )
def novelty_gate ( self , payload : dict [ str , Any ] , cohort : VentureCohort , accepted_payloads : list [ dict [ str , Any ] ] , mandate : CohortIdeationMandate , * , territory : str ) - > dict [ str , Any ] :
hard = self . hard_exclusion_match ( payload , mandate . hard_exclusions )
if hard :
return { " decision " : NoveltyGateDecision . REGENERATE_HARD_EXCLUSION , " classification " : ThesisMatchClassification . DUPLICATE , " similarity_score " : 1.0 , " explanation " : f " Hard-excluded thesis area: { hard } " , " territory " : territory }
soft = self . soft_exclusion_match ( payload , mandate . soft_exclusions )
if soft and not self . has_soft_exception ( payload ) :
return { " decision " : NoveltyGateDecision . REVIEW_SOFT_EXCLUSION , " classification " : ThesisMatchClassification . ADJACENT , " similarity_score " : 0.55 , " explanation " : f " Soft-excluded area lacks material differentiation: { soft } " , " territory " : territory }
registry_match = self . match_payload_to_registry ( payload )
thesis = registry_match . get ( " matched_thesis " )
if thesis and thesis . status == PortfolioThesisStatus . SATURATED and registry_match [ " classification " ] in { ThesisMatchClassification . NEAR_DUPLICATE , ThesisMatchClassification . DUPLICATE , ThesisMatchClassification . COMPETITIVE } :
return { " decision " : NoveltyGateDecision . REGENERATE_DUPLICATE , * * registry_match , " explanation " : " Matched saturated thesis: " + registry_match [ " explanation " ] , " territory " : territory }
cohort_match = self . current_cohort_match ( payload , accepted_payloads )
if cohort_match [ " classification " ] == ThesisMatchClassification . DUPLICATE :
return { " decision " : NoveltyGateDecision . REGENERATE_DUPLICATE , * * cohort_match , " territory " : territory }
2026-08-16 17:10:34 +07:00
if cohort_match [ " classification " ] == ThesisMatchClassification . NEAR_DUPLICATE :
cluster_count = sum ( 1 for item in accepted_payloads if self . semantic_cluster_key ( item ) == cohort_match . get ( " semantic_cluster_key " ) )
if cluster_count > = int ( DEFAULT_DUPLICATE_POLICY [ " max_near_duplicate_per_thesis_per_cohort " ] ) + 1 :
return { " decision " : NoveltyGateDecision . REGENERATE_DUPLICATE , * * cohort_match , " territory " : territory }
2026-08-16 16:39:46 +07:00
if cohort_match [ " classification " ] == ThesisMatchClassification . COMPETITIVE :
canonical = self . canonical_thesis_name ( payload )
competitive_count = sum ( 1 for item in accepted_payloads if self . canonical_thesis_name ( item ) == canonical )
if competitive_count > = int ( DEFAULT_DUPLICATE_POLICY [ " max_competitive_per_thesis_per_cohort " ] ) :
return { " decision " : NoveltyGateDecision . REGENERATE_DUPLICATE , * * cohort_match , " territory " : territory }
return { " decision " : NoveltyGateDecision . ACCEPT , " classification " : registry_match . get ( " classification " , ThesisMatchClassification . NEW_THESIS ) , " similarity_score " : registry_match . get ( " similarity_score " , 0.0 ) , " explanation " : registry_match . get ( " explanation " , " Accepted as novel enough for this cohort. " ) , " territory " : territory , " matched_thesis " : thesis . id if thesis else None , " soft_exception " : soft or " " }
def hard_exclusion_match ( self , payload : dict [ str , Any ] , exclusions : list [ str ] ) - > str :
text = self . payload_text ( payload )
canonical = self . canonical_thesis_name ( payload ) . lower ( )
if " rfp " in text or " request for proposal " in text or " proposal drafting " in text :
return " AI RFP response drafting / proposal automation "
if " churn " in text or " retention playbook " in text or " save play " in text :
return " SaaS churn prediction / retention playbook generation "
for exclusion in exclusions :
if self . _jaccard ( exclusion , canonical + " " + text ) > = 0.25 :
return exclusion
return " "
def soft_exclusion_match ( self , payload : dict [ str , Any ] , exclusions : list [ str ] ) - > str :
text = self . payload_text ( payload )
checks = [ ( " generic contract scanners " , [ " contract " , " scanner " ] ) , ( " Shopify audit products " , [ " shopify " , " audit " ] ) , ( " generic AI content generators " , [ " content " , " generator " ] ) , ( " generic productized consulting " , [ " consulting " ] ) , ( " generic one-off audits " , [ " audit " ] ) , ( " generic chatbot wrappers " , [ " chatbot " ] ) ]
for label , words in checks :
if label in exclusions and all ( word in text for word in words ) :
return label
return " "
def has_soft_exception ( self , payload : dict [ str , Any ] ) - > bool :
rationale = str ( payload . get ( " exception_rationale " , " " ) or payload . get ( " differentiation " , " " ) ) . lower ( )
return any ( word in rationale for word in [ " vertical " , " specific " , " proprietary " , " workflow " , " recurring " , " data " , " platform " , " compliance " , " regulated " ] )
def match_portfolio_thesis ( self , proposal : CompanyProposal ) - > dict [ str , Any ] :
return self . match_payload_to_registry ( self . payload_from_proposal ( proposal ) )
def match_payload_to_registry ( self , payload : dict [ str , Any ] ) - > dict [ str , Any ] :
canonical = self . canonical_thesis_name ( payload )
best = None
for thesis in PortfolioThesis . objects . all ( ) :
score = 1.0 if thesis . canonical_name == canonical else self . _jaccard ( self . payload_text ( payload ) , " " . join ( [ thesis . canonical_name , thesis . concise_thesis , thesis . icp , thesis . problem , thesis . offer , thesis . business_model , thesis . primary_channel ] ) )
if best is None or score > best [ 0 ] :
best = ( score , thesis )
if best is None or best [ 0 ] < 0.18 :
return { " matched_thesis " : None , " similarity_score " : 0.0 , " classification " : ThesisMatchClassification . NEW_THESIS , " explanation " : " No existing portfolio thesis was similar enough. " }
score , thesis = best
classification = ThesisMatchClassification . DUPLICATE if score > = 0.9 else ThesisMatchClassification . NEAR_DUPLICATE if score > = 0.62 else ThesisMatchClassification . COMPETITIVE if score > = 0.38 else ThesisMatchClassification . ADJACENT
return { " matched_thesis " : thesis , " similarity_score " : round ( score , 2 ) , " classification " : classification , " explanation " : f " Matched { thesis . canonical_name } at similarity { score : .2f } . " }
def current_cohort_match ( self , payload : dict [ str , Any ] , accepted_payloads : list [ dict [ str , Any ] ] ) - > dict [ str , Any ] :
canonical = self . canonical_thesis_name ( payload )
2026-08-16 17:10:34 +07:00
cluster_key = self . semantic_cluster_key ( payload )
2026-08-16 16:39:46 +07:00
best_score = 0.0
best_name = " "
for accepted in accepted_payloads :
accepted_name = self . canonical_thesis_name ( accepted )
2026-08-16 17:10:34 +07:00
if str ( accepted . get ( " title " , " " ) ) . strip ( ) . lower ( ) == str ( payload . get ( " title " , " " ) ) . strip ( ) . lower ( ) :
return { " matched_thesis " : None , " similarity_score " : 1.0 , " classification " : ThesisMatchClassification . DUPLICATE , " explanation " : f " Current cohort already accepted exact title { accepted . get ( ' title ' , ' ' ) } . " , " semantic_cluster_key " : cluster_key }
accepted_cluster_key = self . semantic_cluster_key ( accepted )
score = 0.86 if accepted_cluster_key == cluster_key else self . _jaccard ( self . payload_text ( payload ) , self . payload_text ( accepted ) )
2026-08-16 16:39:46 +07:00
if score > best_score :
best_score = score
best_name = accepted_name
classification = ThesisMatchClassification . NEW_THESIS
if best_score > = 0.78 :
classification = ThesisMatchClassification . NEAR_DUPLICATE
elif best_score > = 0.55 :
classification = ThesisMatchClassification . COMPETITIVE
elif best_score > = 0.18 :
classification = ThesisMatchClassification . ADJACENT
2026-08-16 17:10:34 +07:00
return { " matched_thesis " : None , " similarity_score " : round ( best_score , 2 ) , " classification " : classification , " explanation " : f " Current cohort nearest thesis { best_name or ' none ' } at similarity { best_score : .2f } . " , " semantic_cluster_key " : cluster_key }
2026-08-16 16:39:46 +07:00
def create_portfolio_thesis_from_proposal ( self , proposal : CompanyProposal , cohort : VentureCohort ) - > PortfolioThesis :
fingerprint = self . fingerprint_proposal ( proposal )
canonical = self . canonical_thesis_name ( self . payload_from_proposal ( proposal ) )
thesis , _ = PortfolioThesis . objects . update_or_create (
canonical_name = canonical ,
defaults = { " concise_thesis " : proposal . pitch . get ( " One-line thesis " , proposal . description ) , " category " : str ( proposal . metadata . get ( " search_territory " , " " ) ) , " industry " : fingerprint . industry , " icp " : fingerprint . icp , " problem " : fingerprint . problem , " offer " : fingerprint . offer , " business_model " : fingerprint . business_model , " primary_channel " : fingerprint . primary_distribution_channel , " fingerprint " : fingerprint . fingerprint_hash , " status " : PortfolioThesisStatus . NEW , " first_seen_cohort " : cohort , " last_seen_cohort " : cohort , " metadata " : { " created_from_proposal " : str ( proposal . id ) } } ,
)
return thesis
def canonical_thesis_name ( self , payload : dict [ str , Any ] ) - > str :
text = self . payload_text ( payload )
if " rfp " in text or " request for proposal " in text or " proposal drafting " in text :
return " AI RFP response automation for B2B SaaS "
if " churn " in text or " retention " in text or " save play " in text :
return " AI churn/retention intelligence for SaaS "
if " contract " in text and ( " freelance " in text or " freelancer " in text ) :
return " Freelancer contract risk scanning "
if " shopify " in text and " audit " in text :
return " Shopify compliance/audit "
2026-08-16 17:10:34 +07:00
semantic = self . semantic_cluster_key ( payload )
if semantic == " municipal_permit_compliance " :
return " Municipal permit compliance automation "
if semantic == " legacy_modernization_triage " :
return " Legacy modernization triage copilot "
2026-08-16 16:39:46 +07:00
title = str ( payload . get ( " title " , " Untitled thesis " ) ) . strip ( )
return re . sub ( r " \ s+ " , " " , title ) [ : 255 ]
2026-08-16 17:10:34 +07:00
def semantic_cluster_key ( self , payload : dict [ str , Any ] ) - > str :
text = self . payload_text ( payload )
if ( " permit " in text or " permitdoc " in text ) and any ( word in text for word in [ " compliance " , " pre-check " , " precheck " , " municipal " , " contractor " ] ) :
return " municipal_permit_compliance "
if " legacy " in text and any ( word in text for word in [ " modernization " , " modernisation " , " triage " , " copilot " , " agent " ] ) :
return " legacy_modernization_triage "
if " vendor " in text and " onboarding " in text and " compliance " in text :
return " vendor_onboarding_compliance "
title = str ( payload . get ( " title " , " " ) ) . lower ( )
tokens = [ token for token in re . findall ( r " [a-z0-9] { 4,} " , title ) if token not in { " agent " , " copilot " , " checker " , " monitor " , " platform " , " automation " , " workflow " , " service " , " precheck " , " check " } ]
return " _ " . join ( tokens [ : 4 ] ) or self . _fingerprint ( payload ) [ : 12 ]
2026-08-16 16:39:46 +07:00
def payload_text ( self , payload : dict [ str , Any ] ) - > str :
return " " . join ( str ( payload . get ( key , " " ) ) for key in [ " title " , " one_line_thesis " , " description " , " problem " , " target_customer " , " proposed_solution " , " business_model " , " pricing_hypothesis " , " acquisition_strategy " , " validation_plan " , " differentiation " ] ) . lower ( )
def payload_from_proposal ( self , proposal : CompanyProposal ) - > dict [ str , Any ] :
return { " title " : proposal . title , " one_line_thesis " : proposal . pitch . get ( " One-line thesis " , " " ) , " description " : proposal . description , " problem " : proposal . problem , " target_customer " : proposal . target_customer , " proposed_solution " : proposal . proposed_solution , " business_model " : proposal . business_model , " pricing_hypothesis " : proposal . pricing_hypothesis , " acquisition_strategy " : proposal . acquisition_strategy , " validation_plan " : proposal . validation_plan , " differentiation " : proposal . differentiation }
def diversity_metrics ( self , cohort : VentureCohort ) - > dict [ str , Any ] :
clusters = [ cluster for cluster in cohort . thesis_clusters . select_related ( " portfolio_thesis " , " proposal " ) ]
proposals = [ member . proposal for member in cohort . members . select_related ( " proposal " ) ]
fingerprint_rows = [ self . fingerprint_values ( proposal ) for proposal in proposals ]
industries = Counter ( row [ " industry " ] for row in fingerprint_rows )
business_models = Counter ( row [ " business_model " ] for row in fingerprint_rows )
channels = Counter ( row [ " primary_distribution_channel " ] for row in fingerprint_rows )
thesis_counts = Counter ( cluster . portfolio_thesis . canonical_name if cluster . portfolio_thesis else " unclustered " for cluster in clusters )
icp_groups = Counter ( row [ " industry " ] + " : " + row [ " price_band " ] for row in fingerprint_rows )
total = max ( 1 , len ( proposals ) )
return { " unique_thesis_clusters " : len ( thesis_counts ) , " total_companies " : len ( proposals ) , " unique_thesis_clusters_ratio " : round ( len ( thesis_counts ) / total , 2 ) , " unique_industries " : len ( industries ) , " unique_icp_groups " : len ( icp_groups ) , " unique_business_models " : len ( business_models ) , " unique_primary_channels " : len ( channels ) , " largest_thesis_cluster_size " : max ( thesis_counts . values ( ) ) if thesis_counts else 0 , " largest_industry_concentration " : max ( industries . values ( ) ) if industries else 0 , " largest_business_model_concentration " : max ( business_models . values ( ) ) if business_models else 0 , " previous_qwen_baseline " : { " companies " : 10 , " rfp_variants " : 4 , " churn_retention_variants " : 5 } , " thesis_counts " : dict ( thesis_counts ) , " industry_counts " : dict ( industries ) , " business_model_counts " : dict ( business_models ) , " primary_channel_counts " : dict ( channels ) }
def fingerprint_values ( self , proposal : CompanyProposal ) - > dict [ str , Any ] :
try :
fingerprint = proposal . fingerprint
return { " industry " : fingerprint . industry , " business_model " : fingerprint . business_model , " primary_distribution_channel " : fingerprint . primary_distribution_channel , " price_band " : fingerprint . price_band }
except ObjectDoesNotExist :
return self . _fingerprint_data ( proposal )
def _company_payload ( self , mandate : CompanyMandate , * , ideation_index : int | None = None , ideation_context : dict [ str , Any ] | None = None ) - > tuple [ dict [ str , Any ] , str ] :
2026-08-15 20:56:56 +07:00
if self . router is not None :
try :
2026-08-15 21:42:46 +07:00
slot = f " Independent cohort slot: { ideation_index } . Do not use or imitate other cohort ideas; no other ideas are visible. " if ideation_index else " "
2026-08-16 16:39:46 +07:00
context = ideation_context or { }
2026-08-16 17:48:04 +07:00
response = self . router . complete ( ModelRequestContract ( purpose = ModelCapability . PLANNING , model_hint = self . ideation_model_hint , prompt = " Generate exactly ONE startup idea for an AUTONOMOUS Venture Discovery cohort. Return a single JSON object, not a list. The business must be operable by Artifex with <=30 minutes/week routine human involvement. Prefer archetypes: " + json . dumps ( AUTONOMOUS_ARCHETYPES ) + " . Require concrete digital operating loop: discover lead/user -> qualify -> acquire -> checkout -> onboard -> fulfill -> verify -> deliver -> support -> measure -> retain/upsell. Include validation_offer and end_state_business_model as separate fields. Include fulfillment_contract with customer_input, artifex_process, customer_output, quality_verification, failure_recovery, billing_event, support_model, escalation. Avoid enterprise procurement, founder-led calls, bespoke consulting, legal/medical judgment, offline fulfillment, and custom implementation. Respect no spend and no outreach in V0. Keep the $50 to $500 in 30 days mandate. Prefer AI-native businesses where Artifex can deliver most value using agents/local inference, but do not allow ' uses AI ' to substitute for real customer pain. DO NOT propose companies substantially equivalent to these hard-excluded thesis areas, including semantic variants: " + json . dumps ( context . get ( " hard_exclusions " , [ ] ) ) + " . Soft exclusions require material differentiation and an explicit exception rationale: " + json . dumps ( context . get ( " soft_exclusions " , [ ] ) ) + " . Search territory for this slot: " + str ( context . get ( " territory " , " OPEN_CATEGORY " ) ) + " . Include title, one_line_thesis, description, problem, target_customer, proposed_solution, business_model, pricing_hypothesis, acquisition_strategy, validation_plan, capital_requested, time_to_first_dollar_estimate, expected_margin, build_complexity, market_evidence, differentiation, major_risks, confidence, minutes_per_week_human, human_actions_required, and optional exception_rationale. " + slot + " Mandate: " + json . dumps ( { " objective " : mandate . objective , " constraints " : mandate . constraints , " optimization_targets " : mandate . optimization_targets , " ai_native_policy " : AI_NATIVE_POLICY , " ideation_brief " : context . get ( " brief " , { } ) } ) ) )
2026-08-15 21:42:46 +07:00
parsed = extract_json_object ( response . content )
2026-08-15 20:56:56 +07:00
if isinstance ( parsed , dict ) and parsed . get ( " title " ) :
2026-08-16 15:35:55 +07:00
return self . _normalize_payload ( parsed ) , self . ideation_model_hint
2026-08-15 20:56:56 +07:00
except Exception :
pass
payload = self . _fallback_company_payload ( )
payload [ " market_evidence " ] = [ { " type " : " fallback_hypothesis " , " source " : " deterministic_fallback " , " summary " : " No Sol/web research was used; this is an internal hypothesis for test/resilience only. " , " fallback_evidence " : True } ]
payload [ " confidence " ] = 0.52
return payload , " deterministic_fallback "
def _fallback_company_payload ( self ) - > dict [ str , Any ] :
return { " title " : " LaunchLens " , " one_line_thesis " : " A productized validation audit helps solo builders decide whether an AI-enabled microbusiness is worth pursuing before they spend weeks building. " , " description " : " LaunchLens sells a concise validation and launch-readiness report for one microbusiness idea. " , " problem " : " Solo technical founders often overbuild AI products before proving willingness to pay. " , " target_customer " : " Solo technical founders and small service operators considering an AI-assisted microbusiness. " , " proposed_solution " : " A fixed-scope paid audit that evaluates ICP, first-dollar route, validation gates, build plan, risks, and Artifex capability gaps. " , " business_model " : " Productized service first, with optional later software tooling if demand is proven. " , " pricing_hypothesis " : " $49-$99 per audit, with a higher-touch $250 implementation planning upsell after validation. " , " acquisition_strategy " : " Compliant founder-community posts, personal network asks, and content showing anonymized example audits after outreach is approved. " , " validation_plan " : " Before any build, seek 5 credible target-customer responses or 1 willingness-to-pay signal through compliant channels once approved. " , " capital_requested " : " 50 " , " time_to_first_dollar_estimate " : " 3-10 days after outreach is approved " , " expected_margin " : " 70-85 % g ross margin after manual delivery time " , " build_complexity " : " LOW " , " market_evidence " : [ ] , " differentiation " : " Combines venture IC-style diligence with Artifex ' s software/agent execution awareness and explicit capability-gap reporting. " , " major_risks " : [ " Demand may be consulting-like and hard to differentiate. " , " Manual distribution may not produce urgent buyers. " , " No V0 customer evidence exists yet. " ] , " confidence " : 0.52 }
def _normalize_payload ( self , payload : dict [ str , Any ] ) - > dict [ str , Any ] :
fallback = self . _fallback_company_payload ( )
normalized = { key : payload . get ( key , value ) for key , value in fallback . items ( ) }
normalized [ " market_evidence " ] = self . _as_list ( normalized . get ( " market_evidence " ) )
normalized [ " major_risks " ] = self . _as_list ( normalized . get ( " major_risks " ) )
2026-08-16 16:39:46 +07:00
if payload . get ( " exception_rationale " ) :
normalized [ " exception_rationale " ] = payload [ " exception_rationale " ]
2026-08-16 17:48:04 +07:00
for key in [ " validation_offer " , " end_state_business_model " , " fulfillment_contract " , " minutes_per_week_human " , " human_actions_required " ] :
if key in payload :
normalized [ key ] = payload [ key ]
2026-08-15 20:56:56 +07:00
return normalized
2026-08-15 21:42:46 +07:00
def _normalize_research ( self , payload : dict [ str , Any ] , required : list [ str ] ) - > dict [ str , Any ] :
sources = [ ]
for source in self . _as_list ( payload . get ( " sources " ) ) [ : 12 ] :
if isinstance ( source , dict ) and source . get ( " url " ) :
sources . append ( { " type " : " public_web " , " url " : str ( source [ " url " ] ) , " title " : str ( source . get ( " title " , " " ) ) , " category " : str ( source . get ( " category " , " " ) ) , " summary " : str ( source . get ( " summary " , " " ) ) , " fallback_evidence " : False } )
coverage = { category : bool ( dict ( payload . get ( " coverage " , { } ) ) . get ( category ) ) for category in required }
return { " coverage " : coverage , " sources " : sources , " findings " : dict ( payload . get ( " findings " , { } ) ) , " unverified_categories " : [ ] }
2026-08-16 17:10:34 +07:00
def _searxng_sources ( self , proposal : CompanyProposal , required : list [ str ] , * , depth : str = " light " ) - > list [ dict [ str , Any ] ] :
2026-08-16 15:35:55 +07:00
client = self . search_client or SearxngSearchClient . from_resources ( )
2026-08-16 17:48:04 +07:00
self . _last_search_diagnostics = { " queries " : [ ] , " errors " : [ ] }
2026-08-16 15:35:55 +07:00
if client is None :
2026-08-16 17:48:04 +07:00
self . _last_search_diagnostics [ " errors " ] . append ( " no active searxng resource " )
2026-08-16 15:35:55 +07:00
return [ ]
sources = [ ]
2026-08-16 17:10:34 +07:00
limit = 5 if depth == " deep " else 3
2026-08-16 15:35:55 +07:00
query_terms = {
" competitors " : " competitors alternatives " ,
" pricing " : " pricing cost " ,
" customer_pain " : " customer pain problem forum " ,
" market_alternatives " : " alternatives tools services " ,
" regulatory_platform_risks " : " regulatory platform risk compliance " ,
}
for category in required :
2026-08-16 17:10:34 +07:00
query = f " { proposal . title } { proposal . problem [ : 120 ] } { proposal . target_customer } { query_terms . get ( category , category ) } "
2026-08-16 15:35:55 +07:00
try :
2026-08-16 17:48:04 +07:00
results = self . _search_client_results ( client , query , category = category , limit = limit )
if not results :
fallback_query = f " { proposal . title } { query_terms . get ( category , category ) } "
results = self . _search_client_results ( client , fallback_query , category = category , limit = limit )
if not results :
broad_query = f " { proposal . target_customer } { query_terms . get ( category , category ) } "
results = self . _search_client_results ( client , broad_query , category = category , limit = limit )
sources . extend ( results )
except Exception as exc :
self . _last_search_diagnostics [ " errors " ] . append ( f " { category } : { exc } " )
2026-08-16 15:35:55 +07:00
continue
2026-08-16 17:10:34 +07:00
return sources [ : 25 if depth == " deep " else 15 ]
2026-08-16 15:35:55 +07:00
2026-08-16 17:48:04 +07:00
def _search_client_results ( self , client : Any , query : str , * , category : str , limit : int ) - > list [ dict [ str , Any ] ] :
if hasattr ( client , " search_payload " ) :
payload = client . search_payload ( query )
raw_results = payload . get ( " results " , [ ] ) if isinstance ( payload , dict ) else [ ]
unresponsive = payload . get ( " unresponsive_engines " , [ ] ) if isinstance ( payload , dict ) else [ ]
self . _last_search_diagnostics [ " queries " ] . append ( { " category " : category , " query " : query , " result_count " : len ( raw_results ) , " unresponsive_engines " : unresponsive } )
return [
{ " 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 }
for item in raw_results [ : limit * 4 ]
if isinstance ( item , dict ) and item . get ( " url " )
]
results = client . search ( query , category = category , limit = limit )
self . _last_search_diagnostics [ " queries " ] . append ( { " category " : category , " query " : query , " result_count " : len ( results ) } )
return results
2026-08-16 15:35:55 +07:00
def _page_corpus ( self , search_sources : list [ dict [ str , Any ] ] ) - > list [ dict [ str , Any ] ] :
if self . page_fetcher is None :
return [ ]
try :
return self . page_fetcher . fetch_many ( search_sources , max_pages = 8 )
except Exception :
return [ ]
def _merge_search_sources ( self , research : dict [ str , Any ] , search_sources : list [ dict [ str , Any ] ] , required : list [ str ] ) - > dict [ str , Any ] :
seen = { source . get ( " url " ) for source in research . get ( " sources " , [ ] ) }
merged_sources = [ * research . get ( " sources " , [ ] ) ]
for source in search_sources :
if source . get ( " url " ) in seen :
continue
seen . add ( source . get ( " url " ) )
merged_sources . append ( source )
coverage = { category : bool ( dict ( research . get ( " coverage " , { } ) ) . get ( category ) ) for category in required }
for source in merged_sources :
category = str ( source . get ( " category " , " " ) )
if category in coverage and source . get ( " url " ) :
coverage [ category ] = True
return { * * research , " coverage " : coverage , " sources " : merged_sources , " research_available " : True , " search_provider " : " searxng " }
2026-08-16 17:10:34 +07:00
def filter_research_sources ( self , proposal : CompanyProposal , sources : list [ dict [ str , Any ] ] , required : list [ str ] ) - > dict [ str , list [ dict [ str , Any ] ] ] :
accepted = [ ]
rejected = [ ]
seen = set ( )
for source in sources :
if not isinstance ( source , dict ) :
continue
url = str ( source . get ( " url " , " " ) ) . strip ( )
if not url or url in seen :
continue
seen . add ( url )
category = str ( source . get ( " category " , " " ) )
quality = self . source_quality ( proposal , source , required )
if not quality [ " accepted " ] :
rejected . append ( { * * source , " quality " : quality [ " quality " ] , " rejection_reason " : quality [ " reason " ] } )
continue
accepted . append ( { * * source , " quality " : quality [ " quality " ] , " relevance_score " : quality [ " score " ] } )
return { " accepted_sources " : accepted , " rejected_sources " : rejected }
def source_quality ( self , proposal : CompanyProposal , source : dict [ str , Any ] , required : list [ str ] ) - > dict [ str , Any ] :
category = str ( source . get ( " category " , " " ) )
url = str ( source . get ( " url " , " " ) ) . lower ( )
source_text = " " . join ( str ( source . get ( key , " " ) ) for key in [ " title " , " summary " , " content " , " snippet " , " url " ] ) . lower ( )
if category not in required :
return { " accepted " : False , " quality " : " weak " , " score " : 0.0 , " reason " : " unknown research category " }
if any ( noisy in url for noisy in [ " webcache " , " translate.google " , " pinterest " , " facebook.com " , " instagram.com " , " x.com/intent " , " archive.org " , " web.archive.org " ] ) :
return { " accepted " : False , " quality " : " weak " , " score " : 0.0 , " reason " : " archive/social/noisy source " }
company_text = " " . join ( [ proposal . title , proposal . problem , proposal . target_customer , proposal . proposed_solution ] ) . lower ( )
category_terms = {
" competitors " : " competitor competitors alternative alternatives vendor software platform service " ,
" pricing " : " pricing price cost plan subscription fee rates " ,
" customer_pain " : " problem pain challenge complaint forum reddit customer " ,
" market_alternatives " : " alternative alternatives tools services products market solution " ,
" regulatory_platform_risks " : " regulatory compliance risk policy legal platform permit " ,
}
company_score = self . _jaccard ( company_text , source_text )
category_score = self . _jaccard ( category_terms . get ( category , category ) , source_text )
2026-08-16 17:48:04 +07:00
company_overlap = self . _token_overlap ( company_text , source_text )
category_overlap = self . _token_overlap ( category_terms . get ( category , category ) , source_text )
2026-08-16 17:10:34 +07:00
if category . replace ( " _ " , " " ) in source_text :
category_score = max ( category_score , 0.5 )
2026-08-16 17:48:04 +07:00
score = round ( company_score * 0.55 + category_score * 0.25 + min ( company_overlap , 5 ) * 0.03 + min ( category_overlap , 4 ) * 0.025 , 2 )
if company_overlap > = 2 and category_overlap > = 1 :
return { " accepted " : True , " quality " : " strong " if score > = 0.13 or category_overlap > = 2 else " weak " , " score " : score , " reason " : " relevant proposal and category overlap " }
2026-08-16 17:10:34 +07:00
if score < 0.08 :
return { " accepted " : False , " quality " : " weak " , " score " : score , " reason " : " insufficient semantic relevance " }
if score < 0.16 :
return { " accepted " : True , " quality " : " weak " , " score " : score , " reason " : " accepted as weak context only " }
return { " accepted " : True , " quality " : " strong " , " score " : score , " reason " : " strong relevant source " }
2026-08-15 21:42:46 +07:00
def _missing_research ( self , required : list [ str ] , reason : str ) - > dict [ str , Any ] :
return { " coverage " : { category : False for category in required } , " sources " : [ ] , " findings " : { } , " unverified_categories " : required , " research_available " : False , " failure " : reason }
2026-08-15 20:56:56 +07:00
def _as_list ( self , value : Any ) - > list [ Any ] :
if isinstance ( value , list ) :
return value
if value in ( None , " " ) :
return [ ]
return [ value ]
def _money ( self , value : Any ) - > Decimal :
match = re . search ( r " \ d+(?: \ . \ d+)? " , str ( value ) )
if match is None :
return Decimal ( " 50 " )
return min ( Decimal ( match . group ( 0 ) ) , Decimal ( " 50 " ) )
2026-08-15 21:42:46 +07:00
def _token_overlap ( self , source : str , target_text : str ) - > int :
tokens = { token for token in re . findall ( r " [a-z0-9] { 4,} " , source . lower ( ) ) if token not in { " with " , " that " , " from " , " this " , " service " , " business " , " model " , " customer " , " customers " } }
return len ( [ token for token in tokens if token in target_text ] )
def _jaccard ( self , a : str , b : str ) - > float :
left = { token for token in re . findall ( r " [a-z0-9] { 4,} " , a . lower ( ) ) }
right = { token for token in re . findall ( r " [a-z0-9] { 4,} " , b . lower ( ) ) }
if not left or not right :
return 0.0
return len ( left & right ) / len ( left | right )
def _fingerprint_data ( self , proposal : CompanyProposal ) - > dict [ str , Any ] :
text = " " . join ( [ proposal . title , proposal . description , proposal . problem , proposal . target_customer , proposal . proposed_solution , proposal . business_model , proposal . acquisition_strategy ] ) . lower ( )
industry = " shopify/ecommerce " if " shopify " in text or " ecommerce " in text else " developer tools " if " developer " in text or " api " in text else " b2b services " if " b2b " in text else " general business "
model = " productized service " if " service " in proposal . business_model . lower ( ) else " saas " if " saas " in proposal . business_model . lower ( ) else " hybrid "
channel = " outbound/community " if any ( word in text for word in [ " outbound " , " community " , " posts " , " network " ] ) else " marketplace " if " marketplace " in text or " fiverr " in text else " content/seo " if " seo " in text or " content " in text else " direct "
price = self . _money ( proposal . pricing_hypothesis )
price_band = " under_100 " if price < 100 else " 100_500 " if price < = 500 else " over_500 "
first_cash = " under_7_days " if any ( token in proposal . time_to_first_dollar_estimate . lower ( ) for token in [ " 3 " , " 7 " , " week " ] ) else " under_30_days "
regulatory = " high " if any ( word in text for word in [ " legal " , " compliance " , " regulatory " , " permit " , " policy " ] ) else " medium " if " platform " in text else " low "
return { " industry " : industry , " icp " : proposal . target_customer [ : 500 ] , " problem " : proposal . problem [ : 500 ] , " offer " : proposal . proposed_solution [ : 500 ] , " business_model " : model , " primary_distribution_channel " : channel , " price_band " : price_band , " time_to_first_cash_band " : first_cash , " required_capability_set " : [ item . category for item in proposal . capability_requirements . all ( ) ] or [ " outbound sales " , " CRM " , " payments " ] , " geography_dependency " : " local " if any ( word in text for word in [ " local " , " city " , " metro " , " permit " ] ) else " none " , " regulatory_dependency " : regulatory , " online_offline " : " online " if any ( word in text for word in [ " shopify " , " saas " , " api " , " online " , " web " ] ) else " mixed " , " service_software_hybrid " : model }
def _confidence ( self , value : Any ) - > float :
labels = { " low " : 0.35 , " medium " : 0.55 , " moderate " : 0.55 , " high " : 0.75 }
lowered = str ( value ) . strip ( ) . lower ( )
if lowered in labels :
return labels [ lowered ]
match = re . search ( r " \ d+(?: \ . \ d+)? " , lowered )
if match is None :
return 0.55
number = float ( match . group ( 0 ) )
return min ( 1.0 , number / 100 if number > 1 else number )
def _evidence_scores ( self , proposal : CompanyProposal ) - > dict [ str , int ] :
research = proposal . metadata . get ( " research " , { } ) if isinstance ( proposal . metadata , dict ) else { }
coverage_ratio = float ( research . get ( " coverage_ratio " , 0.0 ) or 0.0 )
unverified = set ( research . get ( " unverified_categories " , [ ] ) )
evidence_count = len ( [ item for item in self . _as_list ( proposal . market_evidence ) if isinstance ( item , dict ) and item . get ( " url " ) ] )
low_build = str ( proposal . build_complexity ) . upper ( ) in { " LOW " , " LOW-MEDIUM " }
service_model = " service " in proposal . business_model . lower ( )
margin_numbers = [ int ( value ) for value in re . findall ( r " \ d+ " , proposal . expected_margin ) ]
margin = max ( margin_numbers ) if margin_numbers else 50
demand = min ( 85 , 25 + int ( coverage_ratio * 40 ) + min ( evidence_count * 4 , 20 ) )
pricing = 70 if " pricing " not in unverified else 45
pain = 72 if " customer_pain " not in unverified else 35
competition = 68 if " competitors " not in unverified and " market_alternatives " not in unverified else 38
risk = 72 if " regulatory_platform_risks " not in unverified else 34
distribution = 60 if service_model else 42
build = 78 if low_build else 42
gross_margin = max ( 35 , min ( 85 , margin ) )
capital = 82 if proposal . capital_requested < = Decimal ( " 50 " ) else 30
validation = 78 if " 5 credible " in proposal . validation_plan or " willingness " in proposal . validation_plan . lower ( ) else 42
market_size = 62 if coverage_ratio > = 0.6 else 44
defensibility = 42 + ( 10 if service_model else 0 ) + ( 8 if coverage_ratio > = 0.8 else 0 )
2026-08-16 14:30:40 +07:00
ai_leverage = self . ai_leverage_score ( proposal )
platformization = self . platformization_potential ( proposal )
2026-08-15 21:42:46 +07:00
probability = round ( ( demand * 0.22 + pricing * 0.12 + pain * 0.16 + distribution * 0.14 + build * 0.1 + capital * 0.1 + risk * 0.16 ) , 0 )
2026-08-16 17:48:04 +07:00
autonomy = 55
try :
autonomy = int ( proposal . autonomous_assessment . autonomous_operability_score )
except ObjectDoesNotExist :
pass
return { " Demand Evidence " : demand , " Time-to-First-Dollar Attractiveness " : 76 if service_model else 50 , " Capital Efficiency " : capital , " Validation Affordability " : validation , " Gross Margin Potential " : gross_margin , " Distribution Feasibility " : distribution , " Build Simplicity " : build , " Defensibility " : min ( 75 , defensibility ) , " Market Opportunity " : market_size , " Competitive Position " : competition , " Risk Manageability " : risk , " AI Leverage " : ai_leverage , " Platformization Potential " : platformization , " Autonomous Operability " : autonomy , " Probability of Reaching $500 " : int ( max ( 20 , min ( 80 , probability ) ) ) }
2026-08-16 14:30:40 +07:00
def ai_leverage_score ( self , proposal : CompanyProposal ) - > int :
text = " " . join ( [ proposal . title , proposal . description , proposal . problem , proposal . proposed_solution , proposal . business_model , proposal . differentiation ] ) . lower ( )
score = 20
if any ( term in text for term in [ " ai " , " agent " , " model " , " copilot " , " automation " , " inference " , " llm " ] ) :
score + = 25
if any ( term in text for term in [ " monitor " , " synthesis " , " alerts " , " intelligence " , " workflow " , " document " , " evaluation " , " deployment " , " security " , " data transformation " ] ) :
score + = 18
if any ( term in text for term in [ " recurring " , " monthly " , " subscription " , " platform " , " software " , " api " ] ) :
score + = 15
if " manual " in text and not any ( term in text for term in [ " agent " , " automation " , " software " , " platform " ] ) :
score - = 15
if any ( term in text for term in [ " generic audit " , " emergency fix " , " one-time consulting " ] ) :
score - = 12
return max ( 0 , min ( 100 , score ) )
def platformization_potential ( self , proposal : CompanyProposal ) - > int :
text = " " . join ( [ proposal . title , proposal . description , proposal . proposed_solution , proposal . business_model , proposal . acquisition_strategy , proposal . validation_plan ] ) . lower ( )
score = 25
if any ( term in text for term in [ " platform " , " software " , " saas " , " api " , " dashboard " , " monitor " , " alerts " , " workflow " ] ) :
score + = 25
if any ( term in text for term in [ " repeatable " , " template " , " standardized " , " recurring " , " monthly " , " subscription " ] ) :
score + = 20
if any ( term in text for term in [ " data " , " history " , " knowledge " , " benchmark " , " evaluation " , " repository " ] ) :
score + = 12
if any ( term in text for term in [ " one-time " , " emergency " , " manual only " , " concierge " ] ) :
score - = 12
return max ( 0 , min ( 100 , score ) )
2026-08-15 21:42:46 +07:00
2026-08-15 20:56:56 +07:00
def _pitch ( self , payload : dict [ str , Any ] , * , fallback : bool ) - > dict [ str , Any ] :
2026-08-16 15:40:30 +07:00
pitch = { " Company name " : payload [ " title " ] , " One-line thesis " : payload [ " one_line_thesis " ] , " Problem " : payload [ " problem " ] , " ICP " : payload [ " target_customer " ] , " Why now " : " AI tooling lowers build cost, increasing the risk that founders overbuild before validating demand. " , " Product / service " : payload [ " proposed_solution " ] , " Business model " : payload [ " business_model " ] , " Pricing " : payload [ " pricing_hypothesis " ] , " Route to first customer " : payload [ " acquisition_strategy " ] , " Validation plan " : payload [ " validation_plan " ] , " $50 capital allocation proposal " : { " initial " : " $10 only after approval " , " reserved " : " $40 held until evidence gate " , " v0_spend " : " $0 " } , " Time to first dollar " : payload [ " time_to_first_dollar_estimate " ] , " Path to $500 net cash " : " Sell 6-10 fixed-scope audits at $49-$99 while keeping delivery manual and using sunk Artifex compute. " , " Competition " : " Generic startup consultants, founder communities, AI business idea tools, and DIY validation templates. Public competitor research is unverified unless web research is configured. " , " Differentiation " : payload [ " differentiation " ] , " Build requirements " : [ " report template " , " intake form " , " manual analysis workflow " , " optional landing page after validation approval " ] , " Distribution requirements " : [ " compliant outreach plan " , " community/content channels " , " CRM-lite tracking before first customers " ] , " Risks " : payload [ " major_risks " ] , " What would falsify the thesis " : " No willingness-to-pay signal at $49-$99 or fewer than 5 credible target-customer responses after approved compliant validation. " , " Confidence " : payload [ " confidence " ] , " Evidence caveat " : " Deterministic fallback evidence only; not equivalent to model-generated or public web research. " if fallback else " Generated by configured ideation model; public web research only included if sources are present. " }
2026-08-15 20:56:56 +07:00
return pitch
def _questions_from_pitch ( self , pitch : dict [ str , Any ] ) - > list [ dict [ str , str ] ] :
return [ { " category " : " demand " , " question " : f " What evidence supports demand for { pitch [ ' Company name ' ] } among the stated ICP? " } , { " category " : " urgency " , " question " : " Why will this customer pay now instead of using free templates or advice? " } , { " category " : " distribution " , " question " : " How do you reach the first 10 customers without spam or fake traction? " } , { " category " : " validation " , " question " : " Can this be validated before building the full product? " } , { " category " : " business_model " , " question " : " Why a productized service first instead of SaaS? " } , { " category " : " falsification " , " question " : " What would falsify the thesis within the $50 and 30-day mandate? " } , { " category " : " economics " , " question " : " What happens if acquisition cost or manual delivery time is 3x the estimate? " } , { " category " : " competition " , " question " : " What is the main competitive threat and why is this worth funding over selling an existing Artifex capability? " } ]
def _answer_question ( self , question : ICQuestion ) - > dict [ str , Any ] :
evidence = [ { " source " : " internal_reasoning " , " summary " : " No customer outreach, spend, or fabricated evidence used. " , " fallback_evidence " : True } ]
if question . category in { " demand " , " competition " } and not self . web_research_available :
evidence . append ( { " source " : " capability_gap " , " summary " : " Public web research unavailable; demand/competition claims remain uncertain. " , " fallback_evidence " : True } )
answers = { " demand " : " Demand is not proven. The strongest V0 claim is that the problem is plausible and cheap to test, not that demand exists. " , " urgency " : " The buyer pays only if the report saves them build time or prevents wasted spend; urgency is weakest before a concrete launch decision. " , " distribution " : " After approval, use targeted compliant posts/conversations and track responses manually; V0 performs no outreach. " , " validation " : " Yes. The paid diagnostic can be validated with responses and willingness-to-pay before software build. " , " business_model " : " Service first reduces build risk and can reach first cash faster than SaaS; software should follow only if repeated demand appears. " , " falsification " : " Failure to collect credible responses or willingness-to-pay within the mandate falsifies near-term viability. " , " economics " : " If acquisition or delivery is 3x harder, the company should stop or raise price before building tooling. " , " competition " : " Main threat is generic consulting/free templates. The reason to fund this over selling raw Artifex capability is packaging a buyer-specific outcome. " }
return { " answer " : answers . get ( question . category , " The assumption remains uncertain and must be tested before spend. " ) , " evidence " : evidence , " uncertainty " : " High until public research and customer evidence are available. " , " pitch_changes " : { " confidence_adjustment " : " reduced/held due missing external evidence " } if question . category in { " demand " , " competition " } else { } }
def _capability_requirements ( self , proposal : CompanyProposal ) - > list [ dict [ str , Any ] ] :
2026-08-15 21:42:46 +07:00
research = proposal . metadata . get ( " research " , { } ) if isinstance ( proposal . metadata , dict ) else { }
web_status = CapabilityStatus . MISSING if not self . web_research_available else CapabilityStatus . AVAILABLE if not research . get ( " unverified_categories " ) else CapabilityStatus . PARTIAL
2026-08-16 17:48:04 +07:00
try :
structural = proposal . autonomous_assessment . structural_blockers
except ObjectDoesNotExist :
structural = [ ]
2026-08-15 20:56:56 +07:00
return [
{ " category " : " Company Brain " , " status " : CapabilityStatus . PARTIAL , " rationale " : " Venture reasoning exists in V0 but is not a persistent operating brain. " , " priority " : CapabilityPriority . BEFORE_SCALING , " evidence " : { } } ,
{ " category " : " Board " , " status " : CapabilityStatus . AVAILABLE , " rationale " : " Structured CEO/CTO/CFO/CRO/Independent Director review exists for V0. " , " priority " : CapabilityPriority . BEFORE_VALIDATION , " evidence " : { } } ,
{ " category " : " IC " , " status " : CapabilityStatus . AVAILABLE , " rationale " : " Bounded IC diligence, questions, scoring, and decision vocabulary exist. " , " priority " : CapabilityPriority . BEFORE_VALIDATION , " evidence " : { } } ,
2026-08-15 21:42:46 +07:00
{ " category " : " WEB_MARKET_RESEARCH " , " status " : web_status , " rationale " : " Bounded source-linked web research exists only when configured and category coverage is complete. " , " priority " : CapabilityPriority . BEFORE_VALIDATION , " evidence " : { " web_research_available " : self . web_research_available , * * research } } ,
2026-08-15 20:56:56 +07:00
{ " category " : " software build " , " status " : CapabilityStatus . AVAILABLE , " rationale " : " Task execution, coding, review, tests, and graph runtime exist. " , " priority " : CapabilityPriority . BEFORE_FIRST_CUSTOMER , " evidence " : { } } ,
{ " category " : " frontend design " , " status " : CapabilityStatus . AVAILABLE , " rationale " : " Frontend agents and Django UI path exist. " , " priority " : CapabilityPriority . BEFORE_FIRST_CUSTOMER , " evidence " : { } } ,
2026-08-16 17:48:04 +07:00
{ " category " : " deployment " , " status " : CapabilityStatus . PARTIAL , " rationale " : " Validation can use SUBDOMAIN_DEPLOYMENT under a shared parent domain; dedicated domains are a traction-stage upgrade. " , " priority " : CapabilityPriority . BEFORE_FIRST_CUSTOMER , " evidence " : { " deployment_model " : " SUBDOMAIN_DEPLOYMENT " , " domain_purchase_required_for_validation " : False } } ,
2026-08-15 20:56:56 +07:00
{ " category " : " outbound sales " , " status " : CapabilityStatus . MISSING , " rationale " : " No compliant outreach/sequence/customer contact system exists and V0 forbids outreach. " , " priority " : CapabilityPriority . BEFORE_VALIDATION , " evidence " : { } } ,
{ " category " : " CRM " , " status " : CapabilityStatus . MISSING , " rationale " : " No customer pipeline/contact tracking exists. " , " priority " : CapabilityPriority . BEFORE_VALIDATION , " evidence " : { } } ,
2026-08-16 17:48:04 +07:00
{ " category " : " payments " , " status " : CapabilityStatus . PARTIAL , " rationale " : " Stripe merchant account is available as CONFIGURED_EXTERNAL_PROVIDER, but no real charges or product checkout integration are implemented in this milestone. " , " priority " : CapabilityPriority . BEFORE_FIRST_CUSTOMER , " evidence " : { " payment_provider_state " : " CONFIGURED_EXTERNAL_PROVIDER " , " implementation_state " : " IMPLEMENTATION_MISSING " , " real_charges_allowed " : False } } ,
2026-08-15 20:56:56 +07:00
{ " category " : " invoicing " , " status " : CapabilityStatus . MISSING , " rationale " : " No invoicing workflow exists. " , " priority " : CapabilityPriority . BEFORE_FIRST_CUSTOMER , " evidence " : { } } ,
{ " category " : " customer support " , " status " : CapabilityStatus . MISSING , " rationale " : " No support inbox or customer service workflow exists. " , " priority " : CapabilityPriority . BEFORE_SCALING , " evidence " : { } } ,
{ " category " : " company budget management " , " status " : CapabilityStatus . MISSING , " rationale " : " V0 blocks spend but future validation needs tranche/budget controls. " , " priority " : CapabilityPriority . BEFORE_VALIDATION , " evidence " : { } } ,
{ " category " : " legal/compliance " , " status " : CapabilityStatus . MISSING , " rationale " : " No contracts, terms, privacy, or compliance review workflow exists. " , " priority " : CapabilityPriority . BEFORE_FIRST_CUSTOMER , " evidence " : { } } ,
2026-08-16 17:48:04 +07:00
{ " category " : " COMPANY_STRUCTURAL_HUMAN_DEPENDENCY " , " status " : CapabilityStatus . MISSING if structural else CapabilityStatus . AVAILABLE , " rationale " : " Structural human dependency is inherent to the company model and should route assisted-only if severe; it is not solved by platform infrastructure. " , " priority " : CapabilityPriority . BEFORE_VALIDATION , " evidence " : { " dependency_causes " : structural } } ,
2026-08-15 20:56:56 +07:00
]
2026-08-15 21:42:46 +07:00
def _portfolio_concentration ( self , cohort : VentureCohort ) - > dict [ str , Any ] :
fingerprints = [ self . fingerprint_proposal ( member . proposal ) for member in cohort . members . select_related ( " proposal " ) ]
data = {
" industry_distribution " : dict ( Counter ( fp . industry for fp in fingerprints ) ) ,
" business_model_distribution " : dict ( Counter ( fp . business_model for fp in fingerprints ) ) ,
" primary_channel_distribution " : dict ( Counter ( fp . primary_distribution_channel for fp in fingerprints ) ) ,
" icp_distribution " : dict ( Counter ( fp . icp [ : 80 ] for fp in fingerprints ) ) ,
" capability_dependency_distribution " : dict ( Counter ( cap for fp in fingerprints for cap in fp . required_capability_set ) ) ,
}
flags = [ ]
for key , counts in data . items ( ) :
if counts and max ( counts . values ( ) ) > = max ( 4 , int ( cohort . cohort_size * 0.6 ) ) :
flags . append ( { " type " : " PORTFOLIO_CONCENTRATION " , " dimension " : key , " value " : max ( counts , key = counts . get ) , " count " : max ( counts . values ( ) ) } )
data [ " flags " ] = flags
return data
2026-08-16 14:30:40 +07:00
def _generic_concentration_penalty ( self , proposal : CompanyProposal ) - > int :
text = " " . join ( [ proposal . title , proposal . description , proposal . proposed_solution , proposal . business_model , proposal . acquisition_strategy ] ) . lower ( )
penalty = 0
if " shopify " in text or " ecommerce " in text :
penalty + = 3
if " audit " in text and not any ( term in text for term in [ " ai " , " agent " , " platform " , " monitor " , " automation " ] ) :
penalty + = 4
if " emergency " in text or " fix " in text :
penalty + = 4
if " consulting " in text or " one-time " in text :
penalty + = 3
if " outbound " in text or " community " in text :
penalty + = 2
return penalty
2026-08-15 21:42:46 +07:00
def _collision_summary ( self , cohort : VentureCohort ) - > dict [ str , int ] :
counts = Counter ( cohort . collisions . values_list ( " classification " , flat = True ) )
return { choice : counts . get ( choice , 0 ) for choice in OverlapClassification . values }
def _readable_cohort_report ( self , content : dict [ str , Any ] ) - > str :
ranking = " \n " . join ( f " { row [ ' rank ' ] } . { row [ ' company ' ] } - score { row [ ' ic_score ' ] } , P($500) { row [ ' probability ' ] } %, { row [ ' decision ' ] } " for row in content [ " rankings " ] )
top = " \n " . join ( f " - { row [ ' company ' ] } : { row [ ' thesis ' ] } " for row in content [ " top_3 " ] )
demand = " \n " . join ( f " - { row [ ' capability ' ] } : { row [ ' count ' ] } companies, earliest { row [ ' earliest_stage ' ] } " for row in content [ " capability_demand " ] [ : 15 ] )
2026-08-16 16:39:46 +07:00
rejections = " \n " . join ( f " - Slot { row [ ' slot_index ' ] } attempt { row [ ' attempt ' ] } : { row [ ' decision ' ] } - { row [ ' candidate_title ' ] } ( { row [ ' reason ' ] } ) " for row in content [ " generation_rejections " ] ) or " - none "
clusters = " \n " . join ( f " - { row [ ' company ' ] } : { row [ ' portfolio_thesis ' ] } ( { row [ ' classification ' ] } , { row [ ' similarity_score ' ] } ) " for row in content [ " thesis_clusters " ] ) or " - none "
saturation = " \n " . join ( f " - { row [ ' thesis ' ] } : { row [ ' status_recommendation ' ] } ( { row [ ' proposal_count ' ] } proposals, best { row [ ' best_ic_score ' ] } ) " for row in content [ " saturation_analysis " ] ) or " - none "
return f " # Venture Discovery Cohort Report \n \n Cohort: { content [ ' cohort_id ' ] } \n Mandate: { content [ ' mandate ' ] } \n Total spend: $0 \n Customer outreach: none \n \n ## Ideation Mandate \n { json . dumps ( content [ ' ideation_mandate ' ] , indent = 2 ) } \n \n ## Hard Exclusions \n " + " \n " . join ( f " - { item } " for item in content [ " hard_exclusions " ] ) + " \n \n ## Soft Exclusions \n " + " \n " . join ( f " - { item } " for item in content [ " soft_exclusions " ] ) + " \n \n ## Search Territories \n " + " \n " . join ( f " - { item } " for item in content [ " search_territories " ] ) + f " \n \n ## Generation Rejections \n { rejections } \n \n ## Thesis Registry Before \n { json . dumps ( content [ ' thesis_registry_before ' ] , indent = 2 ) } \n \n ## Thesis Registry After \n { json . dumps ( content [ ' thesis_registry_after ' ] , indent = 2 ) } \n \n ## Thesis Clusters \n { clusters } \n \n ## Saturation Analysis \n { saturation } \n \n ## Idea Diversity Metrics \n { json . dumps ( content [ ' idea_diversity_metrics ' ] , indent = 2 ) } \n \n ## Ranking \n { ranking } \n \n ## Top 3 Finalists \n { top } \n \n ## Collisions \n { json . dumps ( content [ ' collisions ' ] , indent = 2 ) } \n \n ## Portfolio Concentration \n { json . dumps ( content [ ' portfolio_concentration ' ] , indent = 2 ) } \n \n ## Capability Demand \n { demand } \n \n ## Recommended Artifex Build Priorities \n " + " \n " . join ( f " - { item } " for item in content [ " recommended_build_priorities " ] )
2026-08-15 21:42:46 +07:00
2026-08-15 20:56:56 +07:00
def _artifact ( self , proposal , mandate , artifact_type : str , name : str , content : dict [ str , Any ] , readable : str , generated_by : str , * , graph_run = None ) - > VentureArtifact :
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_pitch ( self , pitch : dict [ str , Any ] ) - > str :
return " \n \n " . join ( f " { section } \n { pitch . get ( section , ' ' ) } " for section in PITCH_SECTIONS )
def _readable_mandate ( self , mandate : CompanyMandate ) - > str :
return f " Objective \n { mandate . objective } \n \n Constraints \n { json . dumps ( mandate . constraints , indent = 2 ) } \n \n Optimization targets \n " + " \n " . join ( f " - { item } " for item in mandate . optimization_targets )
def _readable_board ( self , review : CompanyBoardReview ) - > str :
return f " Recommendation: { review . recommendation } \n \n Strengths \n " + " \n " . join ( f " - { item } " for item in review . strengths ) + " \n \n Weaknesses \n " + " \n " . join ( f " - { item } " for item in review . weaknesses )
def _readable_responses ( self , responses : list [ ICResponse ] ) - > str :
return " \n \n " . join ( f " Q: { response . question . question } \n A: { response . answer } \n Uncertainty: { response . uncertainty } " for response in responses )
def _readable_score ( self , decision : ICDecision ) - > str :
scores = " \n " . join ( f " - { key } : { value } /100 " for key , value in decision . component_scores . items ( ) )
return f " Decision: { decision . decision } \n Composite: { decision . composite_score } /100 \n P($500 within 30 days): { decision . probability_500_within_30_days } % \n \n Scores \n { scores } \n \n Condition \n { decision . validation_condition } "
2026-08-15 21:42:46 +07:00
def _readable_research ( self , research : dict [ str , Any ] ) - > str :
sources = " \n " . join ( f " - { source . get ( ' category ' ) } : { source . get ( ' title ' ) } { source . get ( ' url ' ) } " for source in research . get ( " sources " , [ ] ) )
missing = " , " . join ( research . get ( " unverified_categories " , [ ] ) ) or " none "
return f " Coverage \n { json . dumps ( research . get ( ' coverage ' , { } ) , indent = 2 ) } \n \n Sources \n { sources } \n \n Unverified categories \n { missing } "
2026-08-15 20:56:56 +07:00
def _readable_capability_gap ( self , available : list [ str ] , partial : list [ str ] , missing : list [ str ] , ranked : list [ dict [ str , str ] ] ) - > str :
return " AVAILABLE \n " + " \n " . join ( f " - { item } " for item in available ) + " \n \n PARTIAL \n " + " \n " . join ( f " - { item } " for item in partial ) + " \n \n MISSING \n " + " \n " . join ( f " - { item } " for item in missing ) + " \n \n NEXT ARTIFEX CAPABILITIES REQUIRED \n " + " \n " . join ( f " - { item [ ' category ' ] } ( { item [ ' priority ' ] } ) " for item in ranked )
def _readable_memo ( self , memo : dict [ str , Any ] ) - > str :
return json . dumps ( memo , indent = 2 )
def _fingerprint ( self , payload : dict [ str , Any ] ) - > str :
return hashlib . sha256 ( ( str ( payload [ " title " ] ) . lower ( ) + str ( payload [ " target_customer " ] ) . lower ( ) + str ( payload [ " business_model " ] ) . lower ( ) ) . encode ( ) ) . hexdigest ( )