 b207f32d9e
			
		
	
	b207f32d9e
	
	
	
		
			
			- Add complete UCXL address parser with BNF grammar validation - Implement temporal navigation system with bounds checking - Create UCXI HTTP server with REST-like operations - Add comprehensive test suite with 87 passing tests - Integrate with existing BZZZ architecture (opt-in via config) - Support semantic addressing with wildcards and version control Core Features: - UCXL address format: ucxl://agent:role@project:task/temporal/path - Temporal segments: *^, ~~N, ^^N, *~, *~N with navigation logic - UCXI endpoints: GET/PUT/POST/DELETE/ANNOUNCE operations - Production-ready with error handling and graceful shutdown 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			1194 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			1194 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # BZZZ v2 Implementation Roadmap: UCXL Integration
 | |
| 
 | |
| ## Phase 1: Foundation Implementation (Weeks 1-2)
 | |
| 
 | |
| ### Week 1: UCXL Address Parser Implementation
 | |
| 
 | |
| #### 1.1 Replace existing `pkg/protocol/uri.go` with UCXL support
 | |
| 
 | |
| **File:** `/pkg/protocol/ucxl_address.go`
 | |
| 
 | |
| ```go
 | |
| package protocol
 | |
| 
 | |
| import (
 | |
|     "fmt"
 | |
|     "net/url"
 | |
|     "regexp"
 | |
|     "strings"
 | |
|     "time"
 | |
| )
 | |
| 
 | |
| // UCXLAddress represents a parsed UCXL address with temporal navigation
 | |
| // Grammar: ucxl://agent:role@project:task/temporal_segment/path[?query][#fragment]
 | |
| type UCXLAddress struct {
 | |
|     // Core semantic addressing
 | |
|     Agent           string `json:"agent"`            // "gpt4", "claude", "any"
 | |
|     Role            string `json:"role"`             // "architect", "reviewer", "any"
 | |
|     Project         string `json:"project"`          // "bzzz", "chorus", "any"
 | |
|     Task            string `json:"task"`             // "v2-migration", "auth", "any"
 | |
|     
 | |
|     // Temporal navigation
 | |
|     TemporalSegment string `json:"temporal_segment"` // "~~", "^^", "*^", "*~", ISO8601
 | |
|     
 | |
|     // Resource path
 | |
|     Path            string `json:"path"`             // "/decisions/architecture.json"
 | |
|     
 | |
|     // Standard URI components
 | |
|     Query           string `json:"query,omitempty"`
 | |
|     Fragment        string `json:"fragment,omitempty"`
 | |
|     Raw             string `json:"raw"`
 | |
|     
 | |
|     // Resolved temporal information
 | |
|     ResolvedTime    *time.Time `json:"resolved_time,omitempty"`
 | |
| }
 | |
| 
 | |
| // Temporal navigation constants
 | |
| const (
 | |
|     UCXLScheme       = "ucxl"
 | |
|     TemporalBackward = "~~"  // Navigate backward in time
 | |
|     TemporalForward  = "^^"  // Navigate forward in time
 | |
|     TemporalLatest   = "*^"  // Latest entry
 | |
|     TemporalFirst    = "*~"  // First entry
 | |
|     AnyWildcard      = "any" // Wildcard for any component
 | |
| )
 | |
| 
 | |
| // Validation patterns for UCXL components
 | |
| var (
 | |
|     agentPattern     = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$|^any$`)
 | |
|     rolePattern      = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$|^any$`)
 | |
|     projectPattern   = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$|^any$`)
 | |
|     taskPattern      = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$|^any$`)
 | |
|     temporalPattern  = regexp.MustCompile(`^(~~|\^\^|\*\^|\*~|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*Z?)$`)
 | |
|     pathPattern      = regexp.MustCompile(`^(/[a-zA-Z0-9\-_/.]*)?$`)
 | |
| )
 | |
| 
 | |
| // ParseUCXLAddress parses a UCXL URI string
 | |
| func ParseUCXLAddress(uri string) (*UCXLAddress, error) {
 | |
|     if uri == "" {
 | |
|         return nil, fmt.Errorf("empty UCXL address")
 | |
|     }
 | |
|     
 | |
|     if !strings.HasPrefix(uri, UCXLScheme+"://") {
 | |
|         return nil, fmt.Errorf("invalid scheme: expected '%s'", UCXLScheme)
 | |
|     }
 | |
|     
 | |
|     // Parse using standard URL parser
 | |
|     parsedURL, err := url.Parse(uri)
 | |
|     if err != nil {
 | |
|         return nil, fmt.Errorf("failed to parse UCXL address: %w", err)
 | |
|     }
 | |
|     
 | |
|     // Extract agent:role from user info
 | |
|     userInfo := parsedURL.User
 | |
|     if userInfo == nil {
 | |
|         return nil, fmt.Errorf("missing agent:role information")
 | |
|     }
 | |
|     
 | |
|     agent := userInfo.Username()
 | |
|     role, hasRole := userInfo.Password()
 | |
|     if !hasRole {
 | |
|         return nil, fmt.Errorf("missing role information")
 | |
|     }
 | |
|     
 | |
|     // Extract project:task from host
 | |
|     hostParts := strings.Split(parsedURL.Host, ":")
 | |
|     if len(hostParts) != 2 {
 | |
|         return nil, fmt.Errorf("invalid project:task format")
 | |
|     }
 | |
|     
 | |
|     project := hostParts[0]
 | |
|     task := hostParts[1]
 | |
|     
 | |
|     // Parse temporal segment and path
 | |
|     pathParts := strings.SplitN(strings.TrimPrefix(parsedURL.Path, "/"), "/", 2)
 | |
|     temporalSegment := ""
 | |
|     resourcePath := ""
 | |
|     
 | |
|     if len(pathParts) > 0 && pathParts[0] != "" {
 | |
|         temporalSegment = pathParts[0]
 | |
|     }
 | |
|     if len(pathParts) > 1 {
 | |
|         resourcePath = "/" + pathParts[1]
 | |
|     }
 | |
|     
 | |
|     address := &UCXLAddress{
 | |
|         Agent:           agent,
 | |
|         Role:            role,
 | |
|         Project:         project,
 | |
|         Task:            task,
 | |
|         TemporalSegment: temporalSegment,
 | |
|         Path:            resourcePath,
 | |
|         Query:           parsedURL.RawQuery,
 | |
|         Fragment:        parsedURL.Fragment,
 | |
|         Raw:             uri,
 | |
|     }
 | |
|     
 | |
|     if err := address.Validate(); err != nil {
 | |
|         return nil, fmt.Errorf("validation failed: %w", err)
 | |
|     }
 | |
|     
 | |
|     return address, nil
 | |
| }
 | |
| 
 | |
| // Validate validates UCXL address components
 | |
| func (addr *UCXLAddress) Validate() error {
 | |
|     if !agentPattern.MatchString(addr.Agent) {
 | |
|         return fmt.Errorf("invalid agent: %s", addr.Agent)
 | |
|     }
 | |
|     if !rolePattern.MatchString(addr.Role) {
 | |
|         return fmt.Errorf("invalid role: %s", addr.Role)
 | |
|     }
 | |
|     if !projectPattern.MatchString(addr.Project) {
 | |
|         return fmt.Errorf("invalid project: %s", addr.Project)
 | |
|     }
 | |
|     if !taskPattern.MatchString(addr.Task) {
 | |
|         return fmt.Errorf("invalid task: %s", addr.Task)
 | |
|     }
 | |
|     if addr.TemporalSegment != "" && !temporalPattern.MatchString(addr.TemporalSegment) {
 | |
|         return fmt.Errorf("invalid temporal segment: %s", addr.TemporalSegment)
 | |
|     }
 | |
|     if !pathPattern.MatchString(addr.Path) {
 | |
|         return fmt.Errorf("invalid path: %s", addr.Path)
 | |
|     }
 | |
|     return nil
 | |
| }
 | |
| 
 | |
| // ResolveTemporalSegment resolves temporal navigation tokens to actual timestamps
 | |
| func (addr *UCXLAddress) ResolveTemporalSegment(navigator TemporalNavigator) error {
 | |
|     if addr.TemporalSegment == "" {
 | |
|         return nil
 | |
|     }
 | |
|     
 | |
|     switch addr.TemporalSegment {
 | |
|     case TemporalLatest:
 | |
|         timestamp, err := navigator.GetLatestTimestamp(addr)
 | |
|         if err != nil {
 | |
|             return err
 | |
|         }
 | |
|         addr.ResolvedTime = ×tamp
 | |
|     case TemporalFirst:
 | |
|         timestamp, err := navigator.GetFirstTimestamp(addr)
 | |
|         if err != nil {
 | |
|             return err
 | |
|         }
 | |
|         addr.ResolvedTime = ×tamp
 | |
|     case TemporalBackward, TemporalForward:
 | |
|         // These require context of current position
 | |
|         return fmt.Errorf("relative navigation requires current context")
 | |
|     default:
 | |
|         // Parse as ISO8601 timestamp
 | |
|         timestamp, err := time.Parse(time.RFC3339, addr.TemporalSegment)
 | |
|         if err != nil {
 | |
|             return fmt.Errorf("invalid timestamp format: %w", err)
 | |
|         }
 | |
|         addr.ResolvedTime = ×tamp
 | |
|     }
 | |
|     
 | |
|     return nil
 | |
| }
 | |
| 
 | |
| // Matches checks if this address matches another (with wildcard support)
 | |
| func (addr *UCXLAddress) Matches(other *UCXLAddress) bool {
 | |
|     return componentMatches(addr.Agent, other.Agent) &&
 | |
|            componentMatches(addr.Role, other.Role) &&
 | |
|            componentMatches(addr.Project, other.Project) &&
 | |
|            componentMatches(addr.Task, other.Task) &&
 | |
|            pathMatches(addr.Path, other.Path)
 | |
| }
 | |
| 
 | |
| func componentMatches(a, b string) bool {
 | |
|     return a == b || a == AnyWildcard || b == AnyWildcard
 | |
| }
 | |
| 
 | |
| func pathMatches(a, b string) bool {
 | |
|     if a == "" || b == "" {
 | |
|         return true
 | |
|     }
 | |
|     return a == b
 | |
| }
 | |
| 
 | |
| // String returns canonical string representation
 | |
| func (addr *UCXLAddress) String() string {
 | |
|     uri := fmt.Sprintf("%s://%s:%s@%s:%s", UCXLScheme, addr.Agent, addr.Role, addr.Project, addr.Task)
 | |
|     
 | |
|     if addr.TemporalSegment != "" {
 | |
|         uri += "/" + addr.TemporalSegment
 | |
|     }
 | |
|     
 | |
|     if addr.Path != "" {
 | |
|         uri += addr.Path
 | |
|     }
 | |
|     
 | |
|     if addr.Query != "" {
 | |
|         uri += "?" + addr.Query
 | |
|     }
 | |
|     
 | |
|     if addr.Fragment != "" {
 | |
|         uri += "#" + addr.Fragment
 | |
|     }
 | |
|     
 | |
|     return uri
 | |
| }
 | |
| 
 | |
| // ToStorageKey generates a storage key for this address
 | |
| func (addr *UCXLAddress) ToStorageKey() string {
 | |
|     key := fmt.Sprintf("%s/%s/%s/%s", addr.Agent, addr.Role, addr.Project, addr.Task)
 | |
|     
 | |
|     if addr.ResolvedTime != nil {
 | |
|         key += "/" + addr.ResolvedTime.Format(time.RFC3339)
 | |
|     } else if addr.TemporalSegment != "" {
 | |
|         key += "/" + addr.TemporalSegment
 | |
|     }
 | |
|     
 | |
|     if addr.Path != "" {
 | |
|         key += addr.Path
 | |
|     }
 | |
|     
 | |
|     return key
 | |
| }
 | |
| ```
 | |
| 
 | |
| #### 1.2 Temporal Navigator Interface
 | |
| 
 | |
| **File:** `/pkg/temporal/navigator.go`
 | |
| 
 | |
| ```go
 | |
| package temporal
 | |
| 
 | |
| import (
 | |
|     "time"
 | |
|     "github.com/anthonyrawlins/bzzz/pkg/protocol"
 | |
| )
 | |
| 
 | |
| type TemporalNavigator interface {
 | |
|     GetLatestTimestamp(addr *protocol.UCXLAddress) (time.Time, error)
 | |
|     GetFirstTimestamp(addr *protocol.UCXLAddress) (time.Time, error)
 | |
|     NavigateBackward(addr *protocol.UCXLAddress, steps int) (time.Time, error)
 | |
|     NavigateForward(addr *protocol.UCXLAddress, steps int) (time.Time, error)
 | |
|     GetAtTime(addr *protocol.UCXLAddress, timestamp time.Time) (*protocol.UCXLAddress, error)
 | |
| }
 | |
| 
 | |
| type TemporalIndex struct {
 | |
|     // Map address patterns to temporal entries
 | |
|     entries map[string][]TemporalEntry
 | |
| }
 | |
| 
 | |
| type TemporalEntry struct {
 | |
|     Timestamp time.Time                `json:"timestamp"`
 | |
|     Version   int64                    `json:"version"`
 | |
|     Address   *protocol.UCXLAddress    `json:"address"`
 | |
|     Checksum  string                   `json:"checksum"`
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Week 2: UCXI Interface Server
 | |
| 
 | |
| #### 2.1 UCXI Server Implementation
 | |
| 
 | |
| **File:** `/pkg/ucxi/server.go`
 | |
| 
 | |
| ```go
 | |
| package ucxi
 | |
| 
 | |
| import (
 | |
|     "encoding/json"
 | |
|     "net/http"
 | |
|     "github.com/gorilla/mux"
 | |
|     "github.com/anthonyrawlins/bzzz/pkg/protocol"
 | |
|     "github.com/anthonyrawlins/bzzz/pkg/storage"
 | |
|     "github.com/anthonyrawlins/bzzz/pkg/temporal"
 | |
| )
 | |
| 
 | |
| type UCXIServer struct {
 | |
|     contextStore    storage.ContextStore
 | |
|     temporalNav     temporal.TemporalNavigator
 | |
|     router          *mux.Router
 | |
|     port            int
 | |
| }
 | |
| 
 | |
| // Context entry structure
 | |
| type ContextEntry struct {
 | |
|     Address     *protocol.UCXLAddress  `json:"address"`
 | |
|     Content     map[string]interface{} `json:"content"`
 | |
|     Metadata    ContextMetadata        `json:"metadata"`
 | |
|     Version     int64                  `json:"version"`
 | |
|     Checksum    string                 `json:"checksum"`
 | |
|     CreatedAt   time.Time              `json:"created_at"`
 | |
|     UpdatedAt   time.Time              `json:"updated_at"`
 | |
| }
 | |
| 
 | |
| type ContextMetadata struct {
 | |
|     ContentType   string            `json:"content_type"`
 | |
|     Size          int64             `json:"size"`
 | |
|     Tags          []string          `json:"tags"`
 | |
|     Provenance    string            `json:"provenance"`
 | |
|     Relationships map[string]string `json:"relationships"`
 | |
| }
 | |
| 
 | |
| func NewUCXIServer(store storage.ContextStore, nav temporal.TemporalNavigator, port int) *UCXIServer {
 | |
|     server := &UCXIServer{
 | |
|         contextStore: store,
 | |
|         temporalNav:  nav,
 | |
|         router:       mux.NewRouter(),
 | |
|         port:         port,
 | |
|     }
 | |
|     server.setupRoutes()
 | |
|     return server
 | |
| }
 | |
| 
 | |
| func (s *UCXIServer) setupRoutes() {
 | |
|     // UCXI operations
 | |
|     s.router.HandleFunc("/ucxi/{agent}:{role}@{project}:{task}/{temporal}/{path:.*}", s.handleGET).Methods("GET")
 | |
|     s.router.HandleFunc("/ucxi/{agent}:{role}@{project}:{task}/{temporal}/{path:.*}", s.handlePUT).Methods("PUT")
 | |
|     s.router.HandleFunc("/ucxi/{agent}:{role}@{project}:{task}/{temporal}/", s.handlePOST).Methods("POST")
 | |
|     s.router.HandleFunc("/ucxi/{agent}:{role}@{project}:{task}/{temporal}/{path:.*}", s.handleDELETE).Methods("DELETE")
 | |
|     s.router.HandleFunc("/ucxi/announce", s.handleANNOUNCE).Methods("POST")
 | |
|     
 | |
|     // Extended operations
 | |
|     s.router.HandleFunc("/ucxi/navigate/{direction}", s.handleNAVIGATE).Methods("GET")
 | |
|     s.router.HandleFunc("/ucxi/query", s.handleQUERY).Methods("GET")
 | |
|     s.router.HandleFunc("/ucxi/subscribe", s.handleSUBSCRIBE).Methods("POST")
 | |
| }
 | |
| 
 | |
| func (s *UCXIServer) handleGET(w http.ResponseWriter, r *http.Request) {
 | |
|     vars := mux.Vars(r)
 | |
|     
 | |
|     // Construct UCXL address from URL parameters
 | |
|     address := &protocol.UCXLAddress{
 | |
|         Agent:           vars["agent"],
 | |
|         Role:            vars["role"],
 | |
|         Project:         vars["project"],
 | |
|         Task:            vars["task"],
 | |
|         TemporalSegment: vars["temporal"],
 | |
|         Path:            "/" + vars["path"],
 | |
|     }
 | |
|     
 | |
|     // Resolve temporal navigation
 | |
|     if err := address.ResolveTemporalSegment(s.temporalNav); err != nil {
 | |
|         http.Error(w, err.Error(), http.StatusBadRequest)
 | |
|         return
 | |
|     }
 | |
|     
 | |
|     // Retrieve context
 | |
|     entry, err := s.contextStore.Retrieve(address)
 | |
|     if err != nil {
 | |
|         http.Error(w, err.Error(), http.StatusNotFound)
 | |
|         return
 | |
|     }
 | |
|     
 | |
|     w.Header().Set("Content-Type", "application/json")
 | |
|     json.NewEncoder(w).Encode(entry)
 | |
| }
 | |
| 
 | |
| func (s *UCXIServer) handlePUT(w http.ResponseWriter, r *http.Request) {
 | |
|     vars := mux.Vars(r)
 | |
|     
 | |
|     var entry ContextEntry
 | |
|     if err := json.NewDecoder(r.Body).Decode(&entry); err != nil {
 | |
|         http.Error(w, err.Error(), http.StatusBadRequest)
 | |
|         return
 | |
|     }
 | |
|     
 | |
|     // Construct address
 | |
|     address := &protocol.UCXLAddress{
 | |
|         Agent:           vars["agent"],
 | |
|         Role:            vars["role"],
 | |
|         Project:         vars["project"],
 | |
|         Task:            vars["task"],
 | |
|         TemporalSegment: vars["temporal"],
 | |
|         Path:            "/" + vars["path"],
 | |
|     }
 | |
|     
 | |
|     entry.Address = address
 | |
|     entry.UpdatedAt = time.Now()
 | |
|     
 | |
|     if err := s.contextStore.Store(address, &entry); err != nil {
 | |
|         http.Error(w, err.Error(), http.StatusInternalServerError)
 | |
|         return
 | |
|     }
 | |
|     
 | |
|     w.WriteHeader(http.StatusCreated)
 | |
| }
 | |
| 
 | |
| // Additional handlers for POST, DELETE, ANNOUNCE, NAVIGATE, QUERY, SUBSCRIBE...
 | |
| ```
 | |
| 
 | |
| ## Phase 2: Decision Graph & SLURP Integration (Weeks 3-6)
 | |
| 
 | |
| ### Week 3-4: Decision Node Schema
 | |
| 
 | |
| #### 3.1 Decision Node Structure
 | |
| 
 | |
| **File:** `/pkg/decisions/schema.go`
 | |
| 
 | |
| ```go
 | |
| package decisions
 | |
| 
 | |
| import (
 | |
|     "time"
 | |
|     "github.com/anthonyrawlins/bzzz/pkg/protocol"
 | |
| )
 | |
| 
 | |
| // DecisionNode represents a structured decision for SLURP ingestion
 | |
| type DecisionNode struct {
 | |
|     DecisionID    string             `json:"decision_id"`
 | |
|     UCXLAddress   string             `json:"ucxl_address"`
 | |
|     Timestamp     time.Time          `json:"timestamp"`
 | |
|     AgentID       string             `json:"agent_id"`
 | |
|     DecisionType  string             `json:"decision_type"`
 | |
|     Context       DecisionContext    `json:"context"`
 | |
|     Justification Justification      `json:"justification"`
 | |
|     Citations     []Citation         `json:"citations"`
 | |
|     Impacts       []Impact           `json:"impacts"`
 | |
|     Metadata      DecisionMetadata   `json:"metadata"`
 | |
| }
 | |
| 
 | |
| type DecisionContext struct {
 | |
|     Project     string                 `json:"project"`
 | |
|     Task        string                 `json:"task"`
 | |
|     Scope       string                 `json:"scope"`
 | |
|     Phase       string                 `json:"phase"`
 | |
|     Environment string                 `json:"environment"`
 | |
|     Constraints map[string]interface{} `json:"constraints"`
 | |
| }
 | |
| 
 | |
| type Justification struct {
 | |
|     Reasoning              string                 `json:"reasoning"`
 | |
|     AlternativesConsidered []Alternative          `json:"alternatives_considered"`
 | |
|     Criteria               []string               `json:"criteria"`
 | |
|     Confidence             float64                `json:"confidence"`
 | |
|     RiskAssessment         RiskAssessment         `json:"risk_assessment"`
 | |
|     TradeOffs              map[string]interface{} `json:"trade_offs"`
 | |
| }
 | |
| 
 | |
| type Alternative struct {
 | |
|     Name        string                 `json:"name"`
 | |
|     Description string                 `json:"description"`
 | |
|     Pros        []string               `json:"pros"`
 | |
|     Cons        []string               `json:"cons"`
 | |
|     Score       float64                `json:"score"`
 | |
|     Rejected    bool                   `json:"rejected"`
 | |
|     Reason      string                 `json:"reason"`
 | |
|     Metadata    map[string]interface{} `json:"metadata"`
 | |
| }
 | |
| 
 | |
| type Citation struct {
 | |
|     Type         string  `json:"type"`         // "justified_by", "references", "contradicts", "extends"
 | |
|     UCXLAddress  string  `json:"ucxl_address"`
 | |
|     Relevance    string  `json:"relevance"`    // "high", "medium", "low"
 | |
|     Excerpt      string  `json:"excerpt"`
 | |
|     Strength     float64 `json:"strength"`     // 0.0 - 1.0
 | |
|     Verified     bool    `json:"verified"`
 | |
|     VerifiedAt   *time.Time `json:"verified_at,omitempty"`
 | |
| }
 | |
| 
 | |
| type Impact struct {
 | |
|     Type         string                 `json:"type"` // "replaces", "modifies", "creates", "deprecates"
 | |
|     UCXLAddress  string                 `json:"ucxl_address"`
 | |
|     Reason       string                 `json:"reason"`
 | |
|     Severity     string                 `json:"severity"` // "low", "medium", "high", "breaking"
 | |
|     Affected     []string               `json:"affected"`
 | |
|     Migration    *MigrationInfo         `json:"migration,omitempty"`
 | |
|     Metadata     map[string]interface{} `json:"metadata"`
 | |
| }
 | |
| 
 | |
| type MigrationInfo struct {
 | |
|     Required     bool   `json:"required"`
 | |
|     Automated    bool   `json:"automated"`
 | |
|     Instructions string `json:"instructions"`
 | |
|     Timeline     string `json:"timeline"`
 | |
| }
 | |
| 
 | |
| type RiskAssessment struct {
 | |
|     Level       string            `json:"level"`        // "low", "medium", "high"
 | |
|     Factors     []string          `json:"factors"`
 | |
|     Mitigation  []string          `json:"mitigation"`
 | |
|     Monitoring  []string          `json:"monitoring"`
 | |
|     Escalation  *EscalationInfo   `json:"escalation,omitempty"`
 | |
| }
 | |
| 
 | |
| type EscalationInfo struct {
 | |
|     Triggers    []string `json:"triggers"`
 | |
|     Contacts    []string `json:"contacts"`
 | |
|     Procedures  []string `json:"procedures"`
 | |
| }
 | |
| 
 | |
| type DecisionMetadata struct {
 | |
|     SourceRepository string            `json:"source_repository"`
 | |
|     CommitSHA        string            `json:"commit_sha"`
 | |
|     PullRequestID    string            `json:"pull_request_id"`
 | |
|     ReviewedBy       []string          `json:"reviewed_by"`
 | |
|     ApprovedBy       []string          `json:"approved_by"`
 | |
|     Tags             []string          `json:"tags"`
 | |
|     Priority         int               `json:"priority"`
 | |
|     Visibility       string            `json:"visibility"` // "public", "internal", "private"
 | |
|     ExpiresAt        *time.Time        `json:"expires_at,omitempty"`
 | |
|     CustomFields     map[string]interface{} `json:"custom_fields"`
 | |
| }
 | |
| 
 | |
| // Decision types enumeration
 | |
| const (
 | |
|     DecisionTypeArchitecture    = "architecture_choice"
 | |
|     DecisionTypeImplementation  = "implementation_approach"
 | |
|     DecisionTypeTechnology      = "technology_selection"
 | |
|     DecisionTypeProcess         = "process_definition"
 | |
|     DecisionTypeAPI             = "api_design"
 | |
|     DecisionTypeDataModel       = "data_model_design"
 | |
|     DecisionTypeSecurity        = "security_requirement"
 | |
|     DecisionTypeDeployment      = "deployment_strategy"
 | |
|     DecisionTypeRollback        = "rollback_decision"
 | |
|     DecisionTypeDeprecation     = "deprecation_notice"
 | |
| )
 | |
| 
 | |
| // Citation types enumeration
 | |
| const (
 | |
|     CitationJustifiedBy = "justified_by"
 | |
|     CitationReferences  = "references"
 | |
|     CitationContradicts = "contradicts"
 | |
|     CitationExtends     = "extends"
 | |
|     CitationSupersedes  = "supersedes"
 | |
| )
 | |
| 
 | |
| // Validation methods
 | |
| func (dn *DecisionNode) Validate() error {
 | |
|     if dn.DecisionID == "" {
 | |
|         return fmt.Errorf("decision_id is required")
 | |
|     }
 | |
|     
 | |
|     if dn.UCXLAddress == "" {
 | |
|         return fmt.Errorf("ucxl_address is required")
 | |
|     }
 | |
|     
 | |
|     if _, err := protocol.ParseUCXLAddress(dn.UCXLAddress); err != nil {
 | |
|         return fmt.Errorf("invalid ucxl_address: %w", err)
 | |
|     }
 | |
|     
 | |
|     if dn.AgentID == "" {
 | |
|         return fmt.Errorf("agent_id is required")
 | |
|     }
 | |
|     
 | |
|     if dn.DecisionType == "" {
 | |
|         return fmt.Errorf("decision_type is required")
 | |
|     }
 | |
|     
 | |
|     // Validate citations
 | |
|     for i, citation := range dn.Citations {
 | |
|         if citation.UCXLAddress == "" {
 | |
|             return fmt.Errorf("citation[%d] missing ucxl_address", i)
 | |
|         }
 | |
|         
 | |
|         if _, err := protocol.ParseUCXLAddress(citation.UCXLAddress); err != nil {
 | |
|             return fmt.Errorf("citation[%d] invalid ucxl_address: %w", i, err)
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     return nil
 | |
| }
 | |
| 
 | |
| func (dn *DecisionNode) GenerateID() string {
 | |
|     hash := sha256.Sum256([]byte(dn.UCXLAddress + dn.AgentID + dn.Timestamp.Format(time.RFC3339)))
 | |
|     return fmt.Sprintf("%x", hash)[:16]
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Week 5-6: SLURP Integration
 | |
| 
 | |
| #### 5.1 SLURP Client Implementation
 | |
| 
 | |
| **File:** `/pkg/integration/slurp_client.go`
 | |
| 
 | |
| ```go
 | |
| package integration
 | |
| 
 | |
| import (
 | |
|     "bytes"
 | |
|     "encoding/json"
 | |
|     "fmt"
 | |
|     "net/http"
 | |
|     "time"
 | |
|     "github.com/anthonyrawlins/bzzz/pkg/decisions"
 | |
| )
 | |
| 
 | |
| type SLURPClient struct {
 | |
|     baseURL    string
 | |
|     apiKey     string
 | |
|     httpClient *http.Client
 | |
|     batchSize  int
 | |
|     timeout    time.Duration
 | |
| }
 | |
| 
 | |
| type SLURPPublishRequest struct {
 | |
|     Decisions []decisions.DecisionNode `json:"decisions"`
 | |
|     Source    string                   `json:"source"`
 | |
|     Version   string                   `json:"version"`
 | |
|     Timestamp time.Time                `json:"timestamp"`
 | |
| }
 | |
| 
 | |
| type SLURPPublishResponse struct {
 | |
|     Accepted []string `json:"accepted"`
 | |
|     Rejected []string `json:"rejected"`
 | |
|     Errors   []string `json:"errors"`
 | |
| }
 | |
| 
 | |
| func NewSLURPClient(baseURL, apiKey string) *SLURPClient {
 | |
|     return &SLURPClient{
 | |
|         baseURL: baseURL,
 | |
|         apiKey:  apiKey,
 | |
|         httpClient: &http.Client{
 | |
|             Timeout: 30 * time.Second,
 | |
|         },
 | |
|         batchSize: 10,
 | |
|         timeout:   30 * time.Second,
 | |
|     }
 | |
| }
 | |
| 
 | |
| func (sc *SLURPClient) PublishDecisions(nodes []decisions.DecisionNode) (*SLURPPublishResponse, error) {
 | |
|     // Validate all decisions before publishing
 | |
|     for i, node := range nodes {
 | |
|         if err := node.Validate(); err != nil {
 | |
|             return nil, fmt.Errorf("decision[%d] validation failed: %w", i, err)
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     request := SLURPPublishRequest{
 | |
|         Decisions: nodes,
 | |
|         Source:    "bzzz-v2",
 | |
|         Version:   "2.0.0",
 | |
|         Timestamp: time.Now(),
 | |
|     }
 | |
|     
 | |
|     jsonData, err := json.Marshal(request)
 | |
|     if err != nil {
 | |
|         return nil, fmt.Errorf("failed to marshal request: %w", err)
 | |
|     }
 | |
|     
 | |
|     req, err := http.NewRequest("POST", sc.baseURL+"/api/v1/decisions", bytes.NewBuffer(jsonData))
 | |
|     if err != nil {
 | |
|         return nil, fmt.Errorf("failed to create request: %w", err)
 | |
|     }
 | |
|     
 | |
|     req.Header.Set("Content-Type", "application/json")
 | |
|     req.Header.Set("Authorization", "Bearer "+sc.apiKey)
 | |
|     req.Header.Set("User-Agent", "bzzz-v2/2.0.0")
 | |
|     
 | |
|     resp, err := sc.httpClient.Do(req)
 | |
|     if err != nil {
 | |
|         return nil, fmt.Errorf("failed to send request: %w", err)
 | |
|     }
 | |
|     defer resp.Body.Close()
 | |
|     
 | |
|     if resp.StatusCode != http.StatusOK {
 | |
|         return nil, fmt.Errorf("SLURP API error: %d", resp.StatusCode)
 | |
|     }
 | |
|     
 | |
|     var response SLURPPublishResponse
 | |
|     if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
 | |
|         return nil, fmt.Errorf("failed to decode response: %w", err)
 | |
|     }
 | |
|     
 | |
|     return &response, nil
 | |
| }
 | |
| 
 | |
| func (sc *SLURPClient) QueryDecisions(query string) ([]decisions.DecisionNode, error) {
 | |
|     req, err := http.NewRequest("GET", sc.baseURL+"/api/v1/decisions/query", nil)
 | |
|     if err != nil {
 | |
|         return nil, err
 | |
|     }
 | |
|     
 | |
|     q := req.URL.Query()
 | |
|     q.Add("q", query)
 | |
|     req.URL.RawQuery = q.Encode()
 | |
|     
 | |
|     req.Header.Set("Authorization", "Bearer "+sc.apiKey)
 | |
|     
 | |
|     resp, err := sc.httpClient.Do(req)
 | |
|     if err != nil {
 | |
|         return nil, err
 | |
|     }
 | |
|     defer resp.Body.Close()
 | |
|     
 | |
|     var nodes []decisions.DecisionNode
 | |
|     if err := json.NewDecoder(resp.Body).Decode(&nodes); err != nil {
 | |
|         return nil, err
 | |
|     }
 | |
|     
 | |
|     return nodes, nil
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Phase 3: Agent Integration & Testing (Weeks 7-8)
 | |
| 
 | |
| ### Week 7: GPT-4 Agent UCXL Integration
 | |
| 
 | |
| #### 7.1 Updated MCP Tools
 | |
| 
 | |
| **File:** `/mcp-server/src/tools/ucxi-tools.ts`
 | |
| 
 | |
| ```typescript
 | |
| import { Tool } from "@modelcontextprotocol/sdk/types.js";
 | |
| 
 | |
| export const ucxiTools: Record<string, Tool> = {
 | |
|   ucxi_get: {
 | |
|     name: "ucxi_get",
 | |
|     description: "Retrieve context from UCXL address with temporal navigation",
 | |
|     inputSchema: {
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         address: {
 | |
|           type: "string",
 | |
|           description: "UCXL address (e.g., ucxl://gpt4:architect@bzzz:v2/*^/decisions.json)"
 | |
|         },
 | |
|         temporal: {
 | |
|           type: "string",
 | |
|           enum: ["~~", "^^", "*^", "*~"],
 | |
|           description: "Temporal navigation: ~~ (back), ^^ (forward), *^ (latest), *~ (first)"
 | |
|         }
 | |
|       },
 | |
|       required: ["address"]
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   ucxi_put: {
 | |
|     name: "ucxi_put",
 | |
|     description: "Store context at UCXL address",
 | |
|     inputSchema: {
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         address: {
 | |
|           type: "string",
 | |
|           description: "UCXL address where to store context"
 | |
|         },
 | |
|         content: {
 | |
|           type: "object",
 | |
|           description: "Context content to store"
 | |
|         },
 | |
|         metadata: {
 | |
|           type: "object",
 | |
|           properties: {
 | |
|             content_type: { type: "string" },
 | |
|             tags: { type: "array", items: { type: "string" } },
 | |
|             provenance: { type: "string" }
 | |
|           },
 | |
|           description: "Context metadata"
 | |
|         }
 | |
|       },
 | |
|       required: ["address", "content"]
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   ucxi_publish_decision: {
 | |
|     name: "ucxi_publish_decision",
 | |
|     description: "Publish a structured decision to SLURP via UCXI",
 | |
|     inputSchema: {
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         decision_type: {
 | |
|           type: "string",
 | |
|           enum: ["architecture_choice", "implementation_approach", "technology_selection", "api_design"],
 | |
|           description: "Type of decision being published"
 | |
|         },
 | |
|         reasoning: {
 | |
|           type: "string",
 | |
|           description: "Detailed reasoning for the decision"
 | |
|         },
 | |
|         alternatives: {
 | |
|           type: "array",
 | |
|           items: {
 | |
|             type: "object",
 | |
|             properties: {
 | |
|               name: { type: "string" },
 | |
|               description: { type: "string" },
 | |
|               pros: { type: "array", items: { type: "string" } },
 | |
|               cons: { type: "array", items: { type: "string" } },
 | |
|               rejected: { type: "boolean" },
 | |
|               reason: { type: "string" }
 | |
|             }
 | |
|           },
 | |
|           description: "Alternative approaches considered"
 | |
|         },
 | |
|         citations: {
 | |
|           type: "array",
 | |
|           items: {
 | |
|             type: "object",
 | |
|             properties: {
 | |
|               type: { type: "string", enum: ["justified_by", "references", "contradicts"] },
 | |
|               ucxl_address: { type: "string" },
 | |
|               relevance: { type: "string", enum: ["high", "medium", "low"] },
 | |
|               excerpt: { type: "string" }
 | |
|             }
 | |
|           },
 | |
|           description: "Citations supporting the decision"
 | |
|         },
 | |
|         impacts: {
 | |
|           type: "array",
 | |
|           items: {
 | |
|             type: "object",
 | |
|             properties: {
 | |
|               type: { type: "string", enum: ["replaces", "modifies", "creates", "deprecates"] },
 | |
|               ucxl_address: { type: "string" },
 | |
|               reason: { type: "string" },
 | |
|               severity: { type: "string", enum: ["low", "medium", "high", "breaking"] }
 | |
|             }
 | |
|           },
 | |
|           description: "Impacts of this decision"
 | |
|         }
 | |
|       },
 | |
|       required: ["decision_type", "reasoning"]
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   ucxi_query: {
 | |
|     name: "ucxi_query",
 | |
|     description: "Query contexts matching UCXL address pattern",
 | |
|     inputSchema: {
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         pattern: {
 | |
|           type: "string",
 | |
|           description: "UCXL address pattern with wildcards (e.g., any:architect@bzzz:*)"
 | |
|         },
 | |
|         temporal_range: {
 | |
|           type: "object",
 | |
|           properties: {
 | |
|             start: { type: "string", format: "date-time" },
 | |
|             end: { type: "string", format: "date-time" }
 | |
|           },
 | |
|           description: "Temporal range for search"
 | |
|         },
 | |
|         content_filter: {
 | |
|           type: "string",
 | |
|           description: "Full-text search filter for content"
 | |
|         }
 | |
|       },
 | |
|       required: ["pattern"]
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   ucxi_navigate: {
 | |
|     name: "ucxi_navigate",
 | |
|     description: "Navigate temporally through context history",
 | |
|     inputSchema: {
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         current_address: {
 | |
|           type: "string",
 | |
|           description: "Current UCXL address position"
 | |
|         },
 | |
|         direction: {
 | |
|           type: "string",
 | |
|           enum: ["~~", "^^"],
 | |
|           description: "Navigation direction: ~~ (backward) or ^^ (forward)"
 | |
|         },
 | |
|         steps: {
 | |
|           type: "integer",
 | |
|           default: 1,
 | |
|           description: "Number of temporal steps to navigate"
 | |
|         }
 | |
|       },
 | |
|       required: ["current_address", "direction"]
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   ucxi_announce: {
 | |
|     name: "ucxi_announce",
 | |
|     description: "Announce context availability to P2P network",
 | |
|     inputSchema: {
 | |
|       type: "object",
 | |
|       properties: {
 | |
|         address: {
 | |
|           type: "string",
 | |
|           description: "UCXL address to announce"
 | |
|         },
 | |
|         capabilities: {
 | |
|           type: "array",
 | |
|           items: { type: "string" },
 | |
|           description: "Agent capabilities (roles, specializations)"
 | |
|         },
 | |
|         metadata: {
 | |
|           type: "object",
 | |
|           description: "Additional metadata about the context"
 | |
|         }
 | |
|       },
 | |
|       required: ["address"]
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| // Handler implementations
 | |
| export async function handleUCXITool(name: string, args: any): Promise<any> {
 | |
|   const ucxiServerURL = process.env.UCXI_SERVER_URL || "http://localhost:8080";
 | |
|   
 | |
|   switch (name) {
 | |
|     case "ucxi_get":
 | |
|       return await ucxiGet(ucxiServerURL, args.address, args.temporal);
 | |
|       
 | |
|     case "ucxi_put":
 | |
|       return await ucxiPut(ucxiServerURL, args.address, args.content, args.metadata);
 | |
|       
 | |
|     case "ucxi_publish_decision":
 | |
|       return await ucxiPublishDecision(ucxiServerURL, args);
 | |
|       
 | |
|     case "ucxi_query":
 | |
|       return await ucxiQuery(ucxiServerURL, args.pattern, args.temporal_range, args.content_filter);
 | |
|       
 | |
|     case "ucxi_navigate":
 | |
|       return await ucxiNavigate(ucxiServerURL, args.current_address, args.direction, args.steps);
 | |
|       
 | |
|     case "ucxi_announce":
 | |
|       return await ucxiAnnounce(ucxiServerURL, args.address, args.capabilities, args.metadata);
 | |
|       
 | |
|     default:
 | |
|       throw new Error(`Unknown UCXI tool: ${name}`);
 | |
|   }
 | |
| }
 | |
| 
 | |
| async function ucxiGet(serverURL: string, address: string, temporal?: string): Promise<any> {
 | |
|   const url = new URL(`/ucxi/${address}`, serverURL);
 | |
|   if (temporal) {
 | |
|     url.searchParams.set('temporal', temporal);
 | |
|   }
 | |
|   
 | |
|   const response = await fetch(url.toString());
 | |
|   if (!response.ok) {
 | |
|     throw new Error(`UCXI GET failed: ${response.status}`);
 | |
|   }
 | |
|   
 | |
|   return await response.json();
 | |
| }
 | |
| 
 | |
| async function ucxiPublishDecision(serverURL: string, decision: any): Promise<any> {
 | |
|   const response = await fetch(`${serverURL}/ucxi/decisions/publish`, {
 | |
|     method: 'POST',
 | |
|     headers: {
 | |
|       'Content-Type': 'application/json'
 | |
|     },
 | |
|     body: JSON.stringify(decision)
 | |
|   });
 | |
|   
 | |
|   if (!response.ok) {
 | |
|     throw new Error(`Decision publishing failed: ${response.status}`);
 | |
|   }
 | |
|   
 | |
|   return await response.json();
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Week 8: Integration Testing
 | |
| 
 | |
| #### 8.1 End-to-End Test Suite
 | |
| 
 | |
| **File:** `/test/integration/ucxl_e2e_test.go`
 | |
| 
 | |
| ```go
 | |
| package integration
 | |
| 
 | |
| import (
 | |
|     "testing"
 | |
|     "time"
 | |
|     "github.com/stretchr/testify/assert"
 | |
|     "github.com/anthonyrawlins/bzzz/pkg/protocol"
 | |
|     "github.com/anthonyrawlins/bzzz/pkg/ucxi"
 | |
|     "github.com/anthonyrawlins/bzzz/pkg/decisions"
 | |
| )
 | |
| 
 | |
| func TestUCXLE2E(t *testing.T) {
 | |
|     // Start UCXI server
 | |
|     server := setupTestServer(t)
 | |
|     defer server.Close()
 | |
|     
 | |
|     t.Run("AddressParsingAndResolution", func(t *testing.T) {
 | |
|         // Test UCXL address parsing
 | |
|         addr, err := protocol.ParseUCXLAddress("ucxl://gpt4:architect@bzzz:v2/*^/decisions/protocol.json")
 | |
|         assert.NoError(t, err)
 | |
|         assert.Equal(t, "gpt4", addr.Agent)
 | |
|         assert.Equal(t, "architect", addr.Role)
 | |
|         assert.Equal(t, "bzzz", addr.Project)
 | |
|         assert.Equal(t, "v2", addr.Task)
 | |
|         assert.Equal(t, "*^", addr.TemporalSegment)
 | |
|         assert.Equal(t, "/decisions/protocol.json", addr.Path)
 | |
|         
 | |
|         // Test storage key generation
 | |
|         storageKey := addr.ToStorageKey()
 | |
|         assert.Contains(t, storageKey, "gpt4/architect/bzzz/v2")
 | |
|     })
 | |
|     
 | |
|     t.Run("TemporalNavigation", func(t *testing.T) {
 | |
|         // Create temporal sequence
 | |
|         baseAddr := "ucxl://gpt4:architect@bzzz:v2"
 | |
|         
 | |
|         // Store contexts at different times
 | |
|         times := []time.Time{
 | |
|             time.Now().Add(-2 * time.Hour),
 | |
|             time.Now().Add(-1 * time.Hour),
 | |
|             time.Now(),
 | |
|         }
 | |
|         
 | |
|         for i, timestamp := range times {
 | |
|             addr := fmt.Sprintf("%s/%s/decisions/protocol.json", baseAddr, timestamp.Format(time.RFC3339))
 | |
|             content := map[string]interface{}{
 | |
|                 "version": i + 1,
 | |
|                 "decision": fmt.Sprintf("Protocol decision v%d", i+1),
 | |
|             }
 | |
|             
 | |
|             err := storeContext(server, addr, content)
 | |
|             assert.NoError(t, err)
 | |
|         }
 | |
|         
 | |
|         // Test latest navigation (*^)
 | |
|         latestAddr := fmt.Sprintf("%s/*^/decisions/protocol.json", baseAddr)
 | |
|         entry, err := getContext(server, latestAddr)
 | |
|         assert.NoError(t, err)
 | |
|         assert.Equal(t, 3, entry.Content["version"])
 | |
|         
 | |
|         // Test first navigation (*~)
 | |
|         firstAddr := fmt.Sprintf("%s/*~/decisions/protocol.json", baseAddr)
 | |
|         entry, err = getContext(server, firstAddr)
 | |
|         assert.NoError(t, err)
 | |
|         assert.Equal(t, 1, entry.Content["version"])
 | |
|     })
 | |
|     
 | |
|     t.Run("DecisionPublishing", func(t *testing.T) {
 | |
|         // Create decision node
 | |
|         decision := &decisions.DecisionNode{
 | |
|             DecisionID:   "test-decision-001",
 | |
|             UCXLAddress:  "ucxl://gpt4:architect@bzzz:v2/2025-08-07T14:30:00Z/decisions/architecture.json",
 | |
|             Timestamp:    time.Now(),
 | |
|             AgentID:      "gpt4-test-agent",
 | |
|             DecisionType: decisions.DecisionTypeArchitecture,
 | |
|             Context: decisions.DecisionContext{
 | |
|                 Project: "bzzz",
 | |
|                 Task:    "v2-migration",
 | |
|                 Scope:   "protocol-selection",
 | |
|             },
 | |
|             Justification: decisions.Justification{
 | |
|                 Reasoning: "UCXL provides superior semantic addressing and temporal navigation capabilities",
 | |
|                 AlternativesConsidered: []decisions.Alternative{
 | |
|                     {
 | |
|                         Name:        "Extend bzzz:// protocol",
 | |
|                         Description: "Add temporal navigation to existing protocol",
 | |
|                         Pros:        []string{"Minimal changes", "Backward compatibility"},
 | |
|                         Cons:        []string{"Limited semantic expressiveness", "Technical debt"},
 | |
|                         Rejected:    true,
 | |
|                         Reason:      "Insufficient semantic richness for complex context addressing",
 | |
|                     },
 | |
|                 },
 | |
|                 Criteria:   []string{"semantic_richness", "temporal_navigation", "ecosystem_compatibility"},
 | |
|                 Confidence: 0.9,
 | |
|             },
 | |
|             Citations: []decisions.Citation{
 | |
|                 {
 | |
|                     Type:         decisions.CitationJustifiedBy,
 | |
|                     UCXLAddress:  "ucxl://any:any@chorus:requirements/*~/analysis.md",
 | |
|                     Relevance:    "high",
 | |
|                     Excerpt:      "System must support temporal context navigation for audit trails",
 | |
|                     Strength:     0.95,
 | |
|                 },
 | |
|             },
 | |
|             Impacts: []decisions.Impact{
 | |
|                 {
 | |
|                     Type:         "replaces",
 | |
|                     UCXLAddress:  "ucxl://any:any@bzzz:v1/*^/protocol.go",
 | |
|                     Reason:       "Migrating from bzzz:// to ucxl:// addressing scheme",
 | |
|                     Severity:     "breaking",
 | |
|                     Affected:     []string{"protocol", "addressing", "navigation"},
 | |
|                 },
 | |
|             },
 | |
|         }
 | |
|         
 | |
|         // Validate decision
 | |
|         err := decision.Validate()
 | |
|         assert.NoError(t, err)
 | |
|         
 | |
|         // Test decision publishing to SLURP
 | |
|         slurpClient := setupMockSLURPClient(t)
 | |
|         response, err := slurpClient.PublishDecisions([]decisions.DecisionNode{*decision})
 | |
|         assert.NoError(t, err)
 | |
|         assert.Contains(t, response.Accepted, decision.DecisionID)
 | |
|     })
 | |
|     
 | |
|     t.Run("P2PResolution", func(t *testing.T) {
 | |
|         // Test distributed address resolution
 | |
|         addr := "ucxl://any:architect@bzzz:*/*^/decisions"
 | |
|         
 | |
|         // Query should return contexts from multiple agents
 | |
|         entries, err := queryContexts(server, addr)
 | |
|         assert.NoError(t, err)
 | |
|         assert.Greater(t, len(entries), 0)
 | |
|         
 | |
|         // Test wildcard matching
 | |
|         for _, entry := range entries {
 | |
|             assert.Equal(t, "architect", entry.Address.Role)
 | |
|             assert.Equal(t, "bzzz", entry.Address.Project)
 | |
|         }
 | |
|     })
 | |
|     
 | |
|     t.Run("CitationValidation", func(t *testing.T) {
 | |
|         // Test citation chain validation
 | |
|         validator := decisions.NewCitationValidator(server.ContextStore)
 | |
|         
 | |
|         decision := &decisions.DecisionNode{
 | |
|             Citations: []decisions.Citation{
 | |
|                 {
 | |
|                     Type:         decisions.CitationJustifiedBy,
 | |
|                     UCXLAddress:  "ucxl://nonexistent:agent@invalid:project/*^/missing.json",
 | |
|                     Relevance:    "high",
 | |
|                 },
 | |
|             },
 | |
|         }
 | |
|         
 | |
|         err := validator.ValidateCitations(decision)
 | |
|         assert.Error(t, err) // Should fail due to nonexistent citation
 | |
|         
 | |
|         // Test valid citation
 | |
|         decision.Citations[0].UCXLAddress = "ucxl://gpt4:architect@bzzz:v2/*^/requirements.json"
 | |
|         
 | |
|         // Store the referenced context
 | |
|         storeContext(server, decision.Citations[0].UCXLAddress, map[string]interface{}{
 | |
|             "requirement": "temporal navigation support",
 | |
|         })
 | |
|         
 | |
|         err = validator.ValidateCitations(decision)
 | |
|         assert.NoError(t, err)
 | |
|     })
 | |
| }
 | |
| ```
 | |
| 
 | |
| ## Database Schema & Storage
 | |
| 
 | |
| ### Context Storage Schema (BadgerDB)
 | |
| 
 | |
| ```go
 | |
| // Storage keys
 | |
| const (
 | |
|     ContextKeyPrefix    = "ctx:"     // ctx:agent/role/project/task/timestamp/path
 | |
|     TemporalKeyPrefix   = "tmp:"     // tmp:agent/role/project/task -> []TemporalEntry
 | |
|     IndexKeyPrefix      = "idx:"     // idx:field:value -> []address
 | |
|     MetadataKeyPrefix   = "meta:"    // meta:address -> ContextMetadata
 | |
| )
 | |
| 
 | |
| // Indexed fields for efficient querying
 | |
| var IndexedFields = []string{
 | |
|     "agent", "role", "project", "task", 
 | |
|     "content_type", "tags", "timestamp",
 | |
| }
 | |
| 
 | |
| // Key generation functions
 | |
| func GenerateContextKey(addr *protocol.UCXLAddress) string {
 | |
|     return ContextKeyPrefix + addr.ToStorageKey()
 | |
| }
 | |
| 
 | |
| func GenerateTemporalKey(addr *protocol.UCXLAddress) string {
 | |
|     return fmt.Sprintf("%s%s/%s/%s/%s", 
 | |
|         TemporalKeyPrefix, addr.Agent, addr.Role, addr.Project, addr.Task)
 | |
| }
 | |
| ```
 | |
| 
 | |
| This implementation roadmap provides the complete foundation for transforming BZZZ v2 into a UCXL-based semantic context publishing system while maintaining its distributed P2P architecture and integrating seamlessly with the CHORUS infrastructure.
 | |
| 
 | |
| ## Key Files Created:
 | |
| - `/home/tony/chorus/project-queues/active/BZZZ/BZZZ_V2_UCXL_DEVELOPMENT_PLAN.md`
 | |
| - `/home/tony/chorus/project-queues/active/BZZZ/TECHNICAL_ARCHITECTURE.md`
 | |
| - `/home/tony/chorus/project-queues/active/BZZZ/IMPLEMENTATION_ROADMAP.md`
 | |
| 
 | |
| The roadmap shows exactly how to implement UCXL address parsing, temporal navigation, decision node publishing to SLURP, P2P DHT resolution, and MCP integration with detailed code examples and test suites. |