package temporal import ( "context" "fmt" "math" "sort" "sync" "time" "chorus/pkg/ucxl" ) // querySystemImpl implements decision-hop based query operations type querySystemImpl struct { mu sync.RWMutex // Reference to the temporal graph graph *temporalGraphImpl navigator DecisionNavigator analyzer InfluenceAnalyzer detector StalenessDetector // Query optimization queryCache map[string]interface{} cacheTimeout time.Duration lastCacheClean time.Time // Query statistics queryStats map[string]*QueryStatistics } // QueryStatistics represents statistics for different query types type QueryStatistics struct { QueryType string `json:"query_type"` TotalQueries int64 `json:"total_queries"` AverageTime time.Duration `json:"average_time"` CacheHits int64 `json:"cache_hits"` CacheMisses int64 `json:"cache_misses"` LastQuery time.Time `json:"last_query"` } // HopQuery represents a decision-hop based query type HopQuery struct { StartAddress ucxl.Address `json:"start_address"` // Starting point MaxHops int `json:"max_hops"` // Maximum hops to traverse Direction string `json:"direction"` // "forward", "backward", "both" FilterCriteria *HopFilter `json:"filter_criteria"` // Filtering options SortCriteria *HopSort `json:"sort_criteria"` // Sorting options Limit int `json:"limit"` // Maximum results IncludeMetadata bool `json:"include_metadata"` // Include detailed metadata } // HopFilter represents filtering criteria for hop queries type HopFilter struct { ChangeReasons []ChangeReason `json:"change_reasons"` // Filter by change reasons ImpactScopes []ImpactScope `json:"impact_scopes"` // Filter by impact scopes MinConfidence float64 `json:"min_confidence"` // Minimum confidence threshold MaxAge time.Duration `json:"max_age"` // Maximum age of decisions DecisionMakers []string `json:"decision_makers"` // Filter by decision makers Tags []string `json:"tags"` // Filter by context tags Technologies []string `json:"technologies"` // Filter by technologies MinInfluenceCount int `json:"min_influence_count"` // Minimum number of influences ExcludeStale bool `json:"exclude_stale"` // Exclude stale contexts OnlyMajorDecisions bool `json:"only_major_decisions"` // Only major decisions } // HopSort represents sorting criteria for hop queries type HopSort struct { SortBy string `json:"sort_by"` // "hops", "time", "confidence", "influence" SortDirection string `json:"sort_direction"` // "asc", "desc" SecondarySort string `json:"secondary_sort"` // Secondary sort field } // HopQueryResult represents the result of a hop-based query type HopQueryResult struct { Query *HopQuery `json:"query"` // Original query Results []*HopResult `json:"results"` // Query results TotalFound int `json:"total_found"` // Total results found ExecutionTime time.Duration `json:"execution_time"` // Query execution time FromCache bool `json:"from_cache"` // Whether result came from cache QueryPath []*QueryPathStep `json:"query_path"` // Path of query execution Statistics *QueryExecution `json:"statistics"` // Execution statistics } // HopResult represents a single result from a hop query type HopResult struct { Address ucxl.Address `json:"address"` // Context address HopDistance int `json:"hop_distance"` // Decision hops from start TemporalNode *TemporalNode `json:"temporal_node"` // Temporal node data Path []*DecisionStep `json:"path"` // Path from start to this result Relationship string `json:"relationship"` // Relationship type RelevanceScore float64 `json:"relevance_score"` // Relevance to query MatchReasons []string `json:"match_reasons"` // Why this matched Metadata map[string]interface{} `json:"metadata"` // Additional metadata } // QueryPathStep represents a step in query execution path type QueryPathStep struct { Step int `json:"step"` // Step number Operation string `json:"operation"` // Operation performed NodesExamined int `json:"nodes_examined"` // Nodes examined in this step NodesFiltered int `json:"nodes_filtered"` // Nodes filtered out Duration time.Duration `json:"duration"` // Step duration Description string `json:"description"` // Step description } // QueryExecution represents query execution statistics type QueryExecution struct { StartTime time.Time `json:"start_time"` // Query start time EndTime time.Time `json:"end_time"` // Query end time Duration time.Duration `json:"duration"` // Total duration NodesVisited int `json:"nodes_visited"` // Total nodes visited EdgesTraversed int `json:"edges_traversed"` // Total edges traversed CacheAccesses int `json:"cache_accesses"` // Cache access count FilterSteps int `json:"filter_steps"` // Number of filter steps SortOperations int `json:"sort_operations"` // Number of sort operations MemoryUsed int64 `json:"memory_used"` // Estimated memory used } // NewQuerySystem creates a new decision-hop query system func NewQuerySystem(graph *temporalGraphImpl, navigator DecisionNavigator, analyzer InfluenceAnalyzer, detector StalenessDetector) *querySystemImpl { return &querySystemImpl{ graph: graph, navigator: navigator, analyzer: analyzer, detector: detector, queryCache: make(map[string]interface{}), cacheTimeout: time.Minute * 10, lastCacheClean: time.Now(), queryStats: make(map[string]*QueryStatistics), } } // ExecuteHopQuery executes a decision-hop based query func (qs *querySystemImpl) ExecuteHopQuery(ctx context.Context, query *HopQuery) (*HopQueryResult, error) { startTime := time.Now() // Validate query if err := qs.validateQuery(query); err != nil { return nil, fmt.Errorf("invalid query: %w", err) } // Check cache cacheKey := qs.generateCacheKey(query) if cached, found := qs.getFromCache(cacheKey); found { if result, ok := cached.(*HopQueryResult); ok { result.FromCache = true qs.updateQueryStats("hop_query", time.Since(startTime), true) return result, nil } } // Execute query result, err := qs.executeHopQueryInternal(ctx, query) if err != nil { return nil, err } // Set execution time and cache result result.ExecutionTime = time.Since(startTime) result.FromCache = false qs.setCache(cacheKey, result) qs.updateQueryStats("hop_query", result.ExecutionTime, false) return result, nil } // FindDecisionsWithinHops finds all decisions within N hops of a given address func (qs *querySystemImpl) FindDecisionsWithinHops(ctx context.Context, address ucxl.Address, maxHops int, filter *HopFilter) ([]*HopResult, error) { query := &HopQuery{ StartAddress: address, MaxHops: maxHops, Direction: "both", FilterCriteria: filter, SortCriteria: &HopSort{SortBy: "hops", SortDirection: "asc"}, IncludeMetadata: false, } result, err := qs.ExecuteHopQuery(ctx, query) if err != nil { return nil, err } return result.Results, nil } // FindInfluenceChain finds the chain of influence between two decisions func (qs *querySystemImpl) FindInfluenceChain(ctx context.Context, from, to ucxl.Address) ([]*DecisionStep, error) { // Use the temporal graph's path finding return qs.graph.FindDecisionPath(ctx, from, to) } // AnalyzeDecisionGenealogy analyzes the genealogy of decisions for a context func (qs *querySystemImpl) AnalyzeDecisionGenealogy(ctx context.Context, address ucxl.Address) (*DecisionGenealogy, error) { qs.mu.RLock() defer qs.mu.RUnlock() // Get evolution history history, err := qs.graph.GetEvolutionHistory(ctx, address) if err != nil { return nil, fmt.Errorf("failed to get evolution history: %w", err) } // Get decision timeline timeline, err := qs.navigator.GetDecisionTimeline(ctx, address, true, 10) if err != nil { return nil, fmt.Errorf("failed to get decision timeline: %w", err) } // Analyze ancestry ancestry := qs.analyzeAncestry(history) // Analyze descendants descendants := qs.analyzeDescendants(address, 5) // Find influential ancestors influentialAncestors := qs.findInfluentialAncestors(history) // Calculate genealogy metrics metrics := qs.calculateGenealogyMetrics(history, descendants) genealogy := &DecisionGenealogy{ Address: address, DirectAncestors: ancestry.DirectAncestors, AllAncestors: ancestry.AllAncestors, DirectDescendants: descendants.DirectDescendants, AllDescendants: descendants.AllDescendants, InfluentialAncestors: influentialAncestors, GenealogyDepth: ancestry.MaxDepth, BranchingFactor: descendants.BranchingFactor, DecisionTimeline: timeline, Metrics: metrics, AnalyzedAt: time.Now(), } return genealogy, nil } // FindSimilarDecisionPatterns finds decisions with similar patterns func (qs *querySystemImpl) FindSimilarDecisionPatterns(ctx context.Context, referenceAddress ucxl.Address, maxResults int) ([]*SimilarDecisionMatch, error) { qs.mu.RLock() defer qs.mu.RUnlock() // Get reference node refNode, err := qs.graph.getLatestNodeUnsafe(referenceAddress) if err != nil { return nil, fmt.Errorf("reference node not found: %w", err) } matches := make([]*SimilarDecisionMatch, 0) // Compare with all other nodes for _, node := range qs.graph.nodes { if node.UCXLAddress.String() == referenceAddress.String() { continue // Skip self } similarity := qs.calculateDecisionSimilarity(refNode, node) if similarity > 0.3 { // Threshold for meaningful similarity match := &SimilarDecisionMatch{ Address: node.UCXLAddress, TemporalNode: node, SimilarityScore: similarity, SimilarityReasons: qs.getSimilarityReasons(refNode, node), PatternType: qs.identifyPatternType(refNode, node), Confidence: similarity * 0.9, // Slightly lower confidence } matches = append(matches, match) } } // Sort by similarity score sort.Slice(matches, func(i, j int) bool { return matches[i].SimilarityScore > matches[j].SimilarityScore }) // Limit results if maxResults > 0 && len(matches) > maxResults { matches = matches[:maxResults] } return matches, nil } // DiscoverDecisionClusters discovers clusters of related decisions func (qs *querySystemImpl) DiscoverDecisionClusters(ctx context.Context, minClusterSize int) ([]*DecisionCluster, error) { qs.mu.RLock() defer qs.mu.RUnlock() // Use influence analyzer to get clusters analysis, err := qs.analyzer.AnalyzeInfluenceNetwork(ctx) if err != nil { return nil, fmt.Errorf("failed to analyze influence network: %w", err) } // Filter clusters by minimum size clusters := make([]*DecisionCluster, 0) for _, community := range analysis.Communities { if len(community.Nodes) >= minClusterSize { cluster := qs.convertCommunityToCluster(community) clusters = append(clusters, cluster) } } return clusters, nil } // Internal query execution func (qs *querySystemImpl) executeHopQueryInternal(ctx context.Context, query *HopQuery) (*HopQueryResult, error) { execution := &QueryExecution{ StartTime: time.Now(), } queryPath := make([]*QueryPathStep, 0) // Step 1: Get starting node step1Start := time.Now() startNode, err := qs.graph.getLatestNodeUnsafe(query.StartAddress) if err != nil { return nil, fmt.Errorf("start node not found: %w", err) } queryPath = append(queryPath, &QueryPathStep{ Step: 1, Operation: "get_start_node", NodesExamined: 1, NodesFiltered: 0, Duration: time.Since(step1Start), Description: "Retrieved starting node", }) // Step 2: Traverse decision graph step2Start := time.Now() candidates := qs.traverseDecisionGraph(startNode, query.MaxHops, query.Direction) execution.NodesVisited = len(candidates) queryPath = append(queryPath, &QueryPathStep{ Step: 2, Operation: "traverse_graph", NodesExamined: len(candidates), NodesFiltered: 0, Duration: time.Since(step2Start), Description: fmt.Sprintf("Traversed decision graph up to %d hops", query.MaxHops), }) // Step 3: Apply filters step3Start := time.Now() filtered := qs.applyFilters(candidates, query.FilterCriteria) execution.FilterSteps = 1 queryPath = append(queryPath, &QueryPathStep{ Step: 3, Operation: "apply_filters", NodesExamined: len(candidates), NodesFiltered: len(candidates) - len(filtered), Duration: time.Since(step3Start), Description: fmt.Sprintf("Applied filters, removed %d candidates", len(candidates)-len(filtered)), }) // Step 4: Calculate relevance scores step4Start := time.Now() results := qs.calculateRelevanceScores(filtered, startNode, query) queryPath = append(queryPath, &QueryPathStep{ Step: 4, Operation: "calculate_relevance", NodesExamined: len(filtered), NodesFiltered: 0, Duration: time.Since(step4Start), Description: "Calculated relevance scores", }) // Step 5: Sort results step5Start := time.Time{} if query.SortCriteria != nil { step5Start = time.Now() qs.sortResults(results, query.SortCriteria) execution.SortOperations = 1 queryPath = append(queryPath, &QueryPathStep{ Step: 5, Operation: "sort_results", NodesExamined: len(results), NodesFiltered: 0, Duration: time.Since(step5Start), Description: fmt.Sprintf("Sorted by %s %s", query.SortCriteria.SortBy, query.SortCriteria.SortDirection), }) } // Step 6: Apply limit totalFound := len(results) if query.Limit > 0 && len(results) > query.Limit { results = results[:query.Limit] } // Complete execution statistics execution.EndTime = time.Now() execution.Duration = execution.EndTime.Sub(execution.StartTime) result := &HopQueryResult{ Query: query, Results: results, TotalFound: totalFound, ExecutionTime: execution.Duration, FromCache: false, QueryPath: queryPath, Statistics: execution, } return result, nil } func (qs *querySystemImpl) traverseDecisionGraph(startNode *TemporalNode, maxHops int, direction string) []*hopCandidate { candidates := make([]*hopCandidate, 0) visited := make(map[string]bool) // BFS traversal queue := []*hopCandidate{{ node: startNode, distance: 0, path: []*DecisionStep{}, }} for len(queue) > 0 { current := queue[0] queue = queue[1:] nodeID := current.node.ID if visited[nodeID] || current.distance > maxHops { continue } visited[nodeID] = true // Add to candidates (except start node) if current.distance > 0 { candidates = append(candidates, current) } // Add neighbors based on direction if direction == "forward" || direction == "both" { qs.addForwardNeighbors(current, &queue, maxHops) } if direction == "backward" || direction == "both" { qs.addBackwardNeighbors(current, &queue, maxHops) } } return candidates } func (qs *querySystemImpl) applyFilters(candidates []*hopCandidate, filter *HopFilter) []*hopCandidate { if filter == nil { return candidates } filtered := make([]*hopCandidate, 0) for _, candidate := range candidates { if qs.passesFilter(candidate, filter) { filtered = append(filtered, candidate) } } return filtered } func (qs *querySystemImpl) passesFilter(candidate *hopCandidate, filter *HopFilter) bool { node := candidate.node // Change reason filter if len(filter.ChangeReasons) > 0 { found := false for _, reason := range filter.ChangeReasons { if node.ChangeReason == reason { found = true break } } if !found { return false } } // Impact scope filter if len(filter.ImpactScopes) > 0 { found := false for _, scope := range filter.ImpactScopes { if node.ImpactScope == scope { found = true break } } if !found { return false } } // Confidence filter if filter.MinConfidence > 0 && node.Confidence < filter.MinConfidence { return false } // Age filter if filter.MaxAge > 0 && time.Since(node.Timestamp) > filter.MaxAge { return false } // Decision maker filter if len(filter.DecisionMakers) > 0 { if decision, exists := qs.graph.decisions[node.DecisionID]; exists { found := false for _, maker := range filter.DecisionMakers { if decision.Maker == maker { found = true break } } if !found { return false } } else { return false // No decision metadata } } // Technology filter if len(filter.Technologies) > 0 && node.Context != nil { found := false for _, filterTech := range filter.Technologies { for _, nodeTech := range node.Context.Technologies { if nodeTech == filterTech { found = true break } } if found { break } } if !found { return false } } // Tag filter if len(filter.Tags) > 0 && node.Context != nil { found := false for _, filterTag := range filter.Tags { for _, nodeTag := range node.Context.Tags { if nodeTag == filterTag { found = true break } } if found { break } } if !found { return false } } // Influence count filter if filter.MinInfluenceCount > 0 && len(node.Influences) < filter.MinInfluenceCount { return false } // Staleness filter if filter.ExcludeStale && node.Staleness > 0.6 { return false } // Major decisions filter if filter.OnlyMajorDecisions && !qs.isMajorDecision(node) { return false } return true } func (qs *querySystemImpl) calculateRelevanceScores(candidates []*hopCandidate, startNode *TemporalNode, query *HopQuery) []*HopResult { results := make([]*HopResult, len(candidates)) for i, candidate := range candidates { relevanceScore := qs.calculateRelevance(candidate, startNode, query) matchReasons := qs.getMatchReasons(candidate, query.FilterCriteria) results[i] = &HopResult{ Address: candidate.node.UCXLAddress, HopDistance: candidate.distance, TemporalNode: candidate.node, Path: candidate.path, Relationship: qs.determineRelationship(candidate, startNode), RelevanceScore: relevanceScore, MatchReasons: matchReasons, Metadata: qs.buildMetadata(candidate, query.IncludeMetadata), } } return results } func (qs *querySystemImpl) calculateRelevance(candidate *hopCandidate, startNode *TemporalNode, query *HopQuery) float64 { score := 1.0 // Distance-based relevance (closer = more relevant) distanceScore := 1.0 - (float64(candidate.distance-1) / float64(query.MaxHops)) score *= distanceScore // Confidence-based relevance confidenceScore := candidate.node.Confidence score *= confidenceScore // Recency-based relevance age := time.Since(candidate.node.Timestamp) recencyScore := math.Max(0.1, 1.0-age.Hours()/(30*24)) // Decay over 30 days score *= recencyScore // Impact-based relevance var impactScore float64 switch candidate.node.ImpactScope { case ImpactSystem: impactScore = 1.0 case ImpactProject: impactScore = 0.8 case ImpactModule: impactScore = 0.6 case ImpactLocal: impactScore = 0.4 } score *= impactScore return math.Min(1.0, score) } func (qs *querySystemImpl) sortResults(results []*HopResult, sortCriteria *HopSort) { sort.Slice(results, func(i, j int) bool { var aVal, bVal float64 switch sortCriteria.SortBy { case "hops": aVal, bVal = float64(results[i].HopDistance), float64(results[j].HopDistance) case "time": aVal, bVal = float64(results[i].TemporalNode.Timestamp.Unix()), float64(results[j].TemporalNode.Timestamp.Unix()) case "confidence": aVal, bVal = results[i].TemporalNode.Confidence, results[j].TemporalNode.Confidence case "influence": aVal, bVal = float64(len(results[i].TemporalNode.Influences)), float64(len(results[j].TemporalNode.Influences)) case "relevance": aVal, bVal = results[i].RelevanceScore, results[j].RelevanceScore default: aVal, bVal = results[i].RelevanceScore, results[j].RelevanceScore } if sortCriteria.SortDirection == "desc" { return aVal > bVal } return aVal < bVal }) } // Helper methods and types type hopCandidate struct { node *TemporalNode distance int path []*DecisionStep } func (qs *querySystemImpl) addForwardNeighbors(current *hopCandidate, queue *[]*hopCandidate, maxHops int) { if current.distance >= maxHops { return } nodeID := current.node.ID if influences, exists := qs.graph.influences[nodeID]; exists { for _, influencedID := range influences { if influencedNode, exists := qs.graph.nodes[influencedID]; exists { step := &DecisionStep{ Address: current.node.UCXLAddress, TemporalNode: current.node, HopDistance: current.distance, Relationship: "influences", } newPath := append(current.path, step) *queue = append(*queue, &hopCandidate{ node: influencedNode, distance: current.distance + 1, path: newPath, }) } } } } func (qs *querySystemImpl) addBackwardNeighbors(current *hopCandidate, queue *[]*hopCandidate, maxHops int) { if current.distance >= maxHops { return } nodeID := current.node.ID if influencedBy, exists := qs.graph.influencedBy[nodeID]; exists { for _, influencerID := range influencedBy { if influencerNode, exists := qs.graph.nodes[influencerID]; exists { step := &DecisionStep{ Address: current.node.UCXLAddress, TemporalNode: current.node, HopDistance: current.distance, Relationship: "influenced_by", } newPath := append(current.path, step) *queue = append(*queue, &hopCandidate{ node: influencerNode, distance: current.distance + 1, path: newPath, }) } } } } func (qs *querySystemImpl) isMajorDecision(node *TemporalNode) bool { return node.ChangeReason == ReasonArchitectureChange || node.ChangeReason == ReasonDesignDecision || node.ChangeReason == ReasonRequirementsChange || node.ImpactScope == ImpactSystem || node.ImpactScope == ImpactProject } func (qs *querySystemImpl) getMatchReasons(candidate *hopCandidate, filter *HopFilter) []string { reasons := make([]string, 0) if filter == nil { reasons = append(reasons, "no_filters_applied") return reasons } node := candidate.node if len(filter.ChangeReasons) > 0 { for _, reason := range filter.ChangeReasons { if node.ChangeReason == reason { reasons = append(reasons, fmt.Sprintf("change_reason: %s", reason)) } } } if len(filter.ImpactScopes) > 0 { for _, scope := range filter.ImpactScopes { if node.ImpactScope == scope { reasons = append(reasons, fmt.Sprintf("impact_scope: %s", scope)) } } } if filter.MinConfidence > 0 && node.Confidence >= filter.MinConfidence { reasons = append(reasons, fmt.Sprintf("confidence: %.2f >= %.2f", node.Confidence, filter.MinConfidence)) } if filter.MinInfluenceCount > 0 && len(node.Influences) >= filter.MinInfluenceCount { reasons = append(reasons, fmt.Sprintf("influence_count: %d >= %d", len(node.Influences), filter.MinInfluenceCount)) } return reasons } func (qs *querySystemImpl) determineRelationship(candidate *hopCandidate, startNode *TemporalNode) string { if len(candidate.path) == 0 { return "self" } // Look at the last step in the path lastStep := candidate.path[len(candidate.path)-1] return lastStep.Relationship } func (qs *querySystemImpl) buildMetadata(candidate *hopCandidate, includeDetailed bool) map[string]interface{} { metadata := make(map[string]interface{}) metadata["hop_distance"] = candidate.distance metadata["path_length"] = len(candidate.path) metadata["node_id"] = candidate.node.ID metadata["decision_id"] = candidate.node.DecisionID if includeDetailed { metadata["timestamp"] = candidate.node.Timestamp metadata["change_reason"] = candidate.node.ChangeReason metadata["impact_scope"] = candidate.node.ImpactScope metadata["confidence"] = candidate.node.Confidence metadata["staleness"] = candidate.node.Staleness metadata["influence_count"] = len(candidate.node.Influences) metadata["influenced_by_count"] = len(candidate.node.InfluencedBy) if candidate.node.Context != nil { metadata["context_summary"] = candidate.node.Context.Summary metadata["technologies"] = candidate.node.Context.Technologies metadata["tags"] = candidate.node.Context.Tags } if decision, exists := qs.graph.decisions[candidate.node.DecisionID]; exists { metadata["decision_maker"] = decision.Maker metadata["decision_rationale"] = decision.Rationale } } return metadata } // Query validation and caching func (qs *querySystemImpl) validateQuery(query *HopQuery) error { if err := query.StartAddress.Validate(); err != nil { return fmt.Errorf("invalid start address: %w", err) } if query.MaxHops < 1 || query.MaxHops > 20 { return fmt.Errorf("max hops must be between 1 and 20") } if query.Direction != "" && query.Direction != "forward" && query.Direction != "backward" && query.Direction != "both" { return fmt.Errorf("direction must be 'forward', 'backward', or 'both'") } if query.Limit < 0 { return fmt.Errorf("limit cannot be negative") } return nil } func (qs *querySystemImpl) generateCacheKey(query *HopQuery) string { return fmt.Sprintf("hop_query_%s_%d_%s_%v", query.StartAddress.String(), query.MaxHops, query.Direction, query.FilterCriteria != nil) } func (qs *querySystemImpl) getFromCache(key string) (interface{}, bool) { qs.mu.RLock() defer qs.mu.RUnlock() value, exists := qs.queryCache[key] return value, exists } func (qs *querySystemImpl) setCache(key string, value interface{}) { qs.mu.Lock() defer qs.mu.Unlock() // Clean cache if needed if time.Since(qs.lastCacheClean) > qs.cacheTimeout { qs.queryCache = make(map[string]interface{}) qs.lastCacheClean = time.Now() } qs.queryCache[key] = value } func (qs *querySystemImpl) updateQueryStats(queryType string, duration time.Duration, cacheHit bool) { qs.mu.Lock() defer qs.mu.Unlock() stats, exists := qs.queryStats[queryType] if !exists { stats = &QueryStatistics{QueryType: queryType} qs.queryStats[queryType] = stats } stats.TotalQueries++ stats.LastQuery = time.Now() // Update average time if stats.AverageTime == 0 { stats.AverageTime = duration } else { stats.AverageTime = (stats.AverageTime + duration) / 2 } if cacheHit { stats.CacheHits++ } else { stats.CacheMisses++ } } // Additional analysis methods would continue... // This includes genealogy analysis, similarity matching, clustering, etc. // The implementation is getting quite long, so I'll include key supporting types: // DecisionGenealogy represents the genealogy of decisions for a context type DecisionGenealogy struct { Address ucxl.Address `json:"address"` DirectAncestors []ucxl.Address `json:"direct_ancestors"` AllAncestors []ucxl.Address `json:"all_ancestors"` DirectDescendants []ucxl.Address `json:"direct_descendants"` AllDescendants []ucxl.Address `json:"all_descendants"` InfluentialAncestors []*InfluentialAncestor `json:"influential_ancestors"` GenealogyDepth int `json:"genealogy_depth"` BranchingFactor float64 `json:"branching_factor"` DecisionTimeline *DecisionTimeline `json:"decision_timeline"` Metrics *GenealogyMetrics `json:"metrics"` AnalyzedAt time.Time `json:"analyzed_at"` } // Additional supporting types for genealogy and similarity analysis... type InfluentialAncestor struct { Address ucxl.Address `json:"address"` InfluenceScore float64 `json:"influence_score"` GenerationsBack int `json:"generations_back"` InfluenceType string `json:"influence_type"` } type GenealogyMetrics struct { TotalAncestors int `json:"total_ancestors"` TotalDescendants int `json:"total_descendants"` MaxDepth int `json:"max_depth"` AverageBranching float64 `json:"average_branching"` InfluenceSpread float64 `json:"influence_spread"` } type SimilarDecisionMatch struct { Address ucxl.Address `json:"address"` TemporalNode *TemporalNode `json:"temporal_node"` SimilarityScore float64 `json:"similarity_score"` SimilarityReasons []string `json:"similarity_reasons"` PatternType string `json:"pattern_type"` Confidence float64 `json:"confidence"` } // Placeholder implementations for the analysis methods func (qs *querySystemImpl) analyzeAncestry(history []*TemporalNode) *ancestryAnalysis { // Implementation would trace back through parent nodes return &ancestryAnalysis{} } func (qs *querySystemImpl) analyzeDescendants(address ucxl.Address, maxDepth int) *descendantAnalysis { // Implementation would trace forward through influenced nodes return &descendantAnalysis{} } func (qs *querySystemImpl) findInfluentialAncestors(history []*TemporalNode) []*InfluentialAncestor { // Implementation would identify most influential historical decisions return make([]*InfluentialAncestor, 0) } func (qs *querySystemImpl) calculateGenealogyMetrics(history []*TemporalNode, descendants *descendantAnalysis) *GenealogyMetrics { // Implementation would calculate genealogy statistics return &GenealogyMetrics{} } func (qs *querySystemImpl) calculateDecisionSimilarity(node1, node2 *TemporalNode) float64 { // Implementation would compare decision patterns, technologies, etc. return 0.0 } func (qs *querySystemImpl) getSimilarityReasons(node1, node2 *TemporalNode) []string { // Implementation would identify why decisions are similar return make([]string, 0) } func (qs *querySystemImpl) identifyPatternType(node1, node2 *TemporalNode) string { // Implementation would classify the type of similarity pattern return "unknown" } func (qs *querySystemImpl) convertCommunityToCluster(community Community) *DecisionCluster { // Implementation would convert community to decision cluster return &DecisionCluster{ ID: community.ID, Decisions: community.Nodes, ClusterSize: len(community.Nodes), Cohesion: community.Modularity, } } // Supporting analysis types type ancestryAnalysis struct { DirectAncestors []ucxl.Address AllAncestors []ucxl.Address MaxDepth int } type descendantAnalysis struct { DirectDescendants []ucxl.Address AllDescendants []ucxl.Address BranchingFactor float64 }