Complete BZZZ functionality port to CHORUS
🎭 CHORUS now contains full BZZZ functionality adapted for containers Core systems ported: - P2P networking (libp2p with DHT and PubSub) - Task coordination (COOEE protocol) - HMMM collaborative reasoning - SHHH encryption and security - SLURP admin election system - UCXL content addressing - UCXI server integration - Hypercore logging system - Health monitoring and graceful shutdown - License validation with KACHING Container adaptations: - Environment variable configuration (no YAML files) - Container-optimized logging to stdout/stderr - Auto-generated agent IDs for container deployments - Docker-first architecture All proven BZZZ P2P protocols, AI integration, and collaboration features are now available in containerized form. Next: Build and test container deployment. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
313
pkg/integration/decision_publisher.go
Normal file
313
pkg/integration/decision_publisher.go
Normal file
@@ -0,0 +1,313 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"chorus.services/bzzz/pkg/dht"
|
||||
"chorus.services/bzzz/pkg/ucxl"
|
||||
)
|
||||
|
||||
// DecisionPublisher handles publishing decisions to encrypted DHT storage
|
||||
type DecisionPublisher struct {
|
||||
dhtStorage *dht.EncryptedDHTStorage
|
||||
enabled bool
|
||||
}
|
||||
|
||||
// Decision represents a decision made from a HMMM discussion
|
||||
type Decision struct {
|
||||
Type string `json:"type"` // Event type (approval, warning, etc.)
|
||||
Content string `json:"content"` // Human-readable decision content
|
||||
Participants []string `json:"participants"` // Who participated in the decision
|
||||
ConsensusLevel float64 `json:"consensus_level"` // Strength of consensus (0.0-1.0)
|
||||
Timestamp time.Time `json:"timestamp"` // When decision was made
|
||||
DiscussionID string `json:"discussion_id"` // Source discussion ID
|
||||
Confidence float64 `json:"confidence"` // AI confidence in decision extraction
|
||||
Metadata map[string]interface{} `json:"metadata"` // Additional decision metadata
|
||||
UCXLAddress string `json:"ucxl_address"` // Associated UCXL address
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"` // Optional expiration
|
||||
Tags []string `json:"tags"` // Decision tags
|
||||
RelatedDecisions []string `json:"related_decisions,omitempty"` // Related decision hashes
|
||||
}
|
||||
|
||||
// PublishResult contains the result of publishing a decision
|
||||
type PublishResult struct {
|
||||
UCXLAddress string `json:"ucxl_address"`
|
||||
DHTHash string `json:"dht_hash"`
|
||||
Success bool `json:"success"`
|
||||
PublishedAt time.Time `json:"published_at"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// NewDecisionPublisher creates a new decision publisher
|
||||
func NewDecisionPublisher(dhtStorage *dht.EncryptedDHTStorage, enabled bool) *DecisionPublisher {
|
||||
return &DecisionPublisher{
|
||||
dhtStorage: dhtStorage,
|
||||
enabled: enabled,
|
||||
}
|
||||
}
|
||||
|
||||
// PublishDecision publishes a decision to the encrypted DHT storage
|
||||
func (dp *DecisionPublisher) PublishDecision(ctx context.Context, ucxlAddr *ucxl.Address, decision *Decision) (*PublishResult, error) {
|
||||
result := &PublishResult{
|
||||
UCXLAddress: ucxlAddr.String(),
|
||||
PublishedAt: time.Now(),
|
||||
}
|
||||
|
||||
if !dp.enabled {
|
||||
result.Error = "Decision publishing is disabled"
|
||||
log.Printf("📤 Decision publishing skipped (disabled): %s", ucxlAddr.String())
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Enrich decision with UCXL address
|
||||
decision.UCXLAddress = ucxlAddr.String()
|
||||
|
||||
// Serialize decision to JSON
|
||||
decisionJSON, err := json.Marshal(decision)
|
||||
if err != nil {
|
||||
result.Error = fmt.Sprintf("failed to serialize decision: %v", err)
|
||||
return result, fmt.Errorf("failed to serialize decision: %w", err)
|
||||
}
|
||||
|
||||
// Determine creator role from UCXL address
|
||||
creatorRole := ucxlAddr.Role
|
||||
if creatorRole == "any" || creatorRole == "" {
|
||||
creatorRole = "contributor" // Default role for decisions
|
||||
}
|
||||
|
||||
// Store in encrypted DHT
|
||||
err = dp.dhtStorage.StoreUCXLContent(
|
||||
ucxlAddr.String(),
|
||||
decisionJSON,
|
||||
creatorRole,
|
||||
"decision",
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
result.Error = err.Error()
|
||||
return result, fmt.Errorf("failed to store decision in DHT: %w", err)
|
||||
}
|
||||
|
||||
// Generate content hash for reference
|
||||
result.DHTHash = fmt.Sprintf("sha256:%x", sha256.Sum256(decisionJSON))
|
||||
result.Success = true
|
||||
|
||||
log.Printf("📤 Decision published to DHT: %s (hash: %s)", ucxlAddr.String(), result.DHTHash[:16]+"...")
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// RetrieveDecision retrieves a decision from the encrypted DHT storage
|
||||
func (dp *DecisionPublisher) RetrieveDecision(ctx context.Context, ucxlAddr *ucxl.Address) (*Decision, error) {
|
||||
if !dp.enabled {
|
||||
return nil, fmt.Errorf("decision publishing is disabled")
|
||||
}
|
||||
|
||||
// Retrieve from encrypted DHT
|
||||
content, metadata, err := dp.dhtStorage.RetrieveUCXLContent(ucxlAddr.String())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to retrieve decision from DHT: %w", err)
|
||||
}
|
||||
|
||||
// Verify content type
|
||||
if metadata.ContentType != "decision" {
|
||||
return nil, fmt.Errorf("content at address is not a decision (type: %s)", metadata.ContentType)
|
||||
}
|
||||
|
||||
// Deserialize decision
|
||||
var decision Decision
|
||||
if err := json.Unmarshal(content, &decision); err != nil {
|
||||
return nil, fmt.Errorf("failed to deserialize decision: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("📥 Decision retrieved from DHT: %s", ucxlAddr.String())
|
||||
return &decision, nil
|
||||
}
|
||||
|
||||
// ListDecisionsByRole lists decisions accessible by a specific role
|
||||
func (dp *DecisionPublisher) ListDecisionsByRole(ctx context.Context, role string, limit int) ([]*Decision, error) {
|
||||
if !dp.enabled {
|
||||
return nil, fmt.Errorf("decision publishing is disabled")
|
||||
}
|
||||
|
||||
// Get content metadata from DHT
|
||||
metadataList, err := dp.dhtStorage.ListContentByRole(role, limit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list content by role: %w", err)
|
||||
}
|
||||
|
||||
decisions := make([]*Decision, 0)
|
||||
|
||||
// Retrieve each decision
|
||||
for _, metadata := range metadataList {
|
||||
if metadata.ContentType != "decision" {
|
||||
continue // Skip non-decisions
|
||||
}
|
||||
|
||||
// Parse UCXL address
|
||||
addr, err := ucxl.Parse(metadata.Address)
|
||||
if err != nil {
|
||||
log.Printf("⚠️ Invalid UCXL address in decision metadata: %s", metadata.Address)
|
||||
continue
|
||||
}
|
||||
|
||||
// Retrieve decision content
|
||||
decision, err := dp.RetrieveDecision(ctx, addr)
|
||||
if err != nil {
|
||||
log.Printf("⚠️ Failed to retrieve decision %s: %v", metadata.Address, err)
|
||||
continue
|
||||
}
|
||||
|
||||
decisions = append(decisions, decision)
|
||||
|
||||
// Respect limit
|
||||
if len(decisions) >= limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("📋 Listed %d decisions for role: %s", len(decisions), role)
|
||||
return decisions, nil
|
||||
}
|
||||
|
||||
// UpdateDecision updates an existing decision or creates a new version
|
||||
func (dp *DecisionPublisher) UpdateDecision(ctx context.Context, ucxlAddr *ucxl.Address, decision *Decision) (*PublishResult, error) {
|
||||
if !dp.enabled {
|
||||
result := &PublishResult{
|
||||
UCXLAddress: ucxlAddr.String(),
|
||||
PublishedAt: time.Now(),
|
||||
Error: "Decision publishing is disabled",
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Check if decision already exists
|
||||
existingDecision, err := dp.RetrieveDecision(ctx, ucxlAddr)
|
||||
if err == nil {
|
||||
// Decision exists, create related decision reference
|
||||
decision.RelatedDecisions = append(decision.RelatedDecisions, dp.generateDecisionHash(existingDecision))
|
||||
log.Printf("📝 Updating existing decision: %s", ucxlAddr.String())
|
||||
} else {
|
||||
log.Printf("📝 Creating new decision: %s", ucxlAddr.String())
|
||||
}
|
||||
|
||||
// Publish the updated/new decision
|
||||
return dp.PublishDecision(ctx, ucxlAddr, decision)
|
||||
}
|
||||
|
||||
// SearchDecisions searches for decisions matching criteria
|
||||
func (dp *DecisionPublisher) SearchDecisions(ctx context.Context, searchCriteria map[string]string, limit int) ([]*Decision, error) {
|
||||
if !dp.enabled {
|
||||
return nil, fmt.Errorf("decision publishing is disabled")
|
||||
}
|
||||
|
||||
// Convert search criteria to DHT search query
|
||||
query := &dht.SearchQuery{
|
||||
Agent: searchCriteria["agent"],
|
||||
Role: searchCriteria["role"],
|
||||
Project: searchCriteria["project"],
|
||||
Task: searchCriteria["task"],
|
||||
ContentType: "decision",
|
||||
Limit: limit,
|
||||
}
|
||||
|
||||
// Parse time filters if provided
|
||||
if createdAfter := searchCriteria["created_after"]; createdAfter != "" {
|
||||
if t, err := time.Parse(time.RFC3339, createdAfter); err == nil {
|
||||
query.CreatedAfter = t
|
||||
}
|
||||
}
|
||||
|
||||
if createdBefore := searchCriteria["created_before"]; createdBefore != "" {
|
||||
if t, err := time.Parse(time.RFC3339, createdBefore); err == nil {
|
||||
query.CreatedBefore = t
|
||||
}
|
||||
}
|
||||
|
||||
// Search DHT for matching decisions
|
||||
searchResults, err := dp.dhtStorage.SearchContent(query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to search decisions: %w", err)
|
||||
}
|
||||
|
||||
decisions := make([]*Decision, 0, len(searchResults))
|
||||
|
||||
// Retrieve each decision
|
||||
for _, metadata := range searchResults {
|
||||
// Parse UCXL address
|
||||
addr, err := ucxl.Parse(metadata.Address)
|
||||
if err != nil {
|
||||
log.Printf("⚠️ Invalid UCXL address in search results: %s", metadata.Address)
|
||||
continue
|
||||
}
|
||||
|
||||
// Retrieve decision content
|
||||
decision, err := dp.RetrieveDecision(ctx, addr)
|
||||
if err != nil {
|
||||
log.Printf("⚠️ Failed to retrieve decision %s: %v", metadata.Address, err)
|
||||
continue
|
||||
}
|
||||
|
||||
decisions = append(decisions, decision)
|
||||
}
|
||||
|
||||
log.Printf("🔍 Search found %d decisions", len(decisions))
|
||||
return decisions, nil
|
||||
}
|
||||
|
||||
// GetDecisionMetrics returns metrics about decisions in the system
|
||||
func (dp *DecisionPublisher) GetDecisionMetrics(ctx context.Context) (map[string]interface{}, error) {
|
||||
if !dp.enabled {
|
||||
return map[string]interface{}{
|
||||
"enabled": false,
|
||||
"message": "Decision publishing is disabled",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get DHT storage metrics
|
||||
dhtMetrics := dp.dhtStorage.GetMetrics()
|
||||
|
||||
// Add decision-specific metrics
|
||||
metrics := map[string]interface{}{
|
||||
"enabled": true,
|
||||
"dht_storage": dhtMetrics,
|
||||
"last_updated": time.Now(),
|
||||
}
|
||||
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
// generateDecisionHash generates a hash for a decision to use in references
|
||||
func (dp *DecisionPublisher) generateDecisionHash(decision *Decision) string {
|
||||
// Create hash from key decision fields
|
||||
hashData := fmt.Sprintf("%s_%s_%s_%d",
|
||||
decision.Type,
|
||||
decision.UCXLAddress,
|
||||
decision.DiscussionID,
|
||||
decision.Timestamp.Unix(),
|
||||
)
|
||||
|
||||
hash := sha256.Sum256([]byte(hashData))
|
||||
return fmt.Sprintf("decision_%x", hash[:8])
|
||||
}
|
||||
|
||||
// IsEnabled returns whether decision publishing is enabled
|
||||
func (dp *DecisionPublisher) IsEnabled() bool {
|
||||
return dp.enabled
|
||||
}
|
||||
|
||||
// Enable enables decision publishing
|
||||
func (dp *DecisionPublisher) Enable() {
|
||||
dp.enabled = true
|
||||
log.Printf("📤 Decision publishing enabled")
|
||||
}
|
||||
|
||||
// Disable disables decision publishing
|
||||
func (dp *DecisionPublisher) Disable() {
|
||||
dp.enabled = false
|
||||
log.Printf("🚫 Decision publishing disabled")
|
||||
}
|
||||
Reference in New Issue
Block a user