2026-08-15 20:56:56 +07:00
from __future__ import annotations
from agents . venture_discovery import VentureDiscoveryService
from control_plane . ventures . models import CompanyMandate , CompanyProposal , ICDiligence , ICDecision , PortfolioCapabilityGap
from graph . native_runtime import GraphExecutionContext
from graph . registry import NodeHandlerRegistry , NodeResult
from graph . spec import ExecutionGraphSpec , GraphEdgeSpec , GraphNodeSpec
def venture_discovery_graph_v1 ( ) - > ExecutionGraphSpec :
2026-08-15 21:42:46 +07:00
nodes = [ " prepare_mandate " , " generate_company " , " conduct_research " , " board_review " , " revise_pitch " , " ic_first_pass " , " generate_questions " , " company_response " , " red_team " , " final_response " , " score " , " ic_decision " , " capability_analysis " , " produce_investment_memo " , " complete " ]
2026-08-15 20:56:56 +07:00
spec = ExecutionGraphSpec (
name = " venture_discovery " ,
version = 1 ,
graph_type = " VENTURE_DISCOVERY " ,
entry = " prepare_mandate " ,
nodes = { node : GraphNodeSpec ( node , node if node == " complete " else f " venture_ { node } " ) for node in nodes } ,
edges = [ GraphEdgeSpec ( nodes [ index ] , nodes [ index + 1 ] , " success " ) for index in range ( len ( nodes ) - 1 ) ] ,
terminal_nodes = [ " complete " ] ,
metadata = { " description " : " Venture Discovery V0: exactly one startup proposal through Board, IC diligence, scoring, memo, and capability-gap analysis. " } ,
)
spec . validate ( )
return spec
class VentureNode :
idempotent = True
replay_safe = True
destructive = False
def __init__ ( self , service : VentureDiscoveryService , node_type : str ) - > None :
self . service = service
self . node_type = node_type
def mandate ( self , context : GraphExecutionContext ) - > CompanyMandate :
return CompanyMandate . objects . get ( id = context . graph_run . metadata [ " mandate_id " ] )
def proposal ( self , context : GraphExecutionContext ) - > CompanyProposal :
return CompanyProposal . objects . get ( id = context . graph_run . metadata [ " proposal_id " ] )
def diligence ( self , context : GraphExecutionContext ) - > ICDiligence :
return ICDiligence . objects . get ( id = context . graph_run . metadata [ " diligence_id " ] )
def gap ( self , context : GraphExecutionContext ) - > PortfolioCapabilityGap :
return PortfolioCapabilityGap . objects . get ( id = context . graph_run . metadata [ " capability_gap_id " ] )
class PrepareMandateNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
mandate = self . service . create_v0_mandate ( )
metadata = dict ( context . graph_run . metadata )
metadata [ " mandate_id " ] = str ( mandate . id )
metadata [ " exactly_one_company_required " ] = True
metadata [ " no_real_spend " ] = True
metadata [ " no_real_customer_outreach " ] = True
context . graph_run . metadata = metadata
context . graph_run . save ( update_fields = [ " metadata " , " updated_at " ] )
return NodeResult ( " COMPLETE " , " success " , { " mandate_id " : str ( mandate . id ) , " objective " : mandate . objective } )
class GenerateCompanyNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
proposal = self . service . generate_single_company ( self . mandate ( context ) , graph_run = context . graph_run )
metadata = dict ( context . graph_run . metadata )
metadata [ " proposal_id " ] = str ( proposal . id )
context . graph_run . metadata = metadata
context . graph_run . save ( update_fields = [ " metadata " , " updated_at " ] )
return NodeResult ( " COMPLETE " , " success " , { " proposal_id " : str ( proposal . id ) , " company " : proposal . title , " generation_source " : proposal . metadata . get ( " generation_source " ) } )
class BoardReviewNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
review = self . service . board_review ( self . proposal ( context ) , graph_run = context . graph_run )
return NodeResult ( " COMPLETE " , " success " , { " board_review_id " : str ( review . id ) , " recommendation " : review . recommendation } )
2026-08-15 21:42:46 +07:00
class ResearchNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
research = self . service . conduct_market_research ( self . proposal ( context ) , graph_run = context . graph_run )
return NodeResult ( " COMPLETE " , " success " , { " source_count " : len ( research . get ( " sources " , [ ] ) ) , " unverified_categories " : research . get ( " unverified_categories " , [ ] ) } )
2026-08-15 20:56:56 +07:00
class RevisePitchNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
proposal = self . proposal ( context )
latest = proposal . board_reviews . order_by ( " -created_at " ) . first ( )
if latest and latest . revised_pitch :
proposal . pitch = latest . revised_pitch
proposal . save ( update_fields = [ " pitch " , " updated_at " ] )
return NodeResult ( " COMPLETE " , " success " , { " proposal_id " : str ( proposal . id ) , " pitch_revised " : latest is not None } )
class ICFirstPassNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
diligence = self . service . start_ic_diligence ( self . proposal ( context ) , graph_run = context . graph_run )
metadata = dict ( context . graph_run . metadata )
metadata [ " diligence_id " ] = str ( diligence . id )
context . graph_run . metadata = metadata
context . graph_run . save ( update_fields = [ " metadata " , " updated_at " ] )
return NodeResult ( " COMPLETE " , " success " , { " diligence_id " : str ( diligence . id ) , " status " : diligence . status } )
class ICQuestionsNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
questions = self . service . generate_ic_questions ( self . diligence ( context ) , graph_run = context . graph_run )
return NodeResult ( " COMPLETE " , " success " , { " question_count " : len ( questions ) , " question_ids " : [ str ( q . id ) for q in questions ] } )
class CompanyResponseNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
responses = self . service . answer_questions ( self . diligence ( context ) , graph_run = context . graph_run )
return NodeResult ( " COMPLETE " , " success " , { " response_count " : len ( responses ) } )
class RedTeamNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
challenge = self . service . red_team ( self . diligence ( context ) , graph_run = context . graph_run )
return NodeResult ( " COMPLETE " , " success " , { " concern_count " : len ( challenge . get ( " concerns " , [ ] ) ) } )
class FinalResponseNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
response = self . service . final_company_response ( self . diligence ( context ) , graph_run = context . graph_run )
return NodeResult ( " COMPLETE " , " success " , { " kill_criteria_count " : len ( response . get ( " kill_criteria " , [ ] ) ) } )
class ScoreNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
decision = self . service . score_and_decide ( self . diligence ( context ) , graph_run = context . graph_run )
metadata = dict ( context . graph_run . metadata )
metadata [ " decision_id " ] = str ( decision . id )
context . graph_run . metadata = metadata
context . graph_run . save ( update_fields = [ " metadata " , " updated_at " ] )
return NodeResult ( " COMPLETE " , " success " , { " decision_id " : str ( decision . id ) , " score " : decision . composite_score } )
class ICDecisionNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
decision = ICDecision . objects . get ( id = context . graph_run . metadata [ " decision_id " ] )
return NodeResult ( " COMPLETE " , " success " , { " decision " : decision . decision , " initial_tranche " : str ( decision . initial_tranche or " " ) } )
class CapabilityAnalysisNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
gap = self . service . capability_analysis ( self . proposal ( context ) , graph_run = context . graph_run )
metadata = dict ( context . graph_run . metadata )
metadata [ " capability_gap_id " ] = str ( gap . id )
context . graph_run . metadata = metadata
context . graph_run . save ( update_fields = [ " metadata " , " updated_at " ] )
return NodeResult ( " COMPLETE " , " success " , { " capability_gap_id " : str ( gap . id ) , " missing_count " : len ( gap . missing ) } )
class InvestmentMemoNode ( VentureNode ) :
def run ( self , context : GraphExecutionContext ) - > NodeResult :
memo = self . service . produce_investment_memo ( self . diligence ( context ) , self . gap ( context ) , graph_run = context . graph_run )
return NodeResult ( " COMPLETE " , " success " , { " memo_artifact_id " : str ( memo . id ) , " artifact_type " : memo . artifact_type } )
def venture_discovery_registry ( service : VentureDiscoveryService ) - > NodeHandlerRegistry :
registry = NodeHandlerRegistry ( )
2026-08-15 21:42:46 +07:00
for handler in [ PrepareMandateNode ( service , " venture_prepare_mandate " ) , GenerateCompanyNode ( service , " venture_generate_company " ) , ResearchNode ( service , " venture_conduct_research " ) , BoardReviewNode ( service , " venture_board_review " ) , RevisePitchNode ( service , " venture_revise_pitch " ) , ICFirstPassNode ( service , " venture_ic_first_pass " ) , ICQuestionsNode ( service , " venture_generate_questions " ) , CompanyResponseNode ( service , " venture_company_response " ) , RedTeamNode ( service , " venture_red_team " ) , FinalResponseNode ( service , " venture_final_response " ) , ScoreNode ( service , " venture_score " ) , ICDecisionNode ( service , " venture_ic_decision " ) , CapabilityAnalysisNode ( service , " venture_capability_analysis " ) , InvestmentMemoNode ( service , " venture_produce_investment_memo " ) ] :
2026-08-15 20:56:56 +07:00
registry . register ( handler )
return registry