Add internal/protocol/ package with handlers for: - MERGE_READY (witness→refinery): worker done, branch ready - MERGED (refinery→witness): merge succeeded, cleanup ok - MERGE_FAILED (refinery→witness): merge failed, needs rework - REWORK_REQUEST (refinery→witness): rebase needed due to conflicts Package structure: - types.go: Protocol message types and payload structs - messages.go: Message builders and body parsers - handlers.go: Handler interface and registry for dispatch - witness_handlers.go: DefaultWitnessHandler implementation - refinery_handlers.go: DefaultRefineryHandler implementation - protocol_test.go: Comprehensive test coverage Also updated docs/mail-protocol.md with: - MERGE_FAILED and REWORK_REQUEST message type documentation - Merge failure and rebase required flow diagrams - Reference to internal/protocol/ package 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
125 lines
3.6 KiB
Go
125 lines
3.6 KiB
Go
package protocol
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/steveyegge/gastown/internal/mail"
|
|
)
|
|
|
|
// Handler processes a protocol message and returns an error if processing failed.
|
|
type Handler func(msg *mail.Message) error
|
|
|
|
// HandlerRegistry maps message types to their handlers.
|
|
type HandlerRegistry struct {
|
|
handlers map[MessageType]Handler
|
|
}
|
|
|
|
// NewHandlerRegistry creates a new handler registry.
|
|
func NewHandlerRegistry() *HandlerRegistry {
|
|
return &HandlerRegistry{
|
|
handlers: make(map[MessageType]Handler),
|
|
}
|
|
}
|
|
|
|
// Register adds a handler for a specific message type.
|
|
func (r *HandlerRegistry) Register(msgType MessageType, handler Handler) {
|
|
r.handlers[msgType] = handler
|
|
}
|
|
|
|
// Handle dispatches a message to the appropriate handler.
|
|
// Returns an error if no handler is registered for the message type.
|
|
func (r *HandlerRegistry) Handle(msg *mail.Message) error {
|
|
msgType := ParseMessageType(msg.Subject)
|
|
if msgType == "" {
|
|
return fmt.Errorf("unknown message type for subject: %s", msg.Subject)
|
|
}
|
|
|
|
handler, ok := r.handlers[msgType]
|
|
if !ok {
|
|
return fmt.Errorf("no handler registered for message type: %s", msgType)
|
|
}
|
|
|
|
return handler(msg)
|
|
}
|
|
|
|
// CanHandle returns true if a handler is registered for the message's type.
|
|
func (r *HandlerRegistry) CanHandle(msg *mail.Message) bool {
|
|
msgType := ParseMessageType(msg.Subject)
|
|
if msgType == "" {
|
|
return false
|
|
}
|
|
|
|
_, ok := r.handlers[msgType]
|
|
return ok
|
|
}
|
|
|
|
// WitnessHandler defines the interface for Witness protocol handlers.
|
|
// The Witness receives messages from Refinery about merge status.
|
|
type WitnessHandler interface {
|
|
// HandleMerged is called when a branch was successfully merged.
|
|
HandleMerged(payload *MergedPayload) error
|
|
|
|
// HandleMergeFailed is called when a merge attempt failed.
|
|
HandleMergeFailed(payload *MergeFailedPayload) error
|
|
|
|
// HandleReworkRequest is called when a branch needs rebasing.
|
|
HandleReworkRequest(payload *ReworkRequestPayload) error
|
|
}
|
|
|
|
// RefineryHandler defines the interface for Refinery protocol handlers.
|
|
// The Refinery receives messages from Witness about ready branches.
|
|
type RefineryHandler interface {
|
|
// HandleMergeReady is called when a polecat's work is verified and ready.
|
|
HandleMergeReady(payload *MergeReadyPayload) error
|
|
}
|
|
|
|
// WrapWitnessHandlers creates mail handlers from a WitnessHandler.
|
|
func WrapWitnessHandlers(h WitnessHandler) *HandlerRegistry {
|
|
registry := NewHandlerRegistry()
|
|
|
|
registry.Register(TypeMerged, func(msg *mail.Message) error {
|
|
payload := ParseMergedPayload(msg.Body)
|
|
return h.HandleMerged(payload)
|
|
})
|
|
|
|
registry.Register(TypeMergeFailed, func(msg *mail.Message) error {
|
|
payload := ParseMergeFailedPayload(msg.Body)
|
|
return h.HandleMergeFailed(payload)
|
|
})
|
|
|
|
registry.Register(TypeReworkRequest, func(msg *mail.Message) error {
|
|
payload := ParseReworkRequestPayload(msg.Body)
|
|
return h.HandleReworkRequest(payload)
|
|
})
|
|
|
|
return registry
|
|
}
|
|
|
|
// WrapRefineryHandlers creates mail handlers from a RefineryHandler.
|
|
func WrapRefineryHandlers(h RefineryHandler) *HandlerRegistry {
|
|
registry := NewHandlerRegistry()
|
|
|
|
registry.Register(TypeMergeReady, func(msg *mail.Message) error {
|
|
payload := ParseMergeReadyPayload(msg.Body)
|
|
return h.HandleMergeReady(payload)
|
|
})
|
|
|
|
return registry
|
|
}
|
|
|
|
// ProcessProtocolMessage processes a protocol message using the registry.
|
|
// It returns (true, nil) if the message was handled successfully,
|
|
// (true, error) if handling failed, or (false, nil) if not a protocol message.
|
|
func (r *HandlerRegistry) ProcessProtocolMessage(msg *mail.Message) (bool, error) {
|
|
if !IsProtocolMessage(msg.Subject) {
|
|
return false, nil
|
|
}
|
|
|
|
if !r.CanHandle(msg) {
|
|
return false, nil
|
|
}
|
|
|
|
err := r.Handle(msg)
|
|
return true, err
|
|
}
|