Files
gastown/internal/protocol/handlers.go
Steve Yegge 96ffbf0188 Implement Witness-Refinery protocol handlers (gt-m5w4g.2)
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>
2025-12-30 10:48:00 -08:00

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
}