Files
bzzz/IMPLEMENTATION_ROADMAP.md
anthonyrawlins b207f32d9e Implement UCXL Protocol Foundation (Phase 1)
- 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>
2025-08-08 07:38:04 +10:00

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 = &timestamp
case TemporalFirst:
timestamp, err := navigator.GetFirstTimestamp(addr)
if err != nil {
return err
}
addr.ResolvedTime = &timestamp
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 = &timestamp
}
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.