Add delegation relationship type to beads (gt-6r18e.5)
Add Delegation and DelegationTerms structs for tracking work delegation between work units. This enables the HOP pattern of work flowing down and credit cascading up. New types: - Delegation: Links parent and child work units with delegated_by/to - DelegationTerms: Optional terms including portion, deadline, credit_share New functions: - AddDelegation: Create a delegation relationship - RemoveDelegation: Remove a delegation relationship - GetDelegation: Retrieve delegation info for a child work unit - ListDelegationsFrom: List all delegations from a parent 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package beads
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -1377,3 +1378,102 @@ func TestRoleBeadID(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDelegationStruct tests the Delegation struct serialization.
|
||||
func TestDelegationStruct(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
delegation Delegation
|
||||
wantJSON string
|
||||
}{
|
||||
{
|
||||
name: "full delegation",
|
||||
delegation: Delegation{
|
||||
Parent: "hop://accenture.com/eng/proj-123/task-a",
|
||||
Child: "hop://alice@example.com/main-town/gastown/gt-xyz",
|
||||
DelegatedBy: "hop://accenture.com",
|
||||
DelegatedTo: "hop://alice@example.com",
|
||||
Terms: &DelegationTerms{
|
||||
Portion: "backend-api",
|
||||
Deadline: "2025-06-01",
|
||||
CreditShare: 80,
|
||||
},
|
||||
CreatedAt: "2025-01-15T10:00:00Z",
|
||||
},
|
||||
wantJSON: `{"parent":"hop://accenture.com/eng/proj-123/task-a","child":"hop://alice@example.com/main-town/gastown/gt-xyz","delegated_by":"hop://accenture.com","delegated_to":"hop://alice@example.com","terms":{"portion":"backend-api","deadline":"2025-06-01","credit_share":80},"created_at":"2025-01-15T10:00:00Z"}`,
|
||||
},
|
||||
{
|
||||
name: "minimal delegation",
|
||||
delegation: Delegation{
|
||||
Parent: "gt-abc",
|
||||
Child: "gt-xyz",
|
||||
DelegatedBy: "steve",
|
||||
DelegatedTo: "alice",
|
||||
},
|
||||
wantJSON: `{"parent":"gt-abc","child":"gt-xyz","delegated_by":"steve","delegated_to":"alice"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := json.Marshal(tt.delegation)
|
||||
if err != nil {
|
||||
t.Fatalf("json.Marshal failed: %v", err)
|
||||
}
|
||||
if string(got) != tt.wantJSON {
|
||||
t.Errorf("json.Marshal = %s, want %s", string(got), tt.wantJSON)
|
||||
}
|
||||
|
||||
// Test round-trip
|
||||
var parsed Delegation
|
||||
if err := json.Unmarshal(got, &parsed); err != nil {
|
||||
t.Fatalf("json.Unmarshal failed: %v", err)
|
||||
}
|
||||
if parsed.Parent != tt.delegation.Parent {
|
||||
t.Errorf("parsed.Parent = %s, want %s", parsed.Parent, tt.delegation.Parent)
|
||||
}
|
||||
if parsed.Child != tt.delegation.Child {
|
||||
t.Errorf("parsed.Child = %s, want %s", parsed.Child, tt.delegation.Child)
|
||||
}
|
||||
if parsed.DelegatedBy != tt.delegation.DelegatedBy {
|
||||
t.Errorf("parsed.DelegatedBy = %s, want %s", parsed.DelegatedBy, tt.delegation.DelegatedBy)
|
||||
}
|
||||
if parsed.DelegatedTo != tt.delegation.DelegatedTo {
|
||||
t.Errorf("parsed.DelegatedTo = %s, want %s", parsed.DelegatedTo, tt.delegation.DelegatedTo)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDelegationTerms tests the DelegationTerms struct.
|
||||
func TestDelegationTerms(t *testing.T) {
|
||||
terms := &DelegationTerms{
|
||||
Portion: "frontend",
|
||||
Deadline: "2025-03-15",
|
||||
AcceptanceCriteria: "All tests passing, code reviewed",
|
||||
CreditShare: 70,
|
||||
}
|
||||
|
||||
got, err := json.Marshal(terms)
|
||||
if err != nil {
|
||||
t.Fatalf("json.Marshal failed: %v", err)
|
||||
}
|
||||
|
||||
var parsed DelegationTerms
|
||||
if err := json.Unmarshal(got, &parsed); err != nil {
|
||||
t.Fatalf("json.Unmarshal failed: %v", err)
|
||||
}
|
||||
|
||||
if parsed.Portion != terms.Portion {
|
||||
t.Errorf("parsed.Portion = %s, want %s", parsed.Portion, terms.Portion)
|
||||
}
|
||||
if parsed.Deadline != terms.Deadline {
|
||||
t.Errorf("parsed.Deadline = %s, want %s", parsed.Deadline, terms.Deadline)
|
||||
}
|
||||
if parsed.AcceptanceCriteria != terms.AcceptanceCriteria {
|
||||
t.Errorf("parsed.AcceptanceCriteria = %s, want %s", parsed.AcceptanceCriteria, terms.AcceptanceCriteria)
|
||||
}
|
||||
if parsed.CreditShare != terms.CreditShare {
|
||||
t.Errorf("parsed.CreditShare = %d, want %d", parsed.CreditShare, terms.CreditShare)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user