Align SLURP access control with config authority levels
This commit is contained in:
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"chorus/pkg/ucxl"
|
||||
"chorus/pkg/config"
|
||||
"chorus/pkg/ucxl"
|
||||
)
|
||||
|
||||
// ContextNode represents a hierarchical context node in the SLURP system.
|
||||
@@ -19,25 +19,25 @@ type ContextNode struct {
|
||||
UCXLAddress ucxl.Address `json:"ucxl_address"` // Associated UCXL address
|
||||
Summary string `json:"summary"` // Brief description
|
||||
Purpose string `json:"purpose"` // What this component does
|
||||
|
||||
|
||||
// Context metadata
|
||||
Technologies []string `json:"technologies"` // Technologies used
|
||||
Tags []string `json:"tags"` // Categorization tags
|
||||
Insights []string `json:"insights"` // Analytical insights
|
||||
|
||||
|
||||
// Hierarchy control
|
||||
OverridesParent bool `json:"overrides_parent"` // Whether this overrides parent context
|
||||
ContextSpecificity int `json:"context_specificity"` // Specificity level (higher = more specific)
|
||||
AppliesToChildren bool `json:"applies_to_children"` // Whether this applies to child directories
|
||||
|
||||
OverridesParent bool `json:"overrides_parent"` // Whether this overrides parent context
|
||||
ContextSpecificity int `json:"context_specificity"` // Specificity level (higher = more specific)
|
||||
AppliesToChildren bool `json:"applies_to_children"` // Whether this applies to child directories
|
||||
|
||||
// Metadata
|
||||
GeneratedAt time.Time `json:"generated_at"` // When context was generated
|
||||
RAGConfidence float64 `json:"rag_confidence"` // RAG system confidence (0-1)
|
||||
|
||||
|
||||
// Access control
|
||||
EncryptedFor []string `json:"encrypted_for"` // Roles that can access
|
||||
AccessLevel RoleAccessLevel `json:"access_level"` // Required access level
|
||||
|
||||
EncryptedFor []string `json:"encrypted_for"` // Roles that can access
|
||||
AccessLevel RoleAccessLevel `json:"access_level"` // Required access level
|
||||
|
||||
// Custom metadata
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"` // Additional metadata
|
||||
}
|
||||
@@ -47,11 +47,11 @@ type ContextNode struct {
|
||||
type RoleAccessLevel int
|
||||
|
||||
const (
|
||||
AccessPublic RoleAccessLevel = iota // Anyone can access
|
||||
AccessLow // Basic role access
|
||||
AccessMedium // Coordination role access
|
||||
AccessHigh // Decision role access
|
||||
AccessCritical // Master role access only
|
||||
AccessPublic RoleAccessLevel = iota // Anyone can access
|
||||
AccessLow // Basic role access
|
||||
AccessMedium // Coordination role access
|
||||
AccessHigh // Decision role access
|
||||
AccessCritical // Master role access only
|
||||
)
|
||||
|
||||
// EncryptedContext represents role-encrypted context data for DHT storage
|
||||
@@ -75,26 +75,26 @@ type ResolvedContext struct {
|
||||
Technologies []string `json:"technologies"` // Merged technologies
|
||||
Tags []string `json:"tags"` // Merged tags
|
||||
Insights []string `json:"insights"` // Merged insights
|
||||
|
||||
|
||||
// Resolution metadata
|
||||
ContextSourcePath string `json:"context_source_path"` // Primary source context path
|
||||
InheritanceChain []string `json:"inheritance_chain"` // Context inheritance chain
|
||||
ResolutionConfidence float64 `json:"resolution_confidence"` // Overall confidence (0-1)
|
||||
BoundedDepth int `json:"bounded_depth"` // Actual traversal depth used
|
||||
GlobalContextsApplied bool `json:"global_contexts_applied"` // Whether global contexts were applied
|
||||
ResolvedAt time.Time `json:"resolved_at"` // When resolution occurred
|
||||
ContextSourcePath string `json:"context_source_path"` // Primary source context path
|
||||
InheritanceChain []string `json:"inheritance_chain"` // Context inheritance chain
|
||||
ResolutionConfidence float64 `json:"resolution_confidence"` // Overall confidence (0-1)
|
||||
BoundedDepth int `json:"bounded_depth"` // Actual traversal depth used
|
||||
GlobalContextsApplied bool `json:"global_contexts_applied"` // Whether global contexts were applied
|
||||
ResolvedAt time.Time `json:"resolved_at"` // When resolution occurred
|
||||
}
|
||||
|
||||
// ResolutionStatistics represents statistics about context resolution operations
|
||||
type ResolutionStatistics struct {
|
||||
ContextNodes int `json:"context_nodes"` // Total context nodes in hierarchy
|
||||
GlobalContexts int `json:"global_contexts"` // Number of global contexts
|
||||
MaxHierarchyDepth int `json:"max_hierarchy_depth"` // Maximum hierarchy depth allowed
|
||||
CachedResolutions int `json:"cached_resolutions"` // Number of cached resolutions
|
||||
TotalResolutions int `json:"total_resolutions"` // Total resolution operations
|
||||
AverageDepth float64 `json:"average_depth"` // Average traversal depth
|
||||
CacheHitRate float64 `json:"cache_hit_rate"` // Cache hit rate (0-1)
|
||||
LastResetAt time.Time `json:"last_reset_at"` // When stats were last reset
|
||||
ContextNodes int `json:"context_nodes"` // Total context nodes in hierarchy
|
||||
GlobalContexts int `json:"global_contexts"` // Number of global contexts
|
||||
MaxHierarchyDepth int `json:"max_hierarchy_depth"` // Maximum hierarchy depth allowed
|
||||
CachedResolutions int `json:"cached_resolutions"` // Number of cached resolutions
|
||||
TotalResolutions int `json:"total_resolutions"` // Total resolution operations
|
||||
AverageDepth float64 `json:"average_depth"` // Average traversal depth
|
||||
CacheHitRate float64 `json:"cache_hit_rate"` // Cache hit rate (0-1)
|
||||
LastResetAt time.Time `json:"last_reset_at"` // When stats were last reset
|
||||
}
|
||||
|
||||
// ContextScope defines the scope of a context node's application
|
||||
@@ -108,25 +108,25 @@ const (
|
||||
|
||||
// HierarchyStats represents statistics about hierarchy operations
|
||||
type HierarchyStats struct {
|
||||
NodesCreated int `json:"nodes_created"` // Number of nodes created
|
||||
NodesUpdated int `json:"nodes_updated"` // Number of nodes updated
|
||||
FilesAnalyzed int `json:"files_analyzed"` // Number of files analyzed
|
||||
DirectoriesScanned int `json:"directories_scanned"` // Number of directories scanned
|
||||
GenerationTime time.Duration `json:"generation_time"` // Time taken for generation
|
||||
AverageConfidence float64 `json:"average_confidence"` // Average confidence score
|
||||
TotalSize int64 `json:"total_size"` // Total size of analyzed content
|
||||
SkippedFiles int `json:"skipped_files"` // Number of files skipped
|
||||
Errors []string `json:"errors"` // Generation errors
|
||||
NodesCreated int `json:"nodes_created"` // Number of nodes created
|
||||
NodesUpdated int `json:"nodes_updated"` // Number of nodes updated
|
||||
FilesAnalyzed int `json:"files_analyzed"` // Number of files analyzed
|
||||
DirectoriesScanned int `json:"directories_scanned"` // Number of directories scanned
|
||||
GenerationTime time.Duration `json:"generation_time"` // Time taken for generation
|
||||
AverageConfidence float64 `json:"average_confidence"` // Average confidence score
|
||||
TotalSize int64 `json:"total_size"` // Total size of analyzed content
|
||||
SkippedFiles int `json:"skipped_files"` // Number of files skipped
|
||||
Errors []string `json:"errors"` // Generation errors
|
||||
}
|
||||
|
||||
// CacheEntry represents a cached context resolution
|
||||
type CacheEntry struct {
|
||||
Key string `json:"key"` // Cache key
|
||||
ResolvedCtx *ResolvedContext `json:"resolved_ctx"` // Cached resolved context
|
||||
CreatedAt time.Time `json:"created_at"` // When cached
|
||||
ExpiresAt time.Time `json:"expires_at"` // When cache expires
|
||||
AccessCount int `json:"access_count"` // Number of times accessed
|
||||
LastAccessed time.Time `json:"last_accessed"` // When last accessed
|
||||
Key string `json:"key"` // Cache key
|
||||
ResolvedCtx *ResolvedContext `json:"resolved_ctx"` // Cached resolved context
|
||||
CreatedAt time.Time `json:"created_at"` // When cached
|
||||
ExpiresAt time.Time `json:"expires_at"` // When cache expires
|
||||
AccessCount int `json:"access_count"` // Number of times accessed
|
||||
LastAccessed time.Time `json:"last_accessed"` // When last accessed
|
||||
}
|
||||
|
||||
// ValidationResult represents the result of context validation
|
||||
@@ -149,13 +149,13 @@ type ValidationIssue struct {
|
||||
|
||||
// MergeOptions defines options for merging contexts during resolution
|
||||
type MergeOptions struct {
|
||||
PreferParent bool `json:"prefer_parent"` // Prefer parent values over child
|
||||
MergeTechnologies bool `json:"merge_technologies"` // Merge technology lists
|
||||
MergeTags bool `json:"merge_tags"` // Merge tag lists
|
||||
MergeInsights bool `json:"merge_insights"` // Merge insight lists
|
||||
ExcludedFields []string `json:"excluded_fields"` // Fields to exclude from merge
|
||||
WeightParentByDepth bool `json:"weight_parent_by_depth"` // Weight parent influence by depth
|
||||
MinConfidenceThreshold float64 `json:"min_confidence_threshold"` // Minimum confidence to include
|
||||
PreferParent bool `json:"prefer_parent"` // Prefer parent values over child
|
||||
MergeTechnologies bool `json:"merge_technologies"` // Merge technology lists
|
||||
MergeTags bool `json:"merge_tags"` // Merge tag lists
|
||||
MergeInsights bool `json:"merge_insights"` // Merge insight lists
|
||||
ExcludedFields []string `json:"excluded_fields"` // Fields to exclude from merge
|
||||
WeightParentByDepth bool `json:"weight_parent_by_depth"` // Weight parent influence by depth
|
||||
MinConfidenceThreshold float64 `json:"min_confidence_threshold"` // Minimum confidence to include
|
||||
}
|
||||
|
||||
// BatchResolutionRequest represents a batch resolution request
|
||||
@@ -178,12 +178,12 @@ type BatchResolutionResult struct {
|
||||
|
||||
// ContextError represents a context-related error with structured information
|
||||
type ContextError struct {
|
||||
Type string `json:"type"` // Error type (validation, resolution, access, etc.)
|
||||
Message string `json:"message"` // Human-readable error message
|
||||
Code string `json:"code"` // Machine-readable error code
|
||||
Address *ucxl.Address `json:"address"` // Related UCXL address if applicable
|
||||
Context map[string]string `json:"context"` // Additional context information
|
||||
Underlying error `json:"underlying"` // Underlying error if any
|
||||
Type string `json:"type"` // Error type (validation, resolution, access, etc.)
|
||||
Message string `json:"message"` // Human-readable error message
|
||||
Code string `json:"code"` // Machine-readable error code
|
||||
Address *ucxl.Address `json:"address"` // Related UCXL address if applicable
|
||||
Context map[string]string `json:"context"` // Additional context information
|
||||
Underlying error `json:"underlying"` // Underlying error if any
|
||||
}
|
||||
|
||||
func (e *ContextError) Error() string {
|
||||
@@ -199,34 +199,34 @@ func (e *ContextError) Unwrap() error {
|
||||
|
||||
// Common error types and codes
|
||||
const (
|
||||
ErrorTypeValidation = "validation"
|
||||
ErrorTypeResolution = "resolution"
|
||||
ErrorTypeAccess = "access"
|
||||
ErrorTypeStorage = "storage"
|
||||
ErrorTypeEncryption = "encryption"
|
||||
ErrorTypeDHT = "dht"
|
||||
ErrorTypeHierarchy = "hierarchy"
|
||||
ErrorTypeCache = "cache"
|
||||
ErrorTypeTemporalGraph = "temporal_graph"
|
||||
ErrorTypeIntelligence = "intelligence"
|
||||
ErrorTypeValidation = "validation"
|
||||
ErrorTypeResolution = "resolution"
|
||||
ErrorTypeAccess = "access"
|
||||
ErrorTypeStorage = "storage"
|
||||
ErrorTypeEncryption = "encryption"
|
||||
ErrorTypeDHT = "dht"
|
||||
ErrorTypeHierarchy = "hierarchy"
|
||||
ErrorTypeCache = "cache"
|
||||
ErrorTypeTemporalGraph = "temporal_graph"
|
||||
ErrorTypeIntelligence = "intelligence"
|
||||
)
|
||||
|
||||
const (
|
||||
ErrorCodeInvalidAddress = "invalid_address"
|
||||
ErrorCodeInvalidContext = "invalid_context"
|
||||
ErrorCodeInvalidRole = "invalid_role"
|
||||
ErrorCodeAccessDenied = "access_denied"
|
||||
ErrorCodeNotFound = "not_found"
|
||||
ErrorCodeDepthExceeded = "depth_exceeded"
|
||||
ErrorCodeCycleDetected = "cycle_detected"
|
||||
ErrorCodeEncryptionFailed = "encryption_failed"
|
||||
ErrorCodeDecryptionFailed = "decryption_failed"
|
||||
ErrorCodeDHTError = "dht_error"
|
||||
ErrorCodeCacheError = "cache_error"
|
||||
ErrorCodeStorageError = "storage_error"
|
||||
ErrorCodeInvalidConfig = "invalid_config"
|
||||
ErrorCodeTimeout = "timeout"
|
||||
ErrorCodeInternalError = "internal_error"
|
||||
ErrorCodeInvalidAddress = "invalid_address"
|
||||
ErrorCodeInvalidContext = "invalid_context"
|
||||
ErrorCodeInvalidRole = "invalid_role"
|
||||
ErrorCodeAccessDenied = "access_denied"
|
||||
ErrorCodeNotFound = "not_found"
|
||||
ErrorCodeDepthExceeded = "depth_exceeded"
|
||||
ErrorCodeCycleDetected = "cycle_detected"
|
||||
ErrorCodeEncryptionFailed = "encryption_failed"
|
||||
ErrorCodeDecryptionFailed = "decryption_failed"
|
||||
ErrorCodeDHTError = "dht_error"
|
||||
ErrorCodeCacheError = "cache_error"
|
||||
ErrorCodeStorageError = "storage_error"
|
||||
ErrorCodeInvalidConfig = "invalid_config"
|
||||
ErrorCodeTimeout = "timeout"
|
||||
ErrorCodeInternalError = "internal_error"
|
||||
)
|
||||
|
||||
// NewContextError creates a new context error with structured information
|
||||
@@ -292,7 +292,7 @@ func ParseRoleAccessLevel(level string) (RoleAccessLevel, error) {
|
||||
case "critical":
|
||||
return AccessCritical, nil
|
||||
default:
|
||||
return AccessPublic, NewContextError(ErrorTypeValidation, ErrorCodeInvalidRole,
|
||||
return AccessPublic, NewContextError(ErrorTypeValidation, ErrorCodeInvalidRole,
|
||||
fmt.Sprintf("invalid role access level: %s", level))
|
||||
}
|
||||
}
|
||||
@@ -302,8 +302,12 @@ func AuthorityToAccessLevel(authority config.AuthorityLevel) RoleAccessLevel {
|
||||
switch authority {
|
||||
case config.AuthorityMaster:
|
||||
return AccessCritical
|
||||
case config.AuthorityAdmin:
|
||||
return AccessCritical
|
||||
case config.AuthorityDecision:
|
||||
return AccessHigh
|
||||
case config.AuthorityFull:
|
||||
return AccessHigh
|
||||
case config.AuthorityCoordination:
|
||||
return AccessMedium
|
||||
case config.AuthoritySuggestion:
|
||||
@@ -322,23 +326,23 @@ func (cn *ContextNode) Validate() error {
|
||||
}
|
||||
|
||||
if err := cn.UCXLAddress.Validate(); err != nil {
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidAddress,
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidAddress,
|
||||
"invalid UCXL address").WithUnderlying(err).WithAddress(cn.UCXLAddress)
|
||||
}
|
||||
|
||||
if cn.Summary == "" {
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
"context summary cannot be empty").WithAddress(cn.UCXLAddress)
|
||||
}
|
||||
|
||||
if cn.RAGConfidence < 0 || cn.RAGConfidence > 1 {
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
"RAG confidence must be between 0 and 1").WithAddress(cn.UCXLAddress).
|
||||
WithContext("confidence", fmt.Sprintf("%.2f", cn.RAGConfidence))
|
||||
}
|
||||
|
||||
if cn.ContextSpecificity < 0 {
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
"context specificity cannot be negative").WithAddress(cn.UCXLAddress).
|
||||
WithContext("specificity", fmt.Sprintf("%d", cn.ContextSpecificity))
|
||||
}
|
||||
@@ -346,7 +350,7 @@ func (cn *ContextNode) Validate() error {
|
||||
// Validate role access levels
|
||||
for _, role := range cn.EncryptedFor {
|
||||
if role == "" {
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidRole,
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidRole,
|
||||
"encrypted_for roles cannot be empty").WithAddress(cn.UCXLAddress)
|
||||
}
|
||||
}
|
||||
@@ -354,32 +358,32 @@ func (cn *ContextNode) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates a ResolvedContext for consistency and completeness
|
||||
// Validate validates a ResolvedContext for consistency and completeness
|
||||
func (rc *ResolvedContext) Validate() error {
|
||||
if err := rc.UCXLAddress.Validate(); err != nil {
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidAddress,
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidAddress,
|
||||
"invalid UCXL address in resolved context").WithUnderlying(err).WithAddress(rc.UCXLAddress)
|
||||
}
|
||||
|
||||
if rc.Summary == "" {
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
"resolved context summary cannot be empty").WithAddress(rc.UCXLAddress)
|
||||
}
|
||||
|
||||
if rc.ResolutionConfidence < 0 || rc.ResolutionConfidence > 1 {
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
"resolution confidence must be between 0 and 1").WithAddress(rc.UCXLAddress).
|
||||
WithContext("confidence", fmt.Sprintf("%.2f", rc.ResolutionConfidence))
|
||||
}
|
||||
|
||||
if rc.BoundedDepth < 0 {
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
"bounded depth cannot be negative").WithAddress(rc.UCXLAddress).
|
||||
WithContext("depth", fmt.Sprintf("%d", rc.BoundedDepth))
|
||||
}
|
||||
|
||||
if rc.ContextSourcePath == "" {
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
return NewContextError(ErrorTypeValidation, ErrorCodeInvalidContext,
|
||||
"context source path cannot be empty").WithAddress(rc.UCXLAddress)
|
||||
}
|
||||
|
||||
@@ -398,8 +402,8 @@ func (cn *ContextNode) HasRole(role string) bool {
|
||||
|
||||
// CanAccess checks if a role can access this context based on authority level
|
||||
func (cn *ContextNode) CanAccess(role string, authority config.AuthorityLevel) bool {
|
||||
// Master authority can access everything
|
||||
if authority == config.AuthorityMaster {
|
||||
// Master/Admin authority can access everything
|
||||
if authority == config.AuthorityMaster || authority == config.AuthorityAdmin {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -421,16 +425,16 @@ func (cn *ContextNode) Clone() *ContextNode {
|
||||
Summary: cn.Summary,
|
||||
Purpose: cn.Purpose,
|
||||
Technologies: make([]string, len(cn.Technologies)),
|
||||
Tags: make([]string, len(cn.Tags)),
|
||||
Insights: make([]string, len(cn.Insights)),
|
||||
OverridesParent: cn.OverridesParent,
|
||||
Tags: make([]string, len(cn.Tags)),
|
||||
Insights: make([]string, len(cn.Insights)),
|
||||
OverridesParent: cn.OverridesParent,
|
||||
ContextSpecificity: cn.ContextSpecificity,
|
||||
AppliesToChildren: cn.AppliesToChildren,
|
||||
GeneratedAt: cn.GeneratedAt,
|
||||
RAGConfidence: cn.RAGConfidence,
|
||||
EncryptedFor: make([]string, len(cn.EncryptedFor)),
|
||||
AccessLevel: cn.AccessLevel,
|
||||
Metadata: make(map[string]interface{}),
|
||||
AppliesToChildren: cn.AppliesToChildren,
|
||||
GeneratedAt: cn.GeneratedAt,
|
||||
RAGConfidence: cn.RAGConfidence,
|
||||
EncryptedFor: make([]string, len(cn.EncryptedFor)),
|
||||
AccessLevel: cn.AccessLevel,
|
||||
Metadata: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
copy(cloned.Technologies, cn.Technologies)
|
||||
@@ -448,18 +452,18 @@ func (cn *ContextNode) Clone() *ContextNode {
|
||||
// Clone creates a deep copy of the ResolvedContext
|
||||
func (rc *ResolvedContext) Clone() *ResolvedContext {
|
||||
cloned := &ResolvedContext{
|
||||
UCXLAddress: *rc.UCXLAddress.Clone(),
|
||||
Summary: rc.Summary,
|
||||
Purpose: rc.Purpose,
|
||||
Technologies: make([]string, len(rc.Technologies)),
|
||||
Tags: make([]string, len(rc.Tags)),
|
||||
Insights: make([]string, len(rc.Insights)),
|
||||
ContextSourcePath: rc.ContextSourcePath,
|
||||
InheritanceChain: make([]string, len(rc.InheritanceChain)),
|
||||
ResolutionConfidence: rc.ResolutionConfidence,
|
||||
BoundedDepth: rc.BoundedDepth,
|
||||
GlobalContextsApplied: rc.GlobalContextsApplied,
|
||||
ResolvedAt: rc.ResolvedAt,
|
||||
UCXLAddress: *rc.UCXLAddress.Clone(),
|
||||
Summary: rc.Summary,
|
||||
Purpose: rc.Purpose,
|
||||
Technologies: make([]string, len(rc.Technologies)),
|
||||
Tags: make([]string, len(rc.Tags)),
|
||||
Insights: make([]string, len(rc.Insights)),
|
||||
ContextSourcePath: rc.ContextSourcePath,
|
||||
InheritanceChain: make([]string, len(rc.InheritanceChain)),
|
||||
ResolutionConfidence: rc.ResolutionConfidence,
|
||||
BoundedDepth: rc.BoundedDepth,
|
||||
GlobalContextsApplied: rc.GlobalContextsApplied,
|
||||
ResolvedAt: rc.ResolvedAt,
|
||||
}
|
||||
|
||||
copy(cloned.Technologies, rc.Technologies)
|
||||
@@ -468,4 +472,4 @@ func (rc *ResolvedContext) Clone() *ResolvedContext {
|
||||
copy(cloned.InheritanceChain, rc.InheritanceChain)
|
||||
|
||||
return cloned
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user