from __future__ import annotations from django.db import models from control_plane.common import TimestampedModel class CompanyProposalStatus(models.TextChoices): DRAFT = "DRAFT" SUBMITTED = "SUBMITTED" UNDER_DILIGENCE = "UNDER_DILIGENCE" REVISE = "REVISE" FUNDED_RECOMMENDED = "FUNDED_RECOMMENDED" WATCHLIST = "WATCHLIST" REJECTED = "REJECTED" class ICDecisionType(models.TextChoices): FUND_RECOMMENDED = "FUND_RECOMMENDED" CONDITIONAL_FUND = "CONDITIONAL_FUND" REVISE_AND_RESUBMIT = "REVISE_AND_RESUBMIT" WATCHLIST = "WATCHLIST" PIVOT = "PIVOT" REJECT = "REJECT" class CapabilityStatus(models.TextChoices): AVAILABLE = "AVAILABLE" PARTIAL = "PARTIAL" MISSING = "MISSING" class CapabilityPriority(models.TextChoices): BEFORE_VALIDATION = "BEFORE_VALIDATION" BEFORE_FIRST_CUSTOMER = "BEFORE_FIRST_CUSTOMER" BEFORE_SCALING = "BEFORE_SCALING" class EvidenceTier(models.TextChoices): TIER_0_THESIS = "TIER_0_THESIS" TIER_1_PUBLIC_EVIDENCE = "TIER_1_PUBLIC_EVIDENCE" TIER_2_CUSTOMER_SIGNAL = "TIER_2_CUSTOMER_SIGNAL" TIER_3_WILLINGNESS_TO_PAY = "TIER_3_WILLINGNESS_TO_PAY" TIER_4_PAID_CUSTOMER = "TIER_4_PAID_CUSTOMER" TIER_5_REPEATABLE_TRACTION = "TIER_5_REPEATABLE_TRACTION" class OverlapClassification(models.TextChoices): NONE = "NONE" ADJACENT = "ADJACENT" COMPETITIVE = "COMPETITIVE" NEAR_DUPLICATE = "NEAR_DUPLICATE" DUPLICATE = "DUPLICATE" class PortfolioThesisStatus(models.TextChoices): NEW = "NEW" ACTIVE_CANDIDATE = "ACTIVE_CANDIDATE" EXPLORED = "EXPLORED" SATURATED = "SATURATED" PAUSED = "PAUSED" REJECTED = "REJECTED" OPERATING = "OPERATING" WINNER = "WINNER" class ThesisMatchClassification(models.TextChoices): NEW_THESIS = "NEW_THESIS" ADJACENT = "ADJACENT" COMPETITIVE = "COMPETITIVE" NEAR_DUPLICATE = "NEAR_DUPLICATE" DUPLICATE = "DUPLICATE" class NoveltyGateDecision(models.TextChoices): ACCEPT = "ACCEPT" REGENERATE_HARD_EXCLUSION = "REGENERATE_HARD_EXCLUSION" REGENERATE_DUPLICATE = "REGENERATE_DUPLICATE" REVIEW_SOFT_EXCLUSION = "REVIEW_SOFT_EXCLUSION" FAILED_IDEATION = "FAILED_IDEATION" class OpportunityTerritory(models.TextChoices): VERTICAL_AI_WRAPPERS = "VERTICAL_AI_WRAPPERS" ENTERPRISE_WORKFLOW_AUTOMATION = "ENTERPRISE_WORKFLOW_AUTOMATION" SMB_AUTOMATION = "SMB_AUTOMATION" DEVELOPER_AI_INFRASTRUCTURE = "DEVELOPER_AI_INFRASTRUCTURE" INTELLIGENCE_MONITORING = "INTELLIGENCE_MONITORING" DATA_DOCUMENT_AUTOMATION = "DATA_DOCUMENT_AUTOMATION" AI_ENABLED_SERVICE_TO_PLATFORM = "AI_ENABLED_SERVICE_TO_PLATFORM" OPEN_CATEGORY = "OPEN_CATEGORY" class VentureTrack(models.TextChoices): AUTONOMOUS = "AUTONOMOUS" ASSISTED = "ASSISTED" class FounderDependencyLevel(models.TextChoices): NONE = "NONE" LOW = "LOW" MEDIUM = "MEDIUM" HIGH = "HIGH" CRITICAL = "CRITICAL" class AutonomousGateResult(models.TextChoices): AUTONOMOUS_ELIGIBLE = "AUTONOMOUS_ELIGIBLE" AUTONOMOUS_BORDERLINE = "AUTONOMOUS_BORDERLINE" ASSISTED_ONLY = "ASSISTED_ONLY" REJECT_OPERABILITY = "REJECT_OPERABILITY" class AutonomousCandidateStatus(models.TextChoices): NOT_ASSESSED = "NOT_ASSESSED" AUTONOMOUS_CANDIDATE = "AUTONOMOUS_CANDIDATE" ASSISTED_CANDIDATE = "ASSISTED_CANDIDATE" AUTONOMOUS_REJECTED = "AUTONOMOUS_REJECTED" class CompanyMandate(TimestampedModel): objective = models.TextField() constraints = models.JSONField(default=dict, blank=True) optimization_targets = models.JSONField(default=list, blank=True) max_validation_capital = models.DecimalField(max_digits=10, decimal_places=2, default=50) target_net_new_cash = models.DecimalField(max_digits=10, decimal_places=2, default=500) target_window_days = models.PositiveIntegerField(default=30) metadata = models.JSONField(default=dict, blank=True) class VentureThesis(TimestampedModel): mandate = models.ForeignKey(CompanyMandate, on_delete=models.CASCADE, related_name="theses") title = models.CharField(max_length=255) thesis = models.TextField() similarity_fingerprint = models.CharField(max_length=128, blank=True) related_theses = models.JSONField(default=list, blank=True) competitive_theses = models.JSONField(default=list, blank=True) portfolio_visibility = models.JSONField(default=dict, blank=True) metadata = models.JSONField(default=dict, blank=True) evidence_tier = models.CharField(max_length=40, choices=EvidenceTier.choices, default=EvidenceTier.TIER_0_THESIS) class CompanyProposal(TimestampedModel): mandate = models.ForeignKey(CompanyMandate, on_delete=models.PROTECT, related_name="company_proposals") thesis = models.ForeignKey(VentureThesis, on_delete=models.SET_NULL, null=True, blank=True, related_name="company_proposals") title = models.CharField(max_length=255) description = models.TextField() problem = models.TextField() target_customer = models.TextField() proposed_solution = models.TextField() business_model = models.TextField() pricing_hypothesis = models.TextField() acquisition_strategy = models.TextField() validation_plan = models.TextField() capital_requested = models.DecimalField(max_digits=10, decimal_places=2, default=0) time_to_first_dollar_estimate = models.CharField(max_length=120) expected_margin = models.CharField(max_length=120) build_complexity = models.CharField(max_length=80) market_evidence = models.JSONField(default=list, blank=True) differentiation = models.TextField() major_risks = models.JSONField(default=list, blank=True) confidence = models.FloatField(default=0.0) status = models.CharField(max_length=32, choices=CompanyProposalStatus.choices, default=CompanyProposalStatus.DRAFT) pitch = models.JSONField(default=dict, blank=True) metadata = models.JSONField(default=dict, blank=True) evidence_tier = models.CharField(max_length=40, choices=EvidenceTier.choices, default=EvidenceTier.TIER_0_THESIS) class PortfolioThesis(TimestampedModel): canonical_name = models.CharField(max_length=255, unique=True) concise_thesis = models.TextField() category = models.CharField(max_length=120, blank=True) industry = models.CharField(max_length=160, blank=True) icp = models.TextField(blank=True) problem = models.TextField(blank=True) offer = models.TextField(blank=True) business_model = models.CharField(max_length=160, blank=True) primary_channel = models.CharField(max_length=160, blank=True) ai_leverage = models.FloatField(default=0.0) platformization_potential = models.FloatField(default=0.0) fingerprint = models.CharField(max_length=128, blank=True) status = models.CharField(max_length=32, choices=PortfolioThesisStatus.choices, default=PortfolioThesisStatus.NEW) proposal_count = models.PositiveIntegerField(default=0) best_ic_score = models.FloatField(default=0.0) best_company = models.ForeignKey(CompanyProposal, on_delete=models.SET_NULL, null=True, blank=True, related_name="best_for_portfolio_theses") first_seen_cohort = models.ForeignKey("VentureCohort", on_delete=models.SET_NULL, null=True, blank=True, related_name="first_seen_portfolio_theses") last_seen_cohort = models.ForeignKey("VentureCohort", on_delete=models.SET_NULL, null=True, blank=True, related_name="last_seen_portfolio_theses") metadata = models.JSONField(default=dict, blank=True) venture_track = models.CharField(max_length=32, choices=VentureTrack.choices, default=VentureTrack.AUTONOMOUS) best_autonomous_operability_score = models.FloatField(default=0.0) best_founder_dependency = models.CharField(max_length=32, choices=FounderDependencyLevel.choices, default=FounderDependencyLevel.MEDIUM) autonomous_candidate_status = models.CharField(max_length=32, choices=AutonomousCandidateStatus.choices, default=AutonomousCandidateStatus.NOT_ASSESSED) class CohortIdeationMandate(TimestampedModel): cohort = models.OneToOneField("VentureCohort", on_delete=models.CASCADE, related_name="ideation_mandate") objective = models.TextField() desired_opportunity_classes = models.JSONField(default=list, blank=True) hard_exclusions = models.JSONField(default=list, blank=True) soft_exclusions = models.JSONField(default=list, blank=True) opportunity_territories = models.JSONField(default=list, blank=True) portfolio_gaps = models.JSONField(default=list, blank=True) diversity_preferences = models.JSONField(default=dict, blank=True) registry_snapshot = models.JSONField(default=dict, blank=True) created_by = models.CharField(max_length=120, default="Portfolio IC") metadata = models.JSONField(default=dict, blank=True) class PortfolioThesisCluster(TimestampedModel): cohort = models.ForeignKey("VentureCohort", on_delete=models.CASCADE, related_name="thesis_clusters") proposal = models.ForeignKey(CompanyProposal, on_delete=models.CASCADE, related_name="portfolio_thesis_clusters") portfolio_thesis = models.ForeignKey(PortfolioThesis, on_delete=models.SET_NULL, null=True, blank=True, related_name="proposal_clusters") similarity_score = models.FloatField(default=0.0) classification = models.CharField(max_length=32, choices=ThesisMatchClassification.choices) explanation = models.TextField(blank=True) metadata = models.JSONField(default=dict, blank=True) class VentureGenerationRejection(TimestampedModel): cohort = models.ForeignKey("VentureCohort", on_delete=models.CASCADE, related_name="generation_rejections") slot_index = models.PositiveIntegerField() attempt = models.PositiveIntegerField() decision = models.CharField(max_length=40, choices=NoveltyGateDecision.choices) reason = models.TextField(blank=True) candidate = models.JSONField(default=dict, blank=True) matched_thesis = models.ForeignKey(PortfolioThesis, on_delete=models.SET_NULL, null=True, blank=True, related_name="generation_rejections") similarity_score = models.FloatField(default=0.0) metadata = models.JSONField(default=dict, blank=True) class PortfolioSaturationAnalysis(TimestampedModel): cohort = models.ForeignKey("VentureCohort", on_delete=models.CASCADE, related_name="saturation_analyses") portfolio_thesis = models.ForeignKey(PortfolioThesis, on_delete=models.CASCADE, related_name="saturation_analyses") proposal_count = models.PositiveIntegerField(default=0) best_ic_score = models.FloatField(default=0.0) score_spread = models.FloatField(default=0.0) status_recommendation = models.CharField(max_length=32, choices=PortfolioThesisStatus.choices) rationale = models.TextField(blank=True) metadata = models.JSONField(default=dict, blank=True) class AutonomousOperabilityAssessment(TimestampedModel): proposal = models.OneToOneField(CompanyProposal, on_delete=models.CASCADE, related_name="autonomous_assessment") venture_track = models.CharField(max_length=32, choices=VentureTrack.choices, default=VentureTrack.AUTONOMOUS) gate_result = models.CharField(max_length=40, choices=AutonomousGateResult.choices) autonomous_operability_score = models.FloatField(default=0.0) commercial_score = models.FloatField(default=0.0) founder_dependency = models.CharField(max_length=32, choices=FounderDependencyLevel.choices) dependency_causes = models.JSONField(default=list, blank=True) minutes_per_week_human = models.PositiveIntegerField(default=30) human_actions_required = models.JSONField(default=list, blank=True) human_action_categories = models.JSONField(default=list, blank=True) operating_loop = models.JSONField(default=dict, blank=True) fulfillment_contract = models.JSONField(default=dict, blank=True) validation_offer = models.JSONField(default=dict, blank=True) end_state_business_model = models.JSONField(default=dict, blank=True) structural_blockers = models.JSONField(default=list, blank=True) platform_blockers = models.JSONField(default=list, blank=True) component_scores = models.JSONField(default=dict, blank=True) rationale = models.TextField(blank=True) metadata = models.JSONField(default=dict, blank=True) class CompanyBoardReview(TimestampedModel): proposal = models.ForeignKey(CompanyProposal, on_delete=models.CASCADE, related_name="board_reviews") observations = models.JSONField(default=dict, blank=True) strengths = models.JSONField(default=list, blank=True) weaknesses = models.JSONField(default=list, blank=True) key_assumptions = models.JSONField(default=list, blank=True) required_revisions = models.JSONField(default=list, blank=True) recommendation = models.CharField(max_length=80) revised_pitch = models.JSONField(default=dict, blank=True) metadata = models.JSONField(default=dict, blank=True) class ICDiligence(TimestampedModel): proposal = models.ForeignKey(CompanyProposal, on_delete=models.CASCADE, related_name="ic_diligence") status = models.CharField(max_length=32, default="INITIAL_PITCH") rounds = models.JSONField(default=list, blank=True) red_team_challenge = models.JSONField(default=dict, blank=True) final_response = models.JSONField(default=dict, blank=True) metadata = models.JSONField(default=dict, blank=True) class ICQuestion(TimestampedModel): diligence = models.ForeignKey(ICDiligence, on_delete=models.CASCADE, related_name="questions") question = models.TextField() category = models.CharField(max_length=80) evidence_required = models.BooleanField(default=True) round_name = models.CharField(max_length=80, default="Diligence Round 1") metadata = models.JSONField(default=dict, blank=True) class ICResponse(TimestampedModel): question = models.ForeignKey(ICQuestion, on_delete=models.CASCADE, related_name="responses") answer = models.TextField() evidence = models.JSONField(default=list, blank=True) uncertainty = models.TextField(blank=True) pitch_changes = models.JSONField(default=dict, blank=True) metadata = models.JSONField(default=dict, blank=True) class ICDecision(TimestampedModel): diligence = models.OneToOneField(ICDiligence, on_delete=models.CASCADE, related_name="decision") decision = models.CharField(max_length=32, choices=ICDecisionType.choices) component_scores = models.JSONField(default=dict, blank=True) composite_score = models.FloatField(default=0.0) probability_500_within_30_days = models.FloatField(default=0.0) initial_tranche = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) validation_condition = models.TextField(blank=True) evidence_required = models.JSONField(default=list, blank=True) recommended_allocation = models.JSONField(default=dict, blank=True) kill_criteria = models.JSONField(default=list, blank=True) next_decision_point = models.TextField(blank=True) metadata = models.JSONField(default=dict, blank=True) evidence_tier = models.CharField(max_length=40, choices=EvidenceTier.choices, default=EvidenceTier.TIER_0_THESIS) raw_probability_500_within_30_days = models.FloatField(default=0.0) evidence_ceiling = models.FloatField(default=35.0) probability_explanation = models.TextField(blank=True) score_definitions = models.JSONField(default=dict, blank=True) class VentureThesisFingerprint(TimestampedModel): proposal = models.OneToOneField(CompanyProposal, on_delete=models.CASCADE, related_name="fingerprint") industry = models.CharField(max_length=160) icp = models.TextField() problem = models.TextField() offer = models.TextField() business_model = models.CharField(max_length=160) primary_distribution_channel = models.CharField(max_length=160) price_band = models.CharField(max_length=80) time_to_first_cash_band = models.CharField(max_length=80) required_capability_set = models.JSONField(default=list, blank=True) geography_dependency = models.CharField(max_length=120, blank=True) regulatory_dependency = models.CharField(max_length=120, blank=True) online_offline = models.CharField(max_length=80, blank=True) service_software_hybrid = models.CharField(max_length=80, blank=True) fingerprint_hash = models.CharField(max_length=128) metadata = models.JSONField(default=dict, blank=True) class VentureCohort(TimestampedModel): cohort_id = models.CharField(max_length=80, unique=True) mandate = models.ForeignKey(CompanyMandate, on_delete=models.PROTECT, related_name="venture_cohorts") cohort_size = models.PositiveIntegerField(default=10) status = models.CharField(max_length=40, default="PREPARING") graph_run = models.ForeignKey("graph.GraphRun", on_delete=models.SET_NULL, null=True, blank=True, related_name="venture_cohorts") concurrency = models.PositiveIntegerField(default=1) model_versions = models.JSONField(default=dict, blank=True) graph_versions = models.JSONField(default=dict, blank=True) research_policy = models.JSONField(default=dict, blank=True) scoring_policy = models.JSONField(default=dict, blank=True) evidence_calibration_policy = models.JSONField(default=dict, blank=True) metrics = models.JSONField(default=dict, blank=True) metadata = models.JSONField(default=dict, blank=True) class VentureCohortMember(TimestampedModel): cohort = models.ForeignKey(VentureCohort, on_delete=models.CASCADE, related_name="members") proposal = models.ForeignKey(CompanyProposal, on_delete=models.CASCADE, related_name="cohort_memberships") child_graph_run = models.ForeignKey("graph.GraphRun", on_delete=models.SET_NULL, null=True, blank=True, related_name="venture_cohort_members") rank = models.PositiveIntegerField(null=True, blank=True) is_top_3 = models.BooleanField(default=False) portfolio_score = models.FloatField(default=0.0) metadata = models.JSONField(default=dict, blank=True) class Meta: constraints = [models.UniqueConstraint(fields=["cohort", "proposal"], name="unique_venture_cohort_member")] class VentureCollision(TimestampedModel): cohort = models.ForeignKey(VentureCohort, on_delete=models.CASCADE, related_name="collisions") company_a = models.ForeignKey(CompanyProposal, on_delete=models.CASCADE, related_name="collisions_as_a") company_b = models.ForeignKey(CompanyProposal, on_delete=models.CASCADE, related_name="collisions_as_b") classification = models.CharField(max_length=40, choices=OverlapClassification.choices) similarity_score = models.FloatField(default=0.0) explanation = models.TextField(blank=True) overlapping_dimensions = models.JSONField(default=list, blank=True) class PortfolioICReview(TimestampedModel): cohort = models.OneToOneField(VentureCohort, on_delete=models.CASCADE, related_name="portfolio_review") rankings = models.JSONField(default=list, blank=True) top_3 = models.JSONField(default=list, blank=True) concentration = models.JSONField(default=dict, blank=True) capability_demand = models.JSONField(default=list, blank=True) top_3_capability_gaps = models.JSONField(default=list, blank=True) recommended_build_priorities = models.JSONField(default=list, blank=True) metadata = models.JSONField(default=dict, blank=True) class VentureCapabilityDemand(TimestampedModel): cohort = models.ForeignKey(VentureCohort, on_delete=models.CASCADE, related_name="capability_demands") capability = models.CharField(max_length=120) count = models.PositiveIntegerField(default=0) percentage = models.FloatField(default=0.0) earliest_stage = models.CharField(max_length=32, choices=CapabilityPriority.choices) companies = models.JSONField(default=list, blank=True) status_distribution = models.JSONField(default=dict, blank=True) top_3_count = models.PositiveIntegerField(default=0) priority_score = models.FloatField(default=0.0) class CompanyCapabilityRequirement(TimestampedModel): proposal = models.ForeignKey(CompanyProposal, on_delete=models.CASCADE, related_name="capability_requirements") category = models.CharField(max_length=120) status = models.CharField(max_length=32, choices=CapabilityStatus.choices) rationale = models.TextField(blank=True) priority = models.CharField(max_length=32, choices=CapabilityPriority.choices, default=CapabilityPriority.BEFORE_SCALING) evidence = models.JSONField(default=dict, blank=True) class PortfolioCapabilityGap(TimestampedModel): proposal = models.ForeignKey(CompanyProposal, on_delete=models.CASCADE, related_name="capability_gap_reports") available = models.JSONField(default=list, blank=True) partial = models.JSONField(default=list, blank=True) missing = models.JSONField(default=list, blank=True) ranked_missing = models.JSONField(default=list, blank=True) report = models.TextField(blank=True) metadata = models.JSONField(default=dict, blank=True) class VentureArtifact(TimestampedModel): proposal = models.ForeignKey(CompanyProposal, on_delete=models.CASCADE, null=True, blank=True, related_name="artifacts") mandate = models.ForeignKey(CompanyMandate, on_delete=models.CASCADE, null=True, blank=True, related_name="artifacts") graph_run = models.ForeignKey("graph.GraphRun", on_delete=models.SET_NULL, null=True, blank=True, related_name="venture_artifacts") artifact_type = models.CharField(max_length=80) name = models.CharField(max_length=255) content = models.JSONField(default=dict, blank=True) readable = models.TextField(blank=True) generated_by = models.CharField(max_length=120, blank=True)