refactor(store): split TeamStore into focused sub-interfaces

Split monolithic TeamStore (61 methods) into 6 focused interfaces:
- TeamCRUDStore: core team + member operations (13 methods)
- TaskStore: task CRUD + lifecycle transitions (22 methods)
- TaskCommentStore: comments, events, attachments (10 methods)
- TaskRecoveryStore: stale detection + recovery (6 methods)
- TaskFollowupStore: follow-up scheduling (7 methods)
- TeamAccessStore: user access grants (5 methods)

TeamStore is now a composed interface embedding all sub-interfaces,
maintaining full backward compatibility. No implementation changes.
This commit is contained in:
viettranx committed 2026-03-25 08:20:10 +07:00
1 parent 96bf0ddb68
commit 96fbc79cd2
1 file changed
+38 -90
+38 -90
View File
@@ -192,150 +192,98 @@ type ScopeEntry struct {
ChatID string `json:"chat_id"`
}
// TeamStore manages agent teams, tasks, and messages.
type TeamStore interface {
// Team CRUD
// TeamCRUDStore manages core team and member operations.
type TeamCRUDStore interface {
CreateTeam(ctx context.Context, team *TeamData) error
GetTeam(ctx context.Context, teamID uuid.UUID) (*TeamData, error)
GetTeamUnscoped(ctx context.Context, id uuid.UUID) (*TeamData, error)
UpdateTeam(ctx context.Context, teamID uuid.UUID, updates map[string]any) error
DeleteTeam(ctx context.Context, teamID uuid.UUID) error
ListTeams(ctx context.Context) ([]TeamData, error)
// Members
AddMember(ctx context.Context, teamID, agentID uuid.UUID, role string) error
RemoveMember(ctx context.Context, teamID, agentID uuid.UUID) error
ListMembers(ctx context.Context, teamID uuid.UUID) ([]TeamMemberData, error)
// ListIdleMembers returns team members (non-lead) that have no in_progress tasks.
ListIdleMembers(ctx context.Context, teamID uuid.UUID) ([]TeamMemberData, error)
// GetTeamForAgent returns the team that the given agent belongs to.
// Returns nil, nil if the agent is not in any team.
GetTeamForAgent(ctx context.Context, agentID uuid.UUID) (*TeamData, error)
// KnownUserIDs returns distinct user IDs from sessions of team member agents.
// Used by team settings UI to populate user select boxes.
KnownUserIDs(ctx context.Context, teamID uuid.UUID, limit int) ([]string, error)
// Scopes (channel+chatID discovery across tasks and workspace)
ListTaskScopes(ctx context.Context, teamID uuid.UUID) ([]ScopeEntry, error)
}
// Tasks (shared task list)
// TaskStore manages task CRUD, lifecycle transitions, and progress.
type TaskStore interface {
CreateTask(ctx context.Context, task *TeamTaskData) error
UpdateTask(ctx context.Context, taskID uuid.UUID, updates map[string]any) error
// ListTasks returns tasks for a team. orderBy: "priority" or "newest".
// statusFilter: "" = non-completed (default), "completed", "all".
// userID: if non-empty, filter to tasks created by this user.
// channel+chatID: if either is non-empty, filter to that exact scope.
// limit: max rows to return; 0 uses the default (30).
ListTasks(ctx context.Context, teamID uuid.UUID, orderBy string, statusFilter string, userID string, channel string, chatID string, limit int, offset int) ([]TeamTaskData, error)
// GetTask returns a single task by ID with joined agent info.
GetTask(ctx context.Context, taskID uuid.UUID) (*TeamTaskData, error)
// GetTasksByIDs returns multiple tasks by IDs in a single query.
GetTasksByIDs(ctx context.Context, ids []uuid.UUID) ([]TeamTaskData, error)
// SearchTasks performs FTS search over task subject+description.
// userID: if non-empty, filter to tasks created by this user.
SearchTasks(ctx context.Context, teamID uuid.UUID, query string, limit int, userID string) ([]TeamTaskData, error)
// DeleteTask permanently removes a terminal-status task (completed/failed/cancelled).
// Returns ErrTaskNotFound if the task does not exist or is not in a terminal status.
DeleteTask(ctx context.Context, taskID, teamID uuid.UUID) error
// DeleteTasks permanently removes multiple terminal-status tasks in a single query.
// Returns the list of IDs that were actually deleted.
DeleteTasks(ctx context.Context, taskIDs []uuid.UUID, teamID uuid.UUID) ([]uuid.UUID, error)
// ClaimTask atomically transitions a task from pending to in_progress.
// Only one agent can claim a given task (row-level lock, race-safe).
// teamID is validated in the WHERE clause to prevent cross-team task claiming.
ClaimTask(ctx context.Context, taskID, agentID, teamID uuid.UUID) error
// AssignTask admin-assigns a pending task to a specific agent.
// teamID is validated in the WHERE clause to prevent cross-team assignment.
AssignTask(ctx context.Context, taskID, agentID, teamID uuid.UUID) error
// CompleteTask marks a task as completed and unblocks dependent tasks.
// teamID is validated in the WHERE clause to prevent cross-team task completion.
CompleteTask(ctx context.Context, taskID, teamID uuid.UUID, result string) error
// CancelTask marks a non-completed task as cancelled (status=completed, result="CANCELLED: ..."),
// unblocks dependent tasks, and transitions blocked→pending when all blockers are resolved.
// Returns error if the task is already completed or not found.
CancelTask(ctx context.Context, taskID, teamID uuid.UUID, reason string) error
// FailTask marks an in_progress task as failed and stores the error message.
// Unblocks dependent tasks so they aren't stuck.
FailTask(ctx context.Context, taskID, teamID uuid.UUID, errMsg string) error
// FailPendingTask marks a pending or blocked task as failed (post-turn validation).
// Unlike FailTask, accepts pending/blocked source statuses.
FailPendingTask(ctx context.Context, taskID, teamID uuid.UUID, errMsg string) error
// Review workflow
ReviewTask(ctx context.Context, taskID, teamID uuid.UUID) error
ApproveTask(ctx context.Context, taskID, teamID uuid.UUID, comment string) error
RejectTask(ctx context.Context, taskID, teamID uuid.UUID, reason string) error
UpdateTaskProgress(ctx context.Context, taskID, teamID uuid.UUID, percent int, step string) error
RenewTaskLock(ctx context.Context, taskID, teamID uuid.UUID) error
ResetTaskStatus(ctx context.Context, taskID, teamID uuid.UUID) error
}
// Task comments
// TaskCommentStore manages task comments, audit events, and attachments.
type TaskCommentStore interface {
AddTaskComment(ctx context.Context, comment *TeamTaskCommentData) error
ListTaskComments(ctx context.Context, taskID uuid.UUID) ([]TeamTaskCommentData, error)
ListRecentTaskComments(ctx context.Context, taskID uuid.UUID, limit int) ([]TeamTaskCommentData, error)
// Audit events
RecordTaskEvent(ctx context.Context, event *TeamTaskEventData) error
ListTaskEvents(ctx context.Context, taskID uuid.UUID) ([]TeamTaskEventData, error)
// ListTeamEvents returns recent events across all tasks in a team.
ListTeamEvents(ctx context.Context, teamID uuid.UUID, limit, offset int) ([]TeamTaskEventData, error)
// Attachments (path-based, no FK to workspace files)
AttachFileToTask(ctx context.Context, att *TeamTaskAttachmentData) error
GetAttachment(ctx context.Context, attachmentID uuid.UUID) (*TeamTaskAttachmentData, error)
ListTaskAttachments(ctx context.Context, taskID uuid.UUID) ([]TeamTaskAttachmentData, error)
DetachFileFromTask(ctx context.Context, taskID uuid.UUID, path string) error
}
// Follow-up reminders
// TaskRecoveryStore manages stale task detection and recovery.
type TaskRecoveryStore interface {
RecoverAllStaleTasks(ctx context.Context) ([]RecoveredTaskInfo, error)
ForceRecoverAllTasks(ctx context.Context) ([]RecoveredTaskInfo, error)
ListRecoverableTasks(ctx context.Context, teamID uuid.UUID) ([]TeamTaskData, error)
MarkAllStaleTasks(ctx context.Context, olderThan time.Time) ([]RecoveredTaskInfo, error)
MarkInReviewStaleTasks(ctx context.Context, olderThan time.Time) ([]RecoveredTaskInfo, error)
FixOrphanedBlockedTasks(ctx context.Context) ([]RecoveredTaskInfo, error)
}
// TaskFollowupStore manages follow-up reminder scheduling.
type TaskFollowupStore interface {
SetTaskFollowup(ctx context.Context, taskID, teamID uuid.UUID, followupAt time.Time, max int, message, channel, chatID string) error
ClearTaskFollowup(ctx context.Context, taskID uuid.UUID) error
// ListAllFollowupDueTasks returns due followup tasks across all v2 active teams (batch).
ListAllFollowupDueTasks(ctx context.Context) ([]TeamTaskData, error)
IncrementFollowupCount(ctx context.Context, taskID uuid.UUID, nextAt *time.Time) error
// Auto follow-up guardrails (system-level, no LLM dependency)
// ClearFollowupByScope clears followup on all in_progress tasks for a given channel+chatID scope.
ClearFollowupByScope(ctx context.Context, channel, chatID string) (int, error)
// SetFollowupForActiveTasks sets followup on in_progress tasks that don't already have one.
// Matches tasks scoped to the given channel+chatID, or unscoped tasks in the same team.
SetFollowupForActiveTasks(ctx context.Context, teamID uuid.UUID, channel, chatID string, followupAt time.Time, max int, message string) (int, error)
// HasActiveMemberTasks returns true if there are pending/in_progress/blocked tasks
// assigned to agents other than the given agent (typically the lead).
// Used to suppress auto-followup when the lead is waiting for teammates, not the user.
HasActiveMemberTasks(ctx context.Context, teamID uuid.UUID, excludeAgentID uuid.UUID) (bool, error)
}
// Progress
UpdateTaskProgress(ctx context.Context, taskID, teamID uuid.UUID, percent int, step string) error
// Lock renewal (heartbeat to prevent stale recovery of long-running tasks)
RenewTaskLock(ctx context.Context, taskID, teamID uuid.UUID) error
// Stale recovery (batch — all v2 active teams in single query)
// RecoverAllStaleTasks resets in_progress tasks with expired locks back to pending.
RecoverAllStaleTasks(ctx context.Context) ([]RecoveredTaskInfo, error)
// ForceRecoverAllTasks resets ALL in_progress tasks back to pending (ignoring lock expiry).
// Used on startup when no agents are running.
ForceRecoverAllTasks(ctx context.Context) ([]RecoveredTaskInfo, error)
// ListRecoverableTasks returns all pending tasks (including stale in_progress with expired locks).
// Per-team: used by DispatchUnblockedTasks after task completion.
ListRecoverableTasks(ctx context.Context, teamID uuid.UUID) ([]TeamTaskData, error)
// MarkAllStaleTasks sets pending tasks older than olderThan to stale status across all v2 active teams.
MarkAllStaleTasks(ctx context.Context, olderThan time.Time) ([]RecoveredTaskInfo, error)
// MarkInReviewStaleTasks sets in_review tasks older than olderThan to stale across all v2 active teams.
MarkInReviewStaleTasks(ctx context.Context, olderThan time.Time) ([]RecoveredTaskInfo, error)
// FixOrphanedBlockedTasks unblocks blocked tasks where all blockers reached terminal status.
FixOrphanedBlockedTasks(ctx context.Context) ([]RecoveredTaskInfo, error)
// ResetTaskStatus resets a stale or failed task back to pending for retry.
ResetTaskStatus(ctx context.Context, taskID, teamID uuid.UUID) error
// Team user grants
// TeamAccessStore manages user-level team access grants.
type TeamAccessStore interface {
GrantTeamAccess(ctx context.Context, teamID uuid.UUID, userID, role, grantedBy string) error
RevokeTeamAccess(ctx context.Context, teamID uuid.UUID, userID string) error
ListTeamGrants(ctx context.Context, teamID uuid.UUID) ([]TeamUserGrant, error)
ListUserTeams(ctx context.Context, userID string) ([]TeamData, error)
HasTeamAccess(ctx context.Context, teamID uuid.UUID, userID string) (bool, error)
}
// TeamStore composes all team sub-interfaces for backward compatibility.
// New code should depend on the specific sub-interface it needs.
type TeamStore interface {
TeamCRUDStore
TaskStore
TaskCommentStore
TaskRecoveryStore
TaskFollowupStore
TeamAccessStore
}