chore: align slurp config and scaffolding
This commit is contained in:
@@ -13,36 +13,36 @@ import (
|
||||
// decisionNavigatorImpl implements the DecisionNavigator interface
|
||||
type decisionNavigatorImpl struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
|
||||
// Reference to the temporal graph
|
||||
graph *temporalGraphImpl
|
||||
|
||||
|
||||
// Navigation state
|
||||
navigationSessions map[string]*NavigationSession
|
||||
bookmarks map[string]*DecisionBookmark
|
||||
|
||||
|
||||
// Configuration
|
||||
maxNavigationHistory int
|
||||
}
|
||||
|
||||
// NavigationSession represents a navigation session
|
||||
type NavigationSession struct {
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
LastActivity time.Time `json:"last_activity"`
|
||||
CurrentPosition ucxl.Address `json:"current_position"`
|
||||
History []*DecisionStep `json:"history"`
|
||||
Bookmarks []string `json:"bookmarks"`
|
||||
Preferences *NavPreferences `json:"preferences"`
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
LastActivity time.Time `json:"last_activity"`
|
||||
CurrentPosition ucxl.Address `json:"current_position"`
|
||||
History []*DecisionStep `json:"history"`
|
||||
Bookmarks []string `json:"bookmarks"`
|
||||
Preferences *NavPreferences `json:"preferences"`
|
||||
}
|
||||
|
||||
// NavPreferences represents navigation preferences
|
||||
type NavPreferences struct {
|
||||
MaxHops int `json:"max_hops"`
|
||||
MaxHops int `json:"max_hops"`
|
||||
PreferRecentDecisions bool `json:"prefer_recent_decisions"`
|
||||
FilterByConfidence float64 `json:"filter_by_confidence"`
|
||||
IncludeStaleContexts bool `json:"include_stale_contexts"`
|
||||
FilterByConfidence float64 `json:"filter_by_confidence"`
|
||||
IncludeStaleContexts bool `json:"include_stale_contexts"`
|
||||
}
|
||||
|
||||
// NewDecisionNavigator creates a new decision navigator
|
||||
@@ -50,24 +50,24 @@ func NewDecisionNavigator(graph *temporalGraphImpl) DecisionNavigator {
|
||||
return &decisionNavigatorImpl{
|
||||
graph: graph,
|
||||
navigationSessions: make(map[string]*NavigationSession),
|
||||
bookmarks: make(map[string]*DecisionBookmark),
|
||||
bookmarks: make(map[string]*DecisionBookmark),
|
||||
maxNavigationHistory: 100,
|
||||
}
|
||||
}
|
||||
|
||||
// NavigateDecisionHops navigates by decision distance, not time
|
||||
func (dn *decisionNavigatorImpl) NavigateDecisionHops(ctx context.Context, address ucxl.Address,
|
||||
func (dn *decisionNavigatorImpl) NavigateDecisionHops(ctx context.Context, address ucxl.Address,
|
||||
hops int, direction NavigationDirection) (*TemporalNode, error) {
|
||||
|
||||
|
||||
dn.mu.RLock()
|
||||
defer dn.mu.RUnlock()
|
||||
|
||||
|
||||
// Get starting node
|
||||
startNode, err := dn.graph.getLatestNodeUnsafe(address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get starting node: %w", err)
|
||||
}
|
||||
|
||||
|
||||
// Navigate by hops
|
||||
currentNode := startNode
|
||||
for i := 0; i < hops; i++ {
|
||||
@@ -77,23 +77,23 @@ func (dn *decisionNavigatorImpl) NavigateDecisionHops(ctx context.Context, addre
|
||||
}
|
||||
currentNode = nextNode
|
||||
}
|
||||
|
||||
|
||||
return currentNode, nil
|
||||
}
|
||||
|
||||
// GetDecisionTimeline gets timeline ordered by decision sequence
|
||||
func (dn *decisionNavigatorImpl) GetDecisionTimeline(ctx context.Context, address ucxl.Address,
|
||||
func (dn *decisionNavigatorImpl) GetDecisionTimeline(ctx context.Context, address ucxl.Address,
|
||||
includeRelated bool, maxHops int) (*DecisionTimeline, error) {
|
||||
|
||||
|
||||
dn.mu.RLock()
|
||||
defer dn.mu.RUnlock()
|
||||
|
||||
|
||||
// Get evolution history for the primary address
|
||||
history, err := dn.graph.GetEvolutionHistory(ctx, address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get evolution history: %w", err)
|
||||
}
|
||||
|
||||
|
||||
// Build decision timeline entries
|
||||
decisionSequence := make([]*DecisionTimelineEntry, len(history))
|
||||
for i, node := range history {
|
||||
@@ -112,7 +112,7 @@ func (dn *decisionNavigatorImpl) GetDecisionTimeline(ctx context.Context, addres
|
||||
}
|
||||
decisionSequence[i] = entry
|
||||
}
|
||||
|
||||
|
||||
// Get related decisions if requested
|
||||
relatedDecisions := make([]*RelatedDecision, 0)
|
||||
if includeRelated && maxHops > 0 {
|
||||
@@ -136,16 +136,16 @@ func (dn *decisionNavigatorImpl) GetDecisionTimeline(ctx context.Context, addres
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Calculate timeline analysis
|
||||
analysis := dn.analyzeTimeline(decisionSequence, relatedDecisions)
|
||||
|
||||
|
||||
// Calculate time span
|
||||
var timeSpan time.Duration
|
||||
if len(history) > 1 {
|
||||
timeSpan = history[len(history)-1].Timestamp.Sub(history[0].Timestamp)
|
||||
}
|
||||
|
||||
|
||||
timeline := &DecisionTimeline{
|
||||
PrimaryAddress: address,
|
||||
DecisionSequence: decisionSequence,
|
||||
@@ -154,7 +154,7 @@ func (dn *decisionNavigatorImpl) GetDecisionTimeline(ctx context.Context, addres
|
||||
TimeSpan: timeSpan,
|
||||
AnalysisMetadata: analysis,
|
||||
}
|
||||
|
||||
|
||||
return timeline, nil
|
||||
}
|
||||
|
||||
@@ -162,31 +162,31 @@ func (dn *decisionNavigatorImpl) GetDecisionTimeline(ctx context.Context, addres
|
||||
func (dn *decisionNavigatorImpl) FindStaleContexts(ctx context.Context, stalenessThreshold float64) ([]*StaleContext, error) {
|
||||
dn.mu.RLock()
|
||||
defer dn.mu.RUnlock()
|
||||
|
||||
|
||||
staleContexts := make([]*StaleContext, 0)
|
||||
|
||||
|
||||
// Check all nodes for staleness
|
||||
for _, node := range dn.graph.nodes {
|
||||
if node.Staleness >= stalenessThreshold {
|
||||
staleness := &StaleContext{
|
||||
UCXLAddress: node.UCXLAddress,
|
||||
TemporalNode: node,
|
||||
StalenessScore: node.Staleness,
|
||||
LastUpdated: node.Timestamp,
|
||||
Reasons: dn.getStalenessReasons(node),
|
||||
UCXLAddress: node.UCXLAddress,
|
||||
TemporalNode: node,
|
||||
StalenessScore: node.Staleness,
|
||||
LastUpdated: node.Timestamp,
|
||||
Reasons: dn.getStalenessReasons(node),
|
||||
SuggestedActions: dn.getSuggestedActions(node),
|
||||
RelatedChanges: dn.getRelatedChanges(node),
|
||||
Priority: dn.calculateStalePriority(node),
|
||||
RelatedChanges: dn.getRelatedChanges(node),
|
||||
Priority: dn.calculateStalePriority(node),
|
||||
}
|
||||
staleContexts = append(staleContexts, staleness)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Sort by staleness score (highest first)
|
||||
sort.Slice(staleContexts, func(i, j int) bool {
|
||||
return staleContexts[i].StalenessScore > staleContexts[j].StalenessScore
|
||||
})
|
||||
|
||||
|
||||
return staleContexts, nil
|
||||
}
|
||||
|
||||
@@ -195,28 +195,28 @@ func (dn *decisionNavigatorImpl) ValidateDecisionPath(ctx context.Context, path
|
||||
if len(path) == 0 {
|
||||
return fmt.Errorf("empty decision path")
|
||||
}
|
||||
|
||||
|
||||
dn.mu.RLock()
|
||||
defer dn.mu.RUnlock()
|
||||
|
||||
|
||||
// Validate each step in the path
|
||||
for i, step := range path {
|
||||
// Check if the temporal node exists
|
||||
if step.TemporalNode == nil {
|
||||
return fmt.Errorf("step %d has nil temporal node", i)
|
||||
}
|
||||
|
||||
|
||||
nodeID := step.TemporalNode.ID
|
||||
if _, exists := dn.graph.nodes[nodeID]; !exists {
|
||||
return fmt.Errorf("step %d references non-existent node %s", i, nodeID)
|
||||
}
|
||||
|
||||
|
||||
// Validate hop distance
|
||||
if step.HopDistance != i {
|
||||
return fmt.Errorf("step %d has incorrect hop distance: expected %d, got %d",
|
||||
return fmt.Errorf("step %d has incorrect hop distance: expected %d, got %d",
|
||||
i, i, step.HopDistance)
|
||||
}
|
||||
|
||||
|
||||
// Validate relationship to next step
|
||||
if i < len(path)-1 {
|
||||
nextStep := path[i+1]
|
||||
@@ -225,7 +225,7 @@ func (dn *decisionNavigatorImpl) ValidateDecisionPath(ctx context.Context, path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -233,16 +233,16 @@ func (dn *decisionNavigatorImpl) ValidateDecisionPath(ctx context.Context, path
|
||||
func (dn *decisionNavigatorImpl) GetNavigationHistory(ctx context.Context, sessionID string) ([]*DecisionStep, error) {
|
||||
dn.mu.RLock()
|
||||
defer dn.mu.RUnlock()
|
||||
|
||||
|
||||
session, exists := dn.navigationSessions[sessionID]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("navigation session %s not found", sessionID)
|
||||
}
|
||||
|
||||
|
||||
// Return a copy of the history
|
||||
history := make([]*DecisionStep, len(session.History))
|
||||
copy(history, session.History)
|
||||
|
||||
|
||||
return history, nil
|
||||
}
|
||||
|
||||
@@ -250,22 +250,22 @@ func (dn *decisionNavigatorImpl) GetNavigationHistory(ctx context.Context, sessi
|
||||
func (dn *decisionNavigatorImpl) ResetNavigation(ctx context.Context, address ucxl.Address) error {
|
||||
dn.mu.Lock()
|
||||
defer dn.mu.Unlock()
|
||||
|
||||
|
||||
// Clear any navigation sessions for this address
|
||||
for sessionID, session := range dn.navigationSessions {
|
||||
for _, session := range dn.navigationSessions {
|
||||
if session.CurrentPosition.String() == address.String() {
|
||||
// Reset to latest version
|
||||
latestNode, err := dn.graph.getLatestNodeUnsafe(address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get latest node: %w", err)
|
||||
}
|
||||
|
||||
|
||||
session.CurrentPosition = address
|
||||
session.History = []*DecisionStep{}
|
||||
session.LastActivity = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -273,13 +273,13 @@ func (dn *decisionNavigatorImpl) ResetNavigation(ctx context.Context, address uc
|
||||
func (dn *decisionNavigatorImpl) BookmarkDecision(ctx context.Context, address ucxl.Address, hop int, name string) error {
|
||||
dn.mu.Lock()
|
||||
defer dn.mu.Unlock()
|
||||
|
||||
|
||||
// Validate the decision point exists
|
||||
node, err := dn.graph.GetVersionAtDecision(ctx, address, hop)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decision point not found: %w", err)
|
||||
}
|
||||
|
||||
|
||||
// Create bookmark
|
||||
bookmarkID := fmt.Sprintf("%s-%d-%d", address.String(), hop, time.Now().Unix())
|
||||
bookmark := &DecisionBookmark{
|
||||
@@ -293,14 +293,14 @@ func (dn *decisionNavigatorImpl) BookmarkDecision(ctx context.Context, address u
|
||||
Tags: []string{},
|
||||
Metadata: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
|
||||
// Add context information to metadata
|
||||
bookmark.Metadata["change_reason"] = node.ChangeReason
|
||||
bookmark.Metadata["decision_id"] = node.DecisionID
|
||||
bookmark.Metadata["confidence"] = node.Confidence
|
||||
|
||||
|
||||
dn.bookmarks[bookmarkID] = bookmark
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -308,17 +308,17 @@ func (dn *decisionNavigatorImpl) BookmarkDecision(ctx context.Context, address u
|
||||
func (dn *decisionNavigatorImpl) ListBookmarks(ctx context.Context) ([]*DecisionBookmark, error) {
|
||||
dn.mu.RLock()
|
||||
defer dn.mu.RUnlock()
|
||||
|
||||
|
||||
bookmarks := make([]*DecisionBookmark, 0, len(dn.bookmarks))
|
||||
for _, bookmark := range dn.bookmarks {
|
||||
bookmarks = append(bookmarks, bookmark)
|
||||
}
|
||||
|
||||
|
||||
// Sort by creation time (newest first)
|
||||
sort.Slice(bookmarks, func(i, j int) bool {
|
||||
return bookmarks[i].CreatedAt.After(bookmarks[j].CreatedAt)
|
||||
})
|
||||
|
||||
|
||||
return bookmarks, nil
|
||||
}
|
||||
|
||||
@@ -342,14 +342,14 @@ func (dn *decisionNavigatorImpl) navigateForward(currentNode *TemporalNode) (*Te
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("no nodes found for address")
|
||||
}
|
||||
|
||||
|
||||
// Find current node in the list and get the next one
|
||||
for i, node := range nodes {
|
||||
if node.ID == currentNode.ID && i < len(nodes)-1 {
|
||||
return nodes[i+1], nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return nil, fmt.Errorf("no forward navigation possible")
|
||||
}
|
||||
|
||||
@@ -358,12 +358,12 @@ func (dn *decisionNavigatorImpl) navigateBackward(currentNode *TemporalNode) (*T
|
||||
if currentNode.ParentNode == nil {
|
||||
return nil, fmt.Errorf("no backward navigation possible: no parent node")
|
||||
}
|
||||
|
||||
|
||||
parentNode, exists := dn.graph.nodes[*currentNode.ParentNode]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("parent node not found: %s", *currentNode.ParentNode)
|
||||
}
|
||||
|
||||
|
||||
return parentNode, nil
|
||||
}
|
||||
|
||||
@@ -387,7 +387,7 @@ func (dn *decisionNavigatorImpl) analyzeTimeline(sequence []*DecisionTimelineEnt
|
||||
AnalyzedAt: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Calculate change velocity
|
||||
var changeVelocity float64
|
||||
if len(sequence) > 1 {
|
||||
@@ -398,27 +398,27 @@ func (dn *decisionNavigatorImpl) analyzeTimeline(sequence []*DecisionTimelineEnt
|
||||
changeVelocity = float64(len(sequence)-1) / duration.Hours()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Analyze confidence trend
|
||||
confidenceTrend := "stable"
|
||||
if len(sequence) > 1 {
|
||||
firstConfidence := sequence[0].ConfidenceEvolution
|
||||
lastConfidence := sequence[len(sequence)-1].ConfidenceEvolution
|
||||
diff := lastConfidence - firstConfidence
|
||||
|
||||
|
||||
if diff > 0.1 {
|
||||
confidenceTrend = "increasing"
|
||||
} else if diff < -0.1 {
|
||||
confidenceTrend = "decreasing"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Count change reasons
|
||||
reasonCounts := make(map[ChangeReason]int)
|
||||
for _, entry := range sequence {
|
||||
reasonCounts[entry.ChangeReason]++
|
||||
}
|
||||
|
||||
|
||||
// Find dominant reasons
|
||||
dominantReasons := make([]ChangeReason, 0)
|
||||
maxCount := 0
|
||||
@@ -430,19 +430,19 @@ func (dn *decisionNavigatorImpl) analyzeTimeline(sequence []*DecisionTimelineEnt
|
||||
dominantReasons = append(dominantReasons, reason)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Count decision makers
|
||||
makerCounts := make(map[string]int)
|
||||
for _, entry := range sequence {
|
||||
makerCounts[entry.DecisionMaker]++
|
||||
}
|
||||
|
||||
|
||||
// Count impact scope distribution
|
||||
scopeCounts := make(map[ImpactScope]int)
|
||||
for _, entry := range sequence {
|
||||
scopeCounts[entry.ImpactScope]++
|
||||
}
|
||||
|
||||
|
||||
return &TimelineAnalysis{
|
||||
ChangeVelocity: changeVelocity,
|
||||
ConfidenceTrend: confidenceTrend,
|
||||
@@ -456,47 +456,47 @@ func (dn *decisionNavigatorImpl) analyzeTimeline(sequence []*DecisionTimelineEnt
|
||||
|
||||
func (dn *decisionNavigatorImpl) getStalenessReasons(node *TemporalNode) []string {
|
||||
reasons := make([]string, 0)
|
||||
|
||||
|
||||
// Time-based staleness
|
||||
timeSinceUpdate := time.Since(node.Timestamp)
|
||||
if timeSinceUpdate > 7*24*time.Hour {
|
||||
reasons = append(reasons, "not updated in over a week")
|
||||
}
|
||||
|
||||
|
||||
// Influence-based staleness
|
||||
if len(node.InfluencedBy) > 0 {
|
||||
reasons = append(reasons, "influenced by other contexts that may have changed")
|
||||
}
|
||||
|
||||
|
||||
// Confidence-based staleness
|
||||
if node.Confidence < 0.7 {
|
||||
reasons = append(reasons, "low confidence score")
|
||||
}
|
||||
|
||||
|
||||
return reasons
|
||||
}
|
||||
|
||||
func (dn *decisionNavigatorImpl) getSuggestedActions(node *TemporalNode) []string {
|
||||
actions := make([]string, 0)
|
||||
|
||||
|
||||
actions = append(actions, "review context for accuracy")
|
||||
actions = append(actions, "check related decisions for impact")
|
||||
|
||||
|
||||
if node.Confidence < 0.7 {
|
||||
actions = append(actions, "improve context confidence through additional analysis")
|
||||
}
|
||||
|
||||
|
||||
if len(node.InfluencedBy) > 3 {
|
||||
actions = append(actions, "validate dependencies are still accurate")
|
||||
}
|
||||
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
func (dn *decisionNavigatorImpl) getRelatedChanges(node *TemporalNode) []ucxl.Address {
|
||||
// Find contexts that have changed recently and might affect this one
|
||||
relatedChanges := make([]ucxl.Address, 0)
|
||||
|
||||
|
||||
cutoff := time.Now().Add(-24 * time.Hour)
|
||||
for _, otherNode := range dn.graph.nodes {
|
||||
if otherNode.Timestamp.After(cutoff) && otherNode.ID != node.ID {
|
||||
@@ -509,18 +509,18 @@ func (dn *decisionNavigatorImpl) getRelatedChanges(node *TemporalNode) []ucxl.Ad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return relatedChanges
|
||||
}
|
||||
|
||||
func (dn *decisionNavigatorImpl) calculateStalePriority(node *TemporalNode) StalePriority {
|
||||
score := node.Staleness
|
||||
|
||||
|
||||
// Adjust based on influence
|
||||
if len(node.Influences) > 5 {
|
||||
score += 0.2 // Higher priority if it influences many others
|
||||
}
|
||||
|
||||
|
||||
// Adjust based on impact scope
|
||||
switch node.ImpactScope {
|
||||
case ImpactSystem:
|
||||
@@ -530,7 +530,7 @@ func (dn *decisionNavigatorImpl) calculateStalePriority(node *TemporalNode) Stal
|
||||
case ImpactModule:
|
||||
score += 0.1
|
||||
}
|
||||
|
||||
|
||||
if score >= 0.9 {
|
||||
return PriorityCritical
|
||||
} else if score >= 0.7 {
|
||||
@@ -545,7 +545,7 @@ func (dn *decisionNavigatorImpl) validateStepRelationship(step, nextStep *Decisi
|
||||
// Check if there's a valid relationship between the steps
|
||||
currentNodeID := step.TemporalNode.ID
|
||||
nextNodeID := nextStep.TemporalNode.ID
|
||||
|
||||
|
||||
switch step.Relationship {
|
||||
case "influences":
|
||||
if influences, exists := dn.graph.influences[currentNodeID]; exists {
|
||||
@@ -564,6 +564,6 @@ func (dn *decisionNavigatorImpl) validateStepRelationship(step, nextStep *Decisi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user