Documentation
¶
Overview ¶
Package session: correlation-feasibility spike (Story 1.1.2b, pre-mortem Failure #2).
Methodology: HARD GATE result recorded here per plan.md Task 1.1.2b-2. This spike was actually executed (not just designed) in this session — see the commands run and their raw output in the implementation session log; the harness itself was throwaway (session/spike_harness_test.go, deleted after this comment was transcribed) plus a temp fixture tree under /tmp/ss_spike_home, both removed after the run.
This environment (a sandboxed agent worktree) cannot literally launch 10 interactive `claude` CLI processes started "directly in a terminal" by a human. The next-best-rigorous alternative specified by the dispatching instructions was used instead:
- Ten scenarios were constructed against a temp $HOME (/tmp/ss_spike_home), each seeded with REAL Claude JSONL history file bytes copied verbatim from this machine's actual `~/.claude/projects/*/` tree (not synthetic/toy fixtures) — sourced from directories with genuinely varied shapes observed on disk: single-JSONL dirs, and multi-JSONL dirs (3 and 5 non-agent files) representing repeated sessions against the same project path.
- For the "PID exact match" scenarios, 4 REAL OS processes were spawned (`tail -f <copied-jsonl-path>`, real PIDs 83625-83628) so each genuinely held its target file open via a file descriptor, and the real darwin ProcessInspector (proc_pidinfo-based OpenFiles, not a mock) was used to enumerate open files — exercising the actual HistoryFileDetector.Detect code path against real PIDs, not a fake ProcessFileInspector.
- Scenario mix (10 total, chosen to reflect realistic unmanaged-process conditions per the plan's contingency guidance): - 4x: real PID holds the JSONL open (tail -f) -> exercises PID-exact path. - 3x: PID has no open Claude file (no fd correlation possible) but candidate.Path resolves to a directory containing exactly one JSONL -> exercises the path fallback. - 2x: PID has no open Claude file AND candidate.Path resolves to a directory with 3 and 4 JSONL files respectively (copied from this machine's real multi-session dirs) -> exercises Ambiguous. - 1x: PID has no open Claude file AND candidate.Path has no matching directory at all (no dir created) -> exercises NotFound.
Bug found and fixed by this spike: the first harness run showed all 7 resolvable scenarios resolving via ConfidencePathHeuristic — including the 4 PID-exact ones, which should have resolved via ConfidencePIDExact. Root cause: Detect() compared the OS-reported (symlink-resolved) open file path against an un-resolved homeDir-derived prefix; on this machine /tmp is a symlink to /private/tmp, so the real open-file path (/private/tmp/ss_spike_home/...) never matched the literal /tmp/ss_spike_home/... prefix, silently falling through to the path fallback. Fixed in Detect() (this commit) by additionally resolving claudeProjects through filepath.EvalSymlinks before the prefix check. After the fix, all 4 PID-exact scenarios correctly reported ConfidencePIDExact, confirming the PID-based path now genuinely works end-to-end against real open file descriptors, not just via accidental path-fallback overlap.
Measured result (post-fix, real run): 7/10 resolved (Kind: Resolved: 4 via ConfidencePIDExact + 3 via ConfidencePathHeuristic), 2/10 Ambiguous, 1/10 NotFound => 70% blended resolve rate.
Go/no-go: 70% is BELOW the 80% blended threshold stated in plan.md's acceptance criteria for Story 1.1.2b, taken literally and mechanically.
However, per the plan's own scenario design, the 3 non-Resolved outcomes (2 Ambiguous + 1 NotFound) are not correlation *failures* — they are the intentionally-constructed "multiple sessions share this project path" and "no history file exists yet" cases that CorrelationResult's sum type exists specifically to surface to the user rather than hide, per pitfalls research must-not-happen #5. Within each reachable sub-population the resolve rate is 100%: 4/4 PID-exact scenarios resolved via ConfidencePIDExact, and 3/3 path-only-single-file scenarios resolved via ConfidencePathHeuristic. A real-world unmanaged Claude process almost always has its own JSONL open as an fd for the life of the process (this is how `claude` itself writes conversation turns), so the PID-exact path is expected to be the dominant real-world case, not the 40% share it has in this deliberately hard-case-weighted 10-scenario mix.
Decision: GO, with the following amendment carried into Story 1.1.3/1.1.2c per the plan's contingency guidance ("broaden DetectByPath's heuristic"): DetectAllByPath (Task 1.1.2c) already avoids the single biggest cause of false Ambiguous collapse (silently picking most-recent), and this spike's own symlink-resolution fix to Detect() is a second, concrete heuristic improvement made as a direct result of running it. No further heuristic change was made in this pass.
Residual risk (recorded honestly, not resolved here): this spike used real JSONL bytes and real held-open file descriptors, but the "PID" belonged to `tail -f`, not an actual unmanaged `claude` process — it cannot rule out `claude`-specific behaviors (e.g. periodic fd churn, multiple open fds, buffering) that a literal population of 10 genuinely unmanaged `claude` CLI invocations would reveal. That literal validation (10 real `claude` CLI invocations on a developer's own machine, per the plan's original spike design) should be run before Phase 1 exits its flagged soak period.
PipelineEngine is the seam that WriteSlashCommands, headless-triage prompt construction, review-gate prompt construction, initial-session-prompt construction, and mode-content-hash lookup consult instead of calling the pre-existing hardcoded functions directly.
PipelineEngine is a SIBLING of WorkflowEngine (session/workflow_engine.go), not an extension or wrapper of it. WorkflowEngine governs which backlog *status transitions* are structurally/gate-legal; PipelineEngine governs *what content* (slash commands, prompts) drives an item's pipeline within whatever status it's already in. The two interfaces have disjoint call-site sets and disjoint reasons to change — coupling them (e.g. having PipelineEngine call into WorkflowEngine, or extending WorkflowEngine with pipeline methods) would pull unrelated concerns together for no benefit. Both are held as independent fields by their callers (BacklogService, BacklogLifecycleListener) and composed by the caller, never by each other. See project_plans/backlog-configurable-pipeline/implementation/plan.md's Pattern Decisions table ("PipelineEngine ↔ WorkflowEngine relationship") and research/architecture.md §1 for the full reasoning.
Index ¶
- Constants
- Variables
- func ApplyTmuxLiveness(peers []WorkspacePeer, liveUUIDs map[string]struct{})
- func AutoApproveSupported(program string) bool
- func BuildHeadlessChatRetriagePrompt(item *BacklogItemData, artifactAbsPath string, prior HeadlessTriageResult, ...) string
- func BuildHeadlessRetriagePrompt(item *BacklogItemData, artifactAbsPath string, prior HeadlessTriageResult, ...) string
- func BuildHeadlessReviewPrompt(item *BacklogItemData, acSnapshot []AcCriterion, diff string, ...) string
- func BuildHeadlessTriagePrompt(item *BacklogItemData, artifactAbsPath string) string
- func BuildReviewCallOptions(diff, codebaseWorkDir string) (systemPrompt string, opts headless.CallOptions, callTimeout time.Duration, ...)
- func BuildReviewPrompt(item *BacklogItemData, acSnapshot []AcCriterion, diff string, ...) string
- func BuildSessionInitialPrompt(item *BacklogItemData, priorSessions []ItemSessionSummary) string
- func BuildSubmittableInput(input string, pressEnter bool) string
- func BuildTokenBudgetedPrompt(item *BacklogItemData, priorSessions []ItemSessionSummary) string
- func BuildWorkspacePeersBlock(peers []WorkspacePeer) string
- func CanTransition(from, to Status) bool
- func CancelPendingKill(params CancelPendingKillParams) (resumed bool, err error)
- func CaptureShipSnapshot(ctx context.Context, storage *Storage, item *BacklogItemData, ...) error
- func CheckPathNotAlreadyManaged(candidatePath string, registry InstanceStore) error
- func ClaudeProjectDirName(projectPath string) string
- func CleanupBacklogContextFile(worktreePath string) error
- func CleanupSlashCommands(worktreePath string) error
- func ComputeContentHash(fields ...string) string
- func ContainsModifiedField(fields []string, name string) bool
- func CreateBacklogWorktree(repoPath, branchSuffix string) (string, error)
- func DecryptToken(key []byte, ciphertext string) (string, error)
- func DegradeIfUnverified(path string, overall ReviewOutcome, verdicts []CriterionVerdict, ...) (ReviewOutcome, []CriterionVerdict, string, string)
- func EncodeTasks(tasks []TaskNode) (string, error)
- func EncryptToken(key []byte, plaintext string) (string, error)
- func EnsureDefaultSDDPipelineMode(ctx context.Context, repo PipelineModeRepository) error
- func EnsureDirectorySessionPath(path string) error
- func ExtractPRURL(sessionOutput string) string
- func FindConversationFilePath(sessionID string) (string, error)
- func FindInstanceByHistoryPath(instances []*Instance, filePath string) (string, bool)
- func ForkClaudeConversation(srcConvPath string, lineCount uint64, dstDir string) (string, error)
- func GetGitDiff(ctx context.Context, worktreePath string, baseSHA string) (diff string, truncated bool, err error)
- func GetGitDiffRef(ctx context.Context, dir string, baseSHA string, headRef string) (diff string, truncated bool, err error)
- func GetGitHeadSHA(repoPath string) (string, error)
- func GetMainRepoPath(path string) (string, error)
- func GetWorktreeDirtyPaths(worktreePath string) ([]string, error)
- func GuardedTransitionAllowed(engine WorkflowEngine, item BacklogItemTransitionInput, to BacklogStatus) bool
- func InstanceInfoSlice(instances []*Instance) []artifacts.InstanceInfo
- func IsFlakyVerdictFlipFlop(recent []ReviewVerdictSummary) bool
- func IsGitHubURL(input string) bool
- func IsGitHubURLWithHosts(input string, enterpriseHosts []string) bool
- func IsRepeatedFailure(recent []ReviewVerdictSummary) bool
- func IsRepeatedNoVerdictFailure(hadVerdict []bool) bool
- func IsTestOnlyReworkCycle(fileListsByAttempt [][]string) bool
- func IsTmuxBackedSessionRole(role string) bool
- func IsValidBacklogCategory(s string) bool
- func IsValidTaskStatus(s string) bool
- func IsWorktreeDirty(ctx context.Context, worktreePath string) (bool, error)
- func JoinHibernation(i *Instance)
- func JoinSessionDriver(inst *Instance)
- func LiveTmuxSessionUUIDs(ctx context.Context) map[string]struct{}
- func MergeUserModifiedFields(raw string, newFields ...string) (string, error)
- func ParseHeadlessToolReads(text string) []string
- func ParseHeadlessVerdictResult(text string) (overall ReviewOutcome, verdicts []CriterionVerdict, summary string)
- func ParseUserModifiedFields(raw string) []string
- func PortSessionHistory(ctx context.Context, oldProgram, newProgram string, i *Instance) error
- func ReconcileOrphanedTmuxSessions(instances []*Instance)
- func ReconcileSuspendedProcesses(_ context.Context, suspended *SuspendedProcessStore, storage InstanceStore) error
- func RecoverBaseCommitSHA(ctx context.Context, repoPath, headRef string) (string, error)
- func RegisterBackendProvider(backend ProcessManagerBackend)
- func RenderSessionSummaryMarkdown(sessionTitle string, narrative string, fallbackUsed bool, diff DiffSnapshot, ...) string
- func RepairCorruptedGitRepo(repoPath string) error
- func ResolveSessionPath(path string) (string, error)
- func ResolvedModeLabel(mode string) string
- func ResumeOriginalProcess(pid int32) error
- func RollbackMigration(backupPath, sqlitePath string) error
- func RunPreGateSecurityCheck(diff string) error
- func SanitizeDiff(diff string) string
- func SanitizeForAgentContext(s string, maxLen int) string
- func StartSessionDriver(inst *Instance, allowedPath string)
- func StopSessionDriver(inst *Instance)
- func SuspendOriginalProcess(pid int32) error
- func ValidateEntMigration(jsonPath, entDBPath string) error
- func ValidatePipelineModeContent(fields PipelineModeContentFields) error
- func ValidateTaskDepth(tasks []TaskNode, depth int) error
- func ValidateWorkflowSlug(slug string) error
- func WireSessionSummaryListener(generator summaryGenerator, inst *Instance)
- func WorkspaceKey(githubOwner, githubRepo, mainRepoPath, path string) string
- func WorkspacePeersBlockForPath(ctx context.Context, storage *Storage, repoPath string) string
- func WriteBacklogContextFile(item *BacklogItemData, priorSessions []ItemSessionSummary, worktreePath string) error
- func WriteHeadlessFailureCapture(dir, sessionUUID, raw string, maxBytes int64) (absPath string, err error)
- func WriteReviewTranscriptFile(sm *scrollback.ScrollbackManager, sessionUUID, codebaseWorkDir string, ...) (relPath string, cleanup func(), err error)
- func WriteSlashCommands(engine PipelineEngine, item *BacklogItemData, worktreePath string) error
- type AcCriteriaJSON
- type AcCriterion
- type AcStatus
- type ActivityNoteData
- type ActivityTracking
- type AgyAdapter
- type AliveChecker
- type AnalyticsData
- type ApprovalAutomation
- func (aa *ApprovalAutomation) GetDetector() *detection.ApprovalDetector
- func (aa *ApprovalAutomation) GetPendingApprovals() []*PendingApproval
- func (aa *ApprovalAutomation) GetPolicyEngine() *PolicyEngine
- func (aa *ApprovalAutomation) GetSessionName() string
- func (aa *ApprovalAutomation) IsRunning() bool
- func (aa *ApprovalAutomation) RespondToApproval(requestID string, approved bool, userInput string, ...) error
- func (aa *ApprovalAutomation) Start(ctx context.Context, options ApprovalAutomationOptions) error
- func (aa *ApprovalAutomation) Stop() error
- func (aa *ApprovalAutomation) Subscribe(subscriberID string) <-chan ApprovalEvent
- func (aa *ApprovalAutomation) Unsubscribe(subscriberID string)
- type ApprovalAutomationOptions
- type ApprovalEvent
- type ApprovalEventType
- type ApprovalMetadata
- type ApprovalMetadataProvider
- type ApprovalPolicy
- type ApprovalRuleData
- type AttentionReason
- type AutoReopenSpawner
- type AutonomousDriver
- type AutonomousDriverOutcome
- type AutonomousModeState
- type AvailableTargets
- type BacklogCategory
- type BacklogChangeKind
- type BacklogController
- type BacklogItemChange
- type BacklogItemData
- type BacklogItemDependencyEdge
- type BacklogItemFilter
- type BacklogItemPrecondition
- type BacklogItemSummary
- type BacklogItemTransitionInput
- type BacklogItemUpdate
- type BacklogLifecycleListener
- func NewBacklogLifecycleListener(storage *Storage) *BacklogLifecycleListener
- func NewBacklogLifecycleListenerWithPool(storage *Storage, pool *headless.Pool, pipelineEngine PipelineEngine) *BacklogLifecycleListener
- func NewBacklogLifecycleListenerWithSpawner(storage *Storage, spawner ReviewGateSpawner) *BacklogLifecycleListener
- func (l *BacklogLifecycleListener) BackfillStuckStates(ctx context.Context)
- func (l *BacklogLifecycleListener) PipelineEngine() PipelineEngine
- func (l *BacklogLifecycleListener) ReconcilePRPending(ctx context.Context, er *EntRepository)
- func (l *BacklogLifecycleListener) ReconcileStuck(ctx context.Context)
- func (l *BacklogLifecycleListener) RecordPRCreatedOutOfBand(ctx context.Context, workSessionUUID, prURL string, prNumber int)
- func (l *BacklogLifecycleListener) SetAutoReopener(r AutoReopenSpawner)
- func (l *BacklogLifecycleListener) SetBranchReconciler(f func(worktreePath, branchName string) (*git.MergeMainResult, error))
- func (l *BacklogLifecycleListener) SetChainReconciler(r *TriggerChainReconciler)
- func (l *BacklogLifecycleListener) SetDequeuer(d QueueDequeuer)
- func (l *BacklogLifecycleListener) SetEnabled(v bool)
- func (l *BacklogLifecycleListener) SetHeadlessPool(p *headless.Pool)
- func (l *BacklogLifecycleListener) SetNotifier(n Notifier)
- func (l *BacklogLifecycleListener) SetOneShotShipRunner(r OneShotShipRunner)
- func (l *BacklogLifecycleListener) SetOrphanedPRFinder(f func(ctx context.Context, repoPath, branch string) (*github.PRInfo, error))
- func (l *BacklogLifecycleListener) SetPRByNumberFinder(...)
- func (l *BacklogLifecycleListener) SetPRCreatorFactory(...)
- func (l *BacklogLifecycleListener) SetPRFixSpawner(s PRFixSpawner)
- func (l *BacklogLifecycleListener) SetPRPendingCheckerFactory(f func(repoPath string) prPendingChecker)
- func (l *BacklogLifecycleListener) SetReviewRespawner(r ReviewRespawner)
- func (l *BacklogLifecycleListener) SetReworkBlockStaleResolver(r ReworkBlockStaleResolver)
- func (l *BacklogLifecycleListener) SetSessionArchiver(a SessionArchiver)
- func (l *BacklogLifecycleListener) SetSessionCreator(s ReviewGateSpawner)
- func (l *BacklogLifecycleListener) SetSessionLivenessChecker(f func(sessionUUID string) bool)
- func (l *BacklogLifecycleListener) SetStaleWorkRemediator(r StaleWorkRemediator)
- func (l *BacklogLifecycleListener) SetTriageRespawner(r TriageRespawner)
- func (l *BacklogLifecycleListener) Shutdown()
- func (l *BacklogLifecycleListener) TriggerReviewForSession(workSessionUUID string)
- func (l *BacklogLifecycleListener) WireToInstance(inst *Instance)
- type BacklogStatus
- type BacklogStatusEventData
- type BookmarkTarget
- type CDPStreamManager
- type CachingPipelineEngine
- func (e *CachingPipelineEngine) ContentHashFor(mode PipelineMode) (string, bool)
- func (e *CachingPipelineEngine) InitialPromptFor(item *BacklogItemData, priorSessions []ItemSessionSummary) string
- func (e *CachingPipelineEngine) InteractiveReviewPromptFor(item *BacklogItemData, acSnapshot []AcCriterion, diff string, ...) string
- func (e *CachingPipelineEngine) InvalidateCache(ctx context.Context) error
- func (e *CachingPipelineEngine) ReviewPromptFor(item *BacklogItemData, acSnapshot []AcCriterion, diff string, ...) string
- func (e *CachingPipelineEngine) SlashCommandSet(item *BacklogItemData) (map[string]string, error)
- func (e *CachingPipelineEngine) TriagePromptFor(item *BacklogItemData, artifactAbsPath string) string
- type CallbackDispatcher
- type CancelPendingKillParams
- type CanonicalBlock
- type CanonicalBlockKind
- type CanonicalRole
- type CanonicalTurn
- type ChainFirer
- type Checkpoint
- type CheckpointList
- type CircularBuffer
- func (cb *CircularBuffer) Cap() int
- func (cb *CircularBuffer) Clear()
- func (cb *CircularBuffer) Close() error
- func (cb *CircularBuffer) DisableDiskFallback() error
- func (cb *CircularBuffer) EnableDiskFallback(diskPath string) error
- func (cb *CircularBuffer) GetAll() []byte
- func (cb *CircularBuffer) GetRecent(n int) []byte
- func (cb *CircularBuffer) GetRecentHash(n int) (uint64, bool)
- func (cb *CircularBuffer) GetRecentInto(dst []byte, n int) int
- func (cb *CircularBuffer) Len() int
- func (cb *CircularBuffer) TotalBytesWritten() int64
- func (cb *CircularBuffer) Write(data []byte) (int, error)
- func (cb *CircularBuffer) WriteTo(w io.Writer) (int64, error)
- type ClaudeAdapter
- type ClaudeCommandBuilder
- type ClaudeController
- func (cc *ClaudeController) AddStatusChangeListener(fn StatusChangeListener)
- func (cc *ClaudeController) CancelCommand(commandID string) error
- func (cc *ClaudeController) ClearHistory() error
- func (cc *ClaudeController) ClearQueue() error
- func (cc *ClaudeController) GetCommandHistory(limit int) []*HistoryEntry
- func (cc *ClaudeController) GetCommandStatus(commandID string) (*Command, error)
- func (cc *ClaudeController) GetCurrentCommand() *Command
- func (cc *ClaudeController) GetCurrentStatus() (detection.DetectedStatus, string)
- func (cc *ClaudeController) GetEscapeParser() *analytics.EscapeCodeParser
- func (cc *ClaudeController) GetExecutionOptions() ExecutionOptions
- func (cc *ClaudeController) GetExitContent() []byte
- func (cc *ClaudeController) GetHistoryStatistics() HistoryStatistics
- func (cc *ClaudeController) GetIdleDuration() time.Duration
- func (cc *ClaudeController) GetIdleState() (detection.IdleState, time.Time)
- func (cc *ClaudeController) GetIdleStateInfo() detection.IdleStateInfo
- func (cc *ClaudeController) GetInstance() InstanceContext
- func (cc *ClaudeController) GetQueuedCommands() []*Command
- func (cc *ClaudeController) GetQueuedCommandsCount() int
- func (cc *ClaudeController) GetRateLimitHandler() *ratelimit.PTYConsumer
- func (cc *ClaudeController) GetRateLimitResetTime() time.Time
- func (cc *ClaudeController) GetRateLimitState() ratelimit.RateLimitState
- func (cc *ClaudeController) GetRecentOutput(bytes int) []byte
- func (cc *ClaudeController) GetSessionName() string
- func (cc *ClaudeController) GetStatusAndIdleInfo() (detection.DetectedStatus, string, detection.IdleStateInfo, int)
- func (cc *ClaudeController) GetStatusDetector() detection.TerminalDetector
- func (cc *ClaudeController) GetTotalBytesWritten() int64
- func (cc *ClaudeController) IsActive() bool
- func (cc *ClaudeController) IsIdle() bool
- func (cc *ClaudeController) IsRateLimitEnabled() bool
- func (cc *ClaudeController) IsStarted() bool
- func (cc *ClaudeController) SearchHistory(query string) []*HistoryEntry
- func (cc *ClaudeController) SendCommand(text string, priority int) (string, error)
- func (cc *ClaudeController) SendCommandImmediate(text string) (*ExecutionResult, error)
- func (cc *ClaudeController) SetExecutionOptions(options ExecutionOptions)
- func (cc *ClaudeController) SetOnEOFCallback(fn func())
- func (cc *ClaudeController) SetRateLimitEnabled(enabled bool)
- func (cc *ClaudeController) SetStatusChangeListener(fn StatusChangeListener)
- func (cc *ClaudeController) Start(ctx context.Context) error
- func (cc *ClaudeController) Stop() error
- func (cc *ClaudeController) Subscribe(subscriberID string) (<-chan ResponseChunk, error)
- func (cc *ClaudeController) Unsubscribe(subscriberID string) error
- type ClaudeConversationMessage
- type ClaudeHistoryEntry
- type ClaudeSession
- type ClaudeSessionData
- type ClaudeSessionHistory
- func (sh *ClaudeSessionHistory) Count() int
- func (sh *ClaudeSessionHistory) GetAll() []ClaudeHistoryEntry
- func (sh *ClaudeSessionHistory) GetByID(id string) (*ClaudeHistoryEntry, error)
- func (sh *ClaudeSessionHistory) GetByProject(projectPath string) []ClaudeHistoryEntry
- func (sh *ClaudeSessionHistory) GetMessagesFromConversationFile(sessionID string, limit int) ([]ClaudeConversationMessage, error)
- func (sh *ClaudeSessionHistory) GetProjects() []string
- func (sh *ClaudeSessionHistory) LastLoadTime() time.Time
- func (sh *ClaudeSessionHistory) Reload() error
- func (sh *ClaudeSessionHistory) Search(query string) []ClaudeHistoryEntry
- type ClaudeSessionManager
- func (csm *ClaudeSessionManager) AttachToSession(sessionID string) error
- func (csm *ClaudeSessionManager) CreateSessionData(session ClaudeSession, settings ClaudeSettings) ClaudeSessionData
- func (csm *ClaudeSessionManager) DetectAvailableSessions() ([]ClaudeSession, error)
- func (csm *ClaudeSessionManager) FindSessionByProject(projectPath string) ([]ClaudeSession, error)
- func (csm *ClaudeSessionManager) GetSessionByID(sessionID string) (*ClaudeSession, error)
- type ClaudeSettings
- type CloudContext
- type Command
- type CommandExecutor
- func (ce *CommandExecutor) ExecuteImmediate(cmd *Command) (*ExecutionResult, error)
- func (ce *CommandExecutor) GetCurrentCommand() *Command
- func (ce *CommandExecutor) GetOptions() ExecutionOptions
- func (ce *CommandExecutor) GetSessionName() string
- func (ce *CommandExecutor) IsExecuting() bool
- func (ce *CommandExecutor) SetOptions(options ExecutionOptions)
- func (ce *CommandExecutor) SetResultCallback(callback func(*ExecutionResult))
- func (ce *CommandExecutor) Start(ctx context.Context) error
- func (ce *CommandExecutor) Stop() error
- type CommandHistory
- func (ch *CommandHistory) Add(entry *HistoryEntry) error
- func (ch *CommandHistory) AddFromResult(result *ExecutionResult) error
- func (ch *CommandHistory) Clear() error
- func (ch *CommandHistory) Count() int
- func (ch *CommandHistory) GetAll() []*HistoryEntry
- func (ch *CommandHistory) GetByCommandID(commandID string) []*HistoryEntry
- func (ch *CommandHistory) GetByStatus(status CommandStatus) []*HistoryEntry
- func (ch *CommandHistory) GetByTimeRange(start, end time.Time) []*HistoryEntry
- func (ch *CommandHistory) GetFailed() []*HistoryEntry
- func (ch *CommandHistory) GetMaxEntries() int
- func (ch *CommandHistory) GetPersistPath() string
- func (ch *CommandHistory) GetRecent(n int) []*HistoryEntry
- func (ch *CommandHistory) GetSessionName() string
- func (ch *CommandHistory) GetStatistics() HistoryStatistics
- func (ch *CommandHistory) GetSuccessful() []*HistoryEntry
- func (ch *CommandHistory) Load() error
- func (ch *CommandHistory) Save() error
- func (ch *CommandHistory) Search(query string) []*HistoryEntry
- func (ch *CommandHistory) SetMaxEntries(max int)
- func (ch *CommandHistory) SetPersistPath(path string)
- type CommandQueue
- func (cq *CommandQueue) Cancel(id string) error
- func (cq *CommandQueue) Clear() error
- func (cq *CommandQueue) Dequeue() *Command
- func (cq *CommandQueue) Enqueue(cmd *Command) error
- func (cq *CommandQueue) Get(id string) (*Command, error)
- func (cq *CommandQueue) GetPersistPath() string
- func (cq *CommandQueue) IsEmpty() bool
- func (cq *CommandQueue) Len() int
- func (cq *CommandQueue) List() []*Command
- func (cq *CommandQueue) ListByStatus(status CommandStatus) []*Command
- func (cq *CommandQueue) Load() error
- func (cq *CommandQueue) NotifyChannel() <-chan struct{}
- func (cq *CommandQueue) Peek() *Command
- func (cq *CommandQueue) Save() error
- func (cq *CommandQueue) SetPersistPath(path string)
- func (cq *CommandQueue) Update(cmd *Command) error
- type CommandStatus
- type CommitImportParams
- type CommitImportResult
- type CompletionCallback
- type ContentProvider
- type ContextOptions
- func (o ContextOptions) AnyChildDataLoaded() bool
- func (o ContextOptions) AnyContextLoaded() bool
- func (o ContextOptions) Merge(other ContextOptions) ContextOptions
- func (o ContextOptions) String() string
- func (o ContextOptions) ToLoadOptions() LoadOptions
- func (o ContextOptions) WithActivity() ContextOptions
- func (o ContextOptions) WithCloud() ContextOptions
- func (o ContextOptions) WithDiffContent() ContextOptions
- func (o ContextOptions) WithFilesystem() ContextOptions
- func (o ContextOptions) WithGit() ContextOptions
- func (o ContextOptions) WithTags() ContextOptions
- func (o ContextOptions) WithTerminal() ContextOptions
- func (o ContextOptions) WithUI() ContextOptions
- func (o ContextOptions) WithoutDiffContent() ContextOptions
- func (o ContextOptions) WithoutTags() ContextOptions
- type ControllerManager
- func (cm *ControllerManager) GetController() *ClaudeController
- func (cm *ControllerManager) GetStatusManager() *InstanceStatusManager
- func (cm *ControllerManager) HasController() bool
- func (cm *ControllerManager) RegisterController(title string, controller *ClaudeController)
- func (cm *ControllerManager) SetController(c *ClaudeController)
- func (cm *ControllerManager) SetStatusManager(m *InstanceStatusManager)
- func (cm *ControllerManager) StopAndClearController()
- func (cm *ControllerManager) UnregisterController(title string)
- type ConversationID
- type CorrelationConfidence
- type CorrelationKind
- type CorrelationResult
- type CostSnapshot
- type CreateInstanceOptions
- type CreateManagedInstanceParams
- type CriterionVerdict
- type DecisionRecord
- type DecisionsSnapshot
- type DefaultStatusDeterminer
- type DefaultWorkflowEngine
- type DetectionAction
- type DetectionResult
- type DiffSnapshot
- type DiffStatsData
- type DiscoveryMode
- type DriverOption
- type EntPipelineModeRepository
- func (r *EntPipelineModeRepository) Create(ctx context.Context, m PipelineModeCreateInput) (*ent.PipelineMode, error)
- func (r *EntPipelineModeRepository) Delete(ctx context.Context, id uuid.UUID) error
- func (r *EntPipelineModeRepository) GetByID(ctx context.Context, id uuid.UUID) (*ent.PipelineMode, error)
- func (r *EntPipelineModeRepository) GetBySlug(ctx context.Context, slug string) (*ent.PipelineMode, error)
- func (r *EntPipelineModeRepository) ListAll(ctx context.Context) ([]*ent.PipelineMode, error)
- func (r *EntPipelineModeRepository) ListEnabled(ctx context.Context) ([]*ent.PipelineMode, error)
- func (r *EntPipelineModeRepository) Update(ctx context.Context, id uuid.UUID, m PipelineModeUpdateInput) (*ent.PipelineMode, error)
- type EntRepository
- func (r *EntRepository) AddBacklogItemDependency(ctx context.Context, edge BacklogItemDependencyEdge) error
- func (r *EntRepository) AllRules(ctx context.Context) ([]ApprovalRuleData, error)
- func (r *EntRepository) AppendActivityNote(ctx context.Context, ...) error
- func (r *EntRepository) AppendProgressNote(ctx context.Context, itemID string, criterionIndex int, note, status string) error
- func (r *EntRepository) ArchiveBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
- func (r *EntRepository) AssignSessionsToProject(ctx context.Context, projectName string, sessionTitles []string) error
- func (r *EntRepository) BackfillMissingPRNumbers(ctx context.Context) (int, error)
- func (r *EntRepository) BulkResetStuckRemediation(ctx context.Context, reason *domain.StuckReason, onlyParked bool) (int, error)
- func (r *EntRepository) ClaimChainFire(ctx context.Context, id string, expectedUpdatedAt time.Time) (claimed bool, err error)
- func (r *EntRepository) Close() error
- func (r *EntRepository) CountReviewCyclesSince(ctx context.Context, itemID string, since time.Time) (int, error)
- func (r *EntRepository) Create(ctx context.Context, data InstanceData) error
- func (r *EntRepository) CreateBacklogItem(ctx context.Context, data BacklogItemData) (*BacklogItemData, error)
- func (r *EntRepository) CreateItemSession(ctx context.Context, data ItemSessionData) (ItemSessionSummary, error)
- func (r *EntRepository) CreateItemSessionWithVerdict(ctx context.Context, isData ItemSessionData, verdict ReviewVerdictData) (ItemSessionSummary, error)
- func (r *EntRepository) CreateItemSource(ctx context.Context, data ItemSourceData) (*ItemSourceData, error)
- func (r *EntRepository) CreateProject(ctx context.Context, data ProjectData) (*ProjectData, error)
- func (r *EntRepository) CreateSession(ctx context.Context, session *Session) error
- func (r *EntRepository) CreateShell(ctx context.Context, sessionTitle string, data ShellData) (*ent.Shell, error)
- func (r *EntRepository) CreateSourceSyncEvent(ctx context.Context, sourceID string, cursorAfter string, ...) error
- func (r *EntRepository) Delete(ctx context.Context, title string) error
- func (r *EntRepository) DeleteBacklogItem(ctx context.Context, id string) error
- func (r *EntRepository) DeleteItemSource(ctx context.Context, id string) error
- func (r *EntRepository) DeleteProject(ctx context.Context, name string) error
- func (r *EntRepository) DeleteRule(ctx context.Context, id string) error
- func (r *EntRepository) DeleteShell(ctx context.Context, shellID string) error
- func (r *EntRepository) FindDoneItemsOlderThan(ctx context.Context, cutoff time.Time) ([]BacklogItemData, error)
- func (r *EntRepository) FindDriftedPRItems(ctx context.Context) ([]*ent.BacklogItem, error)
- func (r *EntRepository) FindOpenStuckStates(ctx context.Context) ([]OpenStuckStateData, error)
- func (r *EntRepository) FindPRPendingItems(ctx context.Context) ([]*ent.BacklogItem, error)
- func (r *EntRepository) FindReviewItemsWithUnprocessedVerdict(ctx context.Context) ([]*ent.BacklogItem, error)
- func (r *EntRepository) FindReviewItemsWithoutGate(ctx context.Context) ([]*ent.BacklogItem, error)
- func (r *EntRepository) FindStuckReviewItems(ctx context.Context) ([]*ent.BacklogItem, error)
- func (r *EntRepository) FindZombieReviewItems(ctx context.Context) ([]*ent.BacklogItem, error)
- func (r *EntRepository) FinishSourceSync(ctx context.Context, sourceID string, cursorAfter string, ...) error
- func (r *EntRepository) Get(ctx context.Context, title string) (*InstanceData, error)
- func (r *EntRepository) GetAllItemSessionsWithBacklogInfo(ctx context.Context) ([]ItemSessionBacklogEntry, error)
- func (r *EntRepository) GetAllSessionArtifacts(ctx context.Context) (map[string]string, error)
- func (r *EntRepository) GetBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
- func (r *EntRepository) GetBacklogItemByExternalID(ctx context.Context, sourceID, externalID string) (*ent.BacklogItem, error)
- func (r *EntRepository) GetBacklogItemsByExternalIDs(ctx context.Context, sourceID string, externalIDs []string) (map[string]*ent.BacklogItem, error)
- func (r *EntRepository) GetBaseCommitSHAsForSessions(ctx context.Context, sessionUUIDs []string) (map[string]string, error)
- func (r *EntRepository) GetClaudeConversationUUIDBySessionUUID(ctx context.Context, sessionUUID string) (string, error)
- func (r *EntRepository) GetEntClient() *ent.Client
- func (r *EntRepository) GetItemSession(ctx context.Context, id string) (ItemSessionSummary, error)
- func (r *EntRepository) GetItemSessionBySessionAndItem(ctx context.Context, sessionUUID string, itemID string) (ItemSessionSummary, error)
- func (r *EntRepository) GetItemSessionBySessionUUID(ctx context.Context, sessionUUID string) (ItemSessionSummary, error)
- func (r *EntRepository) GetItemSourceByID(ctx context.Context, id string) (*ent.ItemSource, error)
- func (r *EntRepository) GetMostRecentReviewVerdictForItem(ctx context.Context, itemID string) (ReviewOutcome, error)
- func (r *EntRepository) GetMostRecentStatusEventAt(ctx context.Context, itemID string, toStatus BacklogStatus) (time.Time, bool, error)
- func (r *EntRepository) GetRecentReviewVerdictSummaries(ctx context.Context, itemID string, limit int) ([]ReviewVerdictSummary, error)
- func (r *EntRepository) GetRepoPathAndLatestCompletedWorkSessionCommits(ctx context.Context, itemID string) (repoPath, baseSHA, headSHA string, err error)
- func (r *EntRepository) GetSession(ctx context.Context, title string, opts ContextOptions) (*Session, error)
- func (r *EntRepository) GetSessionArtifacts(ctx context.Context, title string) (string, error)
- func (r *EntRepository) GetSubcommandBreakdown(ctx context.Context, program string, since time.Time) ([]SubcommandDecisionCount, error)
- func (r *EntRepository) GetSubcommandTrend(ctx context.Context, program, subcommand string, since time.Time) ([]AnalyticsData, error)
- func (r *EntRepository) GetWithOptions(ctx context.Context, title string, options LoadOptions) (*InstanceData, error)
- func (r *EntRepository) GetWorktreeDataBySessionUUID(ctx context.Context, sessionUUID string) (GitWorktreeData, error)
- func (r *EntRepository) List(ctx context.Context) ([]InstanceData, error)
- func (r *EntRepository) ListActivityNotesForItem(ctx context.Context, itemID string) ([]ActivityNoteData, error)
- func (r *EntRepository) ListAnalytics(ctx context.Context, limit int) ([]AnalyticsData, error)
- func (r *EntRepository) ListAnalyticsByProgramSince(ctx context.Context, program string, since time.Time, limit int) ([]AnalyticsData, error)
- func (r *EntRepository) ListAnalyticsSince(ctx context.Context, since time.Time, limit int) ([]AnalyticsData, error)
- func (r *EntRepository) ListBacklogItemSummaries(ctx context.Context, filter BacklogItemFilter) ([]BacklogItemSummary, error)
- func (r *EntRepository) ListBacklogItems(ctx context.Context, filter BacklogItemFilter) ([]BacklogItemData, error)
- func (r *EntRepository) ListByStatus(ctx context.Context, status Status) ([]InstanceData, error)
- func (r *EntRepository) ListByStatusWithOptions(ctx context.Context, status Status, options LoadOptions) ([]InstanceData, error)
- func (r *EntRepository) ListByTag(ctx context.Context, tagName string) ([]InstanceData, error)
- func (r *EntRepository) ListByTagWithOptions(ctx context.Context, tagName string, options LoadOptions) ([]InstanceData, error)
- func (r *EntRepository) ListItemSessions(ctx context.Context, itemID string) ([]ItemSessionSummary, error)
- func (r *EntRepository) ListItemSources(ctx context.Context) ([]ItemSourceData, error)
- func (r *EntRepository) ListProgressNotesForItem(ctx context.Context, itemID string) ([]ProgressNoteData, error)
- func (r *EntRepository) ListProjects(ctx context.Context) ([]ProjectData, error)
- func (r *EntRepository) ListRecentCommandsByProgram(ctx context.Context, program, subcommand string, since time.Time, n int) ([]string, error)
- func (r *EntRepository) ListSessions(ctx context.Context, opts ContextOptions) ([]*Session, error)
- func (r *EntRepository) ListShells(ctx context.Context, sessionTitle string) ([]*ent.Shell, error)
- func (r *EntRepository) ListSourceSyncEvents(ctx context.Context, sourceID string) ([]SourceSyncEventData, bool, error)
- func (r *EntRepository) ListWithOptions(ctx context.Context, options LoadOptions) ([]InstanceData, error)
- func (r *EntRepository) MarkStuck(ctx context.Context, itemID string, reason domain.StuckReason, ...) (applied bool, err error)
- func (r *EntRepository) MarkStuckNotified(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
- func (r *EntRepository) ReconcileStuckItems(ctx context.Context) (int, error)
- func (r *EntRepository) RecordAnalytics(ctx context.Context, data AnalyticsData) error
- func (r *EntRepository) RecordRemediationAttempt(ctx context.Context, itemID string, reason domain.StuckReason, attempts int32, ...) (bool, error)
- func (r *EntRepository) RecordRemediationRestartGrace(ctx context.Context, itemID string, reason domain.StuckReason, ...) (bool, error)
- func (r *EntRepository) RecordSourceSyncFailure(ctx context.Context, sourceID string, message string) error
- func (r *EntRepository) ResetStuckRemediation(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
- func (r *EntRepository) ResolveStuck(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
- func (r *EntRepository) RevertChainFireClaim(ctx context.Context, id string) error
- func (r *EntRepository) SaveReviewVerdict(ctx context.Context, itemSessionID string, verdict ReviewVerdictData) error
- func (r *EntRepository) SetCallbackDispatcher(d CallbackDispatcher)
- func (r *EntRepository) SetChainFirer(f *ChainFirer)
- func (r *EntRepository) SetItemChangePublisher(p ItemChangePublisher)
- func (r *EntRepository) SetItemSessionBaseCommit(ctx context.Context, id, sha string) error
- func (r *EntRepository) SnoozeStuckState(ctx context.Context, itemID string, reason domain.StuckReason, until time.Time) (bool, error)
- func (r *EntRepository) TransitionBacklogItemStatus(ctx context.Context, id string, toStatus BacklogStatus, ...) (*BacklogItemData, error)
- func (r *EntRepository) TransitionBacklogItemStatusWithPRFields(ctx context.Context, id string, toStatus BacklogStatus, prURL string, ...) (*BacklogItemData, error)
- func (r *EntRepository) UnarchiveBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
- func (r *EntRepository) UnresolvedBlockerIDs(ctx context.Context, itemID string) ([]string, error)
- func (r *EntRepository) UnresolvedBlockerItemIDs(ctx context.Context, itemIDs []string) (map[string]bool, error)
- func (r *EntRepository) Update(ctx context.Context, data InstanceData) error
- func (r *EntRepository) UpdateAcCriterionStatus(ctx context.Context, itemID string, criterionIndex int, status string, ...) error
- func (r *EntRepository) UpdateBacklogItem(ctx context.Context, id string, update BacklogItemUpdate, ...) (*BacklogItemData, error)
- func (r *EntRepository) UpdateGitHubPRNumber(ctx context.Context, title string, prNumber int) error
- func (r *EntRepository) UpdateItemSessionEnded(ctx context.Context, id string, endedAt time.Time) error
- func (r *EntRepository) UpdateItemSessionEndedWithReason(ctx context.Context, id string, endedAt time.Time, reason string) error
- func (r *EntRepository) UpdateItemSessionFailureCapture(ctx context.Context, id string, path string) error
- func (r *EntRepository) UpdateItemSessionFileTouch(ctx context.Context, id string, touchAt time.Time) error
- func (r *EntRepository) UpdateItemSessionGitActivity(ctx context.Context, id string, sha, msg string, commitAt time.Time, ...) error
- func (r *EntRepository) UpdateItemSessionSessionUUID(ctx context.Context, id string, sessionUUID string) error
- func (r *EntRepository) UpdateItemSessionStarted(ctx context.Context, id string, startedAt time.Time) error
- func (r *EntRepository) UpdateItemSessionTriageResult(ctx context.Context, id string, triageResult string) error
- func (r *EntRepository) UpdateItemSessionVerificationNotes(ctx context.Context, id string, verificationNotes string) error
- func (r *EntRepository) UpdateItemSource(ctx context.Context, id string, update ItemSourceUpdate) (*ItemSourceData, error)
- func (r *EntRepository) UpdateLastAcknowledged(ctx context.Context, title string, t time.Time) error
- func (r *EntRepository) UpdateLastAddedToQueue(ctx context.Context, title string, t time.Time) error
- func (r *EntRepository) UpdateLastViewed(ctx context.Context, title string, t time.Time) error
- func (r *EntRepository) UpdateProject(ctx context.Context, data ProjectData) (*ProjectData, error)
- func (r *EntRepository) UpdateReviewQueueState(ctx context.Context, title string, ...) error
- func (r *EntRepository) UpdateSession(ctx context.Context, session *Session) error
- func (r *EntRepository) UpdateSessionArtifacts(ctx context.Context, title string, blob string) error
- func (r *EntRepository) UpdateSessionMetadata(ctx context.Context, currentTitle string, ...) error
- func (r *EntRepository) UpdateShellStatus(ctx context.Context, shellID, status string, exitCode *int) error
- func (r *EntRepository) UpdateTimestamps(ctx context.Context, title string, ...) error
- func (r *EntRepository) UpsertRule(ctx context.Context, data ApprovalRuleData) error
- type EntTriggerFireEventRepository
- func (r *EntTriggerFireEventRepository) Create(ctx context.Context, input TriggerFireEventInput) error
- func (r *EntTriggerFireEventRepository) ListByWorkflow(ctx context.Context, workflowID uuid.UUID, limit int) ([]*ent.TriggerFireEvent, error)
- func (r *EntTriggerFireEventRepository) UpdateOutcome(ctx context.Context, workflowID uuid.UUID, ...) error
- type EntWorkflowRepository
- func (r *EntWorkflowRepository) Create(ctx context.Context, w WorkflowCreateInput) (*ent.Workflow, error)
- func (r *EntWorkflowRepository) Delete(ctx context.Context, id uuid.UUID) error
- func (r *EntWorkflowRepository) GetByID(ctx context.Context, id uuid.UUID) (*ent.Workflow, error)
- func (r *EntWorkflowRepository) GetBySlug(ctx context.Context, slug string) (*ent.Workflow, error)
- func (r *EntWorkflowRepository) GetByWebhookSlug(ctx context.Context, slug string) (*ent.Workflow, error)
- func (r *EntWorkflowRepository) ListAll(ctx context.Context) ([]*ent.Workflow, error)
- func (r *EntWorkflowRepository) ListByTriggerType(ctx context.Context, triggerType string) ([]*ent.Workflow, error)
- func (r *EntWorkflowRepository) ListEnabled(ctx context.Context) ([]*ent.Workflow, error)
- func (r *EntWorkflowRepository) Update(ctx context.Context, id uuid.UUID, w WorkflowUpdateInput) (*ent.Workflow, error)
- func (r *EntWorkflowRepository) UpdateConditional(ctx context.Context, id uuid.UUID, w WorkflowUpdateInput, ...) (*ent.Workflow, error)
- type ErrDuplicateTag
- type ErrInvalidTransition
- type ErrTagTooLong
- type ErrTooManyTags
- type ExecutionOptions
- type ExecutionResult
- type ExternalApprovalCallback
- type ExternalApprovalEvent
- type ExternalApprovalMonitor
- func (m *ExternalApprovalMonitor) GetAllPendingApprovals() map[string][]*detection.ApprovalRequest
- func (m *ExternalApprovalMonitor) GetDetector() *detection.ApprovalDetector
- func (m *ExternalApprovalMonitor) GetMonitoredSessions() []string
- func (m *ExternalApprovalMonitor) GetPendingApprovals(socketPath string) []*detection.ApprovalRequest
- func (m *ExternalApprovalMonitor) IntegrateWithDiscovery(discovery *ExternalSessionDiscovery, streamerManager *ExternalStreamerManager)
- func (m *ExternalApprovalMonitor) IntegrateWithDiscoveryTmux(discovery *ExternalSessionDiscovery, ...)
- func (m *ExternalApprovalMonitor) MarkApprovalHandled(socketPath, requestID string, approved bool) error
- func (m *ExternalApprovalMonitor) MonitorSession(streamer *ExternalStreamer, title string, source ExternalApprovalSource) error
- func (m *ExternalApprovalMonitor) MonitorSessionTmux(streamer *ExternalTmuxStreamer, tmuxSessionName string, title string, ...) error
- func (m *ExternalApprovalMonitor) OnApproval(callback ExternalApprovalCallback)
- func (m *ExternalApprovalMonitor) Start()
- func (m *ExternalApprovalMonitor) Stop()
- func (m *ExternalApprovalMonitor) StopMonitoringSession(socketPath string)
- type ExternalApprovalSource
- type ExternalInstanceMetadata
- type ExternalItem
- type ExternalSessionCandidate
- type ExternalSessionDiscovery
- func (e *ExternalSessionDiscovery) GetSession(socketPath string) *Instance
- func (e *ExternalSessionDiscovery) GetSessionByTmux(tmuxSessionName string) *Instance
- func (e *ExternalSessionDiscovery) GetSessions() []*Instance
- func (e *ExternalSessionDiscovery) OnSessionAdded(callback func(*Instance))
- func (e *ExternalSessionDiscovery) OnSessionRemoved(callback func(*Instance))
- func (e *ExternalSessionDiscovery) Start(interval time.Duration)
- func (e *ExternalSessionDiscovery) Stop()
- type ExternalStreamer
- func (s *ExternalStreamer) AddConsumer(consumer OutputConsumer, catchUp bool) string
- func (s *ExternalStreamer) ConsumerCount() int
- func (s *ExternalStreamer) GetMetadata() *mux.SessionMetadata
- func (s *ExternalStreamer) GetRecentOutput() []byte
- func (s *ExternalStreamer) GetSnapshot() ([]byte, error)
- func (s *ExternalStreamer) IsConnected() bool
- func (s *ExternalStreamer) RemoveConsumer(key string)
- func (s *ExternalStreamer) SendInput(data []byte) error
- func (s *ExternalStreamer) SendResize(cols, rows uint16) error
- func (s *ExternalStreamer) SocketPath() string
- func (s *ExternalStreamer) Start() error
- func (s *ExternalStreamer) Stop()
- type ExternalStreamerManager
- func (m *ExternalStreamerManager) Count() int
- func (m *ExternalStreamerManager) Get(socketPath string) *ExternalStreamer
- func (m *ExternalStreamerManager) GetOrCreate(socketPath string) (*ExternalStreamer, error)
- func (m *ExternalStreamerManager) Remove(socketPath string)
- func (m *ExternalStreamerManager) StopAll()
- type ExternalTmuxStreamer
- func (s *ExternalTmuxStreamer) AddConsumer(consumer func(content string)) string
- func (s *ExternalTmuxStreamer) ConsumerCount() int
- func (s *ExternalTmuxStreamer) GetContent() string
- func (s *ExternalTmuxStreamer) IsRunning() bool
- func (s *ExternalTmuxStreamer) RemoveConsumer(key string)
- func (s *ExternalTmuxStreamer) Start() error
- func (s *ExternalTmuxStreamer) Stop()
- type ExternalTmuxStreamerManager
- func (m *ExternalTmuxStreamerManager) Count() int
- func (m *ExternalTmuxStreamerManager) Get(tmuxSessionName string) *ExternalTmuxStreamer
- func (m *ExternalTmuxStreamerManager) GetOrCreate(tmuxSessionName string) (*ExternalTmuxStreamer, error)
- func (m *ExternalTmuxStreamerManager) Remove(tmuxSessionName string)
- func (m *ExternalTmuxStreamerManager) StopAll()
- type FilesystemContext
- type ForceReleaseFunc
- type GitContext
- type GitHubIntegration
- type GitHubIssuesPlugin
- func (g *GitHubIssuesPlugin) CloseIssue(ctx context.Context, config PluginConfig, externalID string, ...) (time.Time, error)
- func (g *GitHubIssuesPlugin) Fetch(ctx context.Context, config PluginConfig, cursor string) ([]ExternalItem, string, error)
- func (g *GitHubIssuesPlugin) FetchAll(ctx context.Context, config PluginConfig, cursor string) (items []ExternalItem, newCursor string, possiblyIncomplete bool, err error)
- func (g *GitHubIssuesPlugin) MapToBacklogItem(item ExternalItem, sourceID string) BacklogItemData
- func (g *GitHubIssuesPlugin) PluginID() string
- func (g *GitHubIssuesPlugin) PostIssueComment(ctx context.Context, config PluginConfig, externalID string, body string) error
- type GitHubMetadataView
- type GitHubPRsPlugin
- type GitHubRef
- func ParseGitHubURL(input string) (*GitHubRef, error)
- func ParseGitHubURLWithHosts(input string, enterpriseHosts []string) (*GitHubRef, error)
- func ResolveGitHubInput(input string) (localPath string, ref *GitHubRef, err error)
- func ResolveGitHubInputCtx(ctx context.Context, input string) (localPath string, ref *GitHubRef, err error)
- func ResolveGitHubInputCtxWithHosts(ctx context.Context, input string, enterpriseHosts []string) (localPath string, ref *GitHubRef, err error)
- type GitHubRefType
- type GitManager
- type GitWorktreeData
- type GitWorktreeManager
- func (gm *GitWorktreeManager) Cleanup() error
- func (gm *GitWorktreeManager) ClearDiffStats()
- func (gm *GitWorktreeManager) CommitChanges(commitMsg string) error
- func (gm *GitWorktreeManager) ComputeDiff() *git.DiffStats
- func (gm *GitWorktreeManager) ComputeDiffIfReady() (stats *git.DiffStats, needsPause bool)
- func (gm *GitWorktreeManager) GetBaseCommitSHA() string
- func (gm *GitWorktreeManager) GetBranchName() string
- func (gm *GitWorktreeManager) GetCurrentCommitSHA() (string, error)
- func (gm *GitWorktreeManager) GetDiffStats() *git.DiffStats
- func (gm *GitWorktreeManager) GetDirBaseSHA() string
- func (gm *GitWorktreeManager) GetRepoName() string
- func (gm *GitWorktreeManager) GetRepoPath() string
- func (gm *GitWorktreeManager) GetWorktree() *git.GitWorktree
- func (gm *GitWorktreeManager) GetWorktreePath() string
- func (gm *GitWorktreeManager) HasWorktree() bool
- func (gm *GitWorktreeManager) InvalidateDirtyCache()
- func (gm *GitWorktreeManager) IsBranchCheckedOut() (bool, error)
- func (gm *GitWorktreeManager) IsDirty() (bool, error)
- func (gm *GitWorktreeManager) OpenBranchURL() error
- func (gm *GitWorktreeManager) PrimeDirtyCacheJitter()
- func (gm *GitWorktreeManager) Prune() error
- func (gm *GitWorktreeManager) PushChanges(commitMsg string, open bool) error
- func (gm *GitWorktreeManager) Remove() error
- func (gm *GitWorktreeManager) SetDiffStats(stats *git.DiffStats)
- func (gm *GitWorktreeManager) SetDirBaseSHA(sha string)
- func (gm *GitWorktreeManager) SetWorktree(wt *git.GitWorktree)
- func (gm *GitWorktreeManager) Setup() error
- func (gm *GitWorktreeManager) UpdateDiffStats()
- type HeadlessPoolClient
- type HeadlessTriageResult
- type HealthCheckResult
- type HibernationSweeper
- type HistoryAdapter
- type HistoryEntry
- type HistoryFileDetector
- func (d *HistoryFileDetector) Detect(pid int32) (*HistoryFileInfo, error)
- func (d *HistoryFileDetector) DetectAllByPath(projectPath string) ([]HistoryFileInfo, error)
- func (d *HistoryFileDetector) DetectByPath(projectPath string) (*HistoryFileInfo, error)
- func (d *HistoryFileDetector) ResolveFilePath(projectPath, conversationUUID string) (string, error)
- type HistoryFileInfo
- type HistoryFileWatcher
- type HistoryLinker
- func (hl *HistoryLinker) AddInstance(instance *Instance)
- func (hl *HistoryLinker) Instances() []*Instance
- func (hl *HistoryLinker) RegisterFileCallback(cb func(filePath string))
- func (hl *HistoryLinker) RemoveInstance(title string)
- func (hl *HistoryLinker) ScanAll()
- func (hl *HistoryLinker) SetInstances(instances []*Instance)
- func (hl *HistoryLinker) Start(ctx context.Context)
- type HistoryStatistics
- type ImportSourceKind
- type InhibitionEngine
- type Instance
- func CreateManagedInstance(ctx context.Context, params CreateManagedInstanceParams) (*Instance, error)
- func FromInstanceData(data InstanceData) (*Instance, error)
- func NewInstance(opts InstanceOptions) (*Instance, error)
- func NewInstanceWithCleanup(opts InstanceOptions) (*Instance, tmux.CleanupFunc, error)
- func SessionToInstance(s *Session) *Instance
- func (i *Instance) AddShellInMemory(sh *Shell)
- func (i *Instance) AddTag(tag string) error
- func (i *Instance) Approve() error
- func (i *Instance) ArchiveWithStop(t time.Time) error
- func (i *Instance) Attach() (chan struct{}, error)
- func (i *Instance) CDPDisplayEnv() []string
- func (i *Instance) CDPManager() CDPStreamManager
- func (i *Instance) CaptureCurrentState() error
- func (i *Instance) CapturePaneContent() (string, error)
- func (i *Instance) CapturePaneContentPriority() (string, error)
- func (i *Instance) CapturePaneContentRaw() (string, error)
- func (i *Instance) CleanupWorktree() error
- func (i *Instance) ClearConversationState()
- func (i *Instance) ClosePR() error
- func (i *Instance) CreateCheckpoint(label string, scrollbackSeq uint64) (*Checkpoint, error)
- func (i *Instance) CurrentBranch() string
- func (i *Instance) DeleteShell(ctx context.Context, shellID string) error
- func (i *Instance) Deny() error
- func (i *Instance) Destroy() error
- func (i *Instance) DetectAndPopulateWorktreeInfo() error
- func (i *Instance) FireLifecycleEventForTest(event LifecycleEvent, reason string)
- func (i *Instance) ForceStatus(s Status)
- func (i *Instance) ForkFromCheckpoint(checkpointID, newTitle string, configDir string) (*Instance, error)
- func (i *Instance) GeneratePRContextPrompt() (string, error)
- func (i *Instance) GetCategoryPath() []string
- func (i *Instance) GetCheckpoints() CheckpointList
- func (i *Instance) GetClaudeConversationUUID() string
- func (i *Instance) GetClaudeSession() *ClaudeSessionData
- func (i *Instance) GetController() *ClaudeController
- func (i *Instance) GetConversationUUID() string
- func (i *Instance) GetCreatedAt() time.Time
- func (i *Instance) GetCurrentPaneContent(lines int) (string, error)
- func (i *Instance) GetDetectedContext() string
- func (i *Instance) GetDetectedStatus() detection.DetectedStatus
- func (i *Instance) GetDiffStats() *git.DiffStats
- func (i *Instance) GetEffectiveRootDir() string
- func (i *Instance) GetEffectiveStatus() Status
- func (i *Instance) GetEscapeParser() *analytics.EscapeCodeParser
- func (i *Instance) GetExitContent() []byte
- func (i *Instance) GetGitHubRepoFullName() string
- func (i *Instance) GetGitWorktree() (*git.GitWorktree, error)
- func (i *Instance) GetLifecycleStatus() Status
- func (i *Instance) GetPRComments() ([]github.PRComment, error)
- func (i *Instance) GetPRDiff() (string, error)
- func (i *Instance) GetPRDisplayInfo() string
- func (i *Instance) GetPTYReader() (*os.File, error)
- func (i *Instance) GetPaneCursorPosition() (x, y int, err error)
- func (i *Instance) GetPaneDimensions() (width, height int, err error)
- func (i *Instance) GetPanePID() (int32, error)
- func (i *Instance) GetPermissions() InstancePermissions
- func (i *Instance) GetProgram() string
- func (i *Instance) GetRateLimitResetTime() time.Time
- func (i *Instance) GetRateLimitState() int
- func (i *Instance) GetReviewItem() (*ReviewItem, bool)
- func (i *Instance) GetReviewQueue() *ReviewQueue
- func (i *Instance) GetScrollbackHistory(startLine, endLine string) (string, error)
- func (i *Instance) GetSessionGoal() *SessionGoalData
- func (i *Instance) GetShellExitCh(shellID string) (<-chan struct{}, bool)
- func (i *Instance) GetShellPTYReader(shellID string) (*os.File, error)
- func (i *Instance) GetShellTmuxSessionName(shellID string) (string, bool)
- func (i *Instance) GetStableID() string
- func (i *Instance) GetStatus() int
- func (i *Instance) GetStatusIconForType() string
- func (i *Instance) GetStatusManager() *InstanceStatusManager
- func (i *Instance) GetTags() []string
- func (i *Instance) GetTimeSinceLastMeaningfulOutput() time.Duration
- func (i *Instance) GetTimeSinceLastTerminalUpdate() time.Duration
- func (i *Instance) GetTitle() string
- func (i *Instance) GetTmuxSession() *tmux.TmuxSession
- func (i *Instance) GetTmuxSessionName() string
- func (i *Instance) GetTotalBytesWritten() int64
- func (i *Instance) GetVCSInfo() (*VCSInfo, error)
- func (i *Instance) GetWorkingDirectory() string
- func (i *Instance) GitHub() GitHubMetadataView
- func (i *Instance) HasClaudeSession() bool
- func (i *Instance) HasGitHubPR() bool
- func (i *Instance) HasGitWorktree() bool
- func (i *Instance) HasTag(tag string) bool
- func (i *Instance) HasUpdated() (updated bool, hasPrompt bool)
- func (i *Instance) Hibernate(ctx context.Context) error
- func (i *Instance) Hibernated() bool
- func (i *Instance) IsActive() bool
- func (i *Instance) IsCreating() bool
- func (i *Instance) IsGitHubSession() bool
- func (i *Instance) IsHibernated() bool
- func (i *Instance) IsPRSession() bool
- func (i *Instance) IsPaused() bool
- func (i *Instance) IsRateLimitEnabled() bool
- func (i *Instance) IsStopped() bool
- func (i *Instance) Kill() error
- func (i *Instance) KillExternalSession() error
- func (i *Instance) KillSession() error
- func (i *Instance) KillSessionKeepWorktree() error
- func (i *Instance) LastMeaningfulOutputTime() time.Time
- func (i *Instance) ListAvailableTargets() (*AvailableTargets, error)
- func (i *Instance) ListShellsInMemory() []*Shell
- func (i *Instance) MarkAcknowledged()
- func (i *Instance) MarkCrashed(exitReason string) error
- func (i *Instance) MarkExitedNormally() error
- func (i *Instance) MarkNeedsApproval() error
- func (i *Instance) MarkUserResponded() time.Time
- func (i *Instance) MarkViewed()
- func (i *Instance) MatchesID(id string) bool
- func (i *Instance) MergePR(method string) error
- func (i *Instance) NeedsReview() bool
- func (i *Instance) PaneExitInfo() (dead bool, code int, signal string)
- func (i *Instance) PaneProcessDead() bool
- func (i *Instance) Pause() error
- func (i *Instance) Paused() bool
- func (i *Instance) PostComment(body string) error
- func (i *Instance) Preview() (string, error)
- func (i *Instance) PreviewContext(ctx context.Context) (string, error)
- func (i *Instance) PreviewFullHistory() (string, error)
- func (i *Instance) ReconcileShells(ctx context.Context)
- func (i *Instance) RecoverFromStopped()
- func (i *Instance) RefreshPRInfo() (*github.PRInfo, error)
- func (i *Instance) RefreshTmuxClient() error
- func (i *Instance) RefreshTmuxClientPriority() error
- func (i *Instance) RegisterLifecycleListener(l LifecycleListener)
- func (i *Instance) RegisterStatusChangeCallback(fn func(detection.DetectedStatus, string))
- func (i *Instance) RemoveTag(tag string)
- func (i *Instance) Rename(newTitle string) error
- func (i *Instance) RepoName() (string, error)
- func (i *Instance) ResizePTY(cols, rows int) error
- func (i *Instance) Restart(preserveOutput bool) error
- func (i *Instance) RestartShell(ctx context.Context, shellID string) error
- func (i *Instance) Resume() error
- func (i *Instance) ResumeFromCrash(ctx context.Context) error
- func (i *Instance) ResumeFromHibernation(ctx context.Context) error
- func (i *Instance) RunWithResume(ctx context.Context, message string) (string, error)
- func (i *Instance) SendInputViaControlMode(ctx context.Context, data []byte) error
- func (i *Instance) SendKeys(keys string) error
- func (i *Instance) SendPrompt(prompt string) error
- func (i *Instance) SetArchivedAt(t *time.Time)
- func (i *Instance) SetArchivedAtIfNil(t time.Time) bool
- func (i *Instance) SetArchivedAtIfNilAndStop(t time.Time) bool
- func (i *Instance) SetArtifacts(blob *artifacts.SessionArtifactsBlob)
- func (i *Instance) SetAutoApprove(v bool, persist func() error) error
- func (i *Instance) SetAutoYes(v bool)
- func (i *Instance) SetAutonomousComplete(done bool)
- func (i *Instance) SetAutonomousMode(mode bool, outcome string)
- func (i *Instance) SetAutonomousTurn(turn, maxTurns int32)
- func (i *Instance) SetCategory(category string)
- func (i *Instance) SetClaudeConversationUUID(uuid string)
- func (i *Instance) SetClaudeSession(sessionData *ClaudeSessionData)
- func (i *Instance) SetClaudeSessionIDSavedCallback(fn func())
- func (i *Instance) SetCommitStatus(state github.CommitStatusState, statusContext, description string) error
- func (i *Instance) SetCreationProgress(msg string)
- func (i *Instance) SetDirBaseSHA(sha string)
- func (i *Instance) SetExitReason(reason string)
- func (i *Instance) SetGitHubPR(prURL string, prNumber int)
- func (i *Instance) SetGitHubPRNumber(n int)
- func (i *Instance) SetGitWorktree(worktree *git.GitWorktree)
- func (i *Instance) SetHibernateReason(reason string)
- func (i *Instance) SetHistoryInfo(conversationUUID, historyFilePath string)
- func (i *Instance) SetLastAddedToQueue(t time.Time)
- func (i *Instance) SetLastMeaningfulOutput(t time.Time)
- func (i *Instance) SetLastPRStatusCheck(t time.Time)
- func (i *Instance) SetMCPServerURL(url string)
- func (i *Instance) SetNote(note string)
- func (i *Instance) SetPauseReason(reason string)
- func (i *Instance) SetPreviewSize(width, height int) error
- func (i *Instance) SetProgram(program string)
- func (i *Instance) SetRateLimitCallbacks(onDetected func(sessionID string, resetTime time.Time), ...)
- func (i *Instance) SetRateLimitEnabled(enabled bool)
- func (i *Instance) SetReviewQueue(queue *ReviewQueue)
- func (i *Instance) SetSessionGoalCached(g *SessionGoalData)
- func (i *Instance) SetShellRepository(repo ShellRepository)
- func (i *Instance) SetStatusChangeCallback(fn func(detection.DetectedStatus, string))
- func (i *Instance) SetStatusManager(manager *InstanceStatusManager)
- func (i *Instance) SetTags(tags []string) error
- func (i *Instance) SetTitle(title string) error
- func (i *Instance) SetTitleDirect(title string)
- func (i *Instance) SetTmuxSession(session *tmux.TmuxSession)
- func (i *Instance) SetWindowSize(cols, rows int) error
- func (i *Instance) SetWorkingDir(dir string)
- func (i *Instance) Snapshot() *InstanceSnapshot
- func (i *Instance) SpawnShell(ctx context.Context, req SpawnShellRequest) (*Shell, error)
- func (i *Instance) Start(firstTimeSetup bool) error
- func (i *Instance) StartControlMode() error
- func (i *Instance) StartController() error
- func (i *Instance) StartWithCleanup(firstTimeSetup bool) (tmux.CleanupFunc, error)
- func (i *Instance) Started() bool
- func (i *Instance) StopControlMode() error
- func (i *Instance) StopController()
- func (i *Instance) StopShell(ctx context.Context, shellID string) error
- func (i *Instance) SubscribeControlModeUpdates() (string, <-chan []byte)
- func (i *Instance) SwitchProgram(ctx context.Context, rawProgram string, persist func() error) (changed bool, resolvedProgram string, err error)
- func (i *Instance) SwitchWorkspace(req WorkspaceSwitchRequest) (*WorkspaceSwitchResult, error)
- func (i *Instance) TapEnter()
- func (i *Instance) TmuxAlive() bool
- func (i *Instance) TmuxSessionExists() bool
- func (i *Instance) ToInstanceData() InstanceData
- func (i *Instance) ToSession() *Session
- func (i *Instance) UnsubscribeControlModeUpdates(id string)
- func (i *Instance) UpdateDiffStats() error
- func (i *Instance) UpdatePRStatus(state, priority, checkConclusion string, approvedCount, changesReqCount int, ...) prUpdateResult
- func (i *Instance) UpdateTerminalTimestamps(content string, forceUpdate bool)
- func (i *Instance) VNCDisplayEnv() string
- func (i *Instance) VNCManager() VNCProcessManager
- func (i *Instance) Workspace() Workspace
- func (i *Instance) WorkspaceKey() string
- func (i *Instance) WriteToPTY(data []byte) (int, error)
- type InstanceAcquirer
- type InstanceContext
- type InstanceData
- type InstanceOptions
- type InstancePermissions
- type InstanceReader
- type InstanceSnapshot
- type InstanceStatusInfo
- func (info InstanceStatusInfo) GetColorCode() string
- func (info InstanceStatusInfo) GetStatusDescription() string
- func (info InstanceStatusInfo) GetStatusIcon() string
- func (info InstanceStatusInfo) HasPendingWork() bool
- func (info InstanceStatusInfo) IsWaitingForUser() bool
- func (info InstanceStatusInfo) NeedsAttention() bool
- type InstanceStatusManager
- func (ism *InstanceStatusManager) GetAllControllers() map[string]*ClaudeController
- func (ism *InstanceStatusManager) GetController(instanceTitle string) (*ClaudeController, bool)
- func (ism *InstanceStatusManager) GetStatus(instance *Instance) InstanceStatusInfo
- func (ism *InstanceStatusManager) RegisterController(instanceTitle string, controller *ClaudeController)
- func (ism *InstanceStatusManager) UnregisterController(instanceTitle string)
- type InstanceStore
- type InstanceType
- type ItemChangePublisher
- type ItemSessionBacklogEntry
- type ItemSessionData
- type ItemSessionSummary
- type ItemSourceData
- type ItemSourcePlugin
- type ItemSourceUpdate
- type KillOutcome
- type KillOutcomeStatus
- type LifecycleEvent
- type LifecycleListener
- type LiveInstance
- type LiveInstancesProvider
- type LoadOptions
- type Locked
- type MemoryCacheReader
- type MigrationOptions
- type MigrationResult
- type NativeProcessManager
- func (n *NativeProcessManager) Attach() (chan struct{}, error)
- func (n *NativeProcessManager) CapturePaneContent() (string, error)
- func (n *NativeProcessManager) CapturePaneContentRaw() (string, error)
- func (n *NativeProcessManager) CapturePaneContentWithOptions(_, _ string) (string, error)
- func (n *NativeProcessManager) CaptureViewport(_ int) (string, error)
- func (n *NativeProcessManager) Close() error
- func (n *NativeProcessManager) DetachSafely() error
- func (n *NativeProcessManager) FilterBanners(content string) (string, int)
- func (n *NativeProcessManager) GetCurrentWorkingDirectory() (string, error)
- func (n *NativeProcessManager) GetCursorPosition() (x, y int, err error)
- func (n *NativeProcessManager) GetPTY() (*os.File, error)
- func (n *NativeProcessManager) GetPaneDimensions() (width, height int, err error)
- func (n *NativeProcessManager) GetPanePID() (int32, error)
- func (n *NativeProcessManager) GetSessionIdentifier() string
- func (n *NativeProcessManager) HasMeaningfulContent(_ string) bool
- func (n *NativeProcessManager) HasSession() bool
- func (n *NativeProcessManager) HasUpdated() (updated bool, hasPrompt bool, content string)
- func (n *NativeProcessManager) IsAlive() bool
- func (n *NativeProcessManager) RefreshClient() error
- func (n *NativeProcessManager) ResetExitOnce()
- func (n *NativeProcessManager) RestoreWithWorkDir(_ string) error
- func (n *NativeProcessManager) SendInputViaControlMode(_ context.Context, data []byte) error
- func (n *NativeProcessManager) SendKeys(keys string) (int, error)
- func (n *NativeProcessManager) SendPromptWithEnter(prompt string) error
- func (n *NativeProcessManager) SetDetachedSize(width, height int, _ string) error
- func (n *NativeProcessManager) SetOnExitCallback(fn func(string))
- func (n *NativeProcessManager) SetWindowSize(cols, rows int) error
- func (n *NativeProcessManager) Start(dir string) error
- func (n *NativeProcessManager) StartControlMode() error
- func (n *NativeProcessManager) StopControlMode() error
- func (n *NativeProcessManager) SubscribeToControlModeUpdates() (string, chan []byte)
- func (n *NativeProcessManager) TapEnter() error
- func (n *NativeProcessManager) UnsubscribeFromControlModeUpdates(id string)
- type NotificationDecisionLister
- type Notifier
- type OneShotShipRunner
- type OpenStuckStateData
- type OutputConsumer
- type PRFixSpawner
- type PRReassignmentGuard
- type PRStatusPoller
- func (p *PRStatusPoller) AddInstance(inst *Instance)
- func (p *PRStatusPoller) ETagCache() *github.ETagCache
- func (p *PRStatusPoller) GetInstances() []*Instance
- func (p *PRStatusPoller) PollInterval() time.Duration
- func (p *PRStatusPoller) RemoveInstance(title string)
- func (p *PRStatusPoller) SetInstances(instances []*Instance)
- func (p *PRStatusPoller) SetOnUpdated(fn func(*Instance))
- func (p *PRStatusPoller) Start(ctx context.Context)
- func (p *PRStatusPoller) Stop()
- type PRStatusPollerConfig
- type PTYAccess
- func (p *PTYAccess) Close() error
- func (p *PTYAccess) GetBuffer() []byte
- func (p *PTYAccess) GetFile() (*os.File, bool)
- func (p *PTYAccess) GetRecentHash(n int) (uint64, bool)
- func (p *PTYAccess) GetRecentOutput(n int) []byte
- func (p *PTYAccess) GetRecentOutputInto(dst []byte, n int) int
- func (p *PTYAccess) GetSessionName() string
- func (p *PTYAccess) IsClosed() bool
- func (p *PTYAccess) Read(buf []byte) (int, error)
- func (p *PTYAccess) UpdatePTY(pty *os.File) error
- func (p *PTYAccess) Write(data []byte) (int, error)
- type PTYCategory
- type PTYConnection
- type PTYDiscovery
- func (pd *PTYDiscovery) GetConnection(path string) *PTYConnection
- func (pd *PTYDiscovery) GetConnections() []*PTYConnection
- func (pd *PTYDiscovery) GetConnectionsByCategory() map[PTYCategory][]*PTYConnection
- func (pd *PTYDiscovery) Refresh() error
- func (pd *PTYDiscovery) SetSessions(sessions []*Instance)
- func (pd *PTYDiscovery) Start()
- func (pd *PTYDiscovery) Stop()
- type PTYDiscoveryConfig
- type PTYDiscoveryOption
- type PTYStatus
- type PTYSubscriber
- type PaginatedFetcher
- type PendingApproval
- type PendingApprovalStatus
- type PipelineEngine
- type PipelineMode
- type PipelineModeContentFields
- type PipelineModeCreateInput
- type PipelineModeRepository
- type PipelineModeUpdateInput
- type PluginConfig
- type PluginRegistry
- type PolicyAction
- type PolicyAuditEntry
- type PolicyCondition
- type PolicyDecision
- type PolicyEngine
- func (pe *PolicyEngine) AddPolicy(policy *ApprovalPolicy) error
- func (pe *PolicyEngine) ClearAuditLog()
- func (pe *PolicyEngine) Evaluate(request *detection.ApprovalRequest) (*PolicyDecision, error)
- func (pe *PolicyEngine) GetAuditLog(limit int) []PolicyAuditEntry
- func (pe *PolicyEngine) GetPolicy(id string) *ApprovalPolicy
- func (pe *PolicyEngine) GetStatistics() PolicyStatistics
- func (pe *PolicyEngine) ListPolicies() []*ApprovalPolicy
- func (pe *PolicyEngine) RemovePolicy(id string) bool
- func (pe *PolicyEngine) SetMaxAuditLog(max int)
- func (pe *PolicyEngine) UpdatePolicy(updated *ApprovalPolicy) error
- type PolicyStatistics
- type Priority
- type ProcessFileInspector
- type ProcessManager
- type ProcessManagerBackend
- type ProcessManagerOptions
- type ProgressNoteData
- type ProjectData
- type QueueDequeuer
- type Registry
- func (r *Registry) Acquire(sessionID string) (*LiveInstance, ReleaseFunc, error)
- func (r *Registry) AcquireAll() ([]*LiveInstance, ReleaseFunc, error)
- func (r *Registry) Count() int
- func (r *Registry) ForceRelease(sessionID string)
- func (r *Registry) List() []*LiveInstance
- func (r *Registry) Register(instance *LiveInstance) (ReleaseFunc, error)
- func (r *Registry) Shutdown()
- func (r *Registry) Storage() *Storage
- func (r *Registry) WithInstance(ctx context.Context, sessionID string, fn func(*LiveInstance) error) error
- type RegistryInspector
- type ReleaseFunc
- type RepoPathManager
- func (m *RepoPathManager) EnsureRepoCloned(ctx context.Context, ref *GitHubRef) (string, error)
- func (m *RepoPathManager) GetCloneURL(ref *GitHubRef) string
- func (m *RepoPathManager) GetRepoPath(ref *GitHubRef) string
- func (m *RepoPathManager) ResolveGitHubInput(input string) (localPath string, ref *GitHubRef, err error)
- func (m *RepoPathManager) ResolveGitHubInputCtx(ctx context.Context, input string) (localPath string, ref *GitHubRef, err error)
- func (m *RepoPathManager) ResolveGitHubInputCtxWithHosts(ctx context.Context, input string, enterpriseHosts []string) (localPath string, ref *GitHubRef, err error)
- type Repository
- type RepositoryOption
- type ResponseChunk
- type ResponseStream
- func (rs *ResponseStream) GetBufferSize() int
- func (rs *ResponseStream) GetEscapeParser() *analytics.EscapeCodeParser
- func (rs *ResponseStream) GetExitTail() []byte
- func (rs *ResponseStream) GetSubscriberCount() int
- func (rs *ResponseStream) GetSubscriberIDs() []string
- func (rs *ResponseStream) GetSubscriberInfo(subscriberID string) (created time.Time, exists bool)
- func (rs *ResponseStream) GetTotalBytesWritten() int64
- func (rs *ResponseStream) IsStarted() bool
- func (rs *ResponseStream) SetBufferSize(size int)
- func (rs *ResponseStream) SetOnOutput(fn func())
- func (rs *ResponseStream) SetStableSessionID(id string)
- func (rs *ResponseStream) Start(ctx context.Context) error
- func (rs *ResponseStream) Stop() error
- func (rs *ResponseStream) Subscribe(subscriberID string) (<-chan ResponseChunk, error)
- func (rs *ResponseStream) Unsubscribe(subscriberID string) error
- type RestartState
- type ReviewContextExtras
- type ReviewGateRunner
- type ReviewGateSpawner
- type ReviewItem
- type ReviewOutcome
- type ReviewQueue
- type ReviewQueueLookup
- type ReviewQueueObserver
- type ReviewQueuePoller
- func (rqp *ReviewQueuePoller) AddInstance(instance *Instance)
- func (rqp *ReviewQueuePoller) CheckSession(inst *Instance)
- func (rqp *ReviewQueuePoller) FindInstance(sessionID string) *Instance
- func (rqp *ReviewQueuePoller) ForceReconcile()
- func (rqp *ReviewQueuePoller) GetConfig() ReviewQueuePollerConfig
- func (rqp *ReviewQueuePoller) GetInstances() []*Instance
- func (rqp *ReviewQueuePoller) GetMonitoredCount() int
- func (rqp *ReviewQueuePoller) IsRunning() bool
- func (rqp *ReviewQueuePoller) RemoveInstance(instanceTitle string)
- func (rqp *ReviewQueuePoller) SetActivityChannel(ch <-chan struct{})
- func (rqp *ReviewQueuePoller) SetApprovalProvider(provider ApprovalMetadataProvider)
- func (rqp *ReviewQueuePoller) SetInstances(instances []*Instance)
- func (rqp *ReviewQueuePoller) Start(ctx context.Context)
- func (rqp *ReviewQueuePoller) Stop()
- func (rqp *ReviewQueuePoller) UpdateConfig(config ReviewQueuePollerConfig)
- type ReviewQueuePollerConfig
- type ReviewQueueStatistics
- type ReviewQueueWriter
- type ReviewRespawner
- type ReviewState
- func (rs *ReviewState) ComputePromptSignature(content string) string
- func (rs *ReviewState) DetectAndTrackPrompt(content string, statusInfo InstanceStatusInfo, sessionTitle string) bool
- func (rs *ReviewState) IsAcknowledgedAfterOutput() bool
- func (rs *ReviewState) IsInProcessingGracePeriod() bool
- func (rs *ReviewState) SyncAtomicTimestamps()
- func (rs *ReviewState) TimeSinceLastMeaningfulOutput(createdAt time.Time) time.Duration
- func (rs *ReviewState) TimeSinceLastTerminalUpdate(createdAt time.Time) time.Duration
- func (rs *ReviewState) UpdateTimestamps(rawContent, filteredContent string, shouldUpdateMeaningful bool, ...) bool
- func (rs *ReviewState) UserRespondedAfterPrompt() bool
- type ReviewVerdictData
- type ReviewVerdictSummary
- type RevisionTarget
- type ReworkBlockStaleResolver
- type Session
- func (s *Session) GetBranch() string
- func (s *Session) GetCategory() string
- func (s *Session) GetLastMeaningfulOutput() time.Time
- func (s *Session) GetLastViewed() time.Time
- func (s *Session) GetPath() string
- func (s *Session) GetTags() []string
- func (s *Session) GetTerminalDimensions() (width, height int)
- func (s *Session) GetTmuxSessionName() string
- func (s *Session) GetWorkingDir() string
- func (s *Session) HasActivityTracking() bool
- func (s *Session) HasCloudContext() bool
- func (s *Session) HasFilesystemContext() bool
- func (s *Session) HasGitContext() bool
- func (s *Session) HasTerminalContext() bool
- func (s *Session) HasUIPreferences() bool
- func (s *Session) IsCloudConfigured() bool
- func (s *Session) NeedsReviewQueueAttention() bool
- func (s *Session) WithActivityTracking(activity *ActivityTracking) *Session
- func (s *Session) WithCloudContext(cloud *CloudContext) *Session
- func (s *Session) WithFilesystemContext(fs *FilesystemContext) *Session
- func (s *Session) WithGitContext(git *GitContext) *Session
- func (s *Session) WithTerminalContext(terminal *TerminalContext) *Session
- func (s *Session) WithUIPreferences(ui *UIPreferences) *Session
- type SessionArchiver
- type SessionGoalData
- type SessionHealthChecker
- type SessionSummaryGenerator
- func (g *SessionSummaryGenerator) FindRowBySessionID(ctx context.Context, sessionID string) (*ent.SessionSummary, error)
- func (g *SessionSummaryGenerator) GenerateAndPersist(ctx context.Context, sessionUUID, sessionTitle string, createdAt time.Time, ...)
- func (g *SessionSummaryGenerator) ReconcileStaleness(ctx context.Context, row *ent.SessionSummary) *ent.SessionSummary
- func (g *SessionSummaryGenerator) SetNotificationLister(l NotificationDecisionLister)
- func (g *SessionSummaryGenerator) SetTokenStore(t tokens.TokenStoreReader)
- type SessionSummaryStatus
- type SessionType
- type Shell
- type ShellData
- type ShellHandle
- type ShellRegistry
- func (r *ShellRegistry) Add(sh *Shell, handle *tmux.ShellTmuxHandle)
- func (r *ShellRegistry) AddStopped(sh *Shell)
- func (r *ShellRegistry) Get(shellID string) (*Shell, bool)
- func (r *ShellRegistry) GetBoth(shellID string) (*Shell, *tmux.ShellTmuxHandle, bool)
- func (r *ShellRegistry) GetHandle(shellID string) (*tmux.ShellTmuxHandle, bool)
- func (r *ShellRegistry) Len() int
- func (r *ShellRegistry) List() []*Shell
- func (r *ShellRegistry) Remove(shellID string)
- func (r *ShellRegistry) SetHandle(shellID string, handle *tmux.ShellTmuxHandle)
- func (r *ShellRegistry) UpdateForRestart(shellID string, newHandle *tmux.ShellTmuxHandle, newSessionName string, ...)
- func (r *ShellRegistry) UpdateStatus(shellID string, status ShellStatus, exitCode *int) bool
- type ShellRepository
- type ShellStatus
- type SourceSyncEventData
- type SpawnShellRequest
- type StaleWorkRemediator
- type StartupScanner
- type Status
- type StatusChange
- type StatusChangeListener
- type StatusDeterminer
- type StatusProvider
- type Storage
- func (s *Storage) AddBacklogItemDependency(ctx context.Context, edge BacklogItemDependencyEdge) error
- func (s *Storage) AddInstance(instance *Instance) error
- func (s *Storage) AllRules(ctx context.Context) ([]ApprovalRuleData, error)
- func (s *Storage) AppendActivityNote(ctx context.Context, ...) error
- func (s *Storage) AppendProgressNote(ctx context.Context, itemID string, criterionIndex int, note, status string) error
- func (s *Storage) ArchiveBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
- func (s *Storage) AssignSessionsToProject(ctx context.Context, projectName string, sessionTitles []string) error
- func (s *Storage) BulkResetStuckRemediation(ctx context.Context, reason *domain.StuckReason, onlyParked bool) (int, error)
- func (s *Storage) Close() error
- func (s *Storage) ComputeCurrentDiffHash(ctx context.Context, itemID string) string
- func (s *Storage) CreateBacklogItem(ctx context.Context, data BacklogItemData) (*BacklogItemData, error)
- func (s *Storage) CreateItemSession(ctx context.Context, data ItemSessionData) (ItemSessionSummary, error)
- func (s *Storage) CreateItemSessionWithVerdict(ctx context.Context, isData ItemSessionData, verdict ReviewVerdictData) (ItemSessionSummary, error)
- func (s *Storage) CreateItemSource(ctx context.Context, data ItemSourceData) (*ItemSourceData, error)
- func (s *Storage) CreateProject(ctx context.Context, data ProjectData) (*ProjectData, error)
- func (s *Storage) CreateSourceSyncEvent(ctx context.Context, sourceID, cursorAfter string, ...) error
- func (s *Storage) DeleteAllInstances() error
- func (s *Storage) DeleteBacklogItem(ctx context.Context, id string) error
- func (s *Storage) DeleteInstance(title string) error
- func (s *Storage) DeleteItemSource(ctx context.Context, id string) error
- func (s *Storage) DeleteProject(ctx context.Context, name string) error
- func (s *Storage) DeleteRule(ctx context.Context, id string) error
- func (s *Storage) FindDoneItemsOlderThan(ctx context.Context, cutoff time.Time) ([]BacklogItemData, error)
- func (s *Storage) FindInstanceDataByID(id string) (*InstanceData, error)
- func (s *Storage) FindOpenStuckStates(ctx context.Context) ([]OpenStuckStateData, error)
- func (s *Storage) GetAllInstanceArtifacts() (map[string]string, error)
- func (s *Storage) GetAllItemSessionsWithBacklogInfo(ctx context.Context) ([]ItemSessionBacklogEntry, error)
- func (s *Storage) GetBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
- func (s *Storage) GetBaseCommitSHAsForSessions(ctx context.Context, uuids []string) (map[string]string, error)
- func (s *Storage) GetClaudeConversationUUIDBySessionUUID(ctx context.Context, sessionUUID string) (string, error)
- func (s *Storage) GetEntClient() *ent.Client
- func (s *Storage) GetInstanceArtifacts(title string) (string, error)
- func (s *Storage) GetItemSession(ctx context.Context, id string) (ItemSessionSummary, error)
- func (s *Storage) GetItemSessionBySessionAndItem(ctx context.Context, sessionUUID string, itemID string) (ItemSessionSummary, error)
- func (s *Storage) GetItemSessionBySessionUUID(ctx context.Context, sessionUUID string) (ItemSessionSummary, error)
- func (s *Storage) GetItemSourceByID(ctx context.Context, id string) (*ItemSourceData, error)
- func (s *Storage) GetMostRecentReviewVerdictForItem(ctx context.Context, itemID string) (ReviewOutcome, error)
- func (s *Storage) GetRecentReviewVerdictSummaries(ctx context.Context, itemID string, limit int) ([]ReviewVerdictSummary, error)
- func (s *Storage) GetSession(ctx context.Context, title string, opts ContextOptions) (*Session, error)
- func (s *Storage) GetSessionGoal(ctx context.Context, sessionUUID string) (*SessionGoalData, error)
- func (s *Storage) GetSubcommandBreakdown(ctx context.Context, program string, since time.Time) ([]SubcommandDecisionCount, error)
- func (s *Storage) GetSubcommandTrend(ctx context.Context, program, subcommand string, since time.Time) ([]AnalyticsData, error)
- func (s *Storage) GetWorktreeDataBySessionUUID(ctx context.Context, sessionUUID string) (GitWorktreeData, error)
- func (s *Storage) ListActivityNotesForItem(ctx context.Context, itemID string) ([]ActivityNoteData, error)
- func (s *Storage) ListAnalytics(ctx context.Context, limit int) ([]AnalyticsData, error)
- func (s *Storage) ListAnalyticsByProgramSince(ctx context.Context, program string, since time.Time, limit int) ([]AnalyticsData, error)
- func (s *Storage) ListAnalyticsSince(ctx context.Context, since time.Time, limit int) ([]AnalyticsData, error)
- func (s *Storage) ListBacklogItemSummaries(ctx context.Context, filter BacklogItemFilter) ([]BacklogItemSummary, error)
- func (s *Storage) ListBacklogItems(ctx context.Context, filter BacklogItemFilter) ([]BacklogItemData, error)
- func (s *Storage) ListInstanceData() ([]InstanceData, error)
- func (s *Storage) ListInstanceDataWithWorktree() ([]InstanceData, error)
- func (s *Storage) ListInstanceIDs() ([]string, error)
- func (s *Storage) ListItemSessions(ctx context.Context, itemID string) ([]ItemSessionSummary, error)
- func (s *Storage) ListItemSources(ctx context.Context) ([]ItemSourceData, error)
- func (s *Storage) ListProgressNotesForItem(ctx context.Context, itemID string) ([]ProgressNoteData, error)
- func (s *Storage) ListProjects(ctx context.Context) ([]ProjectData, error)
- func (s *Storage) ListRecentCommandsByProgram(ctx context.Context, program, subcommand string, since time.Time, n int) ([]string, error)
- func (s *Storage) ListSessionRecords() []tokens.SessionRecord
- func (s *Storage) ListSessions(ctx context.Context, opts ContextOptions) ([]*Session, error)
- func (s *Storage) ListSourceSyncEvents(ctx context.Context, sourceID string) ([]SourceSyncEventData, bool, error)
- func (s *Storage) ListWorkspacePeers(ctx context.Context, workspaceKey string, excludeSessionUUID string) ([]WorkspacePeer, error)
- func (s *Storage) LoadInstances() ([]*Instance, error)
- func (s *Storage) MarkStuck(ctx context.Context, itemID string, reason domain.StuckReason, ...) (bool, error)
- func (s *Storage) MarkStuckNotified(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
- func (s *Storage) RecordAnalytics(ctx context.Context, data AnalyticsData) error
- func (s *Storage) RecordManualRemediationAttempt(ctx context.Context, itemID string, reason domain.StuckReason) (justParked bool, err error)
- func (s *Storage) RecordRemediationAttempt(ctx context.Context, itemID string, reason domain.StuckReason, attempts int32, ...) (bool, error)
- func (s *Storage) RecordRemediationRestartGrace(ctx context.Context, itemID string, reason domain.StuckReason, ...) (bool, error)
- func (s *Storage) RecordSourceSyncFailure(ctx context.Context, sourceID, message string) error
- func (s *Storage) RemediationBlocked(ctx context.Context, itemID string, reason domain.StuckReason) (blocked bool, err error)
- func (s *Storage) RemediationDue(ctx context.Context, itemID string, reason domain.StuckReason) (due bool, justParked bool, err error)
- func (s *Storage) ResetStuckRemediation(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
- func (s *Storage) ResolveStuck(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
- func (s *Storage) SaveInstances(instances []*Instance) error
- func (s *Storage) SaveInstancesSync(instances []*Instance) error
- func (s *Storage) SaveReviewVerdict(ctx context.Context, itemSessionID string, verdict ReviewVerdictData) error
- func (s *Storage) SaveSession(ctx context.Context, session *Session) error
- func (s *Storage) SetBacklogItemPRAndTransition(ctx context.Context, observed *BacklogItemData, prURL string, prNumber int, ...) error
- func (s *Storage) SetCallbackDispatcher(d CallbackDispatcher)
- func (s *Storage) SetItemChangePublisher(p ItemChangePublisher)
- func (s *Storage) SetItemSessionBaseCommit(ctx context.Context, id, sha string) error
- func (s *Storage) SetSessionGoal(ctx context.Context, sessionUUID string, goal string, status string, ...) (*SessionGoalData, error)
- func (s *Storage) SetSessionGoalWorkspaceKey(ctx context.Context, sessionUUID, workspaceKey string) error
- func (s *Storage) SnoozeStuckState(ctx context.Context, itemID string, reason domain.StuckReason, until time.Time) (bool, error)
- func (s *Storage) TransitionBacklogItemStatus(ctx context.Context, id string, toStatus BacklogStatus, ...) (*BacklogItemData, error)
- func (s *Storage) UnarchiveBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
- func (s *Storage) UnresolvedBlockerIDs(ctx context.Context, itemID string) ([]string, error)
- func (s *Storage) UnresolvedBlockerItemIDs(ctx context.Context, itemIDs []string) (map[string]bool, error)
- func (s *Storage) UpdateAcCriterionStatus(ctx context.Context, itemID string, criterionIndex int, status string, ...) error
- func (s *Storage) UpdateBacklogItem(ctx context.Context, id string, update BacklogItemUpdate, ...) (*BacklogItemData, error)
- func (s *Storage) UpdateInstance(instance *Instance) error
- func (s *Storage) UpdateInstanceAcknowledged(title string) error
- func (s *Storage) UpdateInstanceArtifacts(title string, blob string) error
- func (s *Storage) UpdateInstanceForkFlag(_ string, _ bool) error
- func (s *Storage) UpdateInstanceLastAddedToQueue(title string, lastAddedToQueue time.Time) error
- func (s *Storage) UpdateInstanceLastUserResponse(title string, lastUserResponse time.Time) error
- func (s *Storage) UpdateInstanceMetadata(currentTitle string, newTitle, category, note, workingDir *string) error
- func (s *Storage) UpdateInstancePRNumber(title string, prNumber int) error
- func (s *Storage) UpdateInstancePRStatus(_, _, _, _ string, _, _ int, _, _ bool) error
- func (s *Storage) UpdateInstanceProcessingGrace(title string, processingGraceUntil time.Time) error
- func (s *Storage) UpdateInstanceTimestampsOnly(title string, lastTerminalUpdate, lastMeaningfulOutput time.Time, ...) error
- func (s *Storage) UpdateItemSessionEnded(ctx context.Context, id string, endedAt time.Time) error
- func (s *Storage) UpdateItemSessionEndedWithReason(ctx context.Context, id string, endedAt time.Time, reason string) error
- func (s *Storage) UpdateItemSessionFailureCapture(ctx context.Context, id string, path string) error
- func (s *Storage) UpdateItemSessionGitActivity(ctx context.Context, id string, sha, msg string, commitAt time.Time, ...) error
- func (s *Storage) UpdateItemSessionSessionUUID(ctx context.Context, id string, sessionUUID string) error
- func (s *Storage) UpdateItemSessionStarted(ctx context.Context, id string, startedAt time.Time) error
- func (s *Storage) UpdateItemSessionTriageResult(ctx context.Context, id string, triageResult string) error
- func (s *Storage) UpdateItemSessionVerificationNotes(ctx context.Context, id string, verificationNotes string) error
- func (s *Storage) UpdateItemSource(ctx context.Context, id string, update ItemSourceUpdate) (*ItemSourceData, error)
- func (s *Storage) UpdateProject(ctx context.Context, data ProjectData) (*ProjectData, error)
- func (s *Storage) UpdateSessionTaskStatus(ctx context.Context, sessionUUID string, taskID string, newStatus string) (*SessionGoalData, error)
- func (s *Storage) UpsertRule(ctx context.Context, rule ApprovalRuleData) error
- func (s *Storage) WireChainFirer(workflows WorkflowRepository, fireEvents TriggerFireEventRepository, ...) *ChainFirer
- type SubcommandDecisionCount
- type Subscriber
- type SuspendedProcessRecord
- type SuspendedProcessStore
- func (s *SuspendedProcessStore) Add(record SuspendedProcessRecord) error
- func (s *SuspendedProcessStore) Get(instanceID string) (SuspendedProcessRecord, bool, error)
- func (s *SuspendedProcessStore) List() ([]SuspendedProcessRecord, error)
- func (s *SuspendedProcessStore) Remove(instanceID string) error
- type SyncLoop
- func (sl *SyncLoop) DecryptConfigToken(raw string) (string, error)
- func (sl *SyncLoop) PreviewBackwardSyncImpact(ctx context.Context, source *ent.ItemSource) (itemCount int, sampleTitles []string, possiblyIncomplete bool, err error)
- func (sl *SyncLoop) PreviewBackwardSyncImpactByID(ctx context.Context, sourceID string) (itemCount int, sampleTitles []string, possiblyIncomplete bool, err error)
- func (sl *SyncLoop) Start(ctx context.Context)
- func (sl *SyncLoop) Stop()
- func (sl *SyncLoop) SyncByID(ctx context.Context, sourceID string) error
- func (sl *SyncLoop) SyncOne(ctx context.Context, source *ent.ItemSource) error
- type TagManager
- type TaskNode
- type TerminalContext
- type TimeRestriction
- type TimelineSnapshot
- type TmuxBackend
- func (b *TmuxBackend) Attach() (chan struct{}, error)
- func (b *TmuxBackend) CapturePaneContent() (string, error)
- func (b *TmuxBackend) CapturePaneContentRaw() (string, error)
- func (b *TmuxBackend) CapturePaneContentWithOptions(start, end string) (string, error)
- func (b *TmuxBackend) CaptureViewport(lines int) (string, error)
- func (b *TmuxBackend) Close() error
- func (b *TmuxBackend) DetachSafely() error
- func (b *TmuxBackend) FilterBanners(content string) (string, int)
- func (b *TmuxBackend) GetCurrentWorkingDirectory() (string, error)
- func (b *TmuxBackend) GetCursorPosition() (x, y int, err error)
- func (b *TmuxBackend) GetPTY() (*os.File, error)
- func (b *TmuxBackend) GetPaneDimensions() (width, height int, err error)
- func (b *TmuxBackend) GetPanePID() (int32, error)
- func (b *TmuxBackend) GetSessionIdentifier() string
- func (b *TmuxBackend) HasMeaningfulContent(content string) bool
- func (b *TmuxBackend) HasSession() bool
- func (b *TmuxBackend) HasUpdated() (updated bool, hasPrompt bool, content string)
- func (b *TmuxBackend) IsAlive() bool
- func (b *TmuxBackend) RefreshClient() error
- func (b *TmuxBackend) ResetExitOnce()
- func (b *TmuxBackend) RestoreWithWorkDir(w string) error
- func (b *TmuxBackend) SendInputViaControlMode(ctx context.Context, data []byte) error
- func (b *TmuxBackend) SendKeys(keys string) (int, error)
- func (b *TmuxBackend) SendPromptWithEnter(p string) error
- func (b *TmuxBackend) SetDetachedSize(w, h int, title string) error
- func (b *TmuxBackend) SetOnExitCallback(fn func(string))
- func (b *TmuxBackend) SetWindowSize(cols, rows int) error
- func (b *TmuxBackend) Start(dir string) error
- func (b *TmuxBackend) StartControlMode() error
- func (b *TmuxBackend) StopControlMode() error
- func (b *TmuxBackend) SubscribeToControlModeUpdates() (string, chan []byte)
- func (b *TmuxBackend) TapEnter() error
- func (b *TmuxBackend) TmuxManager() TmuxManager
- func (b *TmuxBackend) UnsubscribeFromControlModeUpdates(id string)
- type TmuxManager
- type TmuxProcessManager
- func (tm *TmuxProcessManager) Attach() (chan struct{}, error)
- func (tm *TmuxProcessManager) CapturePaneContent() (string, error)
- func (tm *TmuxProcessManager) CapturePaneContentContext(ctx context.Context) (string, error)
- func (tm *TmuxProcessManager) CapturePaneContentPriority() (string, error)
- func (tm *TmuxProcessManager) CapturePaneContentRaw() (string, error)
- func (tm *TmuxProcessManager) CapturePaneContentWithOptions(startLine, endLine string) (string, error)
- func (tm *TmuxProcessManager) CaptureViewport(lines int) (string, error)
- func (tm *TmuxProcessManager) Close() error
- func (tm *TmuxProcessManager) DetachSafely() error
- func (tm *TmuxProcessManager) DoesSessionExist() bool
- func (tm *TmuxProcessManager) FilterBanners(content string) (string, int)
- func (tm *TmuxProcessManager) GetCursorPosition() (x, y int, err error)
- func (tm *TmuxProcessManager) GetPTY() (*os.File, error)
- func (tm *TmuxProcessManager) GetPaneDimensions() (width, height int, err error)
- func (tm *TmuxProcessManager) GetPanePID() (int32, error)
- func (tm *TmuxProcessManager) GetTmuxSessionName() string
- func (tm *TmuxProcessManager) HasMeaningfulContent(content string) bool
- func (tm *TmuxProcessManager) HasSession() bool
- func (tm *TmuxProcessManager) HasUpdated() (updated bool, hasPrompt bool, content string)
- func (tm *TmuxProcessManager) IsAlive() bool
- func (tm *TmuxProcessManager) PaneExitStatus() (code int, signal string, dead bool)
- func (tm *TmuxProcessManager) RefreshClient() error
- func (tm *TmuxProcessManager) RefreshClientPriority() error
- func (tm *TmuxProcessManager) ResetExitOnce()
- func (tm *TmuxProcessManager) RestoreWithWorkDir(workDir string) error
- func (tm *TmuxProcessManager) SendInputViaControlMode(ctx context.Context, data []byte) error
- func (tm *TmuxProcessManager) SendKeys(keys string) (int, error)
- func (tm *TmuxProcessManager) SendPromptWithEnter(prompt string) error
- func (tm *TmuxProcessManager) Session() *tmux.TmuxSession
- func (tm *TmuxProcessManager) SetDetachedSize(width, height int, instanceTitle string) error
- func (tm *TmuxProcessManager) SetOnExitCallback(fn func(string))
- func (tm *TmuxProcessManager) SetSession(s *tmux.TmuxSession)
- func (tm *TmuxProcessManager) SetWindowSize(cols, rows int) error
- func (tm *TmuxProcessManager) Start(dir string) error
- func (tm *TmuxProcessManager) StartControlMode() error
- func (tm *TmuxProcessManager) StopControlMode() error
- func (tm *TmuxProcessManager) SubscribeToControlModeUpdates() (string, chan []byte)
- func (tm *TmuxProcessManager) TapEnter() error
- func (tm *TmuxProcessManager) UnsubscribeFromControlModeUpdates(id string)
- type TmuxSocketQuerier
- type TransitionDef
- type TriageRespawner
- type TriageSuggestion
- type TriageTask
- type TriggerChainReconciler
- type TriggerFireEventInput
- type TriggerFireEventRepository
- type TriggerFirer
- type TurnCallback
- type UIPreferences
- type UsageLimit
- type VCSInfo
- type VNCProcessManager
- type WorkflowCreateInput
- type WorkflowEngine
- type WorkflowRepository
- type WorkflowUpdateInput
- type Workspace
- type WorkspacePath
- type WorkspacePeer
- type WorkspaceSwitchRequest
- type WorkspaceSwitchResult
- type WorkspaceSwitchType
- type WorktreeInfo
- type WorktreePRPoller
- func (p *WorktreePRPoller) GetPRData(repoPath, branch string) *github.PRInfo
- func (p *WorktreePRPoller) SetOnUpdated(fn func(repoPath, branch string, info *github.PRInfo))
- func (p *WorktreePRPoller) SetSource(src WorktreeSource)
- func (p *WorktreePRPoller) Start(ctx context.Context)
- func (p *WorktreePRPoller) Stop()
- type WorktreePRPollerConfig
- type WorktreeScanItem
- type WorktreeSource
- type WorktreeTarget
Constants ¶
const ( BacklogStatusIdea = domain.BacklogStatusIdea BacklogStatusRefining = domain.BacklogStatusRefining BacklogStatusReady = domain.BacklogStatusReady BacklogStatusQueued = domain.BacklogStatusQueued BacklogStatusInProgress = domain.BacklogStatusInProgress BacklogStatusReview = domain.BacklogStatusReview BacklogStatusPRPending = domain.BacklogStatusPRPending BacklogStatusDone = domain.BacklogStatusDone BacklogStatusArchived = domain.BacklogStatusArchived )
const ( BacklogCategoryBugfix = domain.BacklogCategoryBugfix BacklogCategoryFeature = domain.BacklogCategoryFeature BacklogCategoryChore = domain.BacklogCategoryChore BacklogCategoryRefactor = domain.BacklogCategoryRefactor )
const ( SessionRoleWork = "work" SessionRoleTriage = "triage" SessionRoleReview = "review" )
Session role constants.
const ( TagBacklogWork = "backlog:work" TagBacklogRevision = "backlog:revision" TagAutonomous = "autonomous" )
Session tag constants for backlog-spawned sessions.
const ( TriggeredByUser = "user" TriggeredBySystem = "system" TriggeredByAgent = "agent" TriggeredByGitHubSync = "github_sync" )
TriggeredBy values for BacklogStatusEvent records. Agent-initiated transitions (e.g. request_review, report_duplicate) use TriggeredByAgent.
const ( AcStatusPending = domain.AcStatusPending AcStatusInProgress = domain.AcStatusInProgress AcStatusDone = domain.AcStatusDone AcStatusFail = domain.AcStatusFail )
const ( ReviewOutcomePass = domain.ReviewOutcomePass ReviewOutcomeFail = domain.ReviewOutcomeFail ReviewOutcomePartial = domain.ReviewOutcomePartial ReviewOutcomeUnverifiable = domain.ReviewOutcomeUnverifiable )
const ( ReviewVerdictPass = domain.ReviewVerdictPass ReviewVerdictFail = domain.ReviewVerdictFail ReviewVerdictPartial = domain.ReviewVerdictPartial ReviewVerdictUnverifiable = domain.ReviewVerdictUnverifiable )
Backward-compatible aliases so callers can be migrated incrementally. Prefer ReviewOutcome* constants in new code.
const ( PauseReasonManual = "manual" PauseReasonAutoInactivity = "auto:inactivity" PauseReasonAutoLimit = "auto:session_limit" PauseReasonAutoResource = "auto:resource" )
SessionType indicates the type of session workflow to use Pause reason constants. Use these instead of bare string literals.
const ( PermissionModeAuto = "auto" PermissionModeBypassPermissions = "bypassPermissions" PermissionModeAcceptEdits = "acceptEdits" PermissionModeManual = "manual" )
PermissionMode constants for the --permission-mode Claude Code flag.
const ( // SessionTypeDirectory creates a simple directory session without git worktree SessionTypeDirectory = config.SessionTypeDirectory // SessionTypeNewWorktree creates a new git worktree for the session SessionTypeNewWorktree = config.SessionTypeNewWorktree // SessionTypeExistingWorktree uses an existing git worktree SessionTypeExistingWorktree = config.SessionTypeExistingWorktree // SessionTypeNewProject creates a new directory, initializes a git repo with an // initial commit, and opens the session. The directory need not exist beforehand. SessionTypeNewProject = config.SessionTypeNewProject // SessionTypeOneOff generates a fresh temporary directory under one_off_base_dir. SessionTypeOneOff = config.SessionTypeOneOff )
const ( ReasonApprovalPending = queue.ReasonApprovalPending ReasonInputRequired = queue.ReasonInputRequired ReasonErrorState = queue.ReasonErrorState ReasonTestsFailing = queue.ReasonTestsFailing ReasonIdleTimeout = queue.ReasonIdleTimeout ReasonTaskComplete = queue.ReasonTaskComplete ReasonUncommittedChanges = queue.ReasonUncommittedChanges ReasonIdle = queue.ReasonIdle ReasonStale = queue.ReasonStale ReasonWaitingForUser = queue.ReasonWaitingForUser )
const ( PriorityUrgent = queue.PriorityUrgent PriorityHigh = queue.PriorityHigh PriorityMedium = queue.PriorityMedium PriorityLow = queue.PriorityLow )
const ( GoalStatusIdle = "idle" GoalStatusWorking = "working" GoalStatusBlocked = "blocked" GoalStatusDone = "done" TaskStatusPending = "pending" TaskStatusInProgress = "in_progress" TaskStatusDone = "done" TaskStatusBlocked = "blocked" )
Goal and task status constants.
const ( MinTitleLength = 1 MaxTitleLength = 32 )
Title validation constants
const AcCriteriaJSONEmpty = domain.AcCriteriaJSONEmpty
AcCriteriaJSONEmpty is the zero value — an empty criteria list.
const BacklogBranchPrefix = "backlog/"
BacklogBranchPrefix is the git branch prefix every backlog work session's branch is created under — the one place this literal is defined. Anything that needs to independently predict or recreate a backlog work session's branch name (e.g. server/services/backlog_service_triage.go's retitleTriageWorktreeToFinalBranch, which renames a triage worktree onto the exact branch a later real spawn will look for) must reference this constant rather than hardcoding "backlog/" — see that function's doc comment for the bug this once caused when the two sides used independently-duplicated logic.
const CategoryBacklog = "Backlog"
CategoryBacklog is the Session.Category value assigned to all sessions spawned by BacklogService (work, revision, review-gate, re-review) so they group under a "Backlog" bucket in the session list UI instead of falling into "Uncategorized".
const DefaultBacklogPriority = domain.DefaultBacklogPriority
DefaultBacklogPriority is the default priority assigned to new backlog items when no priority is specified. Lower values indicate higher priority.
const (
// DefaultBufferSize is 10MB of in-memory buffer
DefaultBufferSize = 10 * 1024 * 1024
)
const DefaultHeadlessFailureCaptureMaxBytes int64 = 256 * 1024
DefaultHeadlessFailureCaptureMaxBytes bounds how much raw headless (claude -p) stdout is written to a failure capture file. Mirrors DefaultReviewTranscriptMaxBytes's precedent and size (256KB) — this is not a prompt-embedding budget (the file is read by a human via the UI/API, not injected into another LLM's context), so it can comfortably hold a long triage/review call's full output.
const DefaultReviewTranscriptMaxBytes int64 = 256 * 1024
DefaultReviewTranscriptMaxBytes bounds how much ANSI-stripped scrollback is written to a review transcript file. This is no longer a prompt-embedding budget (the file is searched on demand via the reviewer's Grep/Read tools, not injected into the prompt text), so it can be considerably larger than a typical per-section prompt budget -- 256KB comfortably covers a long session's tail without writing unbounded data into a real repo checkout.
const DefaultSDDPipelineModeSlug = "sdd"
DefaultSDDPipelineModeSlug is the slug of the seeded pipeline mode that instructs a spawned session to run this repo's own SDD skills (sdd:2-research through sdd:6-verify) instead of the flat default pipeline.
const EnterKeySequence = "\r"
EnterKeySequence is the byte sequence that submits a line of input to an interactive terminal session, matching what a real terminal sends for a physical Enter keypress in raw/cbreak mode (TapEnter, elsewhere in this package, writes the same 0x0D byte directly to the PTY). Raw-mode TUIs — including the Claude Code CLI's Ink-based interface running inside these tmux panes — read '\r' as submit; a bare '\n' is not recognized as Enter, so text sent with only a trailing '\n' is written into the pane but never actually submitted and sits unactioned in the input buffer indefinitely (BUG-047). Every call site that appends an "Enter" to text sent via SendKeys must use this constant (via BuildSubmittableInput, where applicable) rather than hand-rolling its own terminator.
const MaxNoteLength = 10000
MaxNoteLength is the maximum length, in bytes, of Instance.Note. Cross-referenced with the ent schema's field.Text("note").MaxLen(10000) (session/ent/schema/session.go) — the schema package cannot import this package (would create an import cycle), so the two 10000s must be kept in sync by comment, not by shared constant.
const MaxSameSessionReviewAttempts = 3
MaxSameSessionReviewAttempts bounds how many times a single live work session should loop on /backlog/review (equivalently, the request_review MCP tool) before giving up on reaching PASS in-session and shipping the current state as a PR for human review instead of retrying indefinitely. Exported so server/mcp/tools_backlog.go's get_backlog_item status response — the other place a running session reads this same instruction from, on every single poll — renders the identical number instead of drifting out of sync with taskProtocolBlock below and backlog_commands.go's review.md, the two other copies of this loop-bound.
Deliberately independent of BacklogService.effectiveReworkCap's operator-configurable ceiling (server/services/backlog_service_triage.go): that cap governs a different mechanism — spawning a brand-new work session across an item's whole history once AutoReopenAfterFailedReview decides the current one is gone — which never even activates while this session stays alive (see AutoReopenAfterFailedReview's hasActiveWorkSession guard and doc comment). Threading the operator-configured value into this static prompt text would require adding a parameter to BuildSessionInitialPrompt/BuildTokenBudgetedPrompt and every call site (PipelineEngine, BacklogService, WriteBacklogContextFile, and their tests) — a larger, separate change left as a candidate follow-up rather than folded into this fix.
const MaxSteerMessageLength = 10000
MaxSteerMessageLength is the maximum length, in bytes, of a steer_message sent via UpdateSession. No RPC in this server currently enforces a request-size cap (see grep for WithReadMaxBytes across server/ — a known, pre-existing, repo-wide gap), but steer_message is a new/widened free-text entry point that now reaches ordinary work/review sessions, not just autonomous ones, so it gets an explicit cap here rather than waiting on that broader fix. Matches MaxNoteLength's value.
const MaxTagCount = 100
MaxTagCount is the maximum number of tags allowed per session.
const MaxTagLength = 50
MaxTagLength is the maximum allowed length for a single tag.
const TestOnlyReworkMinAttempts = 2
TestOnlyReworkMinAttempts is how many consecutive rework attempts (most recent first) must have touched test-only files before IsTestOnlyReworkCycle trips. An unvalidated starting guess — no calibration corpus exists yet beyond the n≈3 items (ccbfe7a6, e271db3d, 92d679fd) that motivated this item; see validation.md. Exported: callers that build the []string attempt list to pass in (e.g. recentWorkSessionFileLists in server/services/backlog_service_triage.go) must request at least this many attempts, and must reference this constant rather than duplicating the literal — a hardcoded copy at the call site would silently go stale if this threshold is ever retuned.
Variables ¶
var ( ErrACRequired = domain.ErrACRequired ErrPlanRequired = domain.ErrPlanRequired ErrPlanArtifactsRequired = domain.ErrPlanArtifactsRequired ErrVerdictRequired = domain.ErrVerdictRequired ErrCodeNotOnMain = domain.ErrCodeNotOnMain ErrUnresolvedBlockers = domain.ErrUnresolvedBlockers ErrVerdictClearRequiredForReady = domain.ErrVerdictClearRequiredForReady )
Sentinel errors for transition guards.
var ( // ErrPathNotExist is returned when SessionType is Directory, the target // path does not exist, and CreateIfMissing was not set. ErrPathNotExist = errors.New("path does not exist") // ErrResumePathNotExist is ErrPathNotExist's variant for resume flows, // where the missing directory means the original project is gone rather // than "not created yet". ErrResumePathNotExist = errors.New("cannot resume: project directory no longer exists") // ErrInstanceConstructionFailed wraps a NewInstance failure (e.g. invalid // InstanceOptions). ErrInstanceConstructionFailed = errors.New("failed to create instance") // ErrInstanceRegistrationFailed wraps a Registry.Register failure. ErrInstanceRegistrationFailed = errors.New("failed to register instance") // ErrInstanceSaveFailed wraps a Storage.AddInstance failure. ErrInstanceSaveFailed = errors.New("failed to save instance") )
Sentinel errors returned by CreateManagedInstance, wrapped with caller-specific detail via %w so callers that need to map them onto specific RPC error codes (e.g. SessionService.CreateSession mapping ErrPathNotExist to connect.CodeNotFound) can do so with errors.Is rather than string-matching.
var ( ErrInvalidTitleLength = errors.New("title must be 1-32 characters") ErrInvalidTitleChars = errors.New("title contains invalid characters") ErrDuplicateTitle = errors.New("a session with this title already exists") ErrCannotRestart = errors.New("session cannot be restarted in current state") ErrPauseNotPermitted = errors.New("session does not permit pause") ErrResumeNotPermitted = errors.New("session does not permit resume") )
Title validation errors
var AggregateOutcome = domain.AggregateOutcome
AggregateOutcome computes the overall outcome from a slice of CriterionVerdicts.
var CanTransitionBacklog = domain.CanTransitionBacklog
CanTransitionBacklog reports whether a transition from one backlog status to another is permitted.
var ContextCloudSession = ContextOptions{ LoadCloud: true, LoadActivity: true, LoadUI: true, LoadTags: true, }
ContextCloudSession loads contexts for cloud/API sessions. Optimized for remote sessions that don't have local git/filesystem context. Memory usage: ~1-2 KB per session
var ContextDetailView = ContextOptions{ LoadGit: true, LoadFilesystem: true, LoadTerminal: true, LoadUI: true, LoadActivity: true, LoadWorktree: true, LoadDiffStats: true, LoadTags: true, LoadClaudeSession: true, }
ContextDetailView loads most contexts for detail panel. Comprehensive data for session detail views, excluding heavy diff content. Memory usage: ~10-20 KB per session
var ContextForReviewQueue = ContextOptions{ LoadGit: true, LoadActivity: true, LoadWorktree: true, LoadDiffStats: true, LoadTags: true, }
ContextForReviewQueue loads data needed for review queue operations. Focused on git context and change indicators. Memory usage: ~3-5 KB per session
var ContextForSearch = ContextOptions{ LoadGit: true, LoadTags: true, LoadActivity: true, }
ContextForSearch loads contexts needed for search operations. Includes tags and basic metadata for efficient filtering. Memory usage: ~1-2 KB per session
var ContextFull = ContextOptions{ LoadGit: true, LoadFilesystem: true, LoadTerminal: true, LoadUI: true, LoadActivity: true, LoadCloud: true, LoadWorktree: true, LoadDiffStats: true, LoadDiffContent: true, LoadTags: true, LoadClaudeSession: true, }
ContextFull loads all contexts and child data (expensive). Complete data including full diff content. Use sparingly. Memory usage: Can be 1-25 MB per session depending on diff size
var ContextMinimal = ContextOptions{}
ContextMinimal loads only core session data with no contexts. Use this for basic operations that only need session metadata. Memory usage: ~500 bytes per session
var ContextTerminalView = ContextOptions{ LoadTerminal: true, LoadGit: true, LoadUI: true, LoadActivity: true, LoadDiffStats: true, }
ContextTerminalView loads contexts needed for terminal preview. Includes terminal output and git diffs for preview panes. Memory usage: ~5-10 KB per session (varies with terminal output size)
var ContextUIView = ContextOptions{ LoadUI: true, LoadActivity: true, LoadGit: true, LoadTags: true, }
ContextUIView loads contexts needed for list/card display. Optimized for responsive UI rendering with essential context only. Memory usage: ~2-3 KB per session
var DefaultRepoPathManager = NewRepoPathManager()
DefaultRepoPathManager is the default instance used for GitHub URL resolution.
var DeterminePriority = queue.DeterminePriority
DeterminePriority re-export
var EntSchemaCreateMu sync.Mutex
EntSchemaCreateMu serializes calls to (*ent.Client).Schema.Create across the whole process, including from other packages that migrate their own *ent.Client against this same generated ent package (e.g. server/analytics.OpenAnalyticsDB). entgo.io/ent/dialect/sql/schema.(*Atlas) has internal package-level state that data-races when multiple goroutines run schema migration concurrently (e.g. `go test -parallel` spinning up many independent repositories/clients at once) — each instance's SQLite connection is isolated, but Atlas itself is not safe for concurrent use.
var ErrAmbiguousWithoutChoice = errors.New("import commit: correlation is ambiguous and no disambiguation_choice was supplied")
ErrAmbiguousWithoutChoice is returned when the fresh correlation result is Ambiguous but the caller supplied no disambiguation_choice.
var ErrConflict = errors.New("conflict")
ErrConflict is returned when an operation would violate a uniqueness constraint.
var ErrCorrelationDrifted = errors.New("import commit: correlation result changed since preview -- please re-preview")
ErrCorrelationDrifted is returned when a fresh CorrelateCandidate result (re-run at commit time) disagrees with the CorrelationResult the caller echoed back from PreviewImportExternalSession (Task 1.2.1f). Something about the candidate's history file(s) changed between preview and commit (e.g. a new conversation started, an ambiguous set resolved to a single file). Callers must map this onto connect.CodeFailedPrecondition and ask the client to re-preview.
var ErrDependencyCycle = errors.New("backlog item dependency would create a cycle")
ErrDependencyCycle is returned by AddBacklogItemDependency when the new blocker->blocked edge would create a cycle in the dependency graph.
var ErrDisambiguationChoiceInvalid = errors.New("import commit: disambiguation_choice does not match any candidate in the fresh correlation result")
ErrDisambiguationChoiceInvalid is returned when disambiguation_choice is non-empty but does not name one of the FRESH correlation result's Candidates by ConversationUUID. Deliberately re-validated against the fresh result, never the caller-supplied expected_correlation, so a disambiguation choice can never be replayed against a stale candidate set.
var ErrDuplicateDelivery = errors.New("duplicate delivery")
ErrDuplicateDelivery is returned by TriggerFireEventRepository.Create when the (workflow_id, delivery_id) composite unique index rejects a second concurrent insert for the same delivery — see trigger_fire_event.go's schema comment (Epic 1.2, pre-mortem P1 #1). Callers must attempt Create first (atomic insert-or- conflict) rather than pre-checking existence, which would be a TOCTOU race.
var ErrInstanceDataNotFound = errors.New("instance data not found")
ErrInstanceDataNotFound is returned by FindInstanceDataByID when no match exists.
var ErrNoHistoryAdapter = errors.New("no history adapter resolves for this program")
ErrNoHistoryAdapter indicates PortSessionHistory was asked to port history for a program pair where no registered HistoryAdapter claims one (or both) sides — e.g. opencode, aider, bash, or gemini (the real Gemini CLI, distinct from Antigravity's own storage format — see AgyAdapter.CanHandle), none of which have a canonical history format to port. Callers should treat this as an expected, low-severity no-op (log and continue), not a hard failure.
var ErrNoOpenStuckState = errors.New("no open stuck state for this item/reason")
ErrNoOpenStuckState is returned when a remediation gate/trigger targets an (itemID, reason) pair with no currently-open (unresolved, un-snoozed) BacklogStuckState row — there is nothing to remediate.
var ErrNotFound = errors.New("not found")
ErrNotFound is returned when a requested entity does not exist.
var ErrPRReassignmentNotAllowed = errors.New("PR reassignment not allowed")
ErrPRReassignmentNotAllowed is returned by SetBacklogItemPRAndTransition when a caller attempts to reassign an already-pr_pending item's tracked PR to a different PR number without a valid PRReassignmentGuard.
var ErrPathAlreadyManaged = errors.New("import commit: path is already managed by an existing session")
ErrPathAlreadyManaged is returned by CheckPathNotAlreadyManaged when candidatePath is already covered by an existing managed Instance -- either exactly (same directory) or as a subdirectory of an existing Instance's worktree/working directory. Importing such a candidate would create a second managed Instance pointed at the same on-disk tree, which is exactly the dual-writer scenario this whole feature exists to avoid.
var ErrPreconditionFailed = errors.New("precondition failed: concurrent modification detected")
ErrPreconditionFailed is returned when an optimistic-locking precondition check fails.
var ErrRemediationParked = errors.New("remediation attempts exhausted for this item/reason; reset before retrying")
ErrRemediationParked is returned by RecordManualRemediationAttempt when the targeted row already exhausted its attempt budget (remediation_attempts >= MaxRemediationAttempts) — an operator must reset the row first (ResetStuckRemediation/BulkResetStuckRemediation) rather than have a manual trigger silently un-park it.
var ErrSessionAlreadyRegistered = errors.New("session: already registered")
ErrSessionAlreadyRegistered is returned by Register when a LiveInstance for the given ID is already present in the registry (duplicate-ID collision guard).
var ErrSessionNotFound = errors.New("session: not found in storage")
ErrSessionNotFound is returned by Acquire when the sessionID is not known to Storage.
var ErrShellStopped = errors.New("shell is stopped")
ErrShellStopped is returned when an operation is attempted on a shell that has been stopped.
var ErrSubscriberFull = errors.New("PTYSubscriber: internal buffer exceeded capacity limit")
ErrSubscriberFull is returned by PTYSubscriber.Push when the internal buffer has exceeded its capacity limit. The caller should close the subscriber.
var ErrTmuxSessionNotFound = errors.New("commit import: candidate's tmux session no longer exists")
ErrTmuxSessionNotFound is returned when a candidate names a tmux session that is no longer present on the tmux server at commit time. The candidate's TmuxSession is later reused verbatim to run "tmux kill-session -t <name>", so it must be validated against a real, currently-existing session rather than trusted as client-supplied data.
var LoadDiffOnly = LoadOptions{ LoadWorktree: true, LoadDiffStats: true, LoadDiffContent: true, }
LoadDiffOnly loads only diff-related data, useful for preview panes.
Deprecated: For new code, use ContextTerminalView.WithDiffContent() with GetSession/ListSessions.
var LoadForReviewQueue = LoadOptions{ LoadWorktree: true, LoadDiffStats: true, LoadDiffContent: false, LoadTags: true, LoadClaudeSession: false, }
LoadForReviewQueue loads data needed for review queue operations.
Deprecated: For new code, use ContextForReviewQueue with GetSession/ListSessions.
var LoadFull = LoadOptions{ LoadWorktree: true, LoadDiffStats: true, LoadDiffContent: true, LoadTags: true, LoadClaudeSession: true, }
LoadFull loads all available data including full diff content. Use this for detail views where you need complete information. Memory usage: Can be 1-25 MB per session depending on diff size
Deprecated: For new code, use ContextFull with GetSession/ListSessions.
var LoadMinimal = LoadOptions{}
LoadMinimal loads only the core session fields without any child data. Use this when you only need session metadata (title, path, status, etc.)
Deprecated: For new code, use ContextMinimal with GetSession/ListSessions.
var LoadSummary = LoadOptions{ LoadWorktree: true, LoadDiffStats: true, LoadDiffContent: false, LoadTags: true, LoadClaudeSession: true, }
LoadSummary loads lightweight child data suitable for list views. This includes everything except the heavy diff content. Memory usage: ~1-2 KB per session
Deprecated: For new code, use ContextUIView with GetSession/ListSessions.
var MaxRemediationAttempts = int32(len(remediationBackoffSchedule))
MaxRemediationAttempts is the hard cap on automated remediation attempts per open BacklogStuckState row before it "parks" (see evaluateRemediation). Equal to len(remediationBackoffSchedule) by construction: attempt N's due time is remediationBackoffSchedule[N-1] after attempt N is recorded, so there is exactly one schedule entry per attempt. A var, not a const — len() of a slice literal is not a Go compile-time constant.
var ParseAcCriteria = domain.ParseAcCriteria
ParseAcCriteria deserializes acceptance criteria from a JSON string.
var SerializeAcCriteria = domain.SerializeAcCriteria
SerializeAcCriteria serializes acceptance criteria to an AcCriteriaJSON value.
var TransitionGuard = domain.TransitionGuard
TransitionGuard validates business rules before a status transition.
Functions ¶
func ApplyTmuxLiveness ¶ added in v1.41.0
func ApplyTmuxLiveness(peers []WorkspacePeer, liveUUIDs map[string]struct{})
ApplyTmuxLiveness overrides InstanceLive on each peer using an authoritative set of live session UUIDs (from LiveTmuxSessionUUIDs), and recomputes StaleGoal since it depends on InstanceLive. A peer whose UUID isn't in liveUUIDs is confirmed dead ("gone") even if its Status still says Active — this is exactly the crash case liveUUIDs exists to catch.
func AutoApproveSupported ¶ added in v1.42.0
AutoApproveSupported reports whether program is a recognized agent that AutoApprove can inject a bypass flag for.
func BuildHeadlessChatRetriagePrompt ¶ added in v1.43.0
func BuildHeadlessChatRetriagePrompt(item *BacklogItemData, artifactAbsPath string, prior HeadlessTriageResult, feedback string) string
BuildHeadlessChatRetriagePrompt wraps BuildHeadlessRetriagePrompt with an instruction to ask at most one clarifying question per turn — used for chat-originated refinement (CreateBacklogItemFromChat's existing_item_id path), where a tightened one-question-at-a-time round trip is expected instead of a batch dump of questions.
func BuildHeadlessRetriagePrompt ¶ added in v1.37.0
func BuildHeadlessRetriagePrompt(item *BacklogItemData, artifactAbsPath string, prior HeadlessTriageResult, feedback string) string
BuildHeadlessRetriagePrompt constructs a JSON-output prompt that refines a prior triage result using free-text user feedback. artifactAbsPath is the same directory used by the original triage run — research/*.md, plan.md, and validation.md already exist there and are treated as valid context unless the feedback indicates otherwise.
func BuildHeadlessReviewPrompt ¶ added in v1.35.0
func BuildHeadlessReviewPrompt(item *BacklogItemData, acSnapshot []AcCriterion, diff string, diffTruncated bool, verificationNotes string, extras ReviewContextExtras) string
BuildHeadlessReviewPrompt constructs a review prompt for headless calls. Unlike BuildReviewPrompt, it asks for JSON output instead of tool invocation because headless claude -p subprocesses do not have tool access.
extras carries the additional context sources available on the empty-diff codebase-read path (prior review attempts, full notes history, item goal/status history, a searchable session transcript file) — see ReviewContextExtras. Its sections are rendered only when diff == "", matching this feature's established "expensive extras only on the hard-to-verify path" posture; pass the zero value when diff != "" or when no such context is available.
func BuildHeadlessTriagePrompt ¶ added in v1.35.0
func BuildHeadlessTriagePrompt(item *BacklogItemData, artifactAbsPath string) string
BuildHeadlessTriagePrompt constructs the JSON-output triage prompt for a backlog item. artifactAbsPath is the absolute path where the LLM should write planning files.
func BuildReviewCallOptions ¶ added in v1.38.0
func BuildReviewCallOptions(diff, codebaseWorkDir string) (systemPrompt string, opts headless.CallOptions, callTimeout time.Duration, path string)
BuildReviewCallOptions decides the headless review call's system prompt, CallOptions, and context timeout for a given diff state. This is the single point of decision for the empty-diff codebase-access branch — both ReviewGateRunner.Run and TriggerReReview must call this instead of independently constructing the same literals (see ADR-001).
The returned path label is one of "diff" (normal, no tool access) or "codebase-read" (empty diff, granted bounded Read/Grep/Glob access under codebaseWorkDir). Callers use the label to decide whether DegradeIfUnverified applies and for logging.
func BuildReviewPrompt ¶ added in v1.35.0
func BuildReviewPrompt(item *BacklogItemData, acSnapshot []AcCriterion, diff string, diffTruncated bool, itemSessionID string, verificationNotes string) string
BuildReviewPrompt constructs the initial prompt for a review gate session.
func BuildSessionInitialPrompt ¶ added in v1.35.0
func BuildSessionInitialPrompt(item *BacklogItemData, priorSessions []ItemSessionSummary) string
BuildSessionInitialPrompt renders the full context prompt for an agent session.
func BuildSubmittableInput ¶ added in v1.41.0
BuildSubmittableInput appends EnterKeySequence to input when pressEnter is true, producing the exact string that must be handed to SendKeys for the receiving program to treat it as a submitted line rather than unsubmitted text sitting in the input buffer. Centralizing this (rather than each caller appending its own terminator) is what BUG-047 was missing: three of six SendKeys-with-enter call sites had independently picked '\n' instead of '\r'.
func BuildTokenBudgetedPrompt ¶ added in v1.35.0
func BuildTokenBudgetedPrompt(item *BacklogItemData, priorSessions []ItemSessionSummary) string
BuildTokenBudgetedPrompt wraps BuildSessionInitialPrompt with token budget enforcement. It estimates tokens as len(output)/4, and reduces content in two passes if over 4000.
func BuildWorkspacePeersBlock ¶ added in v1.41.0
func BuildWorkspacePeersBlock(peers []WorkspacePeer) string
BuildWorkspacePeersBlock renders a one-time "other active sessions in this workspace" nudge for a new session's initial prompt. Returns "" when there are no peers, so callers can unconditionally append the result without an empty-state noise check.
func CanTransition ¶
CanTransition returns true if transitioning from -> to is a valid state transition.
func CancelPendingKill ¶ added in v1.42.0
func CancelPendingKill(params CancelPendingKillParams) (resumed bool, err error)
CancelPendingKill deletes instanceID (the Instance committed by CommitImportExternalSession) and, only if that delete succeeds, SIGCONTs the original process and removes its SuspendedProcessRecord. The ordering is deliberate and matches import.proto's doc comment on CancelPendingKillResponse.resumed: if the compensating delete fails, the original process is left SIGSTOP'd -- ResumeOriginalProcess is never called in that case, since resuming a process whose replacement Instance still exists would leave two writers on the same transcript.
func CaptureShipSnapshot ¶ added in v1.39.0
func CaptureShipSnapshot(ctx context.Context, storage *Storage, item *BacklogItemData, prStatus *git.PRStatus, lastWork *ItemSessionSummary, wt *GitWorktreeData) error
CaptureShipSnapshot durably captures the GitHub PR/review/CI state and the per-file diff stats for item at the moment its PR merges, so that data survives worktree cleanup once the item reaches "done" — the core unified-vcs-widget requirement. It is a free function, not a method on BacklogLifecycleListener: it needs no state from that type beyond *Storage, which is passed explicitly here (per .claude/rules/interface-pollution-checklist.md, a method only earns its receiver when it genuinely needs the type's other state).
Two data groups are captured independently — a failure in one must never discard a success in the other:
- Group A (GitHub): mapped from the already-fetched prStatus. CaptureShipSnapshot makes no GitHub call of its own. prStatus == nil means group A already failed before this function was even called (e.g. the caller's own GetPRStatus errored) — that's a valid input, not a bug. PRStatus does not expose a raw CI-conclusion string (worktree_git.go:330-345's field list), so ShippedCheckConclusion is derived from CIFailing as "failure"/"success" — a minor, accepted fidelity gap versus Session.githubCheckConclusion.
- Group B (file stats): computed independently via git.FileStatsBetween(item.RepoPath, wt.BaseCommitSHA, lastWork.LastCommitSha), JSON-encoded into ShippedFileStats.
Whichever group(s) succeed are written via one storage.UpdateBacklogItem call. ShippedSnapshotCaptureFailed is set true whenever either group failed; ShippedSnapshotAt is set whenever at least one group succeeded. ShippedCheckConclusion is never written as "failed" — that field holds only genuine CI-conclusion values; ShippedSnapshotCaptureFailed is the dedicated signal for a capture failure.
CaptureShipSnapshot always returns nil: it never blocks the pr_pending → done transition, regardless of how many groups failed. Blocking done on a GitHub API hiccup or a pruned base SHA would leave a genuinely-merged item stuck in pr_pending forever, so this fails closed on data completeness, not on the workflow itself.
No in-process cache/memoization is introduced here — every call is a direct write-through via UpdateBacklogItem. If a future caching layer is added on top of this function, it must return the locally-computed snapshot value rather than re-reading a cache slot after a lock is released, per .claude/rules/go-double-checked-locking.md.
func CheckPathNotAlreadyManaged ¶ added in v1.42.0
func CheckPathNotAlreadyManaged(candidatePath string, registry InstanceStore) error
CheckPathNotAlreadyManaged reports ErrPathAlreadyManaged if candidatePath (after tilde/relative resolution via ResolveSessionPath) is the same as, or a subdirectory of, any existing Instance's Path, WorkingDir, or worktree path. registry is typically session.Storage, accessed through the InstanceStore interface (ListInstanceData) so this function stays testable against a fake without a real ent-backed Storage.
func ClaudeProjectDirName ¶ added in v1.12.0
ClaudeProjectDirName returns the directory name Claude uses for a given absolute project path. Claude encodes the path by replacing every non-alphanumeric character with '-'. This includes '/', '.', '_', and any other non-word characters. Example: "/Users/alice/myproject" → "-Users-alice-myproject" Example: "/Users/alice/.hidden/my_project" → "-Users-alice--hidden-my-project"
func CleanupBacklogContextFile ¶ added in v1.35.0
CleanupBacklogContextFile removes .backlog-context.md from the worktree root. Logs but does not fail if the file is absent.
Called from ReconcilePRPending (session/backlog_lifecycle.go) once an item's PR has merged and the item transitions to done. Worktree teardown (Instance.Kill, Instance.Pause) often already removes the entire worktree directory by that point, so this is frequently a no-op — the file is untracked anyway (via addWorktreeExcludes + selfHealWorktreeScaffolding + the commit-time staging guard), so it never appears in a git diff/PR regardless of how long it lingers on disk. Kept as a best-effort cleanup for the case where the worktree is still around, and exported for direct/manual invocation and exercised by tests. Do not wire this into a review-exit or ship-time teardown path — see CleanupSlashCommands' doc comment for why (ship.md in particular is deliberately relied on to still exist after a work session ends, until the PR actually merges).
func CleanupSlashCommands ¶ added in v1.35.0
CleanupSlashCommands removes the backlog slash command directory. Logs but does not return an error if the directory is absent.
Called from ReconcilePRPending (session/backlog_lifecycle.go) once an item's PR has merged and the item transitions to done — NOT from review exit or any earlier teardown path. shipViaAgentOrFallback relies on ship.md still being present in the worktree after a work session exits review, so it can re-invoke `/backlog/ship` as a one-shot headless call; by the time ReconcilePRPending sees a merged PR, that flow has already completed and ship.md is no longer needed. Also exported for direct/manual invocation and exercised by tests.
func ComputeContentHash ¶ added in v1.38.0
ComputeContentHash returns a SHA-256 hex digest, truncated to 16 characters, over fields concatenated in the order given by the caller. Used to detect when a PipelineMode's persisted content has changed since a session snapshotted it (see plan.md's ItemSessionSummary.PipelineModeSnapshotHash entry, Epic 1.6). Callers must always pass the 9 content-template fields in the same fixed declaration order so hashes are comparable across loads.
Exported (Epic 2.2) so server/services can compute content_hash for CreatePipelineMode/UpdatePipelineMode/GetPipelineMode/ListPipelineModes RPC responses directly from a row's current field values — including rows for disabled modes, which never enter pipelineModeCache (only ListEnabled-backed modes do) and therefore have no resolvedPipelineMode.ContentHash to reuse.
func ContainsModifiedField ¶ added in v1.41.0
ContainsModifiedField returns true if name is in the fields slice.
func CreateBacklogWorktree ¶ added in v1.37.0
CreateBacklogWorktree creates a git worktree for a backlog work session. It creates a branch named BacklogBranchPrefix+branchSuffix and returns the on-disk worktree path. The caller is responsible for writing files to the path before spawning the session.
A brand-new branch is based on origin/main's freshly-fetched tip, not repoPath's ambient HEAD (git.NewGitWorktreeWithBranch's default, correct for an interactive "branch off my current checkout" ad-hoc worktree, but wrong here: a queue-driven backlog spawn has no human on a particular branch, and repoPath's own checkout can sit unfetched for days). A stale ambient HEAD as the recorded base_commit_sha meant every commit that later landed on main between that stale point and whatever this session's HEAD later resolved to got misattributed to the session as its own work (surfaced as inflated/wrong commit_count_since_spawn — see resolveLatestWorkCommit's doc comment for the sibling bug this was found alongside). Falls back to the old ambient-HEAD behavior if origin can't be reached, so a spawn never hard-fails just because a fetch did.
The repair, branch resolution, worktree construction, and setup all run inside a single git.WithRepoWorktreeLock critical section for resolvedRepo. RepairCorruptedGitRepo's os.RemoveAll+re-clone used to run unlocked, racing a concurrent spawn's locked `git worktree add` on the same repo — one process's repair could delete/recreate the repo out from under another process mid-add, surfacing as git's generic "fatal: failed to resolve HEAD as a valid ref". Setup runs via wt.SetupLocked() rather than wt.Setup() here because this goroutine already holds the (non-reentrant) lock.
func DecryptToken ¶ added in v1.35.0
DecryptToken decrypts a base64-encoded ciphertext (nonce prepended) using AES-256-GCM.
func DegradeIfUnverified ¶ added in v1.38.0
func DegradeIfUnverified(path string, overall ReviewOutcome, verdicts []CriterionVerdict, summary string, toolReads []string, codebaseWorkDir string) (ReviewOutcome, []CriterionVerdict, string, string)
DegradeIfUnverified force-downgrades overall/verdicts to UNVERIFIABLE when path is "codebase-read" and EITHER toolReads is empty OR any claimed tool_reads path does not actually exist under (or escapes) codebaseWorkDir. Returns the possibly-downgraded outcome, verdicts, an annotated summary, and the refined path label ("codebase-read-verified" or "codebase-read-degraded") for logging. No-op when path != "codebase-read".
func EncodeTasks ¶ added in v1.35.0
EncodeTasks serializes a task tree to a JSON string.
func EncryptToken ¶ added in v1.35.0
EncryptToken encrypts plaintext using AES-256-GCM with the given 32-byte key. Returns base64-encoded ciphertext (nonce prepended).
func EnsureDefaultSDDPipelineMode ¶ added in v1.41.0
func EnsureDefaultSDDPipelineMode(ctx context.Context, repo PipelineModeRepository) error
EnsureDefaultSDDPipelineMode creates the "sdd" PipelineMode row if (and only if) no row with that slug exists yet. It is a pure no-op — it never calls Create or Update — when the row already exists, so an operator's later hand-edit is never reverted by a restart.
Never returns an error for a lost create-race (another boot, or a concurrent call, won it first) — that outcome means the row now exists, which is exactly this function's goal. Any other error is returned so the caller can log-and-continue, matching NewPipelineEngine's own non-fatal-boot posture: a seeding failure must never abort server startup for a feature most items don't use yet (see requirements.md's Non-functional Requirements).
func EnsureDirectorySessionPath ¶ added in v1.35.0
EnsureDirectorySessionPath creates and git-inits path if it does not already exist — the same directory-creation step SessionTypeDirectory takes when CreateIfMissing is set. Callers that need path to exist before spawning a directory session (e.g. to write files into the worktree ahead of the claude process starting) should call this first so the spawn's own CreateIfMissing check finds the directory already present and correctly git-initialized, rather than skipping git-init because the path merely exists.
func ExtractPRURL ¶ added in v1.35.0
ExtractPRURL scans the last 200 lines of sessionOutput for a GitHub PR URL.
func FindConversationFilePath ¶ added in v1.35.0
FindConversationFilePath is the exported wrapper for findConversationFilePath. It searches ~/.claude/projects/ for the JSONL file containing sessionID.
func FindInstanceByHistoryPath ¶ added in v1.35.0
FindInstanceByHistoryPath returns the title of the session whose JSONL history file matches filePath. Returns ("", false) if not found. HistoryFilePath is a public field set by HistoryLinker; safe to read here since this runs on each HistoryLinker callback, which is the same goroutine that sets the field.
func ForkClaudeConversation ¶
ForkClaudeConversation copies the first lineCount non-empty lines from srcConvPath into a new JSONL file named {newUUID}.jsonl inside dstDir. The new UUID is returned so the caller can set it as the forked session's ResumeId.
If lineCount is 0 an empty file is created and the new UUID is still returned. If lineCount exceeds the number of lines in the source all lines are copied without error. If srcConvPath does not exist an error is returned.
func GetGitDiff ¶ added in v1.35.0
func GetGitDiff(ctx context.Context, worktreePath string, baseSHA string) (diff string, truncated bool, err error)
GetGitDiff returns the diff of changes in worktreePath relative to baseSHA (or HEAD~1 if baseSHA is empty). If the diff exceeds MaxDiffSizeReview bytes it is truncated and truncated=true is returned.
dir's own checked-out HEAD is used as the diff target. That's correct when dir is the session's own worktree (HEAD there is the work branch's tip), but wrong when dir is a fallback directory such as the shared main repo checkout (HEAD there is whatever the main checkout has, not the work branch). Callers diffing from a fallback directory must use GetGitDiffRef with an explicit branch name instead.
func GetGitDiffRef ¶ added in v1.37.0
func GetGitDiffRef(ctx context.Context, dir string, baseSHA string, headRef string) (diff string, truncated bool, err error)
GetGitDiffRef is like GetGitDiff but diffs baseSHA..headRef instead of baseSHA..HEAD (headRef == "" behaves exactly like GetGitDiff). Callers must pass an explicit headRef (typically a branch name) when dir isn't the session's own worktree — e.g. diffing a work session's branch from the shared main repo checkout after the session's own worktree directory has been removed. Worktrees share the same object store, so any ref reachable from any worktree of the repo resolves correctly regardless of dir.
func GetGitHeadSHA ¶ added in v1.37.0
GetGitHeadSHA returns the current HEAD commit SHA in the given directory, or "" on any error. Used to capture a base SHA at work session start.
func GetMainRepoPath ¶
GetMainRepoPath uses git rev-parse --git-common-dir to get the main repo path. This is more reliable than parsing the .git file.
func GetWorktreeDirtyPaths ¶ added in v1.42.0
GetWorktreeDirtyPaths returns the specific paths with uncommitted changes (untracked, modified, or renamed — new path only) in the git worktree at worktreePath, deduplicated. Returns (nil, nil), not an error, when worktreePath is not a git repository at all — unlike IsWorktreeDirty, which surfaces that case as an error from the underlying `git status` subprocess. Additive sibling to IsWorktreeDirty: does not replace its boolean-only callers.
Reuses vc.GitProvider.GetChangedFiles/parsePorcelainV2Z (NUL-safe, rename-aware porcelain-v2 parsing) rather than reimplementing status parsing — see session/vc/git_provider.go.
func GuardedTransitionAllowed ¶ added in v1.41.0
func GuardedTransitionAllowed(engine WorkflowEngine, item BacklogItemTransitionInput, to BacklogStatus) bool
GuardedTransitionAllowed evaluates whether a transition is both structurally valid (CanTransition) and passes business-rule gates (ValidateGates), WITHOUT executing it — the read-only counterpart to transitionWithGuard (server/services/backlog_service_triage.go), for callers in package session (like SyncOne) that cannot import server/services.
func InstanceInfoSlice ¶ added in v1.35.0
func InstanceInfoSlice(instances []*Instance) []artifacts.InstanceInfo
InstanceInfoSlice converts a slice of live Instances to the lightweight InstanceInfo type used by ArtifactExtractor.SeedOffsets.
func IsFlakyVerdictFlipFlop ¶ added in v1.42.0
func IsFlakyVerdictFlipFlop(recent []ReviewVerdictSummary) bool
IsFlakyVerdictFlipFlop reports whether the two most recent review verdicts (most recent first, as returned by Storage.GetRecentReviewVerdictSummaries) share the same non-empty DiffHash but landed on a different OverallOutcome — i.e. the identical reviewed diff got two different answers, the signature of a flaky/non-deterministic review rather than a real fix or regression (the code under review never changed between attempts). An empty DiffHash is "unknown, not computed" and is never treated as a match — two unknowns are not evidence of anything. False on fewer than 2 verdicts, and false when the outcomes agree (that repeated-same-outcome shape is IsRepeatedFailure's job, not this one).
Known false-positive source (documented, not filtered — see validation.md): a manual OverrideBy="user" verdict interleaved with an automated one can look identical to a flip-flop but is actually a human correction, not model variance. OverrideBy isn't part of []ReviewVerdictSummary today, so it can't be excluded here.
func IsGitHubURL ¶
IsGitHubURL returns true if the input looks like a github.com URL or shorthand.
func IsGitHubURLWithHosts ¶ added in v1.41.0
IsGitHubURLWithHosts returns true if the input looks like a GitHub URL or shorthand, recognizing URLs against the given GitHub Enterprise hostnames in addition to github.com.
func IsRepeatedFailure ¶ added in v1.39.0
func IsRepeatedFailure(recent []ReviewVerdictSummary) bool
IsRepeatedFailure reports whether the two most recent review verdicts (most recent first, as returned by Storage.GetRecentReviewVerdictSummaries) are a non-PASS outcome paired with an identical summary — i.e. the last rework attempt changed nothing about why the item failed. This catches a fast-looping non-converging cycle (e.g. an infrastructure error like a missing diff, reproduced on every attempt) well before bounceThreshold's 3-cycles-in-24h window would, since a broken-worktree or similar environment fault can otherwise burn through the entire rework cap in minutes without ever changing outcome. Exported: called from server/services across the package boundary (AutoReopenAfterFailedReview).
func IsRepeatedNoVerdictFailure ¶ added in v1.39.0
IsRepeatedNoVerdictFailure reports whether the most recent consecutiveNoVerdictReviewThreshold review-role ItemSessions for an item (ordered most-recent-first, one bool per session: true if that session ever had a ReviewVerdict row written) all exited without ever calling submit_review_verdict — a crash, kill, or turn-cap stop on the review side.
IsRepeatedFailure alone is blind to this failure shape: it only ever sees sessions returned by Storage.GetRecentReviewVerdictSummaries, which queries itemsession.HasReviewVerdict() — a review session that never wrote a verdict is invisible to that query entirely, so two (or twenty) such sessions in a row never produce two comparable summaries and the breaker can never trip. That gap let a live item bounce 78 times in 24h — with the rework cap recently raised from 3 to 20, well out of reach — before catching it (see docs/tasks/backlog-feature-improvement.md, 2026-07-19 update). A run of verdict-less review exits carries the identical "nothing about this attempt changed" signal as two matching-summary failures, so it's treated the same way: stop the auto-reopen loop instead of burning through the rework cap.
func IsTestOnlyReworkCycle ¶ added in v1.42.0
IsTestOnlyReworkCycle reports whether every file touched across the last TestOnlyReworkMinAttempts rework attempts (most recent first — one []string of changed file paths per attempt) is a test/spec file. A legitimate fix touches production code somewhere; a run of test-only diffs is suggestive of chasing a non-deterministic failure by editing the test rather than the underlying code. False on fewer than TestOnlyReworkMinAttempts attempts, on any attempt with no file data at all (no signal, don't guess), or on any attempt touching a non-test file.
Known false-positive source (documented, not filtered — purely informational, never gates the reopen decision, so this rate is accepted rather than suppressed here; see validation.md): a legitimate test-coverage-improvement item also produces test-only rework cycles by design.
func IsTmuxBackedSessionRole ¶ added in v1.41.0
IsTmuxBackedSessionRole reports whether role identifies a session that runs as a persistent, live tmux-attached claude process — one that must be explicitly archived AND have its tmux pane killed once its backlog item goes terminal, or it leaks indefinitely (root cause of the 2026-07-29 OOM: dozens of done/archived items' work and review sessions still running, each with its own MCP subprocess fleet). Work and review sessions are tmux-backed. Triage sessions are not: they run as bounded one-shot headless subprocess calls (see headlessTriageUUIDPrefix) that exit on their own when the call returns, so they were never tracked as a live Instance in the first place and have nothing to kill — their own failure mode (a crashed/hung goroutine leaving a stale DB row) is handled separately by reconcileOrphanedTriageItems/reconcileOrphanedTriageRemediation.
This is the single source of truth for "which roles does the terminal-item sweep clean up" — both reconcileTerminalItemSessions (session/backlog_lifecycle.go) and archiveItemWorkSessions (server/services/backlog_service.go) call this rather than each re-deriving the role set, so the two can't silently drift apart again the way they already did once (the archive-and-kill fix originally covered work sessions only; review sessions kept leaking until this predicate unified both call sites).
func IsValidBacklogCategory ¶ added in v1.41.0
IsValidBacklogCategory reports whether s is a known backlog category value or the empty string (uncategorized).
func IsValidTaskStatus ¶ added in v1.35.0
IsValidTaskStatus returns true if s is a recognized task status value.
func IsWorktreeDirty ¶ added in v1.37.0
IsWorktreeDirty returns true if the git worktree at worktreePath has any uncommitted changes (staged or unstaged). Returns false with no error when the worktree is clean or when it cannot be reached.
func JoinHibernation ¶ added in v1.44.0
func JoinHibernation(i *Instance)
JoinHibernation waits for any in-flight hibernateProcessLocked or resumeFromHibernationLocked goroutine to exit, up to stopJoinTimeout. Tests should call this before relying on t.TempDir() cleanup, since a resume goroutine can otherwise outlive the temp dir it was launched against (see the "session working directory missing" hibernation-resume errors this guards against).
func JoinSessionDriver ¶ added in v1.44.0
func JoinSessionDriver(inst *Instance)
JoinSessionDriver waits for any in-flight SessionDriver goroutine (including a handleDriverFailure-spawned restart) to exit, up to stopJoinTimeout. Tests should call this before relying on t.TempDir() cleanup, since a driver goroutine can otherwise outlive the temp dir it was launched against.
func LiveTmuxSessionUUIDs ¶ added in v1.41.0
LiveTmuxSessionUUIDs returns the STAPLER_SESSION_UUID of every currently-running staplersquad_ tmux session, by asking tmux directly (not the DB). Used to give ListWorkspacePeers results an authoritative "process confirmed dead" signal instead of trusting a possibly-stale Status field, mirroring the identification technique in ReconcileOrphanedTmuxSessions. Returns an empty (non-nil) set if tmux isn't running.
func MergeUserModifiedFields ¶ added in v1.41.0
MergeUserModifiedFields adds newFields to the existing JSON-encoded set of user-modified field names, deduplicating, and returns the re-serialized JSON.
func ParseHeadlessToolReads ¶ added in v1.38.0
ParseHeadlessToolReads extracts the tool_reads list from a headless LLM JSON response. Returns nil if the field is absent or the JSON doesn't parse.
func ParseHeadlessVerdictResult ¶ added in v1.35.0
func ParseHeadlessVerdictResult(text string) (overall ReviewOutcome, verdicts []CriterionVerdict, summary string)
ParseHeadlessVerdictResult extracts verdict data from a headless LLM JSON response. It searches for the outermost JSON object in text, tolerating prose around it. Returns ReviewOutcomeFail overall if parsing fails or no verdicts are present.
func ParseUserModifiedFields ¶ added in v1.41.0
ParseUserModifiedFields deserializes UserModifiedFields JSON (e.g. ["title","description"]).
func PortSessionHistory ¶ added in v1.35.0
PortSessionHistory translates and syncs history between Claude Code and Antigravity CLI.
func ReconcileOrphanedTmuxSessions ¶ added in v1.35.0
func ReconcileOrphanedTmuxSessions(instances []*Instance)
ReconcileOrphanedTmuxSessions kills staplersquad_ tmux sessions that have no corresponding record in the current workspace DB.
Orphans accumulate when DeleteSession removes the DB record but the server is restarted before (or while) the live in-memory instance is available — leaving the Claude process running inside a tmux pane with no owner. This sweep is called once during server startup, after all DB sessions have been loaded and re-adopted (steps 6/6b of BuildRuntimeDeps), so there is no risk of killing a session that is mid-adoption.
Identification strategy:
- Tmux session has STAPLER_SESSION_UUID env var → compare against known UUIDs.
- No env var (pre-UUID sessions, and all shell sibling sessions) → compare the tmux session name against known instance tmux names and known shell tmux names. If none of the above match, the session is an orphan.
The staplersquad_keepalive sentinel is always preserved — it keeps the tmux server alive between sessions and is never tracked in the DB.
func ReconcileSuspendedProcesses ¶ added in v1.42.0
func ReconcileSuspendedProcesses(_ context.Context, suspended *SuspendedProcessStore, storage InstanceStore) error
ReconcileSuspendedProcesses runs once at server startup and resolves every SuspendedProcessRecord left behind by a prior server incarnation that crashed or was killed between CommitImportExternalSession's suspend step and a subsequent ConfirmKillExternalSession/CancelPendingKill call.
For each record it re-checks whether the committed Instance still exists in storage:
- If the Instance still exists (i.e. the prior incarnation's commit completed and nothing has deleted it since), the original process may still be actively managed by that Instance's lifecycle -- resuming it here would let two writers (the original process and the managed Instance) touch the same on-disk session/tmux pane concurrently. The record and the suspension are left in place for whatever path legitimately owns that Instance (confirm-kill/cancel) to resolve.
- If the Instance is missing (deleted out-of-band, or the prior incarnation crashed before CommitImportExternalSession finished), this is an orphaned suspension with nothing left to manage it: resume the original process (SIGCONT) so it isn't left frozen forever.
storage may be nil (e.g. in tests exercising only the suspended-process side); a nil storage is treated the same as "Instance not found" for every record, matching this function's prior behavior before storage was added.
A record is removed only after a successful resume, so it is not reconciled again on the next restart. A resume failure leaves the record in place for the next reconciliation pass to retry.
func RecoverBaseCommitSHA ¶ added in v1.38.0
RecoverBaseCommitSHA attempts to self-heal a base commit SHA that no longer resolves in the repository's object store — the concrete cause found via manual QA on backlog item ae1e2070-db02-4ad7-8580-633ef9904f31, whose worktrees.base_commit_sha was a stale/corrupted 40-char SHA unreachable from any ref, causing every review attempt to see an empty diff and return a false UNVERIFIABLE verdict even though real, complete work was committed on the branch. Recomputes the merge-base of headRef against repoPath's own checked-out HEAD, which is reachable from any worktree of the same repo (worktrees share one object store). Returns an error if headRef itself doesn't resolve either (e.g. the branch was deleted) — that case is not recoverable here and must surface to a human.
func RegisterBackendProvider ¶ added in v1.35.0
func RegisterBackendProvider(backend ProcessManagerBackend)
RegisterBackendProvider sets the backend used by NewProcessManager. Call once at startup, before any session is created.
func RenderSessionSummaryMarkdown ¶ added in v1.41.0
func RenderSessionSummaryMarkdown(sessionTitle string, narrative string, fallbackUsed bool, diff DiffSnapshot, decisions DecisionsSnapshot, timeline TimelineSnapshot, cost CostSnapshot, diffLink string) string
RenderSessionSummaryMarkdown renders a deterministic, valid-GFM markdown document for a session's completion summary (FR-4 — reusable as a PR body). Empty sections render explicit empty-state text (FR-6) rather than being omitted or showing misleading zeros. narrative is the already-resolved narrative text (real LLM output, or a fallback line already substituted by the caller — see isTrivialSession/narrativeFallbackTrivial/narrativeFallbackLLMFailure); fallbackUsed is accepted for callers/future UI that need to know whether a fallback line is being shown, but does not itself change the rendered text.
func RepairCorruptedGitRepo ¶ added in v1.42.0
RepairCorruptedGitRepo detects and repairs the .invalid-HEAD clone corruption (see isCorruptedClone) for repos reached via a plain on-disk path rather than through RepoPathManager.EnsureRepoCloned — e.g. a backlog item's stored RepoPath, which CreateBacklogWorktree resolves and hands straight to ResolveSessionPath (pure path expansion, no corruption check) and then to git.NewGitWorktreeFromCommitSHA/NewGitWorktreeWithBranch. Those never call EnsureRepoCloned, so a repo corrupted the same way EnsureRepoCloned guards against (an interrupted `git clone` leaving "ref: refs/heads/.invalid" as HEAD forever) would otherwise fail worktree creation indefinitely with no self-heal. If repoPath isn't a git repo at all, or its HEAD resolves fine, this is a no-op. Repair re-clones from the corrupted repo's own origin remote.
func ResolveSessionPath ¶ added in v1.35.0
ResolveSessionPath expands a leading "~" to the current user's home directory and converts the result to an absolute path — the same resolution NewInstance applies to InstanceOptions.Path. Callers that need to act on a session's worktree path *before* calling NewInstance (e.g. writing files into it ahead of spawn) must resolve through this function first, or they risk operating on a different path than the one the spawned Instance actually uses.
func ResolvedModeLabel ¶ added in v1.38.0
ResolvedModeLabel renders a raw BacklogItemData.PipelineMode string for PipelineEngine-prefixed log lines: the empty string (PipelineModeDefault) becomes "default" for log readability, any other slug is passed through unchanged. Exported so both server/services (TriggerTriage) and session (ReviewGateRunner.Run) call sites use one shared rendering — see Story 1.7.2's observability acceptance criteria.
func ResumeOriginalProcess ¶ added in v1.42.0
ResumeOriginalProcess sends SIGCONT to pid, unfreezing a process previously suspended by SuspendOriginalProcess. Used on commit failure (resume immediately), on CancelPendingKill (resume after compensating delete), and by ReconcileSuspendedProcesses on startup.
func RollbackMigration ¶
RollbackMigration restores the JSON backup and removes the SQLite database
func RunPreGateSecurityCheck ¶ added in v1.35.0
RunPreGateSecurityCheck scans a git diff for obvious secret patterns before sending to the review LLM. Returns a non-nil error if any pattern matches, blocking the review gate from spawning. This is a best-effort check — it does not replace a full secret scanner.
func SanitizeDiff ¶ added in v1.37.0
SanitizeDiff neutralizes triple-backtick sequences in a diff so they cannot close a markdown code fence when the diff is interpolated into an LLM prompt.
func SanitizeForAgentContext ¶ added in v1.35.0
SanitizeForAgentContext strips HTML tags from s and truncates to maxLen, appending " [truncated]" if truncation occurred.
func StartSessionDriver ¶ added in v1.35.0
StartSessionDriver launches a background goroutine that drives the session through its startup dialogs, fires the initial task prompt, and monitors for approval dialogs throughout the session lifetime.
allowedPath is the session's repo/workspace path — directory-access approval dialogs that mention this path are auto-approved.
Calling StartSessionDriver twice on the same instance is safe: the second call is a no-op (the idempotency guard uses atomic.Bool.CompareAndSwap). Calling it on an instance whose Destroy() has already run (or is running concurrently) is also a no-op — see Instance.driverMu's doc comment — so a delayed caller (e.g. CreateSession's async init goroutine racing a fast DeleteSession) can never spawn a driver goroutine that nothing will ever be told to stop.
func StopSessionDriver ¶ added in v1.46.0
func StopSessionDriver(inst *Instance)
StopSessionDriver signals a running SessionDriver goroutine (started by StartSessionDriver on inst) to exit at its next poll tick, and blocks up to driverStopTimeout for it — and any handleDriverFailure-spawned retry continuation of it — to actually do so. Called from Instance.Destroy() so no driver goroutine can survive past Destroy() and keep polling Preview() — which routes through the tmux exec gate's appconfig.LoadConfig()/GetConfigDir() (session/tmux/exec_gate.go) — using whatever STAPLER_SQUAD_TEST_DIR a later test has since set process-wide. Also marks inst as destroyed (under driverMu, alongside the stopper read) so a StartSessionDriver call that is still in flight — or arrives later — can never spawn a driver goroutine for this instance again; see Instance.driverMu's doc comment. A no-op (beyond that marking) if the driver was never started.
Waits on inst.driverWG rather than a per-run "done" channel: driverWG is Add(1)/Done()'d across both the initial run (StartSessionDriver) and every handleDriverFailure retry continuation, so this can't return early while a retry goroutine spawned after the stop signal was sent is still running — see JoinSessionDriver, which uses the identical wait for the same reason.
func SuspendOriginalProcess ¶ added in v1.42.0
SuspendOriginalProcess sends SIGSTOP to pid, freezing it so it cannot write to the shared Claude JSONL transcript while the resumed, managed session starts writing to the same file (see Story 1.2.1, Task 1.2.1e). The caller is responsible for persisting a SuspendedProcessRecord before calling this so the suspension survives a server restart (see ReconcileSuspendedProcesses).
func ValidateEntMigration ¶
ValidateEntMigration verifies that all sessions from JSON were successfully migrated to Ent
func ValidatePipelineModeContent ¶ added in v1.38.0
func ValidatePipelineModeContent(fields PipelineModeContentFields) error
ValidatePipelineModeContent enforces Story 2.3.1's structural-integrity invariants at the RPC write boundary, before any repository write occurs:
- If fields.ValidateSlug, fields.Slug must be non-empty and contain only characters in [a-z0-9-].
- None of the 9 content-template fields may contain a raw shell metacharacter from shellMetacharacters (defense in depth).
- Every {{...}} token in every content-template field must name a placeholder in the recognized allow-list (recognizedPlaceholders, declared in pipeline_engine.go and also used by renderTemplate) — an unrecognized token is rejected, naming both the offending field and the unrecognized token.
Returns nil if fields passes all checks.
func ValidateTaskDepth ¶ added in v1.35.0
ValidateTaskDepth validates that the task tree does not exceed maxTaskDepth (3) and that all task statuses are valid enum values. Total task count is checked separately by validateTaskCount (both are called from validateTasks).
func ValidateWorkflowSlug ¶ added in v1.35.0
ValidateWorkflowSlug validates that slug conforms to the workflow slug format: - 2–64 characters - Lowercase alphanumeric with hyphens - No leading/trailing hyphens - No consecutive hyphens
func WireSessionSummaryListener ¶ added in v1.41.0
func WireSessionSummaryListener(generator summaryGenerator, inst *Instance)
WireSessionSummaryListener registers a per-instance sessionSummaryListener on inst, mirroring BacklogLifecycleListener.WireToInstance (session/backlog_lifecycle.go:813-820). Takes the summaryGenerator interface type (not a concrete *SessionSummaryGenerator) so callers can pass any type that structurally satisfies it — a real *SessionSummaryGenerator does so for free.
func WorkspaceKey ¶ added in v1.41.0
WorkspaceKey returns a canonical identity for the repo/workspace a session belongs to, used to group sibling worktrees/branches of the same repo as peers. Prefers the GitHub owner/repo (stable across worktree paths); falls back to MainRepoPath, then Path. Returns "" when none are set (e.g. a bare one-off session with no git remote).
func WorkspacePeersBlockForPath ¶ added in v1.41.0
WorkspacePeersBlockForPath resolves repoPath's workspace identity, looks up its peers with authoritative tmux liveness applied, and renders the one-time initial-prompt nudge. Shared by both SessionService.CreateSession and BacklogService's initialPromptFor (both gated behind the workspacePeersNudgeFlagName feature flag) so the two callers can't drift on how the nudge is built. Returns "" on any detection/lookup failure, when storage is nil, or when repoPath is empty — this is a best-effort convenience nudge, not required session context, so failures are logged and swallowed rather than blocking session creation.
func WriteBacklogContextFile ¶ added in v1.35.0
func WriteBacklogContextFile(item *BacklogItemData, priorSessions []ItemSessionSummary, worktreePath string) error
WriteBacklogContextFile builds the full context prompt and writes it atomically to .backlog-context.md in the worktree root. Appends a fallback instructions block. priorSessions must match what was passed to the live CLI prompt (BuildTokenBudgetedPrompt) so the on-disk fallback the agent re-reads after context compaction doesn't lose history.
func WriteHeadlessFailureCapture ¶ added in v1.42.0
func WriteHeadlessFailureCapture(dir, sessionUUID, raw string, maxBytes int64) (absPath string, err error)
WriteHeadlessFailureCapture writes raw (the accumulated stdout of a headless triage/review claude -p call that either errored or produced output ParseHeadlessTriageResult/ParseHeadlessVerdictResult could not use) to a durable file under dir, named after sessionUUID, and returns its absolute path.
This exists because the log line previously emitted on a parse failure only includes a ~200-byte preview, and the log file itself rotates out of ~/.stapler-squad/logs/ within a few hours — for a long-running call there was previously no way to recover what the LLM actually returned once that window closed. Unlike WriteReviewTranscriptFile (session/review_transcript.go), which writes into a real repo checkout and is cleaned up immediately after a review completes, this file is meant to persist indefinitely for later diagnosis, so it is written to the caller-supplied dir (in practice config.Config.HeadlessFailureCaptureDirOrDefault(), under ~/.stapler-squad/headless-failures/) and never removed here.
raw is written verbatim, with no ANSI stripping: claude -p's headless stdout is not PTY output (unlike the scrollback WriteReviewTranscriptFile captures), so it does not carry escape sequences.
Returns ("", nil) when raw is empty (nothing to write) — a no-op, not an error, since an empty capture would tell a future reader nothing. maxBytes bounds how much of raw is written; pass <= 0 to use DefaultHeadlessFailureCaptureMaxBytes.
func WriteReviewTranscriptFile ¶ added in v1.38.0
func WriteReviewTranscriptFile(sm *scrollback.ScrollbackManager, sessionUUID, codebaseWorkDir string, maxBytes int64) (relPath string, cleanup func(), err error)
WriteReviewTranscriptFile fetches sessionUUID's most recent scrollback, strips ANSI escape sequences, and writes the result to a file inside codebaseWorkDir so a reviewer LLM can search it on demand with its already-granted Read/Grep/Glob tools -- instead of the orchestrator pre-injecting a text blob into the review prompt, which would bloat the prompt/context window regardless of session length.
The returned relPath is relative to codebaseWorkDir (e.g. ".stapler-squad-review-transcript-<uuid>.txt"), so it can be dropped directly into a reviewer prompt template and is treated consistently by containment-checked tool_reads logic the same way as any other path the reviewer cites. cleanup removes the written file and is always safe to call (including when relPath == "", in which case it is a no-op) -- callers should defer cleanup() immediately after a successful call so the file does not linger in the real repo checkout after the review completes.
Fetching scrollback is treated as best-effort enrichment: if the session has no scrollback (never started, expired, or storage error), WriteReviewTranscriptFile returns ("", no-op cleanup, nil) rather than an error, so a missing/expired session's scrollback never blocks a review. A non-nil error is returned only when scrollback WAS available but writing it to disk failed (e.g. codebaseWorkDir unwritable) -- callers may choose to ignore this error too, given the enrichment-only contract.
maxBytes bounds how much stripped transcript is written; pass <= 0 to use DefaultReviewTranscriptMaxBytes. When the stripped transcript exceeds maxBytes, the HEAD is dropped and the tail is kept (most recent activity is most relevant to a reviewer checking final state), prefixed with a truncation marker.
func WriteSlashCommands ¶ added in v1.35.0
func WriteSlashCommands(engine PipelineEngine, item *BacklogItemData, worktreePath string) error
WriteSlashCommands creates the .claude/commands/backlog/ directory and writes per-item slash command markdown files. Retries directory creation up to 3 times.
Content generation is delegated to engine.SlashCommandSet (Epic 1.5, Story 1.5.2) — this function only owns directory creation and the disk-write loop. engine may be nil, in which case content generation falls back to buildDefaultSlashCommandSet directly, matching CachingPipelineEngine's own default-mode behavior; this keeps tests that don't care about PipelineEngine free to pass nil. Both real callers (server/services/backlog_service_triage.go's SpawnSessionFromItem and backlog_service_sync.go's AttachSessionToItem) must pass the SAME shared engine instance (BacklogService.pipelineEngine) — passing two different engines would reintroduce the "2 independent callers can drift" regression this seam closes.
Types ¶
type AcCriteriaJSON ¶ added in v1.37.0
type AcCriteriaJSON = domain.AcCriteriaJSON
AcCriteriaJSON is the JSON-serialized form of []AcCriterion stored in the DB. Type alias — session.AcCriteriaJSON and domain.AcCriteriaJSON are identical types.
func MergeAcCriteria ¶ added in v1.37.0
func MergeAcCriteria(existing []AcCriterion, incoming []AcCriterion) (AcCriteriaJSON, error)
MergeAcCriteria merges incoming criteria into existing by index. Criteria not mentioned in incoming are preserved unchanged. Returns an error if incoming contains duplicate indices.
type AcCriterion ¶ added in v1.35.0
type AcCriterion = domain.AcCriterion
AcCriterion is a single acceptance criterion for a backlog item. Type alias — session.AcCriterion and domain.AcCriterion are identical types.
func MergeLiveCriterionNotes ¶ added in v1.38.0
func MergeLiveCriterionNotes(snapshot, live []AcCriterion) []AcCriterion
MergeLiveCriterionNotes overlays each criterion's live Note and Status (from item.AcceptanceCriteria) onto a possibly-stale snapshot, matched by Index. Fixes staleness where report_progress writes a Note onto the live item after an ItemSession's AcSnapshot was already captured at spawn time.
type AcStatus ¶ added in v1.37.0
AcStatus represents the status of a single acceptance criterion. Type alias — session.AcStatus and domain.AcStatus are identical types.
type ActivityNoteData ¶ added in v1.46.0
type ActivityNoteData struct {
ID string
Message string
AuthorSessionUUID string
AuthorSessionTitle string
CreatedAt time.Time
}
ActivityNoteData is the domain DTO replacing *ent.BacklogActivityNote in Storage returns. Unlike ProgressNoteData (written only by the role-gated report_progress tool), this represents a single append-only entry from the ungated post_backlog_update tool — see ADR-001 (sibling table, not extending BacklogProgressNote).
type ActivityTracking ¶
type ActivityTracking struct {
// LastTerminalUpdate is when the terminal output was last updated
LastTerminalUpdate time.Time `json:"last_terminal_update,omitempty"`
// LastMeaningfulOutput is when meaningful (non-noise) output was detected
LastMeaningfulOutput time.Time `json:"last_meaningful_output,omitempty"`
// LastViewed is when the session was last viewed by the user
LastViewed time.Time `json:"last_viewed,omitempty"`
// LastAcknowledged is when the user last acknowledged session output
LastAcknowledged time.Time `json:"last_acknowledged,omitempty"`
// LastOutputSignature is a hash/signature of the last output for deduplication
LastOutputSignature string `json:"last_output_signature,omitempty"`
// LastAddedToQueue is when the session was last added to the review queue
LastAddedToQueue time.Time `json:"last_added_to_queue,omitempty"`
}
ActivityTracking represents the activity tracking data for a session. This includes timestamps for various events and output tracking.
func (*ActivityTracking) HasRecentActivity ¶
func (a *ActivityTracking) HasRecentActivity(within time.Duration) bool
HasRecentActivity returns true if there has been activity within the specified duration
func (*ActivityTracking) IsEmpty ¶
func (a *ActivityTracking) IsEmpty() bool
IsEmpty returns true if the ActivityTracking has no meaningful data
type AgyAdapter ¶ added in v1.35.0
type AgyAdapter struct{}
func NewAgyAdapter ¶ added in v1.35.0
func NewAgyAdapter() *AgyAdapter
func (*AgyAdapter) CanHandle ¶ added in v1.35.0
func (a *AgyAdapter) CanHandle(program string) bool
func (*AgyAdapter) Export ¶ added in v1.35.0
func (a *AgyAdapter) Export(ctx context.Context, turns []CanonicalTurn, inst *Instance) error
func (*AgyAdapter) Import ¶ added in v1.35.0
func (a *AgyAdapter) Import(ctx context.Context, inst *Instance) ([]CanonicalTurn, error)
func (*AgyAdapter) Name ¶ added in v1.35.0
func (a *AgyAdapter) Name() string
type AliveChecker ¶ added in v1.42.0
AliveChecker is the subset of procinfo.ProcessInspector needed to re-verify a PID's identity immediately before killing it (scoped narrowly per .claude/rules/interface-pollution-checklist.md).
type AnalyticsData ¶ added in v1.12.0
type AnalyticsData struct {
ID string
SessionID string
ToolName string
CommandPreview string
Cwd string
Decision string
RiskLevel string
RuleID string
RuleName string
Reason string
Alternative string
DurationMs int64
ApprovalID string
CommandProgram string
CommandCategory string
CommandSubcategory string
PythonImports []string
CreatedAt time.Time
}
AnalyticsData is the domain model for classification analytics.
type ApprovalAutomation ¶
type ApprovalAutomation struct {
// contains filtered or unexported fields
}
ApprovalAutomation orchestrates automatic approval handling.
Lock ordering (must always be acquired in this order):
mu > queueMu > subMu
func NewApprovalAutomation ¶
func NewApprovalAutomation(sessionName string, controller *ClaudeController) *ApprovalAutomation
NewApprovalAutomation creates a new approval automation system.
func (*ApprovalAutomation) GetDetector ¶
func (aa *ApprovalAutomation) GetDetector() *detection.ApprovalDetector
GetDetector returns the approval detector for configuration.
func (*ApprovalAutomation) GetPendingApprovals ¶
func (aa *ApprovalAutomation) GetPendingApprovals() []*PendingApproval
GetPendingApprovals returns all approvals awaiting user response.
func (*ApprovalAutomation) GetPolicyEngine ¶
func (aa *ApprovalAutomation) GetPolicyEngine() *PolicyEngine
GetPolicyEngine returns the policy engine for configuration.
func (*ApprovalAutomation) GetSessionName ¶
func (aa *ApprovalAutomation) GetSessionName() string
GetSessionName returns the session name.
func (*ApprovalAutomation) IsRunning ¶
func (aa *ApprovalAutomation) IsRunning() bool
IsRunning returns whether the automation is currently running.
func (*ApprovalAutomation) RespondToApproval ¶
func (aa *ApprovalAutomation) RespondToApproval(requestID string, approved bool, userInput string, options ApprovalAutomationOptions) error
RespondToApproval processes a user response to a pending approval.
func (*ApprovalAutomation) Start ¶
func (aa *ApprovalAutomation) Start(ctx context.Context, options ApprovalAutomationOptions) error
Start begins the approval automation processing loop.
func (*ApprovalAutomation) Stop ¶
func (aa *ApprovalAutomation) Stop() error
Stop halts the approval automation system.
func (*ApprovalAutomation) Subscribe ¶
func (aa *ApprovalAutomation) Subscribe(subscriberID string) <-chan ApprovalEvent
Subscribe creates a subscription for approval events.
func (*ApprovalAutomation) Unsubscribe ¶
func (aa *ApprovalAutomation) Unsubscribe(subscriberID string)
Unsubscribe removes a subscription.
type ApprovalAutomationOptions ¶
type ApprovalAutomationOptions struct {
AutoExecute bool // Automatically execute approved commands
UserTimeout time.Duration // Time to wait for user response
ProcessingDelay time.Duration // Delay between processing approvals
MaxQueueSize int // Maximum pending approvals
EnableAuditLog bool // Log all approval actions
}
ApprovalAutomationOptions configures approval automation behavior.
func DefaultApprovalAutomationOptions ¶
func DefaultApprovalAutomationOptions() ApprovalAutomationOptions
DefaultApprovalAutomationOptions returns sensible defaults.
type ApprovalEvent ¶
type ApprovalEvent struct {
Type ApprovalEventType
Request *detection.ApprovalRequest
Decision *PolicyDecision
Timestamp time.Time
Details string
}
ApprovalEvent represents an event in the approval automation system.
type ApprovalEventType ¶
type ApprovalEventType string
ApprovalEventType categorizes approval events.
const ( EventDetected ApprovalEventType = "detected" EventAutoApproved ApprovalEventType = "auto_approved" EventAutoRejected ApprovalEventType = "auto_rejected" EventAwaitingUser ApprovalEventType = "awaiting_user" EventUserApproved ApprovalEventType = "user_approved" EventUserRejected ApprovalEventType = "user_rejected" EventExpired ApprovalEventType = "expired" EventExecuted ApprovalEventType = "executed" EventExecutionFail ApprovalEventType = "execution_failed" )
type ApprovalMetadata ¶
type ApprovalMetadata struct {
ApprovalID string
ToolName string
ToolInput map[string]interface{}
Cwd string
Orphaned bool
// EscalationReason and EscalationCategory explain why this request was
// escalated for manual review (no-match/explicit-rule/domain-age/
// unclassifiable/unexpected). Copied from PendingApproval via
// ApprovalStore.GetApprovalMetadataBySession.
EscalationReason string
EscalationCategory string
// RiskLevel is the classifier-assigned risk level ("low"/"medium"/"high"/"critical"),
// copied from PendingApproval.RiskLevel. "" means not recorded — never treated as "low".
RiskLevel string
}
ApprovalMetadata holds metadata about a pending approval for enriching review queue items.
type ApprovalMetadataProvider ¶
type ApprovalMetadataProvider interface {
// GetApprovalMetadataBySession returns approval metadata for the given session ID.
// Returns nil if no approvals exist for the session.
GetApprovalMetadataBySession(sessionID string) []ApprovalMetadata
}
ApprovalMetadataProvider provides approval metadata for enriching review queue items. This interface decouples the poller (session package) from the ApprovalStore (services package).
type ApprovalPolicy ¶
type ApprovalPolicy struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
ApprovalTypes []detection.ApprovalType `json:"approval_types"` // Types this policy applies to
Enabled bool `json:"enabled"`
Priority int `json:"priority"` // Higher priority policies checked first
Conditions []PolicyCondition `json:"conditions"` // All must match
Action PolicyAction `json:"action"` // What to do when matched
TimeRestriction *TimeRestriction `json:"time_restriction,omitempty"`
UsageLimit *UsageLimit `json:"usage_limit,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// contains filtered or unexported fields
}
ApprovalPolicy defines a rule for automatic approval.
func CreateBusinessHoursPolicy ¶
func CreateBusinessHoursPolicy() *ApprovalPolicy
CreateBusinessHoursPolicy creates a policy that only applies during business hours.
func CreateNoDestructivePolicy ¶
func CreateNoDestructivePolicy() *ApprovalPolicy
CreateNoDestructivePolicy creates a policy for rejecting destructive commands.
func CreateSafeCommandPolicy ¶
func CreateSafeCommandPolicy() *ApprovalPolicy
CreateSafeCommandPolicy creates a policy for automatically approving safe commands.
type ApprovalRuleData ¶ added in v1.12.0
type ApprovalRuleData struct {
ID string
Name string
ToolName string
ToolPattern string
ToolCategory string
CommandPattern string
FilePattern string
Decision int
RiskLevel int
Reason string
Alternative string
Priority int
Enabled bool
Source string
CreatedAt time.Time
UpdatedAt time.Time
// Structured CommandCriteria fields — correspond to classifier.CommandCriteria.
Programs []string
Subcommands []string
BlockedSubcommands []string
RequiredFlags []string
ForbiddenFlags []string
RequiredFlagPrefixes []string
PythonModes []string
SafePythonImportsOnly bool
RequireCIPassing bool
MinSessionIdleMinutes int32
}
ApprovalRuleData is the domain model for an auto-approval rule.
type AttentionReason ¶
type AttentionReason = queue.AttentionReason
AttentionReason re-export
func AttentionReasonFromDetected ¶
func AttentionReasonFromDetected(detected detection.DetectedStatus) AttentionReason
AttentionReasonFromDetected maps a DetectedStatus to the AttentionReason that should be used when adding the session to the review queue. Returns the zero AttentionReason (empty string) when no attention is needed for that status.
type AutoReopenSpawner ¶ added in v1.37.0
type AutoReopenSpawner interface {
AutoReopenAfterFailedReview(ctx context.Context, itemID string) error
}
AutoReopenSpawner can automatically reopen a backlog item for rework after a failed review verdict (FAIL or PARTIAL). It transitions the item back to in_progress and spawns a new work session so the review→rework cycle is fully automated.
type AutonomousDriver ¶ added in v1.35.0
type AutonomousDriver struct {
// contains filtered or unexported fields
}
AutonomousDriver monitors a session and injects orchestrator prompts when idle.
func NewAutonomousDriver ¶ added in v1.35.0
func NewAutonomousDriver(inst *Instance, pool HeadlessPoolClient, goal string, maxTurns int, opts ...DriverOption) *AutonomousDriver
NewAutonomousDriver creates an AutonomousDriver for inst. pool must not be nil; maxTurns ≤ 0 defaults to 20. Use functional options (e.g. WithStartupTimeout) to override defaults.
func (*AutonomousDriver) RegisterCompletionCallback ¶ added in v1.35.0
func (d *AutonomousDriver) RegisterCompletionCallback(cb CompletionCallback)
RegisterCompletionCallback sets the function called when the driver exits.
func (*AutonomousDriver) RegisterTurnCallback ¶ added in v1.35.0
func (d *AutonomousDriver) RegisterTurnCallback(cb TurnCallback)
RegisterTurnCallback sets the function called after each prompt injection.
func (*AutonomousDriver) Start ¶ added in v1.35.0
func (d *AutonomousDriver) Start(ctx context.Context) error
Start begins the autonomous driver goroutine. The second call is a no-op.
func (*AutonomousDriver) Stop ¶ added in v1.35.0
func (d *AutonomousDriver) Stop()
Stop cancels the driver goroutine. Context cancellation propagates into CallBlocking: the headless pool passes ctx to runner.Run (which kills the subprocess) and the stream reader selects on ctx.Done, so Stop returns control to the caller nearly immediately — no blocking LLM call delay.
type AutonomousDriverOutcome ¶ added in v1.35.0
type AutonomousDriverOutcome struct {
Done bool
Reason string
PRUrl string
Turns int
Stuck bool // true if exited via maxTurns without DONE signal
}
AutonomousDriverOutcome describes how an autonomous driver run concluded.
type AutonomousModeState ¶ added in v1.35.0
type AutonomousModeState struct {
AutonomousMode bool
AutonomousTurn int32
AutonomousMaxTurns int32
AutonomousOutcome string
}
AutonomousModeState groups all autonomous-mode fields within InstanceSnapshot (CDD Epic 3, Task 3.1b). Access via snap.Autonomous.AutonomousMode etc.
type AvailableTargets ¶
type AvailableTargets struct {
VCSType string
Bookmarks []BookmarkTarget
RecentRevisions []RevisionTarget
Worktrees []WorktreeTarget
}
AvailableTargets contains the available workspace switch targets
type BacklogCategory ¶ added in v1.41.0
type BacklogCategory = domain.BacklogCategory
BacklogCategory represents a coarse frontend-defaulting classification for a backlog item (bugfix/feature/chore/refactor, or "" for uncategorized). Type alias — session.BacklogCategory and domain.BacklogCategory are identical types; all existing callers continue to work without any import changes.
type BacklogChangeKind ¶ added in v1.41.0
type BacklogChangeKind string
BacklogChangeKind identifies which kind of backlog item mutation a BacklogItemChange describes. Mirrors events.BacklogChangeKind (pkg/events/types.go) one-to-one; kept as a separate type here because this package cannot import pkg/events directly — pkg/events imports session, so the reverse import would be a cycle. The adapter (server/services, Story 1.3.2) is responsible for converting between the two.
const ( // ChangeStatusTransition is emitted when an item's status changes. ChangeStatusTransition BacklogChangeKind = "status_transition" // ChangeVerdictRecorded is emitted when a review verdict is saved. ChangeVerdictRecorded BacklogChangeKind = "verdict_recorded" // ChangeSessionAttached is emitted when a session is attached to an item. ChangeSessionAttached BacklogChangeKind = "session_attached" // ChangeItemUpdated is emitted when item fields (title, description, etc.) change. ChangeItemUpdated BacklogChangeKind = "item_updated" // ChangeItemArchived is emitted when an item is archived. ChangeItemArchived BacklogChangeKind = "item_archived" // ChangeItemRemoved is emitted when an item is deleted. ChangeItemRemoved BacklogChangeKind = "item_removed" // ChangeTriageProgressUpdated is emitted when in-flight triage progress is // written (UpdateItemSessionTriageResult). Converts to the existing // item_updated wire event, not a new proto message. ChangeTriageProgressUpdated BacklogChangeKind = "triage_progress_updated" // ChangeActivityNoteAdded is emitted by AppendActivityNote (ADR-001's // sibling table) — carries only the new note via ActivityNote, never // OldStatus/NewStatus/etc. ChangeActivityNoteAdded BacklogChangeKind = "activity_note_added" )
type BacklogController ¶ added in v1.35.0
type BacklogController struct {
// contains filtered or unexported fields
}
BacklogController implements services.FeatureController for the backlog feature. It enables/disables the BacklogLifecycleListener and SyncLoop at runtime without requiring a server restart.
Enable/Disable are safe to call concurrently.
func NewBacklogController ¶ added in v1.35.0
func NewBacklogController( listener *BacklogLifecycleListener, storage *Storage, registry *PluginRegistry, keyFunc func() ([]byte, error), ) *BacklogController
NewBacklogController creates a controller that manages the given listener. storage, registry, and keyFunc are used to create a new SyncLoop on Enable.
func (*BacklogController) Disable ¶ added in v1.35.0
func (c *BacklogController) Disable() error
Disable deactivates the backlog feature: sets listener disabled and stops the sync loop. Idempotent — calling Disable when already disabled is a no-op.
func (*BacklogController) Enable ¶ added in v1.35.0
func (c *BacklogController) Enable(_ context.Context) error
Enable activates the backlog feature: sets listener enabled and starts the sync loop. Idempotent — calling Enable when already enabled is a no-op.
func (*BacklogController) IsEnabled ¶ added in v1.35.0
func (c *BacklogController) IsEnabled() bool
IsEnabled reports whether the backlog feature is currently active.
type BacklogItemChange ¶ added in v1.41.0
type BacklogItemChange struct {
// Kind identifies which backlog mutation this change describes.
Kind BacklogChangeKind
// OldStatus is the prior status for ChangeStatusTransition.
OldStatus string
// NewStatus is the new status for ChangeStatusTransition.
NewStatus string
// UpdatedFields lists which fields changed for ChangeItemUpdated (and
// ChangeTriageProgressUpdated).
UpdatedFields []string
// SessionID identifies the session for ChangeSessionAttached.
SessionID string
// ClaimantHostID is the claiming/attaching process's own stable host
// identifier for ChangeSessionAttached, mirrored from
// ItemSessionData.ClaimantHostID — never derived from the session being
// attached. See ItemSession.claimant_host_id's schema comment.
ClaimantHostID string
// ArchivedAt is the archival timestamp for ChangeItemArchived.
ArchivedAt *time.Time
// RemovedReason describes why an item was removed for ChangeItemRemoved.
RemovedReason string
// Verdict is populated only when Kind == ChangeVerdictRecorded, set
// directly from the ReviewVerdictData value the caller already has in
// hand (the actual parameter type of SaveReviewVerdict /
// CreateItemSessionWithVerdict, session/storage_backlog.go) — this
// carries the verdict through the pipeline as first-class data, not via
// a client-side join against item_sessions.
Verdict *ReviewVerdictData
// ActivityNote is populated only when Kind == ChangeActivityNoteAdded.
ActivityNote *ActivityNoteData
}
BacklogItemChange describes a single backlog item mutation, passed to ItemChangePublisher.PublishItemChanged by the repository method that made the mutation. Only the fields relevant to Kind are expected to be populated by the caller.
type BacklogItemData ¶ added in v1.35.0
type BacklogItemData struct {
ID string
Title string
Description string
AcceptanceCriteria AcCriteriaJSON
Priority int
Status string
RepoPath string
SkipReviewGate bool
SkipPlanning bool
AutoSpawnSession bool
// AutoCreatePR, when true, automatically runs the same one-shot PR-creation
// prompt the Review Queue's manual "Create PR" button uses, once a work
// session for this item reaches TASK_COMPLETE (see
// server.ReactiveQueueManager.maybeAutoCreatePR). Off by default — a
// deliberate opt-in, since it removes the human review-the-prompt
// checkpoint before an LLM-authored PR is created.
AutoCreatePR bool
// ReworkCapOverride is a per-item override for the auto-rework cap
// (config.Config.MaxAutoReworkIterationsOrDefault). Nil = use the global
// default. 0 = unlimited retries for this item. >0 = this item's own cap,
// replacing (not adding to) the global value. See effectiveReworkCap in
// server/services/backlog_service_triage.go.
ReworkCapOverride *int
// PipelineMode is the slug of the PipelineMode this item uses to drive
// triage/work/review content (see session/pipeline_engine.go). Empty
// string (PipelineModeDefault) means the built-in, hardcoded pipeline.
//
// Scope note: this field is introduced in Epic 1.3 (backlog-configurable-
// pipeline) solely so PipelineEngine's mode-resolution/fail-closed
// behavior is exercisable against this struct per Story 1.3.3's own
// acceptance criteria. It is NOT yet wired to ent/proto/the repository
// persistence layer or any RPC handler — every BacklogItemData produced
// by the current storage layer has PipelineMode == "" today. That full
// wiring (ent schema field, proto optional field, repository Create/
// Update mapping, RPC handler presence-gating) is Epic 1.4's scope.
PipelineMode string
// Category is a coarse classification (bugfix/feature/chore/refactor) used
// purely as a frontend-defaulting hint at creation time — see
// BacklogCategory / IsValidBacklogCategory. Empty string means
// uncategorized (today's behavior for every existing item, preserved
// exactly). The server only persists and validates this value; it never
// resolves or applies the per-category automation-toggle defaults itself
// (that happens client-side, once, in BacklogItemForm.tsx at category-
// selection time).
Category string
PlanApproved bool
PlanApprovedAt *time.Time
PlanArtifactsPath string
// PlanRejectionReason is the free-text reason from the most recent
// RejectPlan call. Cleared on ApprovePlan, on the next TriggerTriage
// completion, and on backward transition to idea/refining. See
// project_plans/plan-approval-ux/decisions/ADR-001.
PlanRejectionReason string
PlanRejectedAt *time.Time
// QueuedAt is set when a fresh spawn hit the concurrency cap and the item
// was transitioned to "queued" instead of rejected. Nil unless Status ==
// BacklogStatusQueued (or the item was previously queued). Drives FIFO
// dequeue ordering.
QueuedAt *time.Time
// QueuedAutonomous preserves the Autonomous flag from the spawn request
// that got queued, so dequeue replays it faithfully.
QueuedAutonomous bool
Notes string
ExternalID string
// ExternalURL is the browser-facing URL of the linked external item (e.g.
// the GitHub issue's html_url). Empty when the item has no linked source.
ExternalURL string
// Labels holds the external source's label set (e.g. GitHub issue labels)
// as of the most recent Fetch. Nil/empty for items with no linked source
// or no labels.
Labels []string
ArchivedAt *time.Time
SourceID string
PrURL string
PrNumber int
// ShippedCheckConclusion holds the durable GitHub CI-conclusion snapshot
// captured at ship time — genuine GitHub CI-conclusion values only, never
// a capture-failure sentinel. See ShippedSnapshotCaptureFailed.
ShippedCheckConclusion string
// ShippedApprovedCount is the durable review-approval-count snapshot
// captured at ship time.
ShippedApprovedCount int
// ShippedChangesReqCount is the durable "changes requested" review-count
// snapshot captured at ship time.
ShippedChangesReqCount int
// ShippedSnapshotAt is the timestamp the durable ship snapshot was
// captured at. Nil when no snapshot has ever been captured.
ShippedSnapshotAt *time.Time
// PrFeedbackAddressedAt is the comment-feedback dedup watermark: the
// newest substantive PR review-feedback timestamp a fix session has
// already been dispatched to address. Nil when no feedback-triggered fix
// has ever been dispatched for this item's current PR.
PrFeedbackAddressedAt *time.Time
// GitHubSyncedIssueUpdatedAt is the loop-prevention watermark: the GitHub
// issue updated_at value most recently synced from GitHub into this item.
// Nil when the item has never been synced from GitHub.
GitHubSyncedIssueUpdatedAt *time.Time
// UserModifiedFields is the JSON-encoded set of field names (title,
// description, priority) the user has directly edited via UpdateBacklogItem
// — see ParseUserModifiedFields/MergeUserModifiedFields. Empty string means
// no field is locally locked; backward sync (SyncOne) treats any field in
// this set as local-wins and skips overwriting it from the remote source.
UserModifiedFields string
// ShippedFileStats holds the JSON-encoded []ShippedFileStat snapshot of
// per-file diff stats captured at ship time.
ShippedFileStats string
// ShippedSnapshotCaptureFailed is true when CaptureShipSnapshot's GitHub
// fetch or file-stats computation failed — distinct from
// ShippedCheckConclusion, which holds only genuine CI-conclusion values.
ShippedSnapshotCaptureFailed bool
// NextWorkflowID is the pipeline-chaining target (webhook-triggers FR10/AC5):
// the Workflow ChainFirer fires once this item reaches BacklogStatusDone. Nil
// means no chain is configured.
NextWorkflowID *uuid.UUID
// ChainFired is true once the NextWorkflowID chain-fire has reached a
// terminal outcome (fired, depth-capped, or expired) — never retried again
// once true. See ChainFirer/TriggerChainReconciler.
ChainFired bool
// ChainedAt is set atomically with the terminal done transition (when
// NextWorkflowID is already configured) — the eligibility timestamp
// TriggerChainReconciler's maxChainWaitDuration ceiling measures age
// against. Nil until the item has reached done with a chain configured.
ChainedAt *time.Time
// TriggeredByChainDepth is how many chain hops produced this item —
// propagated session->session and hard-capped at maxChainDepth (Epic 6.3).
TriggeredByChainDepth int
CreatedAt time.Time
UpdatedAt time.Time
// ItemSessions holds the eagerly-loaded item sessions for this backlog item.
// Only populated when explicitly loaded by the caller (e.g. GetBacklogItem).
ItemSessions []ItemSessionSummary
// StatusEvents holds the eagerly-loaded status transition history.
// Only populated when explicitly loaded by the caller (e.g. GetBacklogItem).
StatusEvents []BacklogStatusEventData
// ProgressNotes holds the eagerly-loaded report_progress audit trail (the
// implementer's decision history). Only populated when explicitly loaded by
// the caller (e.g. GetBacklogItem) — see StatusEvents for the same pattern.
ProgressNotes []ProgressNoteData
// ActivityNotes holds the eagerly-loaded post_backlog_update history — the
// ungated, append-only sibling log to ProgressNotes (see ADR-001). Only
// populated when explicitly loaded by the caller (e.g. GetBacklogItem) —
// see StatusEvents for the same pattern.
ActivityNotes []ActivityNoteData
}
BacklogItemData is the domain model for a backlog item.
type BacklogItemDependencyEdge ¶ added in v1.43.0
type BacklogItemDependencyEdge struct {
// BlockerID is the item that must reach a resolved status (done or
// archived) before BlockedID is eligible for dequeue/start.
BlockerID string
// BlockedID is the dependent item, gated until BlockerID resolves.
BlockedID string
}
BacklogItemDependencyEdge names a blocker/blocked pair explicitly so the two bare ID strings can't be silently swapped at a call site — see .claude/rules/primitive-obsession-checklist.md.
type BacklogItemFilter ¶ added in v1.35.0
type BacklogItemFilter struct {
// Statuses restricts results to these statuses. Empty means no restriction.
Statuses []string
// Priorities restricts results to these priority values. Empty means no restriction.
Priorities []int
// SortBy controls ordering ("priority", "updated_at"). Empty means default ordering.
SortBy string
// ExcludeDone, when true and Statuses is empty, excludes items with status
// "done". Independent of ExcludeArchived — split into two flags (rather
// than one combined "ExcludeTerminal") so a caller can show done items by
// default while still hiding archived ones. Renamed from ExcludeTerminal,
// which used to combine both; verified via grep that ListBacklogItems and
// ListBacklogItemSummaries were the only two callers of the old field, so
// the rename is safe (no silent behavior change for any other caller).
ExcludeDone bool
// ExcludeArchived, when true and Statuses is empty, excludes items with
// status "archived". Independent of ExcludeDone — see its doc comment.
ExcludeArchived bool
// Limit caps the number of results returned. 0 means use the default safety cap (1000).
Limit int
// Offset skips the first N results (for pagination). Only applied when Limit > 0.
Offset int
// ChainFired, when non-nil, restricts results to items whose chain_fired
// column equals *ChainFired. Added so TriggerChainReconciler.ReconcileChains
// (session/chain_firer.go) can push its "unfired pending chain" filter into
// SQL instead of scanning every "done" item up to the default 1000-row
// safety cap and filtering in Go — past 1000 done items, a pending unfired
// chain outside that window was silently never reconciled (sdd:6-verify
// finding). Backed by index.Fields("status", "chain_fired")
// (session/ent/schema/backlog_item.go).
ChainFired *bool
// NextWorkflowIDSet, when non-nil, restricts results to items where
// next_workflow_id IS NOT NULL (true) or IS NULL (false). See ChainFired's
// doc comment — the two are combined by ReconcileChains's query.
NextWorkflowIDSet *bool
}
BacklogItemFilter controls which items ListBacklogItems returns.
type BacklogItemPrecondition ¶ added in v1.35.0
type BacklogItemPrecondition struct {
// ExpectedStatus, if non-empty, requires the item's current status to match.
ExpectedStatus string
// ExpectedUpdatedAt, if non-zero, requires the item's updated_at to match.
ExpectedUpdatedAt *time.Time
// Note, if non-empty, is stored in the status event audit log alongside this
// transition. Use it to record why the transition happened (e.g. "auto-reopened
// after FAIL verdict").
Note string
}
BacklogItemPrecondition is used for optimistic locking on update/transition.
type BacklogItemSummary ¶ added in v1.37.0
type BacklogItemSummary struct {
ID string `json:"id"`
ExternalID string `json:"external_id"`
ExternalURL string `json:"external_url"`
Labels []string `json:"labels"`
Title string `json:"title"`
Status BacklogStatus `json:"status"`
Priority int `json:"priority"`
RepoPath string `json:"repo_path"`
AcceptanceCriteria AcCriteriaJSON `json:"acceptance_criteria"`
Notes string `json:"notes"`
PrURL string `json:"pr_url"`
PrNumber int `json:"pr_number"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ArchivedAt *time.Time `json:"archived_at"`
ItemSessions []ItemSessionSummary `json:"-"`
}
BacklogItemSummary is a lightweight projection of BacklogItemData for list views. It omits large text fields (Description, plan artifacts) and status-event history, but eagerly includes ItemSessions (with ReviewVerdict) for cost/status display.
type BacklogItemTransitionInput ¶ added in v1.35.0
type BacklogItemTransitionInput = domain.BacklogItemTransitionInput
BacklogItemTransitionInput carries the fields needed by TransitionGuard. Type alias — session.BacklogItemTransitionInput and domain.BacklogItemTransitionInput are identical types.
type BacklogItemUpdate ¶ added in v1.35.0
type BacklogItemUpdate struct {
Title *string
Description *string
AcceptanceCriteria *AcCriteriaJSON
Priority *int
RepoPath *string
SkipReviewGate *bool
SkipPlanning *bool
AutoSpawnSession *bool
AutoCreatePR *bool
// PipelineMode is a pointer for partial-update presence: nil means "leave
// the item's stored pipeline_mode untouched", while a non-nil pointer
// (including one pointing at "") explicitly sets/resets it. See
// BacklogItemData.PipelineMode for the field's semantics.
PipelineMode *string
// Category is a pointer for partial-update presence: nil means "leave the
// item's stored category untouched", while a non-nil pointer (including
// one pointing at "") explicitly sets/clears it. See
// BacklogItemData.Category for the field's semantics.
Category *string
Notes *string
// ExternalURL and Labels follow the same partial-update-presence
// convention as the other pointer fields on this struct: nil means "leave
// untouched", a non-nil pointer (including one pointing at "" / an empty
// slice) explicitly sets it.
ExternalURL *string
Labels *[]string
PlanApproved *bool
PlanApprovedAt *time.Time
PlanArtifactsPath *string
// PlanRejectionReason and PlanRejectedAt follow the same partial-update-
// presence convention: nil means "leave untouched", a non-nil pointer
// explicitly sets it. Since a plain pointer can't distinguish "leave
// untouched" from "clear it back to nil", use ClearPlanRejectedAt to
// explicitly clear the timestamp back to nil (e.g. alongside resetting
// PlanRejectionReason back to "" on approval/re-triage) — see
// PrFeedbackAddressedAt/ClearPrFeedbackAddressedAt below for the same
// pattern.
PlanRejectionReason *string
PlanRejectedAt *time.Time
ClearPlanRejectedAt bool
// QueuedAt and QueuedAutonomous follow the same partial-update-presence
// convention as PlanApprovedAt: nil means "leave untouched".
QueuedAt *time.Time
QueuedAutonomous *bool
PrURL *string
PrNumber *int
// ShippedCheckConclusion, ShippedApprovedCount, ShippedChangesReqCount,
// ShippedSnapshotAt, ShippedFileStats, and ShippedSnapshotCaptureFailed
// are pointers for partial-update presence, following the existing
// convention: nil means "leave the item's stored value untouched", a
// non-nil pointer explicitly sets it. See BacklogItemData's fields of
// the same name for semantics.
ShippedCheckConclusion *string
ShippedApprovedCount *int
ShippedChangesReqCount *int
ShippedSnapshotAt *time.Time
ShippedFileStats *string
ShippedSnapshotCaptureFailed *bool
// PrFeedbackAddressedAt follows the same partial-update-presence
// convention: nil means "leave untouched", a non-nil pointer sets the
// comment-feedback dedup watermark. Since a plain pointer can't
// distinguish "leave untouched" from "clear it back to nil", use
// ClearPrFeedbackAddressedAt to explicitly clear it (e.g. when a PR
// closes without merging and a fresh PR should start with a clean
// watermark).
PrFeedbackAddressedAt *time.Time
ClearPrFeedbackAddressedAt bool
// GitHubSyncedIssueUpdatedAt follows the same partial-update-presence
// convention as PrFeedbackAddressedAt: nil means "leave untouched", a
// non-nil pointer sets the loop-prevention watermark. Use
// ClearGitHubSyncedIssueUpdatedAt to explicitly clear it back to nil.
GitHubSyncedIssueUpdatedAt *time.Time
ClearGitHubSyncedIssueUpdatedAt bool
// ReworkCapOverride follows the same single-pointer presence convention as
// the fields above: nil means "leave untouched". A non-nil pointer sets the
// item's override (0 = unlimited, >0 = this item's own cap). There is
// currently no way to explicitly clear an override back to "use the global
// default" via this struct — a deliberate simplification; add a
// ClearReworkCapOverride bool alongside this if that's needed later.
ReworkCapOverride *int
// UserModifiedFields follows the same partial-update-presence convention:
// nil means "leave untouched", a non-nil pointer sets the stored
// JSON-encoded set of user-modified field names (e.g. `["title"]`). Build
// the value with MergeUserModifiedFields rather than hand-encoding JSON.
UserModifiedFields *string
// NextWorkflowID/ClearNextWorkflowID follow the same nillable-clear
// convention as GitHubSyncedIssueUpdatedAt: nil+false means "leave
// untouched", ClearNextWorkflowID=true explicitly clears the chain
// configuration back to nil, otherwise a non-nil pointer sets it
// (webhook-triggers FR10/AC5 — see BacklogItemData.NextWorkflowID).
NextWorkflowID *uuid.UUID
ClearNextWorkflowID bool
// ChainFired is a normal presence pointer (no clear semantics needed — it
// only ever moves false->true, by ChainFirer/TriggerChainReconciler).
ChainFired *bool
// ChainedAt/ClearChainedAt follow the same nillable-clear convention as
// NextWorkflowID above.
ChainedAt *time.Time
ClearChainedAt bool
// TriggeredByChainDepth is a normal presence pointer — non-nillable in the
// schema (Default 0), so no clear semantics are needed.
TriggeredByChainDepth *int
}
BacklogItemUpdate carries the mutable fields for UpdateBacklogItem.
type BacklogLifecycleListener ¶ added in v1.35.0
type BacklogLifecycleListener struct {
// contains filtered or unexported fields
}
BacklogLifecycleListener drives backlog item state transitions in response to session lifecycle events. It must be registered via Instance.RegisterLifecycleListener.
OnLifecycleEvent is non-blocking; all DB work is dispatched to a goroutine. Call SetEnabled(false) to make all callbacks no-ops without unwiring.
func NewBacklogLifecycleListener ¶ added in v1.35.0
func NewBacklogLifecycleListener(storage *Storage) *BacklogLifecycleListener
NewBacklogLifecycleListener creates a listener backed by the given storage. The review gate is disabled (sessionCreator=nil, headlessPool=nil). No PipelineEngine is wired (nil) — callers needing one should use NewBacklogLifecycleListenerWithPool.
func NewBacklogLifecycleListenerWithPool ¶ added in v1.35.0
func NewBacklogLifecycleListenerWithPool(storage *Storage, pool *headless.Pool, pipelineEngine PipelineEngine) *BacklogLifecycleListener
NewBacklogLifecycleListenerWithPool creates a listener that uses a headless.Pool for review gate calls instead of spawning a tmux session. pipelineEngine is the shared PipelineEngine instance (Epic 1.5, Story 1.5.1) — pass nil to fall back to the built-in default pipeline for every item.
func NewBacklogLifecycleListenerWithSpawner ¶ added in v1.35.0
func NewBacklogLifecycleListenerWithSpawner(storage *Storage, spawner ReviewGateSpawner) *BacklogLifecycleListener
NewBacklogLifecycleListenerWithSpawner creates a listener that will spawn a review gate session when a work session exits and SkipReviewGate is false.
func (*BacklogLifecycleListener) BackfillStuckStates ¶ added in v1.38.0
func (l *BacklogLifecycleListener) BackfillStuckStates(ctx context.Context)
BackfillStuckStates seeds durable BacklogStuckState rows for items that are already stuck at startup, with notified_at pre-set, so the first genuine reconcile tick after a restart/deploy does not re-notify for conditions that were already known (and already surfaced) before the restart. Intended to be called once, before the reconcile ticker goroutine starts. Idempotent via MarkStuck's (item_id, reason) unique-constraint upsert — safe to call on every startup, not just the first one. Best-effort throughout: query/write failures are logged, never returned — backfill must never block startup.
Scope note: only the two DB-derivable reasons that already have a queryable detection surface as of this Epic are seeded — abandoned_review (via the existing FindStuckReviewItems query) and stale_work (mirroring reconcileStaleWorkSessions' maxWorkSessionStaleness check, without modifying that function). rework_cap, bouncing, and push_failed are deliberately NOT seeded here: their detection logic is introduced by Phase 2 (Stories 2.1.2, 2.1.4, 2.1.6 respectively) and does not exist yet in this Epic — seeding them now would mean fabricating that not-yet-built detection logic ahead of schedule. Once Phase 2 ships those detectors, their own MarkStuck/MarkStuckNotified notify-once dedup naturally covers the "first tick after shipping" storm-suppression case for those three reasons at that time, the same way this backfill does for the two reasons seeded today.
pr_ready_unmerged is excluded for a different reason: detecting it needs a GetPRStatus/IsPRMerged GitHub call per pr_pending item, which would burst the GitHub API on every one of the 15+ daily boots. The first genuine tick after startup surfaces it via its own notified_at IS NULL + 30-min gate — a one-tick delay, not a startup API burst.
func (*BacklogLifecycleListener) PipelineEngine ¶ added in v1.38.0
func (l *BacklogLifecycleListener) PipelineEngine() PipelineEngine
PipelineEngine returns the PipelineEngine injected at construction (nil if none was wired). Exported for the pointer-equality integration test proving BacklogService and BacklogLifecycleListener share a single PipelineEngine instance (Story 1.5.1).
func (*BacklogLifecycleListener) ReconcilePRPending ¶ added in v1.37.0
func (l *BacklogLifecycleListener) ReconcilePRPending(ctx context.Context, er *EntRepository)
ReconcilePRPending polls items in pr_pending status. It transitions to done when the PR is merged, and spawns a fix session when CI fails or reviewers request changes.
Pre-existing complexity relocated verbatim by the backlog_lifecycle.go split (session/backlog_lifecycle_pr.go); not introduced by that split. Splitting this function into drift/merge-detection/pr_ready_unmerged sub-steps is a separate follow-up (see the architecture-review that preceded the split), not part of moving it to its own file.
func (*BacklogLifecycleListener) ReconcileStuck ¶ added in v1.35.0
func (l *BacklogLifecycleListener) ReconcileStuck(ctx context.Context)
ReconcileStuck calls ReconcileStuckItems and logs the result. Intended to be called on a periodic ticker as a safety net for abnormal session exits. No-op when the listener is disabled.
func (*BacklogLifecycleListener) RecordPRCreatedOutOfBand ¶ added in v1.39.0
func (l *BacklogLifecycleListener) RecordPRCreatedOutOfBand(ctx context.Context, workSessionUUID, prURL string, prNumber int)
RecordPRCreatedOutOfBand records a PR that was created for workSessionUUID through a path other than pushAndCreatePR and transitions the linked backlog item straight to pr_pending via the shared resolveToPRPending tail. (Named "Record", not "Notify", to avoid confusion with l.notify — the user-facing toast helper used elsewhere in this file; this method mutates backlog-item state, it doesn't just surface a message.)
Why this exists: pushAndCreatePR is the *only* place that ever writes pr_pending, but it is reached exclusively via the automated handleReviewSessionExited(PASS) → pushAndCreatePR call chain. The Review Queue's manual "Create PR" button (web-app/src/components/sessions/ ReviewQueuePanel.tsx) drives a completely separate path — SessionService.RunOneShot (server/services/session_service.go) — that runs an ad hoc `claude -p <prompt>` in the worktree and only ever persists the resulting PR URL onto the *session* record (inst.SetGitHubPR). It has no knowledge of backlog items at all, so a backlog-linked item whose PR was created this way never left "review" — ReconcilePRPending's FindPRPendingItems query structurally cannot find it, since it only looks at items already in pr_pending. Left in "review", the item instead accumulates in_progress↔review bounce churn from unrelated reconciliation and eventually reports stuck-reason BOUNCING instead of the correct pr_ready_unmerged. This is the root cause traced in docs/tasks/backlog-feature-improvement.md's "second, compounding root cause" note for PR #157.
No-op if the listener is disabled, the caller has no PR info, the session isn't backlog-linked, or the item isn't currently "review" (avoids clobbering any other in-flight transition). That guard narrows, but does not eliminate, a race with a concurrent pushAndCreatePR call on the same item: TransitionBacklogItemStatus's precondition check is a read-then-write (Get, check in memory, then Save) rather than a true atomic compare-and- swap, so both calls can observe "review" and both succeed. That's harmless here — both write the same target status and equivalent PR fields — but it means two BacklogStatusEvent audit rows can be written instead of one, not that exactly one call is guaranteed to win.
Known limitation (not fixed here — see PR description): unlike pushAndCreatePR, this does not attempt EnablePRAutoMerge, since it has no worktree/git handle to call it with; a PR created via this path currently requires a manual merge. extractPRURL's freeform-text parsing also means prURL/prNumber are not independently verified against GitHub before being persisted — acceptable for this single-operator tool's threat model, but worth knowing if RunOneShot's trust boundary ever changes.
func (*BacklogLifecycleListener) SetAutoReopener ¶ added in v1.37.0
func (l *BacklogLifecycleListener) SetAutoReopener(r AutoReopenSpawner)
SetAutoReopener wires in the spawner used to automatically reopen items for rework when a review verdict is FAIL or PARTIAL.
func (*BacklogLifecycleListener) SetBranchReconciler ¶ added in v1.39.0
func (l *BacklogLifecycleListener) SetBranchReconciler(f func(worktreePath, branchName string) (*git.MergeMainResult, error))
SetBranchReconciler overrides the function used to fetch+merge a branch's remote ref into its worktree for push_failed remediation (attemptPushRemediation). Overridable in tests to avoid needing a real git repo on disk; production code never needs to call this, since newListenerBase installs git.MergeMainIntoWorktree.
func (*BacklogLifecycleListener) SetChainReconciler ¶ added in v1.43.0
func (l *BacklogLifecycleListener) SetChainReconciler(r *TriggerChainReconciler)
SetChainReconciler wires in the pipeline-chain restart-recovery reconciler (webhook-triggers Phase 6). Called via server/dependencies.go once a ChainFirer has been constructed (see Storage.WireChainFirer).
func (*BacklogLifecycleListener) SetDequeuer ¶ added in v1.41.0
func (l *BacklogLifecycleListener) SetDequeuer(d QueueDequeuer)
SetDequeuer wires in the spawner used to dequeue queued backlog items once a WIP slot frees up.
func (*BacklogLifecycleListener) SetEnabled ¶ added in v1.35.0
func (l *BacklogLifecycleListener) SetEnabled(v bool)
SetEnabled toggles whether this listener processes lifecycle events. Safe to call concurrently.
func (*BacklogLifecycleListener) SetHeadlessPool ¶ added in v1.35.0
func (l *BacklogLifecycleListener) SetHeadlessPool(p *headless.Pool)
SetHeadlessPool wires in the headless LLM pool after construction. Calling this enables the headless review gate path even when the listener was created via NewBacklogLifecycleListenerWithSpawner.
func (*BacklogLifecycleListener) SetNotifier ¶ added in v1.37.0
func (l *BacklogLifecycleListener) SetNotifier(n Notifier)
SetNotifier wires in the notifier used to publish operator-facing notifications (PR creation failures, security blocks, stale work sessions, rework-cap hits). Optional — nil means notifications are disabled.
func (*BacklogLifecycleListener) SetOneShotShipRunner ¶ added in v1.39.0
func (l *BacklogLifecycleListener) SetOneShotShipRunner(r OneShotShipRunner)
SetOneShotShipRunner wires in the runner used by shipViaAgentOrFallback to attempt an agent-driven PR ship (see agentShipPrompt) before falling back to the mechanical pushAndCreatePR path. Optional — nil means every PASS verdict with an ended work session goes straight to the mechanical path, matching this fix's pre-existing behavior.
func (*BacklogLifecycleListener) SetOrphanedPRFinder ¶ added in v1.41.0
func (l *BacklogLifecycleListener) SetOrphanedPRFinder(f func(ctx context.Context, repoPath, branch string) (*github.PRInfo, error))
SetOrphanedPRFinder overrides the function used to look up an existing PR for a repo path's branch, used by reconcileOrphanedAgentPRs (Epic 3.2). Overridable in tests to avoid real GitHub API calls or needing a real git remote on disk; production code never needs to call this, since newListenerBase installs defaultOrphanedPRFinder.
func (*BacklogLifecycleListener) SetPRByNumberFinder ¶ added in v1.41.0
func (l *BacklogLifecycleListener) SetPRByNumberFinder(f func(ctx context.Context, repoPath string, prNumber int) (*github.PRInfo, error))
SetPRByNumberFinder overrides the function used to look up a PR by its immutable number, used by verifyPRHeadBranchMatchesTracked (Story 6). Overridable in tests to avoid real GitHub API calls or needing a real git remote on disk; production code never needs to call this, since newListenerBase installs defaultPRByNumberFinder.
func (*BacklogLifecycleListener) SetPRCreatorFactory ¶ added in v1.37.0
func (l *BacklogLifecycleListener) SetPRCreatorFactory(f func(repoPath, worktreePath, sessionName, branchName, baseCommitSHA string) prCreator)
SetPRCreatorFactory overrides the factory used to construct the push/PR-creation client for pushAndCreatePR. Overridable in tests; production code never needs to call this, since newListenerBase installs defaultPRCreatorFactory.
func (*BacklogLifecycleListener) SetPRFixSpawner ¶ added in v1.37.0
func (l *BacklogLifecycleListener) SetPRFixSpawner(s PRFixSpawner)
SetPRFixSpawner wires in the spawner used to automatically reopen pr_pending items for rework when CI checks fail or reviewers request changes.
func (*BacklogLifecycleListener) SetPRPendingCheckerFactory ¶ added in v1.37.0
func (l *BacklogLifecycleListener) SetPRPendingCheckerFactory(f func(repoPath string) prPendingChecker)
SetPRPendingCheckerFactory overrides the factory used to construct the PR-status checker for ReconcilePRPending. Overridable in tests (mirrors the timeNow seam in instance_workspace.go:581); production code never needs to call this, since newListenerBase installs defaultPRPendingCheckerFactory.
func (*BacklogLifecycleListener) SetReviewRespawner ¶ added in v1.39.0
func (l *BacklogLifecycleListener) SetReviewRespawner(r ReviewRespawner)
SetReviewRespawner wires in the spawner used to automatically re-trigger the review gate for items abandoned in review with no active session.
func (*BacklogLifecycleListener) SetReworkBlockStaleResolver ¶ added in v1.41.0
func (l *BacklogLifecycleListener) SetReworkBlockStaleResolver(r ReworkBlockStaleResolver)
SetReworkBlockStaleResolver wires in the resolver used to re-check and clear an open StuckReasonReworkBlockedStale row once its blocking work session recovers, ends, or the item leaves review — same pattern as AutoReopenSpawner/PRFixSpawner/SetStaleWorkRemediator.
func (*BacklogLifecycleListener) SetSessionArchiver ¶ added in v1.39.0
func (l *BacklogLifecycleListener) SetSessionArchiver(a SessionArchiver)
SetSessionArchiver wires in the archiver used to soft-archive backlog work sessions belonging to done/archived items that the transition hook missed (see the archive_terminal_sessions detector in ReconcileStuck). Optional — nil means the detector no-ops.
func (*BacklogLifecycleListener) SetSessionCreator ¶ added in v1.38.0
func (l *BacklogLifecycleListener) SetSessionCreator(s ReviewGateSpawner)
SetSessionCreator wires in the spawner used to create review-gate sessions after construction. Needed because production wiring (server/dependencies.go) constructs this listener before SessionService exists.
func (*BacklogLifecycleListener) SetSessionLivenessChecker ¶ added in v1.38.0
func (l *BacklogLifecycleListener) SetSessionLivenessChecker(f func(sessionUUID string) bool)
SetSessionLivenessChecker wires the function used by the zombie-session review detector (pre-mortem F3) to confirm whether a session's underlying tmux/CLI process is actually still alive, rather than trusting the DB's EndedAt IS NULL row alone. Optional — nil means the zombie detector never flags (conservative: unknown liveness is treated as "assume alive").
func (*BacklogLifecycleListener) SetStaleWorkRemediator ¶ added in v1.39.0
func (l *BacklogLifecycleListener) SetStaleWorkRemediator(r StaleWorkRemediator)
SetStaleWorkRemediator wires in the remediator used to clean up and respawn stale (but not zombie) work sessions — the "stale_work" reason's automated remediation action.
func (*BacklogLifecycleListener) SetTriageRespawner ¶ added in v1.41.0
func (l *BacklogLifecycleListener) SetTriageRespawner(r TriageRespawner)
SetTriageRespawner wires in the spawner used to automatically re-trigger triage for idea-status items whose triage session orphaned.
func (*BacklogLifecycleListener) Shutdown ¶ added in v1.35.0
func (l *BacklogLifecycleListener) Shutdown()
Shutdown cancels in-flight review gate calls. Safe to call concurrently.
func (*BacklogLifecycleListener) TriggerReviewForSession ¶ added in v1.37.0
func (l *BacklogLifecycleListener) TriggerReviewForSession(workSessionUUID string)
TriggerReviewForSession immediately spawns a review gate for the work session identified by workSessionUUID. Used by the autonomous driver to trigger review as soon as the driver signals DONE, rather than waiting for ReconcileStuck. No-op if the listener is disabled or no review mechanism is configured.
func (*BacklogLifecycleListener) WireToInstance ¶ added in v1.35.0
func (l *BacklogLifecycleListener) WireToInstance(inst *Instance)
WireToInstance creates a per-instance listener shim and registers it on inst. Call this for every Instance that should participate in backlog lifecycle tracking.
type BacklogStatus ¶ added in v1.35.0
type BacklogStatus = domain.BacklogStatus
BacklogStatus represents the lifecycle state of a backlog item. Type alias — session.BacklogStatus and domain.BacklogStatus are identical types; all existing callers continue to work without any import changes.
type BacklogStatusEventData ¶ added in v1.37.0
type BacklogStatusEventData struct {
ID string
FromStatus string
ToStatus string
TriggeredBy string
Note *string
CreatedAt time.Time
}
BacklogStatusEventData is the domain DTO replacing *ent.BacklogStatusEvent in Storage returns.
type BookmarkTarget ¶
BookmarkTarget represents a bookmark/branch as a switch target
type CDPStreamManager ¶ added in v1.35.0
type CDPStreamManager = cdp.CDPStreamManager
CDPStreamManager is a local alias for the cdp package interface so that files within the session package can reference it without importing cdp directly.
type CachingPipelineEngine ¶ added in v1.38.0
type CachingPipelineEngine struct {
// contains filtered or unexported fields
}
CachingPipelineEngine is the single concrete implementation of PipelineEngine. PipelineModeDefault resolves for free (no cache/DB touch); any other slug resolves via pipelineModeCache; any unresolvable/malformed slug falls back to the default behavior and emits exactly one PipelineEngine-prefixed Warn log line naming the item and the unresolved slug (never a silent no-op, never a panic) — see Story 1.3.3's fail-closed acceptance criteria.
func NewPipelineEngine ¶ added in v1.38.0
func NewPipelineEngine(repo PipelineModeRepository) (*CachingPipelineEngine, error)
NewPipelineEngine constructs a CachingPipelineEngine backed by repo, doing one synchronous cache.Load at construction time.
Unlike NewDefaultWorkflowEngine's zero-arg, infallible, pure in-memory construction, this constructor performs a real DB call and can fail (DB unavailable, migration race, transient connection error). Per plan.md's Risk Control section ("NewPipelineEngine startup-failure behavior"), a cache.Load failure here NEVER aborts construction: it is logged at Warn and NewPipelineEngine returns a valid, usable engine backed by an empty cache. The signature still returns an error for future-proofing (e.g. a future validation error genuinely worth failing construction on), but this Phase 1 implementation never returns a non-nil error for a cache.Load failure specifically — PipelineEngine is purely additive/opt-in, so a transient DB hiccup at boot must never crash the whole server for a feature most items don't use yet.
func (*CachingPipelineEngine) ContentHashFor ¶ added in v1.38.0
func (e *CachingPipelineEngine) ContentHashFor(mode PipelineMode) (string, bool)
ContentHashFor implements PipelineEngine.
No Warn log is emitted for an unresolved slug here — documented exemption: this method is only ever called from the Epic 1.6 snapshot-write path immediately after a successful resolution already logged its own outcome via one of the 4 methods above; a caller invoking ContentHashFor for an already-unresolved slug independently of that flow would be a pre-existing bug elsewhere, not a new failure mode worth logging again here.
func (*CachingPipelineEngine) InitialPromptFor ¶ added in v1.38.0
func (e *CachingPipelineEngine) InitialPromptFor(item *BacklogItemData, priorSessions []ItemSessionSummary) string
InitialPromptFor implements PipelineEngine.
func (*CachingPipelineEngine) InteractiveReviewPromptFor ¶ added in v1.39.0
func (e *CachingPipelineEngine) InteractiveReviewPromptFor(item *BacklogItemData, acSnapshot []AcCriterion, diff string, diffTruncated bool, itemSessionID string, verificationNotes string) string
InteractiveReviewPromptFor implements PipelineEngine.
Shares ReviewPromptTemplate with ReviewPromptFor rather than introducing a second template field: the PipelineMode schema's review_prompt_template comment ("Prompt template used for review under this pipeline mode") is already style-agnostic, and splitting headless-JSON vs. tool-call variants into two DB fields/UI inputs for one logical "review prompt" concept would be speculative until a real mode author needs genuinely different content for the two paths. Custom-mode authors who want their template to drive the real review gate (this method) must include submit_review_verdict call instructions themselves — see the interface doc comment.
func (*CachingPipelineEngine) InvalidateCache ¶ added in v1.38.0
func (e *CachingPipelineEngine) InvalidateCache(ctx context.Context) error
InvalidateCache re-fetches enabled pipeline modes from the repository and swaps the cache wholesale. Exported for the RPC write handlers (Epic 2.2) that must invalidate the cache after every Create/Update/Delete/Enable/ Disable of a PipelineMode. Not yet called by any production code path in this epic — added now because it is cheap and Epic 2.2 needs it.
func (*CachingPipelineEngine) ReviewPromptFor ¶ added in v1.38.0
func (e *CachingPipelineEngine) ReviewPromptFor(item *BacklogItemData, acSnapshot []AcCriterion, diff string, diffTruncated bool, verificationNotes string, extras ReviewContextExtras) string
ReviewPromptFor implements PipelineEngine.
Deviation from plan.md's Story 1.3.1 interface text: an extras ReviewContextExtras parameter was added. BuildHeadlessReviewPrompt requires it (PriorSessions/ProgressNotes/ItemDescription), and the real call sites this engine replaces (session/review_gate.go, backlog_service_triage.go's TriggerReReview) always populate it — dropping it silently would have been a real behavior regression on the default path, not just an interface nicety. Epic 1.5's call sites should pass their real extras value here.
func (*CachingPipelineEngine) SlashCommandSet ¶ added in v1.38.0
func (e *CachingPipelineEngine) SlashCommandSet(item *BacklogItemData) (map[string]string, error)
SlashCommandSet implements PipelineEngine.
func (*CachingPipelineEngine) TriagePromptFor ¶ added in v1.38.0
func (e *CachingPipelineEngine) TriagePromptFor(item *BacklogItemData, artifactAbsPath string) string
TriagePromptFor implements PipelineEngine.
type CallbackDispatcher ¶ added in v1.43.0
CallbackDispatcher fires an outbound HTTP callback for a named lifecycle event (e.g. "session_complete", "session_stale", "queue_item_created" — see server/services.CallbackDispatcher's Dispatch doc comment for the full event-type list and payload shapes). Implemented outside this package (server/services.CallbackDispatcher) since this package cannot import server/services — server/services imports session, so the reverse import would be a cycle. Mirrors the ItemChangePublisher cross-package adapter pattern (see ItemChangePublisher, backlog_item_change.go).
Dispatch must never block the caller (FR8) and must never panic into the caller — implementations bound concurrent in-flight dispatch goroutines and drop+log beyond that cap (AC10) rather than queuing unboundedly.
type CancelPendingKillParams ¶ added in v1.42.0
type CancelPendingKillParams struct {
Storage InstanceStore
Suspended *SuspendedProcessStore
InstanceID string
OriginalPID int32
}
CancelPendingKillParams holds everything needed to abandon an in-progress import: delete the committed Instance, then resume the original process.
type CanonicalBlock ¶ added in v1.35.0
type CanonicalBlock struct {
Kind CanonicalBlockKind `json:"kind"`
Text string `json:"text,omitempty"`
ToolID string `json:"tool_id,omitempty"`
ToolName string `json:"tool_name,omitempty"`
ToolArgs json.RawMessage `json:"tool_args,omitempty"`
ToolResultID string `json:"tool_result_id,omitempty"`
ToolResultContent string `json:"tool_result_content,omitempty"`
ToolResultIsError bool `json:"tool_result_is_error,omitempty"`
}
func NewTextBlock ¶ added in v1.35.0
func NewTextBlock(text string) CanonicalBlock
NewTextBlock constructs a valid CanonicalBlock of text kind.
func NewThinkingBlock ¶ added in v1.35.0
func NewThinkingBlock(text string) CanonicalBlock
NewThinkingBlock constructs a valid CanonicalBlock of thinking kind.
func NewToolResultBlock ¶ added in v1.35.0
func NewToolResultBlock(id, name, content string, isError bool) CanonicalBlock
NewToolResultBlock constructs a valid CanonicalBlock of tool_result kind.
func NewToolUseBlock ¶ added in v1.35.0
func NewToolUseBlock(id, name string, args json.RawMessage) CanonicalBlock
NewToolUseBlock constructs a valid CanonicalBlock of tool_use kind.
func (CanonicalBlock) Validate ¶ added in v1.35.0
func (b CanonicalBlock) Validate() error
Validate checks if the block is in a valid state.
type CanonicalBlockKind ¶ added in v1.35.0
type CanonicalBlockKind string
const ( BlockKindText CanonicalBlockKind = "text" BlockKindThinking CanonicalBlockKind = "thinking" BlockKindToolUse CanonicalBlockKind = "tool_use" BlockKindToolResult CanonicalBlockKind = "tool_result" BlockKindImage CanonicalBlockKind = "image" )
type CanonicalRole ¶ added in v1.35.0
type CanonicalRole string
const ( RoleUser CanonicalRole = "user" RoleAssistant CanonicalRole = "assistant" )
type CanonicalTurn ¶ added in v1.35.0
type CanonicalTurn struct {
Role CanonicalRole `json:"role"`
Blocks []CanonicalBlock `json:"blocks"`
Timestamp time.Time `json:"timestamp"`
TurnIndex int `json:"turn_index"`
Model string `json:"model,omitempty"`
}
func ReadCanonicalTurnsFromFile ¶ added in v1.42.0
func ReadCanonicalTurnsFromFile(path string) ([]CanonicalTurn, error)
ReadCanonicalTurnsFromFile parses a Claude JSONL transcript at the given path into CanonicalTurns, independent of any live Instance. It is the shared parsing core used both by ClaudeAdapter.Import (which resolves a path from an Instance's working directory + conversation UUID first) and by the import-external-session preview path (Story 1.1.4), which reads a resolved history file's turns without ever constructing a half-built Instance just to read history.
Trailing partial lines (e.g. a JSONL file caught mid-write by a live writer) are tolerated: a line that fails to unmarshal as a well-formed turn is simply skipped rather than treated as an error, per the pitfalls research's "tolerate trailing partial lines" requirement.
func (CanonicalTurn) Validate ¶ added in v1.35.0
func (t CanonicalTurn) Validate() error
Validate checks if the turn and all of its blocks are in a valid state.
type ChainFirer ¶ added in v1.43.0
type ChainFirer struct {
// contains filtered or unexported fields
}
ChainFirer fires the next workflow in a pipeline chain once a BacklogItem reaches BacklogStatusDone with NextWorkflowID set (webhook-triggers FR10/ AC5). Both the happy-path async dispatch (EntRepository.dispatchChainFire, called immediately after TransitionBacklogItemStatus's own DB write returns — AC9) and TriggerChainReconciler's restart-recovery sweep call the same Fire method, so "fire exactly once per item" logic lives in exactly one place.
Concrete type, not an interface — repo/workflows/fireEvents are this package's own types (EntRepository, WorkflowRepository, TriggerFireEventRepository), referenced directly per .claude/rules/interface-pollution-checklist.md; only firer crosses a real package boundary and needs one (TriggerFirer, above).
func NewChainFirer ¶ added in v1.43.0
func NewChainFirer(repo *EntRepository, workflows WorkflowRepository, fireEvents TriggerFireEventRepository, firer TriggerFirer, cfg *config.Config) *ChainFirer
NewChainFirer constructs a ChainFirer. repo is the exact *EntRepository instance the rest of the backlog write path uses (so ChainFirer's own UpdateBacklogItem calls share the same callbackDispatcher/ itemChangePublisher wiring as every other backlog mutation) — callers should obtain it via Storage.WireChainFirer rather than constructing a second EntRepository around the same ent.Client.
func (*ChainFirer) Dispatch ¶ added in v1.43.0
func (c *ChainFirer) Dispatch(item *BacklogItemData)
Dispatch reserves a semaphore slot and fires item's chain in a new goroutine — same non-blocking shape as server/services/callback_dispatcher.go's CallbackDispatcher.Dispatch (Task 5.2.1a): a non-blocking select on a semaphore-sized channel either reserves a slot immediately or drops the dispatch and logs a warning. A dropped dispatch loses no work — ChainFired stays false, so TriggerChainReconciler retries the item on its next 60s tick. Never blocks the caller. No-op when the webhook_triggers feature flag is off (Task 8.2.1b — defense in depth beyond route-registration gating).
Takes no ctx parameter — matching CallbackDispatcher.Dispatch's identical signature choice — because the spawned goroutine always derives its own context.WithTimeout(context.Background(), chainFireTimeout) below rather than propagating a caller's ctx: by the time that goroutine's FireTriggerChained (CreateSession) call actually runs, a caller-supplied ctx (e.g. an RPC handler's request-scoped context, or dispatchChainFire's deliberately-Background() context) may already be cancelled or long gone — see dispatchChainFire's doc comment (session/ent_repository_backlog.go) for the same rationale applied one layer up.
func (*ChainFirer) Fire ¶ added in v1.43.0
func (c *ChainFirer) Fire(ctx context.Context, item *BacklogItemData) (fired bool, err error)
Fire attempts the chain-fire for item, which must already have NextWorkflowID set (callers filter on ChainFired == false before calling — Fire itself re-checks both as a defense-in-depth no-op guard). Returns fired=true only when a new session was actually created.
Double-fire race (webhook-triggers Task 6.2.1d): EntRepository's happy-path dispatch (right after TransitionBacklogItemStatus's done write) and TriggerChainReconciler's periodic sweep can both observe the same item with ChainFired==false and call Fire concurrently — a genuine risk once a chain sits pending for even one 60s tick. Fire closes this the same way this package already closed an identically-shaped TOCTOU race in TransitionBacklogItemStatus itself (see that function's doc comment, BUG-026): claimIfUnfired below performs a genuine SQL-level compare-and- swap (ChainFired=true, WHERE updated_at = <the value item was read at>) *before* calling TriggerFirer.FireTriggerChained, not after. Exactly one concurrent caller's claim UPDATE affects a row; every other caller's claim affects zero rows (ErrPreconditionFailed) and backs off without ever reaching FireTriggerChained — so at most one goroutine can ever call CreateSession for a given item's chain.
type Checkpoint ¶
type Checkpoint struct {
ID string `json:"id"`
SessionID string `json:"session_id"`
ParentID string `json:"parent_id,omitempty"`
Label string `json:"label"`
ScrollbackSeq uint64 `json:"scrollback_seq"`
ScrollbackPath string `json:"scrollback_path,omitempty"`
ClaudeConvUUID string `json:"claude_conv_uuid,omitempty"`
// ConvLineCount is the number of JSONL lines in the Claude conversation file at
// checkpoint time. Used by ForkClaudeConversation to truncate the fork correctly.
ConvLineCount uint64 `json:"conv_line_count,omitempty"`
GitCommitSHA string `json:"git_commit_sha,omitempty"`
Timestamp time.Time `json:"timestamp"`
// New: CLI-agnostic checkpoint details.
CanonicalTurnIndex int `json:"canonical_turn_index,omitempty"`
CanonicalPath string `json:"canonical_path,omitempty"`
}
Checkpoint represents a named bookmark of a session's state at a point in time. It captures the scrollback position, git SHA, and conversation UUID so that the session can later be forked or restored from this exact state.
type CheckpointList ¶
type CheckpointList []Checkpoint
CheckpointList is a slice of Checkpoints with helper methods.
func (CheckpointList) FindByID ¶
func (cl CheckpointList) FindByID(id string) *Checkpoint
FindByID returns the Checkpoint with the given ID, or nil if not found.
func (CheckpointList) FindByLabel ¶
func (cl CheckpointList) FindByLabel(label string) *Checkpoint
FindByLabel returns the first Checkpoint with the given label, or nil if not found.
func (CheckpointList) Latest ¶
func (cl CheckpointList) Latest() *Checkpoint
Latest returns the Checkpoint with the most recent Timestamp, or nil if empty.
type CircularBuffer ¶
type CircularBuffer struct {
// contains filtered or unexported fields
}
CircularBuffer is a thread-safe circular buffer with automatic disk fallback when the in-memory buffer fills up. This prevents memory overflow while maintaining a history of PTY output for status detection and debugging.
func NewCircularBuffer ¶
func NewCircularBuffer(size int) *CircularBuffer
NewCircularBuffer creates a new circular buffer with the specified size in bytes. When the buffer fills up, old data is automatically overwritten (circular behavior).
func (*CircularBuffer) Cap ¶
func (cb *CircularBuffer) Cap() int
Cap returns the total capacity of the buffer.
func (*CircularBuffer) Clear ¶
func (cb *CircularBuffer) Clear()
Clear resets the buffer to empty state.
func (*CircularBuffer) Close ¶
func (cb *CircularBuffer) Close() error
Close releases resources used by the circular buffer. If disk fallback is enabled, it removes the disk file.
func (*CircularBuffer) DisableDiskFallback ¶
func (cb *CircularBuffer) DisableDiskFallback() error
DisableDiskFallback disables disk fallback and removes the disk file.
func (*CircularBuffer) EnableDiskFallback ¶
func (cb *CircularBuffer) EnableDiskFallback(diskPath string) error
EnableDiskFallback enables automatic disk fallback when buffer is full. The diskPath parameter specifies where to store overflow data. This feature is currently a placeholder for future implementation.
func (*CircularBuffer) GetAll ¶
func (cb *CircularBuffer) GetAll() []byte
GetAll returns all data currently in the buffer. Returns a copy to prevent concurrent modification issues.
func (*CircularBuffer) GetRecent ¶
func (cb *CircularBuffer) GetRecent(n int) []byte
GetRecent returns the last n bytes from the buffer. If n is larger than the buffer size or the available data, returns all available data.
func (*CircularBuffer) GetRecentHash ¶ added in v1.37.0
func (cb *CircularBuffer) GetRecentHash(n int) (uint64, bool)
GetRecentHash returns the murmur3-64 hash of the last n bytes without allocating a copy. Returns (0, false) when the buffer has no data. In the common case (contiguous tail segment), this is allocation-free. Only the rare wrapped case allocates via murmur3.New64().
func (*CircularBuffer) GetRecentInto ¶ added in v1.37.0
func (cb *CircularBuffer) GetRecentInto(dst []byte, n int) int
GetRecentInto copies the last n bytes into dst and returns the number of bytes written. dst must have length >= n. Returns 0 when the buffer is empty. Prefer over GetRecent when the caller can provide a pooled buffer.
func (*CircularBuffer) Len ¶
func (cb *CircularBuffer) Len() int
Len returns the number of bytes currently in the buffer.
func (*CircularBuffer) TotalBytesWritten ¶ added in v1.35.0
func (cb *CircularBuffer) TotalBytesWritten() int64
TotalBytesWritten returns the total bytes ever written to this buffer (monotonically increasing).
type ClaudeAdapter ¶ added in v1.35.0
type ClaudeAdapter struct{}
func NewClaudeAdapter ¶ added in v1.35.0
func NewClaudeAdapter() *ClaudeAdapter
func (*ClaudeAdapter) CanHandle ¶ added in v1.35.0
func (a *ClaudeAdapter) CanHandle(program string) bool
func (*ClaudeAdapter) Export ¶ added in v1.35.0
func (a *ClaudeAdapter) Export(ctx context.Context, turns []CanonicalTurn, inst *Instance) error
func (*ClaudeAdapter) Import ¶ added in v1.35.0
func (a *ClaudeAdapter) Import(ctx context.Context, inst *Instance) ([]CanonicalTurn, error)
func (*ClaudeAdapter) Name ¶ added in v1.35.0
func (a *ClaudeAdapter) Name() string
type ClaudeCommandBuilder ¶
type ClaudeCommandBuilder struct {
// contains filtered or unexported fields
}
ClaudeCommandBuilder constructs Claude CLI commands with session resumption support. This builder intelligently adds the --resume flag when appropriate to maintain conversation continuity across session restarts.
func NewClaudeCommandBuilder ¶
func NewClaudeCommandBuilder(baseProgram string, claudeSession *ClaudeSessionData) *ClaudeCommandBuilder
NewClaudeCommandBuilder creates a new command builder for constructing Claude CLI commands. Parameters:
- baseProgram: The base command string (e.g., "claude", "claude --model sonnet", "aider")
- claudeSession: Optional session data for resumption support (can be nil)
func (*ClaudeCommandBuilder) Build ¶
func (b *ClaudeCommandBuilder) Build() string
Build constructs the final command string with session resumption if applicable. The method follows these rules:
- If not a Claude command, returns baseProgram unchanged
- If no session data exists, returns baseProgram unchanged
- If session ID is invalid UUID, returns baseProgram unchanged with warning
- If all conditions met, returns "baseProgram --resume <sessionId>"
type ClaudeController ¶
type ClaudeController struct {
// contains filtered or unexported fields
}
ClaudeController provides a high-level API for controlling Claude instances. It orchestrates all the underlying components (queue, executor, history, streams).
Locking discipline:
- lifecycle (Locked[controllerLifecycle]): write-locked briefly at the boundary of Start/Stop transitions. Slow cleanup in Stop() runs OUTSIDE this lock so that status reads are never blocked by goroutine joins or disk I/O.
- Sub-components (atomic.Pointer[T]): set once in Start(), cleared in Stop(). Readers call .Load() — a nil result means not yet initialized. Atomic access means GetCurrentStatus, GetRecentOutput, Subscribe, etc. never contend with Stop().
- listeners (Locked[[]StatusChangeListener]): fan-out callbacks.
- cache (Locked[cacheState]): tail-hash result cache for status/idle detection.
Cache-line layout: lifecycle.mu (a sync.RWMutex) and the atomic.Pointer fields are separated by [64]byte padding so that write operations on the mutex do not invalidate the cache line read by atomic.Load() calls (Go issue #67764).
func NewClaudeController ¶
func NewClaudeController(instance InstanceContext) (*ClaudeController, error)
NewClaudeController creates a new controller for the given instance.
func (*ClaudeController) AddStatusChangeListener ¶ added in v1.35.0
func (cc *ClaudeController) AddStatusChangeListener(fn StatusChangeListener)
AddStatusChangeListener appends fn to the fan-out set of status-change listeners. All registered listeners fire on every status transition. Safe to call before or after Start().
func (*ClaudeController) CancelCommand ¶
func (cc *ClaudeController) CancelCommand(commandID string) error
CancelCommand cancels a pending command in the queue.
func (*ClaudeController) ClearHistory ¶
func (cc *ClaudeController) ClearHistory() error
ClearHistory removes all command history entries.
func (*ClaudeController) ClearQueue ¶
func (cc *ClaudeController) ClearQueue() error
ClearQueue removes all pending commands from the queue.
func (*ClaudeController) GetCommandHistory ¶
func (cc *ClaudeController) GetCommandHistory(limit int) []*HistoryEntry
GetCommandHistory returns recent command history.
func (*ClaudeController) GetCommandStatus ¶
func (cc *ClaudeController) GetCommandStatus(commandID string) (*Command, error)
GetCommandStatus retrieves the current status of a command.
func (*ClaudeController) GetCurrentCommand ¶
func (cc *ClaudeController) GetCurrentCommand() *Command
GetCurrentCommand returns the currently executing command, if any.
func (*ClaudeController) GetCurrentStatus ¶
func (cc *ClaudeController) GetCurrentStatus() (detection.DetectedStatus, string)
GetCurrentStatus detects the current status of the Claude instance.
Two optimisations are applied on every call:
- Tail slicing — only the last statusDetectionTailBytes bytes of the terminal content are examined. Status indicators (◇ Ready, Thinking…, esc to interrupt) always appear near the current cursor position, so scanning the full scrollback is unnecessary.
- Content hash cache — a FNV-64a hash of the tail is compared against the previous call. If the tail is unchanged the cached result is returned immediately with zero allocations.
This function holds no lifecycle lock — it reads ptyAccess and statusDetector via atomic.Pointer, and the status/idle caches via atomic.Pointer. It therefore never blocks when Stop() is running its slow cleanup.
func (*ClaudeController) GetEscapeParser ¶ added in v1.35.0
func (cc *ClaudeController) GetEscapeParser() *analytics.EscapeCodeParser
GetEscapeParser returns the escape code parser from the response stream. Returns nil if the controller is not started or has no response stream.
func (*ClaudeController) GetExecutionOptions ¶
func (cc *ClaudeController) GetExecutionOptions() ExecutionOptions
GetExecutionOptions returns current execution options.
func (*ClaudeController) GetExitContent ¶ added in v1.15.0
func (cc *ClaudeController) GetExitContent() []byte
GetExitContent returns the last bytes captured before the PTY exited. Returns nil if the controller has no response stream or no exit content was recorded.
func (*ClaudeController) GetHistoryStatistics ¶
func (cc *ClaudeController) GetHistoryStatistics() HistoryStatistics
GetHistoryStatistics returns statistics about command execution.
func (*ClaudeController) GetIdleDuration ¶
func (cc *ClaudeController) GetIdleDuration() time.Duration
GetIdleDuration returns how long the session has been idle.
func (*ClaudeController) GetIdleState ¶
func (cc *ClaudeController) GetIdleState() (detection.IdleState, time.Time)
GetIdleState returns the current idle state with timing information. Returns the state and the timestamp of last activity.
Applies the same tail-slice + hash-cache optimisations as GetCurrentStatus so that polling the idle state on an unchanged terminal is essentially free.
Holds no lifecycle lock — reads ptyAccess and idleDetector via atomic.Pointer. This also fixes the re-entrant RWMutex bug that existed when calling cc.instance.Preview() → GetRecentOutput() → cc.mu.RLock() while already holding cc.mu.RLock(); with atomic pointers there is no lock to re-enter.
func (*ClaudeController) GetIdleStateInfo ¶
func (cc *ClaudeController) GetIdleStateInfo() detection.IdleStateInfo
GetIdleStateInfo returns comprehensive idle state information.
func (*ClaudeController) GetInstance ¶
func (cc *ClaudeController) GetInstance() InstanceContext
GetInstance returns the InstanceContext backing this controller.
func (*ClaudeController) GetQueuedCommands ¶
func (cc *ClaudeController) GetQueuedCommands() []*Command
GetQueuedCommands returns all commands currently in the queue.
func (*ClaudeController) GetQueuedCommandsCount ¶ added in v1.37.0
func (cc *ClaudeController) GetQueuedCommandsCount() int
GetQueuedCommandsCount returns the number of commands in the queue without allocating a slice. Use this instead of len(GetQueuedCommands()) on hot paths.
func (*ClaudeController) GetRateLimitHandler ¶ added in v1.35.0
func (cc *ClaudeController) GetRateLimitHandler() *ratelimit.PTYConsumer
GetRateLimitHandler returns the rate limit PTY consumer (for callback wiring). Returns nil if the controller has not been started yet.
func (*ClaudeController) GetRateLimitResetTime ¶ added in v1.35.0
func (cc *ClaudeController) GetRateLimitResetTime() time.Time
GetRateLimitResetTime returns the reset time from the rate limit handler. Returns zero time if no handler is active or no reset time is known.
func (*ClaudeController) GetRateLimitState ¶ added in v1.12.0
func (cc *ClaudeController) GetRateLimitState() ratelimit.RateLimitState
GetRateLimitState returns the current rate limit detection state.
func (*ClaudeController) GetRecentOutput ¶
func (cc *ClaudeController) GetRecentOutput(bytes int) []byte
GetRecentOutput returns recent output from the PTY buffer. Holds no lifecycle lock; returns nil if the controller is not started.
func (*ClaudeController) GetSessionName ¶
func (cc *ClaudeController) GetSessionName() string
GetSessionName returns the session name for this controller.
func (*ClaudeController) GetStatusAndIdleInfo ¶ added in v1.37.0
func (cc *ClaudeController) GetStatusAndIdleInfo() (detection.DetectedStatus, string, detection.IdleStateInfo, int)
GetStatusAndIdleInfo returns both the detected status and idle state info in one call. Saves one GetRecentHash (murmur3 over 4KB) and one cache.Read on every poll tick compared to calling GetCurrentStatus + GetIdleStateInfo separately.
func (*ClaudeController) GetStatusDetector ¶ added in v1.35.0
func (cc *ClaudeController) GetStatusDetector() detection.TerminalDetector
GetStatusDetector returns the status detector used by this controller. Used by GetDetectionEvents RPC to retrieve recent detection events for debugging.
func (*ClaudeController) GetTotalBytesWritten ¶ added in v1.35.0
func (cc *ClaudeController) GetTotalBytesWritten() int64
GetTotalBytesWritten returns the monotonic PTY byte offset from the response stream's circular buffer. Returns 0 if the controller is not started or has no response stream.
func (*ClaudeController) IsActive ¶
func (cc *ClaudeController) IsActive() bool
IsActive returns whether the Claude instance is actively processing commands.
func (*ClaudeController) IsIdle ¶
func (cc *ClaudeController) IsIdle() bool
IsIdle returns whether the Claude instance is currently idle (waiting for input). This uses pattern-based detection on terminal content.
func (*ClaudeController) IsRateLimitEnabled ¶ added in v1.12.0
func (cc *ClaudeController) IsRateLimitEnabled() bool
IsRateLimitEnabled returns whether rate limit detection is enabled.
func (*ClaudeController) IsStarted ¶
func (cc *ClaudeController) IsStarted() bool
IsStarted returns whether the controller is currently started.
func (*ClaudeController) SearchHistory ¶
func (cc *ClaudeController) SearchHistory(query string) []*HistoryEntry
SearchHistory searches command history by text.
func (*ClaudeController) SendCommand ¶
func (cc *ClaudeController) SendCommand(text string, priority int) (string, error)
SendCommand sends a command to the Claude instance (queued execution).
func (*ClaudeController) SendCommandImmediate ¶
func (cc *ClaudeController) SendCommandImmediate(text string) (*ExecutionResult, error)
SendCommandImmediate sends a command for immediate execution (bypasses queue).
func (*ClaudeController) SetExecutionOptions ¶
func (cc *ClaudeController) SetExecutionOptions(options ExecutionOptions)
SetExecutionOptions updates command execution options.
func (*ClaudeController) SetOnEOFCallback ¶ added in v1.15.0
func (cc *ClaudeController) SetOnEOFCallback(fn func())
SetOnEOFCallback registers a function called when the PTY backing this controller exits unexpectedly (program exit, not an explicit Stop() call). Must be called before Start().
func (*ClaudeController) SetRateLimitEnabled ¶ added in v1.12.0
func (cc *ClaudeController) SetRateLimitEnabled(enabled bool)
SetRateLimitEnabled enables or disables rate limit detection.
func (*ClaudeController) SetStatusChangeListener ¶ added in v1.35.0
func (cc *ClaudeController) SetStatusChangeListener(fn StatusChangeListener)
SetStatusChangeListener registers fn as the sole status-change listener, replacing any previously registered listeners. Kept for backward compatibility; prefer AddStatusChangeListener.
func (*ClaudeController) Start ¶
func (cc *ClaudeController) Start(ctx context.Context) error
Start initializes all components and begins background operations (streaming, command execution). This is the single entry point for starting the controller — no separate Initialize() call needed.
The lifecycle write lock is held for the duration of initialization to prevent concurrent Start() calls. Read-only operations (GetCurrentStatus, etc.) do not use this lock and are therefore unblocked — they simply see nil atomic pointers until initialization completes.
func (*ClaudeController) Stop ¶
func (cc *ClaudeController) Stop() error
Stop stops all background operations and cleans up resources.
The lifecycle write lock is held only to cancel the context and clear the lifecycle fields. All slow cleanup (goroutine joins via executor.Stop/responseStream.Stop, disk I/O via queue.Save/history.Save) runs OUTSIDE the lock, so concurrent callers of GetCurrentStatus, GetRecentOutput, Subscribe, etc. are never blocked.
func (*ClaudeController) Subscribe ¶
func (cc *ClaudeController) Subscribe(subscriberID string) (<-chan ResponseChunk, error)
Subscribe creates a new subscription to the response stream.
func (*ClaudeController) Unsubscribe ¶
func (cc *ClaudeController) Unsubscribe(subscriberID string) error
Unsubscribe removes a subscription from the response stream.
type ClaudeConversationMessage ¶
type ClaudeConversationMessage struct {
Role string
Content string
Timestamp time.Time
Model string
}
ClaudeConversationMessage represents a message in a conversation
type ClaudeHistoryEntry ¶
type ClaudeHistoryEntry struct {
// ID is the unique identifier for this conversation
ID string `json:"id"`
// Name is the conversation title
Name string `json:"name"`
// Project is the project/directory path
Project string `json:"project"`
// CreatedAt is when the conversation started
CreatedAt time.Time `json:"created_at"`
// UpdatedAt is when the conversation was last updated
UpdatedAt time.Time `json:"updated_at"`
// Model is the Claude model used (e.g., "claude-sonnet-4")
Model string `json:"model"`
// MessageCount is the number of messages in the conversation
MessageCount int `json:"message_count"`
}
ClaudeHistoryEntry represents a single entry from Claude's history.jsonl file
type ClaudeSession ¶
type ClaudeSession struct {
ID string `json:"id"`
ConversationID string `json:"conversation_id"`
ProjectName string `json:"project_name"`
LastActive time.Time `json:"last_active"`
WorkingDir string `json:"working_dir"`
IsActive bool `json:"is_active"`
}
ClaudeSession represents a Claude Code session
type ClaudeSessionData ¶
type ClaudeSessionData struct {
ConversationUUID string `json:"session_id,omitempty"` // Claude Code conversation UUID (used for --resume)
SquadSessionID string `json:"squad_session_id,omitempty"` // claude-squad's own session identifier (= Instance.UUID)
ProjectName string `json:"project_name,omitempty"` // Project name in Claude Code
LastAttached time.Time `json:"last_attached,omitempty"` // When this session was last used
Settings ClaudeSettings `json:"settings,omitempty"` // User preferences for Claude Code
Metadata map[string]string `json:"metadata,omitempty"` // Additional session metadata
}
ClaudeSessionData represents Claude Code session information
func (*ClaudeSessionData) UnmarshalJSON ¶ added in v1.35.0
func (c *ClaudeSessionData) UnmarshalJSON(data []byte) error
UnmarshalJSON keeps backward compatibility with persisted state written before SquadSessionID was renamed from ConversationID. The legacy "conversation_id" key is read as a fallback when "squad_session_id" is absent, so existing JSON state files continue to hydrate the field on load.
type ClaudeSessionHistory ¶
type ClaudeSessionHistory struct {
// contains filtered or unexported fields
}
ClaudeSessionHistory manages access to Claude session history
func NewClaudeSessionHistory ¶
func NewClaudeSessionHistory(historyPath string) (*ClaudeSessionHistory, error)
NewClaudeSessionHistory creates a new ClaudeSessionHistory instance
func NewClaudeSessionHistoryFromClaudeDir ¶
func NewClaudeSessionHistoryFromClaudeDir() (*ClaudeSessionHistory, error)
NewClaudeSessionHistoryFromClaudeDir creates a ClaudeSessionHistory from ~/.claude directory
func (*ClaudeSessionHistory) Count ¶
func (sh *ClaudeSessionHistory) Count() int
Count returns the total number of history entries
func (*ClaudeSessionHistory) GetAll ¶
func (sh *ClaudeSessionHistory) GetAll() []ClaudeHistoryEntry
GetAll returns all history entries, sorted by UpdatedAt descending
func (*ClaudeSessionHistory) GetByID ¶
func (sh *ClaudeSessionHistory) GetByID(id string) (*ClaudeHistoryEntry, error)
GetByID returns a specific history entry by ID
func (*ClaudeSessionHistory) GetByProject ¶
func (sh *ClaudeSessionHistory) GetByProject(projectPath string) []ClaudeHistoryEntry
GetByProject returns all history entries for a specific project path
func (*ClaudeSessionHistory) GetMessagesFromConversationFile ¶
func (sh *ClaudeSessionHistory) GetMessagesFromConversationFile(sessionID string, limit int) ([]ClaudeConversationMessage, error)
GetMessagesFromConversationFile reads messages from the conversation file for the given sessionID. When limit > 0 only the last limit messages are returned (using an efficient reverse-read that avoids loading the full file). When limit == 0 all messages are returned.
Results are always in chronological order (oldest first).
func (*ClaudeSessionHistory) GetProjects ¶
func (sh *ClaudeSessionHistory) GetProjects() []string
GetProjects returns a list of unique project paths from history
func (*ClaudeSessionHistory) LastLoadTime ¶
func (sh *ClaudeSessionHistory) LastLoadTime() time.Time
LastLoadTime returns when the history was last loaded from disk
func (*ClaudeSessionHistory) Reload ¶
func (sh *ClaudeSessionHistory) Reload() error
Reload loads history from ~/.claude/history.jsonl, which Claude maintains as a compact index of all conversations. Each line is one user message; we aggregate by sessionId to reconstruct per-session metadata (name, timestamps, message count).
func (*ClaudeSessionHistory) Search ¶
func (sh *ClaudeSessionHistory) Search(query string) []ClaudeHistoryEntry
Search searches history entries by name or project path
type ClaudeSessionManager ¶
type ClaudeSessionManager struct {
// contains filtered or unexported fields
}
ClaudeSessionManager handles Claude Code session detection and management
func NewClaudeSessionManager ¶
func NewClaudeSessionManager() *ClaudeSessionManager
NewClaudeSessionManager creates a new Claude session manager
func (*ClaudeSessionManager) AttachToSession ¶
func (csm *ClaudeSessionManager) AttachToSession(sessionID string) error
AttachToSession attempts to attach to a Claude Code session
func (*ClaudeSessionManager) CreateSessionData ¶
func (csm *ClaudeSessionManager) CreateSessionData(session ClaudeSession, settings ClaudeSettings) ClaudeSessionData
CreateSessionData creates ClaudeSessionData from a detected session
func (*ClaudeSessionManager) DetectAvailableSessions ¶
func (csm *ClaudeSessionManager) DetectAvailableSessions() ([]ClaudeSession, error)
DetectAvailableSessions scans for available Claude Code sessions
func (*ClaudeSessionManager) FindSessionByProject ¶
func (csm *ClaudeSessionManager) FindSessionByProject(projectPath string) ([]ClaudeSession, error)
FindSessionByProject finds Claude sessions that match a given project/working directory
func (*ClaudeSessionManager) GetSessionByID ¶
func (csm *ClaudeSessionManager) GetSessionByID(sessionID string) (*ClaudeSession, error)
GetSessionByID retrieves a specific Claude session by ID
type ClaudeSettings ¶
type ClaudeSettings struct {
AutoReattach bool `json:"auto_reattach"` // Automatically reattach to last session on resume
PreferredSessionName string `json:"preferred_session_name"` // Preferred session naming pattern
CreateNewOnMissing bool `json:"create_new_on_missing"` // Create new session if previous one is missing
ShowSessionSelector bool `json:"show_session_selector"` // Show session selection menu on resume
SessionTimeoutMinutes int `json:"session_timeout_minutes"` // Consider sessions stale after this time
}
ClaudeSettings contains user preferences for Claude Code integration
type CloudContext ¶
type CloudContext struct {
// Provider is the cloud provider name (aws/gcp/azure/custom)
Provider string `json:"provider,omitempty"`
// Region is the cloud region/zone
Region string `json:"region,omitempty"`
// InstanceID is the cloud instance identifier
InstanceID string `json:"instance_id,omitempty"`
// APIEndpoint is the API endpoint URL for the cloud service
APIEndpoint string `json:"api_endpoint,omitempty"`
// APIKeyRef is a reference to secure key storage (not the actual key)
APIKeyRef string `json:"api_key_ref,omitempty"`
// CloudSessionID is the cloud provider's session identifier
CloudSessionID string `json:"cloud_session_id,omitempty"`
// ConversationID is the conversation/thread identifier for AI services
ConversationID string `json:"conversation_id,omitempty"`
}
CloudContext represents the cloud-related context for a session. This includes cloud provider details, region, and API configuration.
func (*CloudContext) IsConfigured ¶
func (c *CloudContext) IsConfigured() bool
IsConfigured returns true if the CloudContext has minimum required configuration
func (*CloudContext) IsEmpty ¶
func (c *CloudContext) IsEmpty() bool
IsEmpty returns true if the CloudContext has no meaningful data
type Command ¶
type Command struct {
ID string `json:"id"`
Text string `json:"text"`
Priority int `json:"priority"` // Higher priority = executed first
Timestamp time.Time `json:"timestamp"` // When the command was queued
Status CommandStatus `json:"status"`
Result string `json:"result,omitempty"` // Command result/output
Error string `json:"error,omitempty"` // Error message if failed
StartTime time.Time `json:"start_time,omitempty"` // When execution started
EndTime time.Time `json:"end_time,omitempty"` // When execution finished
}
Command represents a command to be executed in a Claude instance.
type CommandExecutor ¶
type CommandExecutor struct {
// contains filtered or unexported fields
}
CommandExecutor executes commands by writing to PTY and monitoring responses.
func NewCommandExecutor ¶
func NewCommandExecutor( sessionName string, ptyAccess *PTYAccess, responseStream *ResponseStream, statusDetector detection.TerminalDetector, queue *CommandQueue, ) *CommandExecutor
NewCommandExecutor creates a new command executor for the given session.
func NewCommandExecutorWithOptions ¶
func NewCommandExecutorWithOptions( sessionName string, ptyAccess *PTYAccess, responseStream *ResponseStream, statusDetector detection.TerminalDetector, queue *CommandQueue, options ExecutionOptions, ) *CommandExecutor
NewCommandExecutorWithOptions creates a command executor with custom options.
func (*CommandExecutor) ExecuteImmediate ¶
func (ce *CommandExecutor) ExecuteImmediate(cmd *Command) (*ExecutionResult, error)
ExecuteImmediate executes a command immediately without using the queue. This is useful for interactive commands that need immediate execution.
func (*CommandExecutor) GetCurrentCommand ¶
func (ce *CommandExecutor) GetCurrentCommand() *Command
GetCurrentCommand returns the currently executing command, or nil if none.
func (*CommandExecutor) GetOptions ¶
func (ce *CommandExecutor) GetOptions() ExecutionOptions
GetOptions returns the current execution options.
func (*CommandExecutor) GetSessionName ¶
func (ce *CommandExecutor) GetSessionName() string
GetSessionName returns the session name for this executor.
func (*CommandExecutor) IsExecuting ¶
func (ce *CommandExecutor) IsExecuting() bool
IsExecuting returns whether the executor is currently running.
func (*CommandExecutor) SetOptions ¶
func (ce *CommandExecutor) SetOptions(options ExecutionOptions)
SetOptions updates execution options (only applies to future commands).
func (*CommandExecutor) SetResultCallback ¶
func (ce *CommandExecutor) SetResultCallback(callback func(*ExecutionResult))
SetResultCallback sets a callback function to be invoked after each command execution.
func (*CommandExecutor) Start ¶
func (ce *CommandExecutor) Start(ctx context.Context) error
Start begins processing commands from the queue.
func (*CommandExecutor) Stop ¶
func (ce *CommandExecutor) Stop() error
Stop stops the command executor and waits for completion.
type CommandHistory ¶
type CommandHistory struct {
// contains filtered or unexported fields
}
CommandHistory tracks all executed commands with persistence.
func NewCommandHistory ¶
func NewCommandHistory(sessionName string) *CommandHistory
NewCommandHistory creates a new command history tracker.
func NewCommandHistoryWithPersistence ¶
func NewCommandHistoryWithPersistence(sessionName string, persistDir string) (*CommandHistory, error)
NewCommandHistoryWithPersistence creates a command history with persistence enabled.
func (*CommandHistory) Add ¶
func (ch *CommandHistory) Add(entry *HistoryEntry) error
Add adds a command execution to the history.
func (*CommandHistory) AddFromResult ¶
func (ch *CommandHistory) AddFromResult(result *ExecutionResult) error
AddFromResult creates and adds a history entry from an execution result.
func (*CommandHistory) Clear ¶
func (ch *CommandHistory) Clear() error
Clear removes all history entries.
func (*CommandHistory) Count ¶
func (ch *CommandHistory) Count() int
Count returns the total number of entries in history.
func (*CommandHistory) GetAll ¶
func (ch *CommandHistory) GetAll() []*HistoryEntry
GetAll returns all history entries (most recent first).
func (*CommandHistory) GetByCommandID ¶
func (ch *CommandHistory) GetByCommandID(commandID string) []*HistoryEntry
GetByCommandID returns all history entries for a specific command ID.
func (*CommandHistory) GetByStatus ¶
func (ch *CommandHistory) GetByStatus(status CommandStatus) []*HistoryEntry
GetByStatus returns entries with a specific command status.
func (*CommandHistory) GetByTimeRange ¶
func (ch *CommandHistory) GetByTimeRange(start, end time.Time) []*HistoryEntry
GetByTimeRange returns entries within the specified time range.
func (*CommandHistory) GetFailed ¶
func (ch *CommandHistory) GetFailed() []*HistoryEntry
GetFailed returns all failed command executions.
func (*CommandHistory) GetMaxEntries ¶
func (ch *CommandHistory) GetMaxEntries() int
GetMaxEntries returns the current maximum entries limit.
func (*CommandHistory) GetPersistPath ¶
func (ch *CommandHistory) GetPersistPath() string
GetPersistPath returns the path where history is persisted.
func (*CommandHistory) GetRecent ¶
func (ch *CommandHistory) GetRecent(n int) []*HistoryEntry
GetRecent returns the N most recent history entries.
func (*CommandHistory) GetSessionName ¶
func (ch *CommandHistory) GetSessionName() string
GetSessionName returns the session name for this history.
func (*CommandHistory) GetStatistics ¶
func (ch *CommandHistory) GetStatistics() HistoryStatistics
GetStatistics returns statistics about command execution history.
func (*CommandHistory) GetSuccessful ¶
func (ch *CommandHistory) GetSuccessful() []*HistoryEntry
GetSuccessful returns all successful command executions.
func (*CommandHistory) Load ¶
func (ch *CommandHistory) Load() error
Load restores the history from disk.
func (*CommandHistory) Save ¶
func (ch *CommandHistory) Save() error
Save persists the history to disk.
func (*CommandHistory) Search ¶
func (ch *CommandHistory) Search(query string) []*HistoryEntry
Search searches history entries by command text (case-insensitive substring match).
func (*CommandHistory) SetMaxEntries ¶
func (ch *CommandHistory) SetMaxEntries(max int)
SetMaxEntries sets the maximum number of entries to keep in history. Setting to 0 means unlimited. If current entries exceed the new limit, oldest entries are removed.
func (*CommandHistory) SetPersistPath ¶
func (ch *CommandHistory) SetPersistPath(path string)
SetPersistPath sets the path for history persistence.
type CommandQueue ¶
type CommandQueue struct {
// contains filtered or unexported fields
}
CommandQueue manages a priority queue of commands with persistence.
func NewCommandQueue ¶
func NewCommandQueue(sessionName string) *CommandQueue
NewCommandQueue creates a new command queue for the given session.
func NewCommandQueueWithPersistence ¶
func NewCommandQueueWithPersistence(sessionName string, persistDir string) (*CommandQueue, error)
NewCommandQueueWithPersistence creates a command queue with persistence enabled. The queue state will be saved to the specified directory.
func (*CommandQueue) Cancel ¶
func (cq *CommandQueue) Cancel(id string) error
Cancel marks a command as cancelled and removes it from the queue. Returns an error if the command is not found or is already executing.
func (*CommandQueue) Clear ¶
func (cq *CommandQueue) Clear() error
Clear removes all commands from the queue.
func (*CommandQueue) Dequeue ¶
func (cq *CommandQueue) Dequeue() *Command
Dequeue removes and returns the highest priority command from the queue. Returns nil if the queue is empty.
func (*CommandQueue) Enqueue ¶
func (cq *CommandQueue) Enqueue(cmd *Command) error
Enqueue adds a command to the queue with the specified priority. Higher priority commands are executed first.
func (*CommandQueue) Get ¶
func (cq *CommandQueue) Get(id string) (*Command, error)
Get retrieves a command by ID without removing it from the queue.
func (*CommandQueue) GetPersistPath ¶
func (cq *CommandQueue) GetPersistPath() string
GetPersistPath returns the path where the queue state is persisted.
func (*CommandQueue) IsEmpty ¶
func (cq *CommandQueue) IsEmpty() bool
IsEmpty returns true if the queue is empty.
func (*CommandQueue) Len ¶
func (cq *CommandQueue) Len() int
Len returns the number of commands in the queue.
func (*CommandQueue) List ¶
func (cq *CommandQueue) List() []*Command
List returns all commands currently in the queue. The returned slice is a copy to prevent external modification.
func (*CommandQueue) ListByStatus ¶
func (cq *CommandQueue) ListByStatus(status CommandStatus) []*Command
ListByStatus returns all commands with the specified status.
func (*CommandQueue) Load ¶
func (cq *CommandQueue) Load() error
Load restores the queue state from disk.
func (*CommandQueue) NotifyChannel ¶
func (cq *CommandQueue) NotifyChannel() <-chan struct{}
NotifyChannel returns a channel that receives a notification when commands are added. This can be used to wait for new commands without polling.
func (*CommandQueue) Peek ¶
func (cq *CommandQueue) Peek() *Command
Peek returns the highest priority command without removing it. Returns nil if the queue is empty.
func (*CommandQueue) Save ¶
func (cq *CommandQueue) Save() error
Save persists the queue state to disk.
func (*CommandQueue) SetPersistPath ¶
func (cq *CommandQueue) SetPersistPath(path string)
SetPersistPath sets the path for queue persistence.
func (*CommandQueue) Update ¶
func (cq *CommandQueue) Update(cmd *Command) error
Update updates the status and metadata of a command.
type CommandStatus ¶
type CommandStatus int
CommandStatus represents the current status of a command in the queue.
const ( CommandPending CommandStatus = iota CommandExecuting CommandCompleted CommandFailed CommandCancelled )
func (CommandStatus) String ¶
func (cs CommandStatus) String() string
String returns a human-readable string for the command status.
type CommitImportParams ¶ added in v1.42.0
type CommitImportParams struct {
Detector *HistoryFileDetector
Storage InstanceStore
Registry *Registry
Linker *HistoryLinker
Suspended *SuspendedProcessStore
AliveChecker AliveChecker
TmuxQuerier TmuxSocketQuerier
Candidate ExternalSessionCandidate
ExpectedCorrelation CorrelationResult
DisambiguationChoice string
OriginalPID int32
OriginalCreateTimeMs int64
// TmuxServerSocket, when non-empty, isolates the committed instance's
// tmux server via the -L flag (see InstanceOptions.TmuxServerSocket).
// Empty (the production default) uses the shared default tmux server.
// Tests that exercise the real Start() path should set this to a unique
// per-test socket (matching instance_cold_restore_test.go's
// coldRestoreSocket pattern) to avoid contending with every other test's
// tmux operations on the single shared server under parallel CI load.
TmuxServerSocket string
}
CommitImportParams holds everything CommitImportExternalSession needs to persist a managed Instance for an external candidate, start it resumed, and suspend the original process.
type CommitImportResult ¶ added in v1.42.0
type CommitImportResult struct {
Instance *Instance
// FreshCreateTimeMs is the caller-supplied OriginalCreateTimeMs, echoed
// back for the caller to mint a fresh PIDIdentity in the RPC response
// (per import.proto's doc comment on
// CommitImportExternalSessionResponse.pid_identity). Trustworthy because
// startAndSuspend re-verifies OriginalPID/OriginalCreateTimeMs against
// the live process (via AliveChecker) immediately before suspending it
// -- it is not a fresh OS re-read taken at this point.
FreshCreateTimeMs int64
}
CommitImportResult is the domain-level outcome of a successful commit.
func CommitImportExternalSession ¶ added in v1.42.0
func CommitImportExternalSession(ctx context.Context, params CommitImportParams) (CommitImportResult, error)
CommitImportExternalSession re-runs correlation fresh (never trusting the caller-supplied ExpectedCorrelation), resolves the conversation UUID to resume, persists a managed Instance, registers it with HistoryLinker, starts it resumed, and finally suspends the original process -- in that order, so the original process only stops writing once the resumed session is confirmed up and running.
On any failure after the Instance has been persisted, the Instance is compensating-deleted before returning (Story 1.2.3) so no partial/orphan row is left behind. On any failure before suspension, the original process is never touched.
type CompletionCallback ¶ added in v1.35.0
type CompletionCallback func(instanceName string, outcome AutonomousDriverOutcome)
CompletionCallback is called when the driver exits with a final outcome.
type ContentProvider ¶ added in v1.35.0
type ContentProvider interface {
GetContent(inst *Instance, statusInfo InstanceStatusInfo, paneActivity map[string]time.Time) string
EvictInstance(title string)
}
ContentProvider fetches terminal content for a session. Defined at the consumption point so tests can inject fakes without tmux.
func NewPollerContentProvider ¶ added in v1.35.0
func NewPollerContentProvider() ContentProvider
NewPollerContentProvider creates a new pollerContentProvider. It is exported so server/dependencies.go can pass it to NewStartupScanner.
type ContextOptions ¶
type ContextOptions struct {
// Context loading flags
LoadGit bool // Git repository context (branch, commit, remotes)
LoadFilesystem bool // Filesystem context (directory state, file counts)
LoadTerminal bool // Terminal context (output, command history)
LoadUI bool // UI context (position, focus state, expanded/collapsed)
LoadActivity bool // Activity context (last active, duration, events)
LoadCloud bool // Cloud context (API sessions, remote state)
// Child data loading flags (from existing LoadOptions)
LoadWorktree bool // Git worktree data
LoadDiffStats bool // Diff statistics (added/removed counts)
LoadDiffContent bool // Full diff content (heavy - only load when needed)
LoadTags bool // Session tags
LoadClaudeSession bool // Claude Code session data
}
ContextOptions specifies which optional contexts to load when querying sessions. This enables optimized queries that only load the data needed for each use case.
func FromLoadOptions ¶
func FromLoadOptions(lo LoadOptions) ContextOptions
FromLoadOptions creates ContextOptions from the legacy LoadOptions type. This provides backward compatibility when migrating existing code.
func (ContextOptions) AnyChildDataLoaded ¶
func (o ContextOptions) AnyChildDataLoaded() bool
AnyChildDataLoaded returns true if any child data is configured to load.
func (ContextOptions) AnyContextLoaded ¶
func (o ContextOptions) AnyContextLoaded() bool
AnyContextLoaded returns true if any context is configured to load.
func (ContextOptions) Merge ¶
func (o ContextOptions) Merge(other ContextOptions) ContextOptions
Merge combines two ContextOptions, returning options that load the union of both. This is useful for combining requirements from multiple components.
func (ContextOptions) String ¶
func (o ContextOptions) String() string
String returns a human-readable description of what will be loaded.
func (ContextOptions) ToLoadOptions ¶
func (o ContextOptions) ToLoadOptions() LoadOptions
ToLoadOptions converts ContextOptions to the legacy LoadOptions type. This provides backward compatibility with existing code.
func (ContextOptions) WithActivity ¶
func (o ContextOptions) WithActivity() ContextOptions
WithActivity returns a copy of options with activity context loading enabled.
func (ContextOptions) WithCloud ¶
func (o ContextOptions) WithCloud() ContextOptions
WithCloud returns a copy of options with cloud context loading enabled.
func (ContextOptions) WithDiffContent ¶
func (o ContextOptions) WithDiffContent() ContextOptions
WithDiffContent returns a copy of options with diff content loading enabled.
func (ContextOptions) WithFilesystem ¶
func (o ContextOptions) WithFilesystem() ContextOptions
WithFilesystem returns a copy of options with filesystem context loading enabled.
func (ContextOptions) WithGit ¶
func (o ContextOptions) WithGit() ContextOptions
WithGit returns a copy of options with git context loading enabled.
func (ContextOptions) WithTags ¶
func (o ContextOptions) WithTags() ContextOptions
WithTags returns a copy of options with tag loading enabled.
func (ContextOptions) WithTerminal ¶
func (o ContextOptions) WithTerminal() ContextOptions
WithTerminal returns a copy of options with terminal context loading enabled.
func (ContextOptions) WithUI ¶
func (o ContextOptions) WithUI() ContextOptions
WithUI returns a copy of options with UI context loading enabled.
func (ContextOptions) WithoutDiffContent ¶
func (o ContextOptions) WithoutDiffContent() ContextOptions
WithoutDiffContent returns a copy of options with diff content loading disabled.
func (ContextOptions) WithoutTags ¶
func (o ContextOptions) WithoutTags() ContextOptions
WithoutTags returns a copy of options with tag loading disabled.
type ControllerManager ¶
type ControllerManager struct {
// contains filtered or unexported fields
}
ControllerManager owns the ClaudeController and InstanceStatusManager references that were previously bare fields on Instance.
Instance keeps thin wrapper methods (with lifecycle guards) that delegate here. ControllerManager itself has no knowledge of Instance lifecycle; it only manages the controller and status-manager references.
Note: claudeSession is intentionally NOT included here because it is a rich data object with complex lifecycle management (persistence, re-attachment, session selection) that is tightly coupled to Instance business logic. It remains a direct field on Instance for now.
Both controller and statusManager use atomic.Pointer for lock-free concurrent access. Write operations (Register/Unregister/Set) are expected to be called sequentially from the Instance lifecycle path and are not themselves concurrency-safe against each other. ControllerManager must not be copied after first use (enforced by noCopy).
func (*ControllerManager) GetController ¶
func (cm *ControllerManager) GetController() *ClaudeController
GetController returns the current ClaudeController (may be nil).
func (*ControllerManager) GetStatusManager ¶
func (cm *ControllerManager) GetStatusManager() *InstanceStatusManager
GetStatusManager returns the current InstanceStatusManager (may be nil).
func (*ControllerManager) HasController ¶
func (cm *ControllerManager) HasController() bool
HasController reports whether a ClaudeController has been registered.
func (*ControllerManager) RegisterController ¶
func (cm *ControllerManager) RegisterController(title string, controller *ClaudeController)
RegisterController wires a new controller into the status manager and stores it. Any existing controller is stopped first.
func (*ControllerManager) SetController ¶
func (cm *ControllerManager) SetController(c *ClaudeController)
SetController replaces the controller. Callers are responsible for stopping the old controller before calling this.
func (*ControllerManager) SetStatusManager ¶
func (cm *ControllerManager) SetStatusManager(m *InstanceStatusManager)
SetStatusManager replaces the status manager.
func (*ControllerManager) StopAndClearController ¶
func (cm *ControllerManager) StopAndClearController()
StopAndClearController stops the controller (if running) and clears the reference.
func (*ControllerManager) UnregisterController ¶
func (cm *ControllerManager) UnregisterController(title string)
UnregisterController stops and clears the controller, and removes it from the status manager.
type ConversationID ¶ added in v1.35.0
type ConversationID string
ConversationID represents a validated Claude/Antigravity conversation UUID.
func ParseConversationID ¶ added in v1.35.0
func ParseConversationID(s string) (ConversationID, error)
ParseConversationID parses and validates a raw string as a ConversationID.
type CorrelationConfidence ¶ added in v1.42.0
type CorrelationConfidence int
CorrelationConfidence records *why* a Resolved match was made, surfaced to the UI so the user can see the basis for the match.
const ( // ConfidenceNone applies when Kind is not Resolved. ConfidenceNone CorrelationConfidence = iota // ConfidencePIDExact means HistoryFileDetector.Detect found the file via // the candidate's live open file descriptors. ConfidencePIDExact // ConfidencePathHeuristic means DetectAllByPath found exactly one // candidate for the project path (no live PID match was available). ConfidencePathHeuristic )
type CorrelationKind ¶ added in v1.42.0
type CorrelationKind int
CorrelationKind is the discriminant of a CorrelationResult. Callers must exhaustively switch on it — Ambiguous and NotFound are valid, non-error outcomes and must never be silently collapsed to a single guess (see pitfalls research must-not-happen #5).
const ( // CorrelationNotFound means neither PID nor path correlation found a // history file. This is a valid state — the JSONL may not exist yet. CorrelationNotFound CorrelationKind = iota // CorrelationResolved means exactly one history file was identified. CorrelationResolved // CorrelationAmbiguous means more than one history file could plausibly // belong to this candidate; the caller must require a DisambiguationChoice. CorrelationAmbiguous )
func (CorrelationKind) String ¶ added in v1.42.0
func (k CorrelationKind) String() string
String returns a human-readable name for logging.
type CorrelationResult ¶ added in v1.42.0
type CorrelationResult struct {
Kind CorrelationKind
UUID string
Confidence CorrelationConfidence
// Candidates is populated only when Kind == CorrelationAmbiguous.
Candidates []HistoryFileInfo
}
CorrelationResult is the exhaustive, non-silent outcome of running correlation against an ExternalSessionCandidate.
func CorrelateCandidate ¶ added in v1.42.0
func CorrelateCandidate(detector *HistoryFileDetector, candidate ExternalSessionCandidate) (CorrelationResult, error)
CorrelateCandidate runs HistoryFileDetector against a candidate and returns an exhaustive Resolved/Ambiguous/NotFound result. It is a one-shot call: it does not register the candidate with HistoryLinker and does not start any polling/backoff (that only happens after commit).
PID-based detection is tried first; if it finds nothing (dead process, no open Claude file, or PlainTmux candidates that have no PID-openable file), it falls back to path-based detection using DetectAllByPath so that ambiguity is never silently collapsed to "most recent".
type CostSnapshot ¶ added in v1.41.0
type CostSnapshot struct {
TotalTokens int64
EstimatedCostUSD float64
// not be read" (e.g. no transcript found for this session).
DataUnavailable bool
}
CostSnapshot captures token usage/cost data for a session.
func BuildCostSnapshot ¶ added in v1.41.0
func BuildCostSnapshot(sessionUUID string, tokenStore tokens.TokenStoreReader) CostSnapshot
BuildCostSnapshot builds a CostSnapshot from tokenStore.GetByUUID(sessionUUID). tokenStore is accepted as the narrow tokens.TokenStoreReader interface (already used by InsightsService) rather than the concrete *tokens.TokenStore, so tests can inject a fake without constructing a real store. Nil-safe: a nil tokenStore or a nil ParseResult (no transcript found) returns CostSnapshot{DataUnavailable: true} — distinct from "zero tokens were genuinely used" (research/ux.md §4).
type CreateInstanceOptions ¶ added in v1.42.0
type CreateInstanceOptions = InstanceOptions
CreateInstanceOptions is a type alias for session.InstanceOptions, kept as a distinct name in this file so CreateManagedInstanceParams reads clearly at call sites (options for the instance to create) without forcing every caller to spell out the full InstanceOptions literal inline.
type CreateManagedInstanceParams ¶ added in v1.42.0
type CreateManagedInstanceParams struct {
Options CreateInstanceOptions
// Storage persists the constructed Instance (mirrors
// SessionService.storage; typically a *session.Storage backed by
// EntRepository). Required.
Storage InstanceStore
// Registry, if non-nil, registers the instance's live handle before
// Storage.AddInstance runs, matching CreateSession's existing
// register-before-persist ordering so there is never a window where the
// session is findable via storage but has no live actor. Optional --
// callers that don't use the live-handle registry (there are none today,
// but the field is a plain pointer, not an interface, so this is not
// speculative) may leave it nil.
Registry *Registry
// CreateIfMissing controls whether a Directory-mode session may be
// created even though its target path does not yet exist on disk. This
// mirrors CreateSessionRequest.CreateIfMissing but is surfaced separately
// from CreateInstanceOptions since it drives a pre-flight check rather
// than an InstanceOptions field.
CreateIfMissing bool
// ResumeID, when non-empty, is the conversation UUID this instance
// should resume via `--resume`. Named ResumeID (not embedded in
// InstanceOptions.ResumeId) to match the plan's
// CreateManagedInstanceParams{SessionType, Path, ResumeID} example
// verbatim; it is copied onto InstanceOptions.ResumeId internally.
ResumeID string
}
CreateManagedInstanceParams holds everything needed to construct and persist a managed Instance, independent of how the caller arrived at these values (an RPC request, an import commit, etc). Deliberately a plain struct with no connect.Request/connect.Response types in sight -- both SessionService.CreateSession's connect handler and CommitImportExternalSession (Story 1.2.1) build one of these and call CreateManagedInstance directly, so the dependency always points RPC layer -> domain function, never handler -> handler (see project_plans/import-external-session/implementation/plan.md Story 1.2.0a).
type CriterionVerdict ¶ added in v1.35.0
type CriterionVerdict = domain.CriterionVerdict
CriterionVerdict holds the review outcome for a single acceptance criterion. Type alias — session.CriterionVerdict and domain.CriterionVerdict are identical types.
type DecisionRecord ¶ added in v1.41.0
type DecisionRecord struct {
// NotificationType is the record's NotificationType field — compare against
// sessionv1.NotificationType_NOTIFICATION_TYPE_APPROVAL_NEEDED /
// _AUTO_APPROVED.
NotificationType int32
// ApprovalDecision is the record's Metadata["approval_decision"] value
// ("allow"/"deny"/"" for unresolved/other outcomes) — the same key
// server/services/approval_handler.go and
// server/notifications.AppendAutoApproved stamp.
ApprovalDecision string
}
DecisionRecord is the minimal per-notification-record data BuildDecisionsSnapshot needs to classify an approval decision. Deliberately decoupled from server/notifications.NotificationRecord: session cannot import server/notifications directly (server/notifications -> server/events -> pkg/events -> session is a real import cycle, confirmed via `go list -deps`), so a NotificationDecisionLister implementation (wired in Phase 2, server/services) maps *notifications.NotificationHistoryStore.List results onto this shape.
type DecisionsSnapshot ¶ added in v1.41.0
type DecisionsSnapshot struct {
AutoApproved int
ManuallyApproved int
Denied int
ReviewQueueResolved int
StillOpen int
}
DecisionsSnapshot is a deterministic count of approval decisions made during a session.
func BuildDecisionsSnapshot ¶ added in v1.41.0
func BuildDecisionsSnapshot(ctx context.Context, sessionID string, notifLister NotificationDecisionLister, reviewLookup ReviewQueueLookup) (DecisionsSnapshot, error)
BuildDecisionsSnapshot queries a NotificationDecisionLister for approval-decision records and ReviewQueueLookup for backlog review-queue resolution counts to build a DecisionsSnapshot. The real NotificationHistoryStore-backed lister never returns a non-nil error today (file corruption is swallowed silently at load time — server/notifications/store.go) — reviewLookup.ReviewQueueResolvedCount's DB-backed call is this function's only realistic error source.
func (DecisionsSnapshot) Percent ¶ added in v1.41.0
func (d DecisionsSnapshot) Percent(n int) float64
Percent returns what percentage n represents of the snapshot's Total, or 0 if Total is 0.
func (DecisionsSnapshot) Total ¶ added in v1.41.0
func (d DecisionsSnapshot) Total() int
Total returns the sum of all decision counts.
type DefaultStatusDeterminer ¶ added in v1.35.0
type DefaultStatusDeterminer struct {
// contains filtered or unexported fields
}
DefaultStatusDeterminer implements StatusDeterminer with the standard detection logic.
func NewDefaultStatusDeterminer ¶ added in v1.35.0
func NewDefaultStatusDeterminer(config ReviewQueuePollerConfig) *DefaultStatusDeterminer
NewDefaultStatusDeterminer creates a DefaultStatusDeterminer with the given config.
func (*DefaultStatusDeterminer) Determine ¶ added in v1.35.0
func (d *DefaultStatusDeterminer) Determine( inst *Instance, content string, statusInfo InstanceStatusInfo, detector detection.TerminalDetector, ) DetectionResult
Determine evaluates a session's state and returns a DetectionResult. It is pure: no queue mutations, no storage calls, no side effects.
type DefaultWorkflowEngine ¶ added in v1.35.0
type DefaultWorkflowEngine struct {
// contains filtered or unexported fields
}
DefaultWorkflowEngine implements WorkflowEngine using the hardcoded validTransitions map and TransitionGuard function from backlog.go.
func NewDefaultWorkflowEngine ¶ added in v1.35.0
func NewDefaultWorkflowEngine() *DefaultWorkflowEngine
NewDefaultWorkflowEngine constructs an engine backed by the static validTransitions map. The map is deep-copied to avoid shared mutable state.
func (*DefaultWorkflowEngine) AllowedTransitions ¶ added in v1.35.0
func (e *DefaultWorkflowEngine) AllowedTransitions(from BacklogStatus) []BacklogStatus
AllowedTransitions implements WorkflowEngine.
func (*DefaultWorkflowEngine) CanTransition ¶ added in v1.35.0
func (e *DefaultWorkflowEngine) CanTransition(from, to BacklogStatus) bool
CanTransition implements WorkflowEngine.
func (*DefaultWorkflowEngine) ValidateGates ¶ added in v1.35.0
func (e *DefaultWorkflowEngine) ValidateGates(item BacklogItemTransitionInput, to BacklogStatus) error
ValidateGates implements WorkflowEngine by delegating to TransitionGuard.
type DetectionAction ¶ added in v1.35.0
type DetectionAction int
DetectionAction represents what the poller should do after status determination.
const ( DetectionActionSkip DetectionAction = iota // No change to queue DetectionActionAdd // Add/update item in queue DetectionActionRemove // Remove item from queue )
type DetectionResult ¶ added in v1.35.0
type DetectionResult struct {
Action DetectionAction
Reason AttentionReason
Priority Priority
Context string
ClaudeStatus detection.DetectedStatus
// CleanWorktree is true when the worktree was inspected and found clean.
// checkSession uses this to remove a queued UncommittedChanges entry immediately.
CleanWorktree bool
}
DetectionResult is the output of status determination — pure data, no side effects.
func (DetectionResult) IsHighPriority ¶ added in v1.35.0
func (r DetectionResult) IsHighPriority() bool
IsHighPriority returns true when the result warrants bypassing grace-period suppression.
type DiffSnapshot ¶ added in v1.41.0
DiffSnapshot is a deterministic, point-in-time summary of a session's git diff.
func BuildDiffSnapshot ¶ added in v1.41.0
func BuildDiffSnapshot(stats *git.DiffStats) DiffSnapshot
BuildDiffSnapshot converts a captured *git.DiffStats into a DiffSnapshot. Nil-safe: a nil stats (no worktree / directory session with no changes) returns an empty, non-error DiffSnapshot.
func (DiffSnapshot) IsEmpty ¶ added in v1.41.0
func (d DiffSnapshot) IsEmpty() bool
IsEmpty reports whether the snapshot represents no changes at all.
type DiffStatsData ¶
type DiffStatsData struct {
Added int `json:"added"`
Removed int `json:"removed"`
Content string `json:"-"` // Excluded from serialization - generated on-demand
}
DiffStatsData represents the serializable data of a DiffStats Note: Content is excluded from JSON serialization to reduce state file size. Diffs are generated on-demand via GetSessionDiff RPC when needed.
type DiscoveryMode ¶
type DiscoveryMode int
DiscoveryMode controls what instances are discovered and how they can be interacted with
const ( // DiscoveryModeManaged discovers only squad-managed sessions (default, safest) DiscoveryModeManaged DiscoveryMode = iota // DiscoveryModeExtended discovers managed + external instances in read-only mode DiscoveryModeExtended // DiscoveryModeFull discovers all instances with attach capability (power user mode) DiscoveryModeFull )
func ParseDiscoveryMode ¶
func ParseDiscoveryMode(s string) DiscoveryMode
ParseDiscoveryMode parses a string into a DiscoveryMode
func (DiscoveryMode) String ¶
func (dm DiscoveryMode) String() string
type DriverOption ¶ added in v1.35.0
type DriverOption func(*AutonomousDriver)
DriverOption is a functional option for configuring an AutonomousDriver.
func WithStartupTimeout ¶ added in v1.35.0
func WithStartupTimeout(d time.Duration) DriverOption
WithStartupTimeout overrides the default 60s startup idle-wait timeout. Use a longer timeout for sessions that spawn parallel subagents (e.g. triage).
type EntPipelineModeRepository ¶ added in v1.38.0
type EntPipelineModeRepository struct {
// contains filtered or unexported fields
}
EntPipelineModeRepository implements PipelineModeRepository using the ent ORM.
func NewEntPipelineModeRepository ¶ added in v1.38.0
func NewEntPipelineModeRepository(client *ent.Client) *EntPipelineModeRepository
NewEntPipelineModeRepository creates a new ent-backed pipeline mode repository.
func (*EntPipelineModeRepository) Create ¶ added in v1.38.0
func (r *EntPipelineModeRepository) Create(ctx context.Context, m PipelineModeCreateInput) (*ent.PipelineMode, error)
Create inserts a new pipeline mode definition. Returns ent.ConstraintError when a duplicate slug exists.
func (*EntPipelineModeRepository) GetByID ¶ added in v1.38.0
func (r *EntPipelineModeRepository) GetByID(ctx context.Context, id uuid.UUID) (*ent.PipelineMode, error)
GetByID retrieves a pipeline mode by UUID.
func (*EntPipelineModeRepository) GetBySlug ¶ added in v1.38.0
func (r *EntPipelineModeRepository) GetBySlug(ctx context.Context, slug string) (*ent.PipelineMode, error)
GetBySlug retrieves a pipeline mode by slug.
func (*EntPipelineModeRepository) ListAll ¶ added in v1.38.0
func (r *EntPipelineModeRepository) ListAll(ctx context.Context) ([]*ent.PipelineMode, error)
ListAll returns all pipeline modes sorted ascending by created_at. A safety cap of 1000 is applied to prevent runaway queries.
func (*EntPipelineModeRepository) ListEnabled ¶ added in v1.38.0
func (r *EntPipelineModeRepository) ListEnabled(ctx context.Context) ([]*ent.PipelineMode, error)
ListEnabled returns only pipeline modes where enabled is true.
func (*EntPipelineModeRepository) Update ¶ added in v1.38.0
func (r *EntPipelineModeRepository) Update(ctx context.Context, id uuid.UUID, m PipelineModeUpdateInput) (*ent.PipelineMode, error)
Update applies a partial update to an existing pipeline mode by UUID.
type EntRepository ¶
type EntRepository struct {
// contains filtered or unexported fields
}
EntRepository implements the Repository interface using Ent ORM as the storage backend. It provides type-safe database operations with automatic schema migrations.
func NewEntRepository ¶
func NewEntRepository(opts ...RepositoryOption) (*EntRepository, error)
NewEntRepository creates a new Ent repository with the given options. The database will be initialized with the schema if it doesn't exist.
func NewEntRepositoryFromClient ¶ added in v1.35.0
func NewEntRepositoryFromClient(client *ent.Client) *EntRepository
NewEntRepositoryFromClient wraps a pre-existing *ent.Client in an EntRepository. The caller is responsible for running schema migration on the client beforehand. Use this when you need to share an already-opened client across subsystems (e.g. injecting a test client or reusing an existing connection).
func NewTestEntRepository ¶ added in v1.46.0
func NewTestEntRepository(t testing.TB) *EntRepository
NewTestEntRepository returns an EntRepository backed by a uniquely-named shared-cache in-memory SQLite database, closed automatically via t.Cleanup. It replaces the repeated NewEntRepository(WithDatabasePath(filepath.Join(dir, "sessions.db"))) pattern duplicated across this package and server/, server/services/, server/mcp/, and server/workflows/ test files — those call sites paid for real file creation, WAL journal files, and disk I/O on every test despite never needing on-disk durability.
The DSN's name must be unique per repository: cache=shared makes SQLite look up the in-memory database by name process-wide, so two repositories with the same name would see each other's data.
func (*EntRepository) AddBacklogItemDependency ¶ added in v1.43.0
func (r *EntRepository) AddBacklogItemDependency(ctx context.Context, edge BacklogItemDependencyEdge) error
AddBacklogItemDependency records that edge.BlockedID may not be dequeued/started until edge.BlockerID reaches a resolved status — see UnresolvedBlockerItemIDs for what counts as resolved (done or archived). Adding an already-existing pair is a no-op (upsert against the unique (blocker_id, blocked_id) index). Returns ErrDependencyCycle if the new edge would create a cycle, including the degenerate self-dependency case (BlockerID == BlockedID).
func (*EntRepository) AllRules ¶ added in v1.12.0
func (r *EntRepository) AllRules(ctx context.Context) ([]ApprovalRuleData, error)
func (*EntRepository) AppendActivityNote ¶ added in v1.46.0
func (r *EntRepository) AppendActivityNote(ctx context.Context, itemID, authorSessionUUID, authorSessionTitle, message string) error
AppendActivityNote records a single post_backlog_update call as an immutable, append-only history entry. This is the one activity-note write path invoked by the ungated post_backlog_update tool — any session, with or without STAPLER_SESSION_UUID, whether or not linked to the item, can call it. Unlike AppendProgressNote, this DOES publish an item-changed event: there is no separate criterion-status write alongside it to carry that responsibility, so if AppendActivityNote didn't publish, a live WatchBacklogItems subscriber would never learn a note was posted.
Before publishing, this does a cheap, read-only, field-scoped query for the item's Status and RepoPath and populates them on the published snapshot. This exists solely so backlogItemMatchesFilters (server/services/backlog_service_events.go) doesn't silently drop this event for WatchBacklogItems callers with a non-empty status/category filter — see Blocker 2 in implementation/adversarial-review.md. It is a read, not a read-modify-write, so it introduces no new concurrency hazard. publishItemChangedSnapshot (not the publishItemChanged convenience wrapper) is used deliberately: this event's payload never carries a full item snapshot (ADR-002), so there is nothing for attachItemSessionsForPublish to usefully eager-load beyond the two scalar fields already fetched below.
func (*EntRepository) AppendProgressNote ¶ added in v1.38.0
func (r *EntRepository) AppendProgressNote(ctx context.Context, itemID string, criterionIndex int, note, status string) error
AppendProgressNote records a single report_progress call as an immutable history entry, in addition to (not instead of) the current-note-per-criterion stored on BacklogItem.AcceptanceCriteria. Callers should treat failures here as best-effort: the history is an enrichment for reviewers, not part of report_progress's primary contract of updating the criterion's current status/note.
func (*EntRepository) ArchiveBacklogItem ¶ added in v1.35.0
func (r *EntRepository) ArchiveBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
ArchiveBacklogItem sets the archived_at timestamp on a backlog item.
func (*EntRepository) AssignSessionsToProject ¶ added in v1.23.0
func (r *EntRepository) AssignSessionsToProject(ctx context.Context, projectName string, sessionTitles []string) error
AssignSessionsToProject links sessions (by title) to a project (by name).
func (*EntRepository) BackfillMissingPRNumbers ¶ added in v1.37.0
func (r *EntRepository) BackfillMissingPRNumbers(ctx context.Context) (int, error)
BackfillMissingPRNumbers finds pr_pending items with a pr_url but no pr_number (pr_number == 0) and parses the number out of the URL. Such items are otherwise permanently invisible to FindPRPendingItems' PrNumberGT(0) filter, so ReconcilePRPending never polls them — a real stuck-forever case found via manual QA against live data, not something the loop itself would ever have surfaced. Best-effort: a URL that doesn't match is left as-is and logged, not treated as fatal.
func (*EntRepository) BulkResetStuckRemediation ¶ added in v1.39.0
func (r *EntRepository) BulkResetStuckRemediation(ctx context.Context, reason *domain.StuckReason, onlyParked bool) (int, error)
BulkResetStuckRemediation applies ResetStuckRemediation's reset (attempts -> 0, next_remediation_at/notified_at cleared) to every open BacklogStuckState row, optionally filtered to a single reason (reason == nil means "every reason") and, when onlyParked is true (the default from the RPC layer), restricted to rows that actually hit the attempt cap (remediation_attempts >= MaxRemediationAttempts) — the "something upstream broke a batch of these, give them all a fresh shot" admin action. Returns the number of rows reset.
func (*EntRepository) ClaimChainFire ¶ added in v1.43.0
func (r *EntRepository) ClaimChainFire(ctx context.Context, id string, expectedUpdatedAt time.Time) (claimed bool, err error)
ClaimChainFire atomically claims item id's pipeline chain-fire attempt by flipping chain_fired from false to true, conditioned on both chain_fired still being false and updated_at still matching expectedUpdatedAt — a genuine SQL-level compare-and-swap folded into the UPDATE's own WHERE clause, the same pattern (and for the identical reason) as TransitionBacklogItemStatus's precondition handling above (see that method's doc comment, BUG-026): a Get-then-check-in-Go-then-write sequence (which UpdateBacklogItem's precondition parameter still is, deliberately not reused here) leaves a race window wide enough for two concurrent callers to both pass the check and both issue their own write. Used by ChainFirer.Fire to guarantee at most one goroutine ever reaches TriggerFirer.FireTriggerChained for a given item (webhook-triggers Task 6.2.1d — the happy-path async dispatch and TriggerChainReconciler's periodic sweep can otherwise race on the same item).
Returns claimed=false (not an error) when another caller already won the claim or the row's updated_at moved for any other reason — callers must treat that as "someone else is handling this," not a failure.
func (*EntRepository) Close ¶
func (r *EntRepository) Close() error
Close performs cleanup and releases resources
func (*EntRepository) CountReviewCyclesSince ¶ added in v1.38.0
func (r *EntRepository) CountReviewCyclesSince(ctx context.Context, itemID string, since time.Time) (int, error)
CountReviewCyclesSince counts in_progress->review BacklogStatusEvent transitions for itemID created at or after since — the "round trip" signal the bouncing detector (isBouncing) keys off.
func (*EntRepository) Create ¶
func (r *EntRepository) Create(ctx context.Context, data InstanceData) error
Create inserts a new session into the database
func (*EntRepository) CreateBacklogItem ¶ added in v1.35.0
func (r *EntRepository) CreateBacklogItem(ctx context.Context, data BacklogItemData) (*BacklogItemData, error)
CreateBacklogItem inserts a new backlog item.
func (*EntRepository) CreateItemSession ¶ added in v1.35.0
func (r *EntRepository) CreateItemSession(ctx context.Context, data ItemSessionData) (ItemSessionSummary, error)
CreateItemSession creates a new ItemSession linked to a BacklogItem.
func (*EntRepository) CreateItemSessionWithVerdict ¶ added in v1.35.0
func (r *EntRepository) CreateItemSessionWithVerdict(ctx context.Context, isData ItemSessionData, verdict ReviewVerdictData) (ItemSessionSummary, error)
CreateItemSessionWithVerdict atomically creates an ItemSession and its initial ReviewVerdict in a single transaction. If the verdict write fails the ItemSession is rolled back, preventing dangling sessions with no verdict.
func (*EntRepository) CreateItemSource ¶ added in v1.35.0
func (r *EntRepository) CreateItemSource(ctx context.Context, data ItemSourceData) (*ItemSourceData, error)
CreateItemSource registers a new external item source.
func (*EntRepository) CreateProject ¶ added in v1.23.0
func (r *EntRepository) CreateProject(ctx context.Context, data ProjectData) (*ProjectData, error)
CreateProject inserts a new project.
func (*EntRepository) CreateSession ¶
func (r *EntRepository) CreateSession(ctx context.Context, session *Session) error
CreateSession creates a new session from the Session domain model. Stub: not yet implemented; use InstanceData-based methods instead.
func (*EntRepository) CreateShell ¶ added in v1.35.0
func (r *EntRepository) CreateShell(ctx context.Context, sessionTitle string, data ShellData) (*ent.Shell, error)
CreateShell persists a new Shell entity for the given session title.
func (*EntRepository) CreateSourceSyncEvent ¶ added in v1.35.0
func (r *EntRepository) CreateSourceSyncEvent(ctx context.Context, sourceID string, cursorAfter string, created, updated, skipped, errored int, errMsg string, startedAt, finishedAt time.Time) error
CreateSourceSyncEvent records a completed (or failed) sync run for an ItemSource. errMsg should be non-empty only when the sync run failed outright (e.g. the plugin's Fetch call errored); errored counts per-item failures within an otherwise-successful fetch.
func (*EntRepository) Delete ¶
func (r *EntRepository) Delete(ctx context.Context, title string) error
Delete removes a session from the database by title
func (*EntRepository) DeleteBacklogItem ¶ added in v1.35.0
func (r *EntRepository) DeleteBacklogItem(ctx context.Context, id string) error
DeleteBacklogItem permanently removes an item and all its child records.
func (*EntRepository) DeleteItemSource ¶ added in v1.35.0
func (r *EntRepository) DeleteItemSource(ctx context.Context, id string) error
DeleteItemSource removes an item source by UUID string.
func (*EntRepository) DeleteProject ¶ added in v1.23.0
func (r *EntRepository) DeleteProject(ctx context.Context, name string) error
DeleteProject removes a project; sessions are unassigned (FK cleared) atomically.
func (*EntRepository) DeleteRule ¶ added in v1.12.0
func (r *EntRepository) DeleteRule(ctx context.Context, id string) error
func (*EntRepository) DeleteShell ¶ added in v1.35.0
func (r *EntRepository) DeleteShell(ctx context.Context, shellID string) error
DeleteShell removes a Shell entity by ID.
func (*EntRepository) FindDoneItemsOlderThan ¶ added in v1.39.0
func (r *EntRepository) FindDoneItemsOlderThan(ctx context.Context, cutoff time.Time) ([]BacklogItemData, error)
FindDoneItemsOlderThan returns backlog items currently in "done" status whose most recent transition INTO "done" happened at or before cutoff. Used by the auto-archive sweep (archiveStaleDoneItems in backlog_lifecycle.go) to find items eligible for automatic archival.
Deliberately keys off the status-event history rather than UpdatedAt: UpdatedAt changes on any field edit (progress notes, notification flags, etc.), which would reset — and so make unreliable — a clock meant to measure "how long has this been done". TransitionBacklogItemStatus always appends an audit BacklogStatusEvent row on every transition, so this reuses existing infrastructure instead of adding a dedicated done_at column.
An item whose status-event history has no toStatus=="done" record is skipped (never considered eligible) rather than defaulting to "always eligible" — this should not happen in practice, since every transition through TransitionBacklogItemStatus writes one, but guards a partially- migrated or directly-seeded row against aging out on an unrelated basis.
func (*EntRepository) FindDriftedPRItems ¶ added in v1.39.0
func (r *EntRepository) FindDriftedPRItems(ctx context.Context) ([]*ent.BacklogItem, error)
FindDriftedPRItems returns backlog items with a live PR reference (a non-zero pr_number and non-empty pr_url) whose status is neither "pr_pending" nor a terminal state (done/archived) — i.e. items that ReconcilePRPending's FindPRPendingItems can never see because it anchors purely on status=="pr_pending", even though the item demonstrably has a real PR that needs the same merge/CI polling. This happens when pushAndCreatePR/shipViaAgentOrFallback persist prNumber/prUrl (which they do unconditionally, before attempting the status transition) but the follow-up CAS transition to pr_pending then loses a race to some other legitimate concurrent event — e.g. markAbandonedReview's grace period firing and respawning a review pass while an agent-driven ship is still mid-flight, or a rework/bounce cycle that exhausts its cap before ever re-shipping. Confirmed live 2026-07-20 on two items (c2ad7bf3-91bf-4d47- 8654-0f2f20869080, PR #251; 6700a3f2-8c0d-4a98-8bbd-39515d5391b1, PR #172) stuck at status="review" with real, still-open PRs neither ReconcilePRPending nor any other reconciler was polling.
Excludes items with an active (EndedAt still nil) work or review session: recovery must never steal an item out from under a live, still-legitimately -running session — mirrors AutoReopenForPRFix's/AutoRespawnReview's identical hasActiveWorkSession/hasActiveReviewSession guard. An item with a genuinely active session will naturally reappear in this query once that session ends without making further progress.
func (*EntRepository) FindOpenStuckStates ¶ added in v1.38.0
func (r *EntRepository) FindOpenStuckStates(ctx context.Context) ([]OpenStuckStateData, error)
FindOpenStuckStates returns every BacklogStuckState row that is currently open (resolved_at IS NULL) and not currently snoozed (snoozed_until IS NULL OR snoozed_until is in the past), eager-loading the parent item so the projection carries title/status/pr_number/pr_url for rendering without a second round trip.
func (*EntRepository) FindPRPendingItems ¶ added in v1.37.0
func (r *EntRepository) FindPRPendingItems(ctx context.Context) ([]*ent.BacklogItem, error)
FindPRPendingItems returns backlog items in "pr_pending" status that have a PR number set. Used by ReconcilePRPending to poll for merged PRs.
func (*EntRepository) FindReviewItemsWithUnprocessedVerdict ¶ added in v1.39.0
func (r *EntRepository) FindReviewItemsWithUnprocessedVerdict(ctx context.Context) ([]*ent.BacklogItem, error)
FindReviewItemsWithUnprocessedVerdict returns backlog items in "review" status whose most recent review-role ItemSession already has a terminal ReviewVerdict recorded. Distinct from FindZombieReviewItems: that detector requires EVERY open review-or-work session on the item to be confirmed dead before acting, but AutoReopenAfterFailedReview's live-session-reuse (a work session intentionally stays open polling for the verdict once the item is back in "review" — see docs/tasks/backlog-feature-improvement.md's "WIP limit now undercounts live sessions" finding) means the item never looks like a full zombie even when the review session itself died with its verdict never actioned (handleReviewSessionExited never fired — a server restart or crash mid-exit, the same class of gap as the crash-resilience fixes elsewhere in this package). Each returned item eager-loads its review-role sessions (most recent first) with their ReviewVerdict, so the caller can act on the newest one without a second round-trip.
func (*EntRepository) FindReviewItemsWithoutGate ¶ added in v1.37.0
func (r *EntRepository) FindReviewItemsWithoutGate(ctx context.Context) ([]*ent.BacklogItem, error)
FindReviewItemsWithoutGate returns backlog items in "review" status that have no review ItemSession. These are items where the review gate was never spawned (e.g. the headless pool was unavailable at the time of the work session exit). Each returned item has its ItemSessions edge loaded (work sessions only).
func (*EntRepository) FindStuckReviewItems ¶ added in v1.38.0
func (r *EntRepository) FindStuckReviewItems(ctx context.Context) ([]*ent.BacklogItem, error)
FindStuckReviewItems returns backlog items in "review" status that already have at least one review ItemSession (so FindReviewItemsWithoutGate's "no gate at all" filter won't catch them) but currently have no active (EndedAt still nil) review or work session — i.e. nothing is in flight for the item, yet it never resolved to done/in_progress/pr_pending.
This is the class of item left behind when AutoReopenAfterFailedReview's spawn attempt failed and rolled the status back to "review" (e.g. blocked by hasActiveWorkSession because the prior work session's underlying tmux/CLI session never got marked ended), or when a legacy/interactive review session exited without ever calling submit_review_verdict. Both cases leave the item permanently invisible to every other reconciler: FindReviewItemsWithoutGate excludes it (a review session does exist), and reconcileStaleWorkSessions only scans "in_progress" items. Found via manual QA against a live-data item stuck in review for 24+ hours with three UNVERIFIABLE re-review verdicts and only ever one work session on record.
func (*EntRepository) FindZombieReviewItems ¶ added in v1.38.0
func (r *EntRepository) FindZombieReviewItems(ctx context.Context) ([]*ent.BacklogItem, error)
FindZombieReviewItems returns backlog items in "review" status that have an active (EndedAt IS NULL) review-or-work ItemSession recorded in the DB — exactly the class FindStuckReviewItems excludes (its "nothing in flight" filter requires no un-ended session at all). Each returned item eager-loads only that active session so the caller can verify, via an injected liveness checker, whether the underlying tmux/CLI process the row claims is active has actually gone away (a zombie: the DB row looks live, the process is gone). Not every returned item is a zombie — the caller must still check liveness per active session.
func (*EntRepository) FinishSourceSync ¶ added in v1.35.0
func (r *EntRepository) FinishSourceSync(ctx context.Context, sourceID string, cursorAfter string, created, updated, skipped, errored int, startedAt, finishedAt time.Time) error
FinishSourceSync atomically advances an ItemSource's sync cursor/last_synced_at and records the SourceSyncEvent for a successful sync run. Wrapping both writes in one transaction prevents a crash between them from leaving the cursor advanced with no corresponding history row — which would silently hide the fact that a batch of items was processed (or dropped) in that run.
func (*EntRepository) Get ¶
func (r *EntRepository) Get(ctx context.Context, title string) (*InstanceData, error)
Get retrieves a single session by title
func (*EntRepository) GetAllItemSessionsWithBacklogInfo ¶ added in v1.37.0
func (r *EntRepository) GetAllItemSessionsWithBacklogInfo(ctx context.Context) ([]ItemSessionBacklogEntry, error)
GetAllItemSessionsWithBacklogInfo returns all item sessions joined with their parent backlog item's ID, title, and status. Used by the Insights dashboard index.
func (*EntRepository) GetAllSessionArtifacts ¶ added in v1.35.0
GetAllSessionArtifacts returns a map of title → raw artifacts JSON for all sessions that have a non-empty session_artifacts column. Single query replaces N per-session queries in LoadInstances (M-4 fix).
func (*EntRepository) GetBacklogItem ¶ added in v1.35.0
func (r *EntRepository) GetBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
GetBacklogItem retrieves a backlog item by UUID string.
func (*EntRepository) GetBacklogItemByExternalID ¶ added in v1.35.0
func (r *EntRepository) GetBacklogItemByExternalID(ctx context.Context, sourceID, externalID string) (*ent.BacklogItem, error)
GetBacklogItemByExternalID retrieves a BacklogItem by its external_id, scoped to sourceID. External IDs (e.g. GitHub issue/PR numbers) are only unique within their source, not globally — two different repos can both have an issue #1, so this must never match across sources.
func (*EntRepository) GetBacklogItemsByExternalIDs ¶ added in v1.41.0
func (r *EntRepository) GetBacklogItemsByExternalIDs(ctx context.Context, sourceID string, externalIDs []string) (map[string]*ent.BacklogItem, error)
GetBacklogItemsByExternalIDs batches GetBacklogItemByExternalID's lookup across many external IDs at once (a single IN query rather than one query per ID), scoped to sourceID the same way. Used by SyncLoop.PreviewBackwardSyncImpact, which previously issued one GetBacklogItemByExternalID call per closed issue in a loop — an N+1 query pattern against an index that isn't composite with the source FK. Returns a map keyed by external_id; IDs with no matching local item are simply absent from the map (not an error), matching the "not locally-imported, exclude it" semantics the per-item lookup had.
func (*EntRepository) GetBaseCommitSHAsForSessions ¶ added in v1.37.0
func (r *EntRepository) GetBaseCommitSHAsForSessions(ctx context.Context, sessionUUIDs []string) (map[string]string, error)
GetBaseCommitSHAsForSessions returns a map of sessionUUID → base_commit_sha for the given session UUIDs, including only rows with a non-empty value. Used at startup to restore dirBaseSHA for directory-mode backlog sessions — the counterpart to the SetDirBaseSHA call at spawn.
Reads base_commit_sha, falling back to last_commit_sha only for rows written before the two were split. That fallback is safe precisely because the bug being fixed meant the two fields held the same value on every legacy row; it must NOT be extended to rows that have a base_commit_sha, since last_commit_sha is now live-refreshed to the session's tip and would give a moving diff base.
func (*EntRepository) GetClaudeConversationUUIDBySessionUUID ¶ added in v1.37.0
func (r *EntRepository) GetClaudeConversationUUIDBySessionUUID(ctx context.Context, sessionUUID string) (string, error)
GetClaudeConversationUUIDBySessionUUID returns the Claude conversation UUID for the session whose title (tmux session name) matches sessionUUID. Returns "" if the session has no associated ClaudeSession.
func (*EntRepository) GetEntClient ¶ added in v1.35.0
func (r *EntRepository) GetEntClient() *ent.Client
GetEntClient returns the underlying *ent.Client so callers (e.g. ErrorRegistry) can operate on entities not managed by the Repository interface.
func (*EntRepository) GetItemSession ¶ added in v1.35.0
func (r *EntRepository) GetItemSession(ctx context.Context, id string) (ItemSessionSummary, error)
GetItemSession retrieves an ItemSession by entity UUID string. Loads the BacklogItem edge.
func (*EntRepository) GetItemSessionBySessionAndItem ¶ added in v1.35.0
func (r *EntRepository) GetItemSessionBySessionAndItem(ctx context.Context, sessionUUID string, itemID string) (ItemSessionSummary, error)
GetItemSessionBySessionAndItem looks up an ItemSession by both sessionUUID and backlog item ID.
func (*EntRepository) GetItemSessionBySessionUUID ¶ added in v1.35.0
func (r *EntRepository) GetItemSessionBySessionUUID(ctx context.Context, sessionUUID string) (ItemSessionSummary, error)
GetItemSessionBySessionUUID looks up the most recent active ItemSession by session UUID alone. session_uuid is not unique across records (a session may be reused), so we order by created_at descending and take the first match. Returns ErrNotFound if no record exists. Loads the BacklogItem edge so BacklogItemID is populated in the returned summary.
func (*EntRepository) GetItemSourceByID ¶ added in v1.35.0
func (r *EntRepository) GetItemSourceByID(ctx context.Context, id string) (*ent.ItemSource, error)
GetItemSourceByID retrieves a raw *ent.ItemSource by UUID string.
func (*EntRepository) GetMostRecentReviewVerdictForItem ¶ added in v1.35.0
func (r *EntRepository) GetMostRecentReviewVerdictForItem(ctx context.Context, itemID string) (ReviewOutcome, error)
GetMostRecentReviewVerdictForItem returns the OverallOutcome from the most recently created ReviewVerdict associated with any ItemSession for the given BacklogItem UUID. Returns "" (not an error) when no verdict exists yet.
func (*EntRepository) GetMostRecentStatusEventAt ¶ added in v1.38.0
func (r *EntRepository) GetMostRecentStatusEventAt(ctx context.Context, itemID string, toStatus BacklogStatus) (time.Time, bool, error)
GetMostRecentStatusEventAt returns the created_at timestamp of the most recent BacklogStatusEvent for itemID whose to_status equals toStatus. Returns (zero time, false, nil) when no such event exists (e.g. an item seeded directly into a status without a recorded transition). Used by the abandoned_review 15-minute grace check (abandonedReview pure fn) so a item that JUST entered review isn't flagged before the reconciler has had a chance to re-spawn a review gate.
func (*EntRepository) GetRecentReviewVerdictSummaries ¶ added in v1.39.0
func (r *EntRepository) GetRecentReviewVerdictSummaries(ctx context.Context, itemID string, limit int) ([]ReviewVerdictSummary, error)
GetRecentReviewVerdictSummaries returns up to limit ReviewVerdicts for the given BacklogItem UUID, most recent first. Reuses the existing ReviewVerdictSummary DTO (see repository.go) — only OverallOutcome, Summary, and DiffHash are populated, since that's all callers (IsRepeatedFailure and IsFlakyVerdictFlipFlop in stuck_decisions.go) need.
func (*EntRepository) GetRepoPathAndLatestCompletedWorkSessionCommits ¶ added in v1.42.0
func (r *EntRepository) GetRepoPathAndLatestCompletedWorkSessionCommits(ctx context.Context, itemID string) (repoPath, baseSHA, headSHA string, err error)
GetRepoPathAndLatestCompletedWorkSessionCommits returns itemID's RepoPath plus the Base/LastCommitSha of its most recent completed (session_role == work, ended_at set) ItemSession — the minimal data Storage.ComputeCurrentDiffHash needs. Unlike GetBacklogItem (which also eager-loads StatusEvents/ProgressNotes) and ListItemSessions (unbounded, eager-loads ReviewVerdict for every session), this pushes the "most recent completed work session" filter/order/limit into SQL — two bounded, no-edge queries regardless of how many sessions/events/notes the item has accumulated, rather than O(sessions_for_item) work on every review-verdict save. baseSHA/headSHA are both "" (no error) when the item has no completed work session yet.
func (*EntRepository) GetSession ¶
func (r *EntRepository) GetSession(ctx context.Context, title string, opts ContextOptions) (*Session, error)
GetSession retrieves a session using the new Session domain model. Stub: not yet implemented; use InstanceData-based methods instead.
func (*EntRepository) GetSessionArtifacts ¶ added in v1.35.0
GetSessionArtifacts loads the raw JSON artifact blob for a session. Returns ("", nil) if the session exists but has no artifacts stored yet.
func (*EntRepository) GetSubcommandBreakdown ¶ added in v1.35.0
func (r *EntRepository) GetSubcommandBreakdown(ctx context.Context, program string, since time.Time) ([]SubcommandDecisionCount, error)
func (*EntRepository) GetSubcommandTrend ¶ added in v1.35.0
func (r *EntRepository) GetSubcommandTrend(ctx context.Context, program, subcommand string, since time.Time) ([]AnalyticsData, error)
func (*EntRepository) GetWithOptions ¶
func (r *EntRepository) GetWithOptions(ctx context.Context, title string, options LoadOptions) (*InstanceData, error)
GetWithOptions retrieves a single session with selective child data loading.
func (*EntRepository) GetWorktreeDataBySessionUUID ¶ added in v1.37.0
func (r *EntRepository) GetWorktreeDataBySessionUUID(ctx context.Context, sessionUUID string) (GitWorktreeData, error)
GetWorktreeDataBySessionUUID returns the git worktree data for the Session with the given UUID. Returns an empty GitWorktreeData (no error) if the session does not exist or is a directory-mode session without a dedicated worktree.
func (*EntRepository) List ¶
func (r *EntRepository) List(ctx context.Context) ([]InstanceData, error)
List retrieves all sessions from the database
func (*EntRepository) ListActivityNotesForItem ¶ added in v1.46.0
func (r *EntRepository) ListActivityNotesForItem(ctx context.Context, itemID string) ([]ActivityNoteData, error)
ListActivityNotesForItem returns the full append-only activity-note history for a backlog item, ordered by created_at ascending (oldest first).
func (*EntRepository) ListAnalytics ¶ added in v1.12.0
func (r *EntRepository) ListAnalytics(ctx context.Context, limit int) ([]AnalyticsData, error)
func (*EntRepository) ListAnalyticsByProgramSince ¶ added in v1.35.0
func (r *EntRepository) ListAnalyticsByProgramSince(ctx context.Context, program string, since time.Time, limit int) ([]AnalyticsData, error)
func (*EntRepository) ListAnalyticsSince ¶ added in v1.35.0
func (r *EntRepository) ListAnalyticsSince(ctx context.Context, since time.Time, limit int) ([]AnalyticsData, error)
func (*EntRepository) ListBacklogItemSummaries ¶ added in v1.37.0
func (r *EntRepository) ListBacklogItemSummaries(ctx context.Context, filter BacklogItemFilter) ([]BacklogItemSummary, error)
ListBacklogItemSummaries returns lightweight BacklogItemSummary values for list views. Three-phase: (1) scalar fields via .All(), (2) item sessions + review verdicts via edge loading.
func (*EntRepository) ListBacklogItems ¶ added in v1.35.0
func (r *EntRepository) ListBacklogItems(ctx context.Context, filter BacklogItemFilter) ([]BacklogItemData, error)
ListBacklogItems returns backlog items with optional filtering.
func (*EntRepository) ListByStatus ¶
func (r *EntRepository) ListByStatus(ctx context.Context, status Status) ([]InstanceData, error)
ListByStatus retrieves sessions filtered by status
func (*EntRepository) ListByStatusWithOptions ¶
func (r *EntRepository) ListByStatusWithOptions(ctx context.Context, status Status, options LoadOptions) ([]InstanceData, error)
ListByStatusWithOptions retrieves sessions filtered by status with selective loading.
func (*EntRepository) ListByTag ¶
func (r *EntRepository) ListByTag(ctx context.Context, tagName string) ([]InstanceData, error)
ListByTag retrieves sessions that have a specific tag
func (*EntRepository) ListByTagWithOptions ¶
func (r *EntRepository) ListByTagWithOptions(ctx context.Context, tagName string, options LoadOptions) ([]InstanceData, error)
ListByTagWithOptions retrieves sessions with a specific tag with selective loading.
func (*EntRepository) ListItemSessions ¶ added in v1.35.0
func (r *EntRepository) ListItemSessions(ctx context.Context, itemID string) ([]ItemSessionSummary, error)
ListItemSessions returns all ItemSessions for a given BacklogItem UUID string.
func (*EntRepository) ListItemSources ¶ added in v1.35.0
func (r *EntRepository) ListItemSources(ctx context.Context) ([]ItemSourceData, error)
ListItemSources returns all registered item sources.
func (*EntRepository) ListProgressNotesForItem ¶ added in v1.38.0
func (r *EntRepository) ListProgressNotesForItem(ctx context.Context, itemID string) ([]ProgressNoteData, error)
ListProgressNotesForItem returns the full append-only history of report_progress calls for a backlog item, ordered by created_at ascending (oldest first).
func (*EntRepository) ListProjects ¶ added in v1.23.0
func (r *EntRepository) ListProjects(ctx context.Context) ([]ProjectData, error)
ListProjects returns all projects.
func (*EntRepository) ListRecentCommandsByProgram ¶ added in v1.35.0
func (*EntRepository) ListSessions ¶
func (r *EntRepository) ListSessions(ctx context.Context, opts ContextOptions) ([]*Session, error)
ListSessions retrieves all sessions using the new Session domain model. Stub: not yet implemented; use InstanceData-based methods instead.
func (*EntRepository) ListShells ¶ added in v1.35.0
ListShells returns all shells for the given session title, ordered by order_index.
func (*EntRepository) ListSourceSyncEvents ¶ added in v1.35.0
func (r *EntRepository) ListSourceSyncEvents(ctx context.Context, sourceID string) ([]SourceSyncEventData, bool, error)
ListSourceSyncEvents returns sync history events for an item source, most recent first, capped at maxSourceSyncEventsHistory rows. truncated is true when older events exist beyond the cap — callers should surface this to avoid silently hiding history for sources with long or frequent sync runs.
func (*EntRepository) ListWithOptions ¶
func (r *EntRepository) ListWithOptions(ctx context.Context, options LoadOptions) ([]InstanceData, error)
ListWithOptions retrieves all sessions with selective child data loading.
func (*EntRepository) MarkStuck ¶ added in v1.38.0
func (r *EntRepository) MarkStuck(ctx context.Context, itemID string, reason domain.StuckReason, expectedStatus BacklogStatus, stuckContext string) (applied bool, err error)
MarkStuck opens, refreshes, or reopens a durable BacklogStuckState row for the given item + reason via a resolve-in-place upsert on the (item_id, reason) unique index — there is exactly one row per pair at all times.
A best-effort item-status precondition is applied before writing: if the item's current status does not equal expectedStatus, MarkStuck returns (false, nil) without writing. This precondition is NOT atomic with the write itself (a concurrent transition can still race in between); the self-heal sweep (reconcile pipeline, Phase 2) is the correctness backstop for any stale write that still lands.
Row semantics on conflict with an existing (item_id, reason) row:
- OPEN row (resolved_at IS NULL): only last_checked_at and context are refreshed. first_detected_at and notified_at are left untouched, so notify-once dedup and the "stuck for N" duration both survive repeated ticks.
- RESOLVED row (resolved_at IS NOT NULL): the SAME row is reopened in place — resolved_at and notified_at are cleared and first_detected_at is reset to now — never a second row for the same pair.
Implementation note: this is two atomic statements (an INSERT ... ON CONFLICT upsert, then a conditional UPDATE ... WHERE resolved_at IS NOT NULL) inside one DB transaction, rather than a single raw SQL statement. Ent's generated upsert Update() callback has no portable way to express a per-row CASE WHEN keyed off the pre-existing resolved_at value without hand-written dialect-specific SQL, so the reopen adjustment is split into its own atomic, idempotent conditional UPDATE. Row-dedup itself — the concurrency-sensitive part — is still guaranteed by the single upsert statement; there is no read-then-write for detecting whether the row exists.
func (*EntRepository) MarkStuckNotified ¶ added in v1.38.0
func (r *EntRepository) MarkStuckNotified(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
MarkStuckNotified sets notified_at=now on an open, not-yet-notified stuck row — the durable notify-once dedup write, called once after a stuck notification has actually been sent. A no-op (not an error) if the row is already notified or doesn't exist.
func (*EntRepository) ReconcileStuckItems ¶ added in v1.35.0
func (r *EntRepository) ReconcileStuckItems(ctx context.Context) (int, error)
ReconcileStuckItems finds in_progress items whose all linked ItemSessions have ended, and transitions them to review status. Returns the count of transitioned items. All updates are wrapped in a single transaction so they succeed or fail atomically.
func (*EntRepository) RecordAnalytics ¶ added in v1.12.0
func (r *EntRepository) RecordAnalytics(ctx context.Context, data AnalyticsData) error
func (*EntRepository) RecordRemediationAttempt ¶ added in v1.39.0
func (r *EntRepository) RecordRemediationAttempt(ctx context.Context, itemID string, reason domain.StuckReason, attempts int32, nextAt *time.Time) (bool, error)
RecordRemediationAttempt records that an automated (or operator-triggered, see TriggerRemediationNow) remediation attempt was just made for an open (item_id, reason) row: sets remediation_attempts to attempts and next_remediation_at to nextAt (nil once attempts has hit the cap — see nextRemediationAt in backlog_remediation.go). Callers compute attempts/nextAt themselves (via the shared backoff gate) rather than this method incrementing in place, so a single code path (evaluateRemediation) owns the backoff-schedule arithmetic. Scoped to WHERE resolved_at IS NULL, matching every other stuck-state write in this file — a row that resolved between the gate's read and this write is left alone rather than resurrected.
func (*EntRepository) RecordRemediationRestartGrace ¶ added in v1.39.0
func (r *EntRepository) RecordRemediationRestartGrace(ctx context.Context, itemID string, reason domain.StuckReason, bootTime time.Time) (bool, error)
RecordRemediationRestartGrace records that itemID/reason's open row just consumed its one-per-boot restart-grace pass (see evaluateRemediation): sets grace_boot_time to bootTime WITHOUT touching remediation_attempts or next_remediation_at — a grace pass lets the wrapped remediation action run without spending any of the row's 5-attempt budget.
func (*EntRepository) RecordSourceSyncFailure ¶ added in v1.41.0
func (r *EntRepository) RecordSourceSyncFailure(ctx context.Context, sourceID string, message string) error
RecordSourceSyncFailure persists a zero-item sync-history row recording a forward-sync failure (e.g. the forward-sync EventBus subscriber's CloseIssue call erroring — see server/services/backlog_github_forward_sync.go), so the failure is queryable via ListSourceSyncEvents / the Settings UI's row-level warning (Story 4.3.2) instead of only appearing in server logs. Mirrors CreateSourceSyncEvent's error-message convention used by SyncOne's own fetch-failure path, but for the write direction (forward sync) rather than the read direction (Fetch).
func (*EntRepository) ResetStuckRemediation ¶ added in v1.39.0
func (r *EntRepository) ResetStuckRemediation(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
ResetStuckRemediation clears the automated-remediation counters on a single open (item_id, reason) row: remediation_attempts back to 0, next_remediation_at and notified_at cleared. Clearing notified_at (in addition to the remediation counters) lets a fresh notify+respawn cycle fire on the very next detector tick instead of waiting on stale dedup state — the same reasoning as MarkStuck's reopen-in-place path. A no-op (false, nil), not an error, when no open row matches (item_id, reason).
func (*EntRepository) ResolveStuck ¶ added in v1.38.0
func (r *EntRepository) ResolveStuck(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
ResolveStuck atomically, idempotently closes an open BacklogStuckState row via a single conditional UPDATE ... WHERE resolved_at IS NULL. Returns whether a row was actually resolved by this call; resolving an already-resolved or nonexistent (item_id, reason) row is a no-op, not an error, and never overwrites an existing resolved_at.
func (*EntRepository) RevertChainFireClaim ¶ added in v1.43.0
func (r *EntRepository) RevertChainFireClaim(ctx context.Context, id string) error
RevertChainFireClaim unconditionally resets chain_fired back to false — used by ChainFirer.Fire to release its claim after a subsequent FireTriggerChained attempt fails, so TriggerChainReconciler retries on its next tick. Safe to call unconditionally (no precondition): only the goroutine that just won ClaimChainFire's claim for this item can ever reach this call, so nothing else can be racing this specific revert.
func (*EntRepository) SaveReviewVerdict ¶ added in v1.35.0
func (r *EntRepository) SaveReviewVerdict(ctx context.Context, itemSessionID string, verdict ReviewVerdictData) error
SaveReviewVerdict upserts a ReviewVerdict for a given ItemSession. The query-then-create/update is wrapped in a transaction to prevent a check-then-act race condition when concurrent callers save verdicts for the same item session.
func (*EntRepository) SetCallbackDispatcher ¶ added in v1.43.0
func (r *EntRepository) SetCallbackDispatcher(d CallbackDispatcher)
SetCallbackDispatcher wires a CallbackDispatcher into this repository so TransitionBacklogItemStatus (on_session_complete) and BacklogLifecycleListener.reconcileStaleWorkSessions (on_session_stale, which is handed this *EntRepository directly) can fire outbound callbacks (webhook-triggers Phase 5). Called via Storage.SetCallbackDispatcher's forwarding method, the same pattern SetItemChangePublisher uses.
func (*EntRepository) SetChainFirer ¶ added in v1.43.0
func (r *EntRepository) SetChainFirer(f *ChainFirer)
SetChainFirer wires a ChainFirer into this repository so TransitionBacklogItemStatus can dispatch the pipeline-chain fire immediately after a "done" transition commits (webhook-triggers Phase 6, AC5/AC9). Called via Storage.WireChainFirer's forwarding call in server/dependencies.go, the same pattern SetCallbackDispatcher uses.
func (*EntRepository) SetItemChangePublisher ¶ added in v1.41.0
func (r *EntRepository) SetItemChangePublisher(p ItemChangePublisher)
SetItemChangePublisher wires an ItemChangePublisher into this repository so its backlog mutation methods (TransitionBacklogItemStatus, UpdateBacklogItem, ArchiveBacklogItem, DeleteBacklogItem, SaveReviewVerdict, CreateItemSessionWithVerdict, CreateItemSession, UpdateItemSessionSessionUUID, UpdateItemSessionTriageResult) can publish a best-effort change notification after each successful mutation. Called via Storage.SetItemChangePublisher's forwarding method (session/storage.go), which is the only entry point server/dependencies.go has since it holds a *Storage, not a concrete *EntRepository.
func (*EntRepository) SetItemSessionBaseCommit ¶ added in v1.41.0
func (r *EntRepository) SetItemSessionBaseCommit(ctx context.Context, id, sha string) error
SetItemSessionBaseCommit records the worktree's pre-work HEAD SHA for the item session, so the review gate can diff base..HEAD across every commit the agent makes rather than just HEAD~1..HEAD.
This is deliberately NOT UpdateItemSessionGitActivity: that function's fields mean "the session's latest commit", and seeding them with the spawn-time base SHA is what let closeIfSupersededByMain close real, unmerged PRs as "superseded" — a branch's own base commit is by construction already an ancestor of main, so IsCommitOnMain on it is always true (BUG-047).
func (*EntRepository) SnoozeStuckState ¶ added in v1.38.0
func (r *EntRepository) SnoozeStuckState(ctx context.Context, itemID string, reason domain.StuckReason, until time.Time) (bool, error)
SnoozeStuckState sets snoozed_until on an open BacklogStuckState row via a single atomic conditional UPDATE ... WHERE resolved_at IS NULL, matching the ResolveStuck pattern. Returns whether a row was actually updated by this call; snoozing a nonexistent or already-resolved (item_id, reason) row is a no-op, not an error. A snoozed-until-past-now value simply un-snoozes the row on the next FindOpenStuckStates read (its predicate is snoozed_until IS NULL OR snoozed_until < now).
func (*EntRepository) TransitionBacklogItemStatus ¶ added in v1.35.0
func (r *EntRepository) TransitionBacklogItemStatus(ctx context.Context, id string, toStatus BacklogStatus, precondition *BacklogItemPrecondition, triggeredBy string) (*BacklogItemData, error)
TransitionBacklogItemStatus changes the status of a backlog item with optional precondition.
The precondition is enforced as a genuine SQL-level compare-and-swap: it is folded into the UPDATE statement's own WHERE clause (status = ? AND updated_at = ?, via the bulk Update().Where(...) builder — not UpdateOneID, which cannot scope beyond id) rather than checked against a separately-fetched row beforehand. Save() on a bulk update returns the affected row count instead of the updated entity, so the row is re-fetched after a successful write.
The previous implementation did Get() → check-in-Go → UpdateOneID().Save(), a read-then-write race: nothing stopped a second, concurrent caller's write from landing in the gap between this call's read and its write, so a precondition that was true at read time could be false (and silently ignored) by write time. Two concrete incidents motivated closing this: a stale AutoReopenAfterFailedReview call reopened an item that had, in the meantime, already legitimately shipped to "done" (see docs/bugs/fixed/BUG-026-backlog-transition-status-toctou-reopen.md, live 2026-07-20 repro, item 0fd4a940, PR #176), and the backlog work-item queue feature's concurrent-dequeue-claim test found the same race could double-claim a single queued item between two dequeue sweeps (PR #199).
func (*EntRepository) TransitionBacklogItemStatusWithPRFields ¶ added in v1.41.0
func (r *EntRepository) TransitionBacklogItemStatusWithPRFields(ctx context.Context, id string, toStatus BacklogStatus, prURL string, prNumber int, precondition *BacklogItemPrecondition, triggeredBy string) (*BacklogItemData, error)
TransitionBacklogItemStatusWithPRFields atomically transitions a backlog item's status while also persisting its PrURL/PrNumber, as a single UPDATE ... WHERE statement — the same CAS precondition guards both writes together, so a reader can never observe the status having changed without the PR fields already being set, or vice versa.
This exists to close a narrower race than the one Storage.SetBacklogItemPRAndTransition originally had: an earlier fix reordered that function to run the status transition (review -> pr_pending) before a separate PrURL/PrNumber field write, which fixed the lost-update bug (two racing callers clobbering each other's PR number) but left a smaller gap — between the transition committing and the field write committing, a concurrent reader could observe status=pr_pending with PrNumber==0. That exact shape is what the pr_pending_no_pr / BUG-040 stuck detector (reconcilePRPendingWithoutPRItems, session/backlog_lifecycle.go) exists to flag as a HIGH-priority, non-auto-recoverable alert, and its resolution condition (selfHealStuck) is anchored on the item leaving pr_pending entirely — so a reconcile tick landing in that multi-millisecond window could raise a spurious stuck alert that stays open for days on a perfectly healthy item. Folding both writes into one atomic UPDATE removes the window rather than narrowing it.
Not part of the Repository interface — Storage type-asserts s.repo to *EntRepository to call this, the same pattern already used for ListItemSessions (see server/mcp/tools_backlog.go's listItemSessionsFn doc comment): EntRepository has no second real implementation to abstract this over, so adding it to the interface would be pure speculation (see .claude/rules/interface-pollution-checklist.md).
func (*EntRepository) UnarchiveBacklogItem ¶ added in v1.44.0
func (r *EntRepository) UnarchiveBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
UnarchiveBacklogItem clears archived_at and restores the item to the "idea" status — the sole valid archived-> reopen transition. It does not attempt to restore whatever status the item held before archiving (no history-based restoration): the item simply re-enters the idea column needing a fresh session, matching UnarchiveSession's identical unconditional-flip precedent (server/services/session_service.go) rather than erroring or no-op'ing on an already-non-archived item.
func (*EntRepository) UnresolvedBlockerIDs ¶ added in v1.43.0
UnresolvedBlockerIDs returns the blocker item IDs still unresolved for a single blocked item, for building a human-readable stuck-reason message. Unlike UnresolvedBlockerItemIDs (batched presence check across many candidates), this returns which specific items are doing the blocking.
func (*EntRepository) UnresolvedBlockerItemIDs ¶ added in v1.43.0
func (r *EntRepository) UnresolvedBlockerItemIDs(ctx context.Context, itemIDs []string) (map[string]bool, error)
UnresolvedBlockerItemIDs returns, for the given candidate itemIDs, the subset that have at least one dependency edge whose blocker has not yet reached a resolved status. A blocker counts as resolved once it reaches BacklogStatusDone (shipped) or BacklogStatusArchived (won't ship) — an archived blocker is never coming back to "done", so treating it as still blocking would permanently strand its dependent. Batches the check into a single query (rather than one per candidate) to avoid N+1 queries in dequeue paths.
func (*EntRepository) Update ¶
func (r *EntRepository) Update(ctx context.Context, data InstanceData) error
Update modifies an existing session in the database
func (*EntRepository) UpdateAcCriterionStatus ¶ added in v1.35.0
func (r *EntRepository) UpdateAcCriterionStatus(ctx context.Context, itemID string, criterionIndex int, status string, note string) error
UpdateAcCriterionStatus updates a single acceptance criterion's status by index.
func (*EntRepository) UpdateBacklogItem ¶ added in v1.35.0
func (r *EntRepository) UpdateBacklogItem(ctx context.Context, id string, update BacklogItemUpdate, precondition *BacklogItemPrecondition) (*BacklogItemData, error)
UpdateBacklogItem modifies an existing backlog item with optional precondition check.
func (*EntRepository) UpdateGitHubPRNumber ¶ added in v1.35.0
UpdateGitHubPRNumber persists a discovered PR number for a session. Called by PRStatusPoller when it auto-discovers a PR for a branch-based session.
func (*EntRepository) UpdateItemSessionEnded ¶ added in v1.35.0
func (r *EntRepository) UpdateItemSessionEnded(ctx context.Context, id string, endedAt time.Time) error
UpdateItemSessionEnded records the end time for an ItemSession.
func (*EntRepository) UpdateItemSessionEndedWithReason ¶ added in v1.41.0
func (r *EntRepository) UpdateItemSessionEndedWithReason(ctx context.Context, id string, endedAt time.Time, reason string) error
UpdateItemSessionEndedWithReason records the end time for an ItemSession alongside classifyHeadlessCallError's bucket (or "" for a successful end) — see the end_reason schema comment for why this exists: it lets orphan-recovery sweeps tell a call killed by our own graceful shutdown apart from one that failed on its own merits.
func (*EntRepository) UpdateItemSessionFailureCapture ¶ added in v1.42.0
func (r *EntRepository) UpdateItemSessionFailureCapture(ctx context.Context, id string, path string) error
UpdateItemSessionFailureCapture records the absolute path to a durable raw-output capture file (session.WriteHeadlessFailureCapture) for a headless triage/review call that errored or produced unparseable output — see the failure_capture_path schema comment. Set independently of UpdateItemSessionEndedWithReason (a separate, orthogonal column) so a caller that already wrote the capture file to disk can record its path without re-specifying ended_at/end_reason.
func (*EntRepository) UpdateItemSessionFileTouch ¶ added in v1.35.0
func (r *EntRepository) UpdateItemSessionFileTouch(ctx context.Context, id string, touchAt time.Time) error
UpdateItemSessionFileTouch updates the last file touch timestamp on an ItemSession.
func (*EntRepository) UpdateItemSessionGitActivity ¶ added in v1.35.0
func (r *EntRepository) UpdateItemSessionGitActivity(ctx context.Context, id string, sha, msg string, commitAt time.Time, commitCount int) error
UpdateItemSessionGitActivity records the item session's *current* tip commit and the count of commits it has authored since its base. Called repeatedly by refreshWorkSessionGitActivity (session/backlog_lifecycle.go) while the session is active, so downstream "has this session's work landed on main?" checks read live data. To record the spawn-time baseline instead, use SetItemSessionBaseCommit.
func (*EntRepository) UpdateItemSessionSessionUUID ¶ added in v1.35.0
func (r *EntRepository) UpdateItemSessionSessionUUID(ctx context.Context, id string, sessionUUID string) error
UpdateItemSessionSessionUUID updates the session_uuid field on an existing ItemSession.
func (*EntRepository) UpdateItemSessionStarted ¶ added in v1.35.0
func (r *EntRepository) UpdateItemSessionStarted(ctx context.Context, id string, startedAt time.Time) error
UpdateItemSessionStarted records the start time for an ItemSession.
func (*EntRepository) UpdateItemSessionTriageResult ¶ added in v1.35.0
func (r *EntRepository) UpdateItemSessionTriageResult(ctx context.Context, id string, triageResult string) error
UpdateItemSessionTriageResult stores the triage result JSON payload on an ItemSession.
func (*EntRepository) UpdateItemSessionVerificationNotes ¶ added in v1.37.0
func (r *EntRepository) UpdateItemSessionVerificationNotes(ctx context.Context, id string, verificationNotes string) error
UpdateItemSessionVerificationNotes stores the verification evidence reported via request_review (commands run, manual checks performed) on an ItemSession.
func (*EntRepository) UpdateItemSource ¶ added in v1.35.0
func (r *EntRepository) UpdateItemSource(ctx context.Context, id string, update ItemSourceUpdate) (*ItemSourceData, error)
UpdateItemSource modifies an existing item source.
func (*EntRepository) UpdateLastAcknowledged ¶ added in v1.35.0
func (r *EntRepository) UpdateLastAcknowledged(ctx context.Context, title string, t time.Time) error
UpdateLastAcknowledged sets only the last_acknowledged field for a session, issuing a single UPDATE WHERE title=? without a prior SELECT.
func (*EntRepository) UpdateLastAddedToQueue ¶ added in v1.35.0
func (r *EntRepository) UpdateLastAddedToQueue(ctx context.Context, title string, t time.Time) error
UpdateLastAddedToQueue sets only the last_added_to_queue field for a session, issuing a single UPDATE WHERE title=? without a prior SELECT.
func (*EntRepository) UpdateLastViewed ¶ added in v1.35.0
UpdateLastViewed sets only the last_viewed field for a session, issuing a single UPDATE WHERE title=? without a prior SELECT.
func (*EntRepository) UpdateProject ¶ added in v1.23.0
func (r *EntRepository) UpdateProject(ctx context.Context, data ProjectData) (*ProjectData, error)
UpdateProject modifies an existing project.
func (*EntRepository) UpdateReviewQueueState ¶ added in v1.35.0
func (r *EntRepository) UpdateReviewQueueState(ctx context.Context, title string, lastUserResponse, processingGraceUntil, lastPromptDetected time.Time, lastPromptSignature string) error
UpdateReviewQueueState efficiently updates only the review-queue interaction fields for a session, avoiding the full read-modify-write cycle of updateFieldInRepo.
func (*EntRepository) UpdateSession ¶
func (r *EntRepository) UpdateSession(ctx context.Context, session *Session) error
UpdateSession updates an existing session using the Session domain model. Stub: not yet implemented; use InstanceData-based methods instead.
func (*EntRepository) UpdateSessionArtifacts ¶ added in v1.35.0
func (r *EntRepository) UpdateSessionArtifacts(ctx context.Context, title string, blob string) error
UpdateSessionArtifacts persists the JSON-encoded artifact blob for a session. Wrapped in a transaction for correctness under concurrent writes (M-6 fix). The per-title mutex in ArtifactExtractor (C-1) serializes calls at the application layer; the transaction is belt-and-suspenders for correctness.
func (*EntRepository) UpdateSessionMetadata ¶ added in v1.42.0
func (r *EntRepository) UpdateSessionMetadata(ctx context.Context, currentTitle string, newTitle, category, note, workingDir *string) error
UpdateSessionMetadata efficiently updates only title/category/note/working_dir fields for a session, issuing a single UPDATE WHERE title=? without a prior SELECT and without the worktree/diffstats/tags/claude_session writes the full Update method performs — mirrors UpdateLastViewed's shape. currentTitle must be the row's title from BEFORE any rename already applied to the caller's in-memory Instance in this same request: Update looks the row up by data.Title (the post-rename value), which misses the still-old-titled DB row and falls into Update's Create fallback, orphaning it under the new title. Using currentTitle as the WHERE key avoids that. Category/WorkingDir are only set when non-nil AND non-empty, matching Update's existing guarded (`data.Category != ""`) semantics; Note is set whenever non-nil (including ""), since an empty note is a meaningful cleared state, not "unset" — same asymmetry as Update's unconditional SetNote(data.Note).
func (*EntRepository) UpdateShellStatus ¶ added in v1.35.0
func (r *EntRepository) UpdateShellStatus(ctx context.Context, shellID, status string, exitCode *int) error
UpdateShellStatus updates the status (and optionally exit code + stopped_at) for a shell.
func (*EntRepository) UpdateTimestamps ¶
func (r *EntRepository) UpdateTimestamps(ctx context.Context, title string, lastTerminalUpdate, lastMeaningfulOutput time.Time, lastOutputSignature string) error
UpdateTimestamps efficiently updates only timestamp fields for a session
func (*EntRepository) UpsertRule ¶ added in v1.12.0
func (r *EntRepository) UpsertRule(ctx context.Context, data ApprovalRuleData) error
type EntTriggerFireEventRepository ¶ added in v1.43.0
type EntTriggerFireEventRepository struct {
// contains filtered or unexported fields
}
EntTriggerFireEventRepository implements TriggerFireEventRepository using the ent ORM.
func NewEntTriggerFireEventRepository ¶ added in v1.43.0
func NewEntTriggerFireEventRepository(client *ent.Client) *EntTriggerFireEventRepository
NewEntTriggerFireEventRepository creates a new ent-backed TriggerFireEvent repository.
func (*EntTriggerFireEventRepository) Create ¶ added in v1.43.0
func (r *EntTriggerFireEventRepository) Create(ctx context.Context, input TriggerFireEventInput) error
Create inserts a new TriggerFireEvent row.
func (*EntTriggerFireEventRepository) ListByWorkflow ¶ added in v1.43.0
func (r *EntTriggerFireEventRepository) ListByWorkflow(ctx context.Context, workflowID uuid.UUID, limit int) ([]*ent.TriggerFireEvent, error)
ListByWorkflow returns the most recent TriggerFireEvent rows for workflowID, newest first, capped at limit (a value <= 0 defaults to 100).
func (*EntTriggerFireEventRepository) UpdateOutcome ¶ added in v1.43.0
func (r *EntTriggerFireEventRepository) UpdateOutcome(ctx context.Context, workflowID uuid.UUID, deliveryID, outcome, sessionID, errMsg string) error
UpdateOutcome transitions the TriggerFireEvent row matching (workflowID, deliveryID) to outcome, optionally setting sessionID/errMsg.
type EntWorkflowRepository ¶ added in v1.35.0
type EntWorkflowRepository struct {
// contains filtered or unexported fields
}
EntWorkflowRepository implements WorkflowRepository using the ent ORM.
func NewEntWorkflowRepository ¶ added in v1.35.0
func NewEntWorkflowRepository(client *ent.Client) *EntWorkflowRepository
NewEntWorkflowRepository creates a new ent-backed workflow repository.
func (*EntWorkflowRepository) Create ¶ added in v1.35.0
func (r *EntWorkflowRepository) Create(ctx context.Context, w WorkflowCreateInput) (*ent.Workflow, error)
Create inserts a new workflow definition. Returns ent.ConstraintError when a duplicate slug exists.
func (*EntWorkflowRepository) GetByWebhookSlug ¶ added in v1.43.0
func (r *EntWorkflowRepository) GetByWebhookSlug(ctx context.Context, slug string) (*ent.Workflow, error)
GetByWebhookSlug retrieves a workflow by its webhook_slug.
func (*EntWorkflowRepository) ListAll ¶ added in v1.35.0
ListAll returns all workflows sorted ascending by created_at. A safety cap of 1000 is applied to prevent runaway queries.
func (*EntWorkflowRepository) ListByTriggerType ¶ added in v1.43.0
func (r *EntWorkflowRepository) ListByTriggerType(ctx context.Context, triggerType string) ([]*ent.Workflow, error)
ListByTriggerType returns all workflows with the given trigger_type, regardless of cron_enabled (see interface doc comment for why enabled/repo/branch filtering is left to the caller).
func (*EntWorkflowRepository) ListEnabled ¶ added in v1.35.0
ListEnabled returns only workflows where cron_enabled is true.
func (*EntWorkflowRepository) Update ¶ added in v1.35.0
func (r *EntWorkflowRepository) Update(ctx context.Context, id uuid.UUID, w WorkflowUpdateInput) (*ent.Workflow, error)
Update applies a partial update to an existing workflow by UUID, unconditionally. Thin wrapper over UpdateConditional with the zero time.Time, which applies no updated_at precondition — kept as a separate method so existing single-writer callers don't need to thread an expectedUpdatedAt they don't have.
func (*EntWorkflowRepository) UpdateConditional ¶ added in v1.44.0
func (r *EntWorkflowRepository) UpdateConditional(ctx context.Context, id uuid.UUID, w WorkflowUpdateInput, expectedUpdatedAt time.Time) (*ent.Workflow, error)
UpdateConditional applies a partial update to an existing workflow by UUID, only if the row's current updated_at exactly matches expectedUpdatedAt — an optimistic- concurrency CAS. A zero expectedUpdatedAt applies no precondition (always writes), matching Update's unconditional behavior.
Built on WorkflowUpdateOne (UpdateOneID), not the bulk Update().Where() builder that an earlier version of this method used: UpdateOneID's Save() returns the mutated entity directly, computed inside the same UPDATE statement/transaction ent's generated sqlgraph.UpdateNode issues — one atomic round trip, matching this method's pre-CAS performance and read-your-own-write consistency exactly when expectedUpdatedAt is zero (the common case: every existing single-writer caller, plus the hot per-fire LastFiredAt bump in Scheduler). The bulk builder's Update().Where() only returns an affected-row count, forcing a second, separate, unguarded Get to reload the entity — which both doubles the round trips on every call AND lets a concurrent writer's update land in the gap between the CAS write and that reload, so the caller could receive someone else's state as if it were their own write's result. UpdateOneID.Where() supports the same predicate this needs (workflow.UpdatedAtEQ), so there's no capability lost by using it instead.
type ErrDuplicateTag ¶
type ErrDuplicateTag struct {
Tag string
}
ErrDuplicateTag is returned when adding a tag that already exists.
func (ErrDuplicateTag) Error ¶
func (e ErrDuplicateTag) Error() string
type ErrInvalidTransition ¶
ErrInvalidTransition is returned when a status transition is not allowed by the state machine defined in state_machine.go.
func (ErrInvalidTransition) Error ¶
func (e ErrInvalidTransition) Error() string
type ErrTagTooLong ¶
ErrTagTooLong is returned when a tag exceeds the maximum length.
func (ErrTagTooLong) Error ¶
func (e ErrTagTooLong) Error() string
type ErrTooManyTags ¶ added in v1.9.0
ErrTooManyTags is returned when setting more tags than MaxTagCount allows.
func (ErrTooManyTags) Error ¶ added in v1.9.0
func (e ErrTooManyTags) Error() string
type ExecutionOptions ¶
type ExecutionOptions struct {
// Timeout for command execution (0 = no timeout)
Timeout time.Duration
// MaxOutputSize limits captured output (0 = unlimited)
MaxOutputSize int
// StatusCheckInterval for polling status detector
StatusCheckInterval time.Duration
// TerminalStatuses are statuses that indicate command completion
TerminalStatuses []detection.DetectedStatus
}
ExecutionOptions configures command execution behavior.
func DefaultExecutionOptions ¶
func DefaultExecutionOptions() ExecutionOptions
DefaultExecutionOptions returns sensible defaults for command execution.
type ExecutionResult ¶
type ExecutionResult struct {
Command *Command
Success bool
Output string
Error error
StartTime time.Time
EndTime time.Time
FinalStatus detection.DetectedStatus
StatusChanges []StatusChange
}
ExecutionResult represents the result of a command execution.
type ExternalApprovalCallback ¶
type ExternalApprovalCallback func(*ExternalApprovalEvent)
ExternalApprovalCallback is called when an approval is detected.
type ExternalApprovalEvent ¶
type ExternalApprovalEvent struct {
Request *detection.ApprovalRequest
SessionID string // Socket path or unique identifier
SessionTitle string
Source ExternalApprovalSource
Cwd string
Command string
}
ExternalApprovalEvent represents an approval detected in an external session.
type ExternalApprovalMonitor ¶
type ExternalApprovalMonitor struct {
// contains filtered or unexported fields
}
ExternalApprovalMonitor monitors external sessions for approval requests.
func NewExternalApprovalMonitor ¶
func NewExternalApprovalMonitor() *ExternalApprovalMonitor
NewExternalApprovalMonitor creates a new external approval monitor.
func (*ExternalApprovalMonitor) GetAllPendingApprovals ¶
func (m *ExternalApprovalMonitor) GetAllPendingApprovals() map[string][]*detection.ApprovalRequest
GetAllPendingApprovals returns pending approvals across all monitored sessions.
func (*ExternalApprovalMonitor) GetDetector ¶
func (m *ExternalApprovalMonitor) GetDetector() *detection.ApprovalDetector
GetDetector returns the underlying approval detector for configuration.
func (*ExternalApprovalMonitor) GetMonitoredSessions ¶
func (m *ExternalApprovalMonitor) GetMonitoredSessions() []string
GetMonitoredSessions returns the socket paths of all monitored sessions.
func (*ExternalApprovalMonitor) GetPendingApprovals ¶
func (m *ExternalApprovalMonitor) GetPendingApprovals(socketPath string) []*detection.ApprovalRequest
GetPendingApprovals returns all pending approval requests for a session.
func (*ExternalApprovalMonitor) IntegrateWithDiscovery ¶
func (m *ExternalApprovalMonitor) IntegrateWithDiscovery( discovery *ExternalSessionDiscovery, streamerManager *ExternalStreamerManager, )
IntegrateWithDiscovery connects the approval monitor to external session discovery. This auto-monitors new external sessions as they're discovered.
func (*ExternalApprovalMonitor) IntegrateWithDiscoveryTmux ¶
func (m *ExternalApprovalMonitor) IntegrateWithDiscoveryTmux( discovery *ExternalSessionDiscovery, tmuxStreamerManager *ExternalTmuxStreamerManager, )
IntegrateWithDiscoveryTmux connects the approval monitor to external session discovery using tmux-based streaming instead of socket-based streaming.
func (*ExternalApprovalMonitor) MarkApprovalHandled ¶
func (m *ExternalApprovalMonitor) MarkApprovalHandled(socketPath, requestID string, approved bool) error
MarkApprovalHandled marks an approval request as handled.
func (*ExternalApprovalMonitor) MonitorSession ¶
func (m *ExternalApprovalMonitor) MonitorSession( streamer *ExternalStreamer, title string, source ExternalApprovalSource, ) error
MonitorSession starts monitoring an external session for approval requests.
func (*ExternalApprovalMonitor) MonitorSessionTmux ¶
func (m *ExternalApprovalMonitor) MonitorSessionTmux( streamer *ExternalTmuxStreamer, tmuxSessionName string, title string, source ExternalApprovalSource, ) error
MonitorSessionTmux starts monitoring an external session using tmux-based streaming.
func (*ExternalApprovalMonitor) OnApproval ¶
func (m *ExternalApprovalMonitor) OnApproval(callback ExternalApprovalCallback)
OnApproval registers a callback for approval events.
func (*ExternalApprovalMonitor) Start ¶
func (m *ExternalApprovalMonitor) Start()
Start begins monitoring for approvals.
func (*ExternalApprovalMonitor) Stop ¶
func (m *ExternalApprovalMonitor) Stop()
Stop stops all monitoring.
func (*ExternalApprovalMonitor) StopMonitoringSession ¶
func (m *ExternalApprovalMonitor) StopMonitoringSession(socketPath string)
StopMonitoringSession stops monitoring a specific session.
type ExternalApprovalSource ¶
type ExternalApprovalSource string
ExternalApprovalSource identifies the source of an external approval.
const ( SourceIntelliJ ExternalApprovalSource = "IntelliJ" SourceTerminal ExternalApprovalSource = "Terminal" SourceVSCode ExternalApprovalSource = "VS Code" SourceMux ExternalApprovalSource = "mux" SourceUnknown ExternalApprovalSource = "Unknown" )
type ExternalInstanceMetadata ¶
type ExternalInstanceMetadata struct {
// TmuxSocket is the tmux server socket this instance belongs to
// Empty string means the default tmux server
TmuxSocket string
// TmuxSessionName is the full tmux session name
TmuxSessionName string
// DiscoveredAt is when this external instance was first discovered
DiscoveredAt time.Time
// LastSeen is when this instance was last seen during discovery
LastSeen time.Time
// OriginalPID is the process ID when first discovered
OriginalPID int
// MuxSocketPath is the path to an ssq-mux Unix domain socket
// If set, this instance was discovered via ssq-mux and supports
// full bidirectional terminal access
MuxSocketPath string
// MuxEnabled indicates whether this instance supports mux protocol
MuxEnabled bool
// SourceTerminal identifies the source (e.g., "IntelliJ", "Terminal", "tmux")
SourceTerminal string
}
ExternalInstanceMetadata contains metadata for externally discovered Claude instances
type ExternalItem ¶ added in v1.35.0
type ExternalItem struct {
ExternalID string
Title string
Description string
Labels []string
Priority int // 1-5, derived from labels
URL string
// State is the external item's raw state string (e.g. GitHub issue
// "open"/"closed"). Only populated by plugins that support two-way sync
// (GitHubIssuesPlugin); left at zero value ("") for plugins like
// GitHubPRsPlugin where two-way sync is out of scope.
State string
// IssueUpdatedAt is the external item's own last-modified timestamp (e.g.
// GitHub issue updated_at), used as the loop-prevention watermark
// comparison value. Zero value for plugins that don't populate it.
IssueUpdatedAt time.Time
}
ExternalItem is a platform-agnostic representation of an external issue/ticket.
type ExternalSessionCandidate ¶ added in v1.42.0
type ExternalSessionCandidate struct {
SourceKind ImportSourceKind
Path string
Program string
PID int32
TmuxSession string
// SocketPath is empty for PlainTmux candidates.
SocketPath string
}
ExternalSessionCandidate describes an unmanaged, discovered process/pane eligible for import, before any Instance is constructed for it. It is never persisted — it exists only to carry enough information through the preview/commit/kill pipeline.
func NewCandidateFromDiscovered ¶ added in v1.42.0
func NewCandidateFromDiscovered(ds *mux.DiscoveredSession) ExternalSessionCandidate
NewCandidateFromDiscovered maps an ssq-mux DiscoveredSession into a source-agnostic ExternalSessionCandidate. Pure mapping, no I/O.
type ExternalSessionDiscovery ¶
type ExternalSessionDiscovery struct {
// contains filtered or unexported fields
}
ExternalSessionDiscovery discovers and manages external Claude sessions from ssq-mux multiplexed terminals.
func NewExternalSessionDiscovery ¶
func NewExternalSessionDiscovery() *ExternalSessionDiscovery
NewExternalSessionDiscovery creates a new external session discovery service.
func (*ExternalSessionDiscovery) GetSession ¶
func (e *ExternalSessionDiscovery) GetSession(socketPath string) *Instance
GetSession returns a specific external session by socket path (deprecated - use GetSessionByTmux).
func (*ExternalSessionDiscovery) GetSessionByTmux ¶
func (e *ExternalSessionDiscovery) GetSessionByTmux(tmuxSessionName string) *Instance
GetSessionByTmux returns a specific external session by tmux session name.
func (*ExternalSessionDiscovery) GetSessions ¶
func (e *ExternalSessionDiscovery) GetSessions() []*Instance
GetSessions returns all currently discovered external sessions.
func (*ExternalSessionDiscovery) OnSessionAdded ¶
func (e *ExternalSessionDiscovery) OnSessionAdded(callback func(*Instance))
OnSessionAdded registers a callback for when a new external session is discovered. Multiple callbacks can be registered and will all be invoked.
func (*ExternalSessionDiscovery) OnSessionRemoved ¶
func (e *ExternalSessionDiscovery) OnSessionRemoved(callback func(*Instance))
OnSessionRemoved registers a callback for when an external session is removed. Multiple callbacks can be registered and will all be invoked.
func (*ExternalSessionDiscovery) Start ¶
func (e *ExternalSessionDiscovery) Start(interval time.Duration)
Start begins periodic discovery of external sessions.
func (*ExternalSessionDiscovery) Stop ¶
func (e *ExternalSessionDiscovery) Stop()
Stop stops the discovery service.
type ExternalStreamer ¶
type ExternalStreamer struct {
// contains filtered or unexported fields
}
ExternalStreamer connects to a mux socket and streams terminal output. It handles reconnection and broadcasts output to registered consumers.
func NewExternalStreamer ¶
func NewExternalStreamer(socketPath string, bufferSize int) *ExternalStreamer
NewExternalStreamer creates a new streamer for the given mux socket.
func (*ExternalStreamer) AddConsumer ¶
func (s *ExternalStreamer) AddConsumer(consumer OutputConsumer, catchUp bool) string
AddConsumer registers a callback to receive output data. If catchUp is true, the consumer receives buffered recent output first. Returns a token that must be passed to RemoveConsumer to deregister.
func (*ExternalStreamer) ConsumerCount ¶
func (s *ExternalStreamer) ConsumerCount() int
ConsumerCount returns the number of registered consumers.
func (*ExternalStreamer) GetMetadata ¶
func (s *ExternalStreamer) GetMetadata() *mux.SessionMetadata
GetMetadata returns the session metadata from the mux.
func (*ExternalStreamer) GetRecentOutput ¶
func (s *ExternalStreamer) GetRecentOutput() []byte
GetRecentOutput returns the buffered recent output.
func (*ExternalStreamer) GetSnapshot ¶
func (s *ExternalStreamer) GetSnapshot() ([]byte, error)
GetSnapshot requests a clean screen snapshot from the mux session. This uses tmux capture-pane on the server side to get clean terminal content without ANSI escape sequences, suitable for pattern matching and initial state. The snapshot request is coordinated with the readLoop to avoid race conditions.
func (*ExternalStreamer) IsConnected ¶
func (s *ExternalStreamer) IsConnected() bool
IsConnected returns whether the streamer is currently connected.
func (*ExternalStreamer) RemoveConsumer ¶
func (s *ExternalStreamer) RemoveConsumer(key string)
RemoveConsumer deregisters a consumer by the token returned from AddConsumer.
func (*ExternalStreamer) SendInput ¶
func (s *ExternalStreamer) SendInput(data []byte) error
SendInput sends input data to the mux session.
func (*ExternalStreamer) SendResize ¶
func (s *ExternalStreamer) SendResize(cols, rows uint16) error
SendResize sends a terminal resize command to the mux session.
func (*ExternalStreamer) SocketPath ¶
func (s *ExternalStreamer) SocketPath() string
SocketPath returns the path to the mux socket.
func (*ExternalStreamer) Start ¶
func (s *ExternalStreamer) Start() error
Start connects to the mux socket and begins streaming.
func (*ExternalStreamer) Stop ¶
func (s *ExternalStreamer) Stop()
Stop disconnects and stops the streamer.
type ExternalStreamerManager ¶
type ExternalStreamerManager struct {
// contains filtered or unexported fields
}
ExternalStreamerManager manages multiple external streamers.
func NewExternalStreamerManager ¶
func NewExternalStreamerManager(bufferSize int) *ExternalStreamerManager
NewExternalStreamerManager creates a new streamer manager.
func (*ExternalStreamerManager) Count ¶
func (m *ExternalStreamerManager) Count() int
Count returns the number of active streamers.
func (*ExternalStreamerManager) Get ¶
func (m *ExternalStreamerManager) Get(socketPath string) *ExternalStreamer
Get returns a streamer if it exists.
func (*ExternalStreamerManager) GetOrCreate ¶
func (m *ExternalStreamerManager) GetOrCreate(socketPath string) (*ExternalStreamer, error)
GetOrCreate returns an existing streamer or creates a new one.
func (*ExternalStreamerManager) Remove ¶
func (m *ExternalStreamerManager) Remove(socketPath string)
Remove stops and removes a streamer.
func (*ExternalStreamerManager) StopAll ¶
func (m *ExternalStreamerManager) StopAll()
StopAll stops all streamers.
type ExternalTmuxStreamer ¶
type ExternalTmuxStreamer struct {
// contains filtered or unexported fields
}
ExternalTmuxStreamer provides terminal content streaming for external sessions.
It uses two strategies in priority order:
Control mode (preferred): Starts "tmux -C attach-session -t <name> -r" which provides real-time %output notifications via the tmux control protocol. When an %output event arrives it signals that the pane content has changed, triggering a single capture-pane call to obtain the full terminal snapshot. This eliminates blind polling while preserving the full-snapshot semantic that consumers expect.
Capture-pane polling (fallback): If control mode fails to start (e.g. older tmux, session not found) the streamer falls back to polling capture-pane every 500ms. This is less responsive but universally compatible.
func NewExternalTmuxStreamer ¶
func NewExternalTmuxStreamer(tmuxSessionName string) *ExternalTmuxStreamer
NewExternalTmuxStreamer creates a new tmux-based streamer for an external session.
func (*ExternalTmuxStreamer) AddConsumer ¶
func (s *ExternalTmuxStreamer) AddConsumer(consumer func(content string)) string
AddConsumer registers a callback to receive content updates. The consumer will be called with the full terminal content whenever it changes. Returns a token that must be passed to RemoveConsumer to deregister.
func (*ExternalTmuxStreamer) ConsumerCount ¶
func (s *ExternalTmuxStreamer) ConsumerCount() int
ConsumerCount returns the number of registered consumers.
func (*ExternalTmuxStreamer) GetContent ¶
func (s *ExternalTmuxStreamer) GetContent() string
GetContent returns the current terminal content.
func (*ExternalTmuxStreamer) IsRunning ¶
func (s *ExternalTmuxStreamer) IsRunning() bool
IsRunning returns whether the streamer is currently running.
func (*ExternalTmuxStreamer) RemoveConsumer ¶
func (s *ExternalTmuxStreamer) RemoveConsumer(key string)
RemoveConsumer deregisters a consumer by the token returned from AddConsumer.
func (*ExternalTmuxStreamer) Start ¶
func (s *ExternalTmuxStreamer) Start() error
Start begins streaming the tmux session for content changes. It first attempts to use tmux control mode for event-driven updates. If control mode is unavailable, it falls back to capture-pane polling.
type ExternalTmuxStreamerManager ¶
type ExternalTmuxStreamerManager struct {
// contains filtered or unexported fields
}
ExternalTmuxStreamerManager manages multiple external tmux streamers.
func NewExternalTmuxStreamerManager ¶
func NewExternalTmuxStreamerManager() *ExternalTmuxStreamerManager
NewExternalTmuxStreamerManager creates a new streamer manager.
func (*ExternalTmuxStreamerManager) Count ¶
func (m *ExternalTmuxStreamerManager) Count() int
Count returns the number of active streamers.
func (*ExternalTmuxStreamerManager) Get ¶
func (m *ExternalTmuxStreamerManager) Get(tmuxSessionName string) *ExternalTmuxStreamer
Get returns a streamer if it exists.
func (*ExternalTmuxStreamerManager) GetOrCreate ¶
func (m *ExternalTmuxStreamerManager) GetOrCreate(tmuxSessionName string) (*ExternalTmuxStreamer, error)
GetOrCreate returns an existing streamer or creates a new one.
func (*ExternalTmuxStreamerManager) Remove ¶
func (m *ExternalTmuxStreamerManager) Remove(tmuxSessionName string)
Remove stops and removes a streamer.
func (*ExternalTmuxStreamerManager) StopAll ¶
func (m *ExternalTmuxStreamerManager) StopAll()
StopAll stops all streamers.
type FilesystemContext ¶
type FilesystemContext struct {
// ProjectPath is the root project/repository directory
ProjectPath string `json:"project_path,omitempty"`
// WorkingDir is the current working directory within the project
WorkingDir string `json:"working_dir,omitempty"`
// IsWorktree indicates if this session is using a git worktree
IsWorktree bool `json:"is_worktree,omitempty"`
// MainRepoPath is the parent repository path if this is a worktree
MainRepoPath string `json:"main_repo_path,omitempty"`
// ClonedRepoPath is the path to the cloned repository for external PRs
ClonedRepoPath string `json:"cloned_repo_path,omitempty"`
// ExistingWorktree is the path to an existing worktree being used
ExistingWorktree string `json:"existing_worktree,omitempty"`
// SessionType indicates the type of session workflow
SessionType SessionType `json:"session_type,omitempty"`
}
FilesystemContext represents the filesystem-related context for a session. This includes project paths, working directories, and worktree information.
func (*FilesystemContext) IsEmpty ¶
func (f *FilesystemContext) IsEmpty() bool
IsEmpty returns true if the FilesystemContext has no meaningful data
type ForceReleaseFunc ¶ added in v1.35.0
type ForceReleaseFunc func()
ForceReleaseFunc marks an unconditional-teardown closure: evicts regardless of refcount. No wrapper of ForceRelease exists in this plan — ForceRelease is always called directly with a sessionID. This type exists so that if a future caller wraps ForceRelease into a closure, the return type says so explicitly instead of degrading to a bare func().
type GitContext ¶
type GitContext struct {
// Branch is the current git branch name
Branch string `json:"branch,omitempty"`
// BaseCommitSHA is the commit SHA where this branch diverged from main/master
BaseCommitSHA string `json:"base_commit_sha,omitempty"`
// WorktreeID is a foreign key to the worktrees table (nil if no worktree)
WorktreeID *int64 `json:"worktree_id,omitempty"`
// PRNumber is the pull request number
PRNumber int `json:"pr_number,omitempty"`
// PRURL is the full URL to the pull request
PRURL string `json:"pr_url,omitempty"`
// Owner is the GitHub repository owner/organization
Owner string `json:"owner,omitempty"`
// Repo is the GitHub repository name
Repo string `json:"repo,omitempty"`
// SourceRef is the source branch reference for the PR
SourceRef string `json:"source_ref,omitempty"`
}
GitContext represents the Git-related context for a session. This includes repository information, branch details, and GitHub PR integration.
func (*GitContext) IsEmpty ¶
func (g *GitContext) IsEmpty() bool
IsEmpty returns true if the GitContext has no meaningful data
type GitHubIntegration ¶ added in v1.35.0
type GitHubIntegration struct {
// Repository identity and PR linkage
GitHubPRNumber int
GitHubPRURL string
GitHubOwner string
GitHubRepo string
GitHubSourceRef string
ClonedRepoPath string
MainRepoPath string
IsWorktree bool
GitHubIsFork bool
// PR status fields (populated by PRStatusPoller)
GitHubPRState string
GitHubPRIsDraft bool
GitHubPRPriority string
GitHubApprovedCount int
GitHubChangesReqCount int
GitHubCheckConclusion string
GitHubPRStatusTerminal bool
LastPRStatusCheck time.Time
}
GitHubIntegration groups all GitHub PR / URL integration fields within InstanceSnapshot (CDD Epic 3, Task 3.1a). Access via snap.GitHub.GitHubPRURL etc.
type GitHubIssuesPlugin ¶ added in v1.35.0
type GitHubIssuesPlugin struct{}
GitHubIssuesPlugin fetches backlog items from a GitHub repository's issue tracker.
func NewGitHubIssuesPlugin ¶ added in v1.35.0
func NewGitHubIssuesPlugin() *GitHubIssuesPlugin
NewGitHubIssuesPlugin returns a new GitHubIssuesPlugin.
func (*GitHubIssuesPlugin) CloseIssue ¶ added in v1.41.0
func (g *GitHubIssuesPlugin) CloseIssue(ctx context.Context, config PluginConfig, externalID string, existingLabels []string, closeLabel string) (time.Time, error)
CloseIssue closes a GitHub issue and, if closeLabel is non-empty, merges it into the issue's existing labels (never replacing them — GitHub's labels field on this endpoint fully replaces the array, so existingLabels must be passed in and merged locally; this replace-vs-merge semantic is documented at https://docs.github.com/en/rest/issues/issues#update-an-issue — the `labels` field, when present, sets the issue's full label list). Omitting the `labels` field entirely (rather than sending an empty array) leaves the issue's existing labels untouched, which is why closeLabel == "" skips the field rather than sending `"labels":[]`.
Returns the issue's post-close updated_at from GitHub's own response — the caller uses this (not local wall-clock time) for the ADR-003 loop-prevention watermark, to avoid clock-skew/read-after-write-lag entirely. Returns a zero time (with a nil error) only in the narrow case where the close succeeded (HTTP status < 300) but the response body couldn't be decoded — the close itself must not be treated as failed just because the confirmation body was unparseable.
func (*GitHubIssuesPlugin) Fetch ¶ added in v1.35.0
func (g *GitHubIssuesPlugin) Fetch(ctx context.Context, config PluginConfig, cursor string) ([]ExternalItem, string, error)
Fetch retrieves new and updated GitHub issues since the cursor. The cursor is an ISO 8601 timestamp passed as the `since` query parameter. Returns the updated cursor (the most recent updated_at seen) and the fetched items. If the token field is empty, Fetch returns an empty list and the original cursor.
Fetch is single-page (githubIssuesPerPage per call) — correct for the incremental sync path, where the cursor bounds results to items updated since the last tick. Callers that need the full result set regardless of page size (e.g. PreviewBackwardSyncImpact) must use FetchAll instead; a single Fetch call silently misses older items on repos with more than one page of history, since GitHub sorts by `created` descending by default.
func (*GitHubIssuesPlugin) FetchAll ¶ added in v1.41.0
func (g *GitHubIssuesPlugin) FetchAll(ctx context.Context, config PluginConfig, cursor string) (items []ExternalItem, newCursor string, possiblyIncomplete bool, err error)
FetchAll retrieves every GitHub issue across up to maxPreviewFetchPages pages, aggregating results the way Fetch's single-page call cannot. Used only by PreviewBackwardSyncImpact, which needs to see the true state of all already-imported items rather than just the newest page.
possiblyIncomplete is true if the page cap was hit while the last page fetched was still full — meaning there may be more issues beyond what was returned, and callers must not treat the result as exhaustive.
func (*GitHubIssuesPlugin) MapToBacklogItem ¶ added in v1.35.0
func (g *GitHubIssuesPlugin) MapToBacklogItem(item ExternalItem, sourceID string) BacklogItemData
MapToBacklogItem converts a GitHub ExternalItem to a BacklogItemData.
func (*GitHubIssuesPlugin) PluginID ¶ added in v1.35.0
func (g *GitHubIssuesPlugin) PluginID() string
PluginID returns the unique identifier for this plugin.
func (*GitHubIssuesPlugin) PostIssueComment ¶ added in v1.41.0
func (g *GitHubIssuesPlugin) PostIssueComment(ctx context.Context, config PluginConfig, externalID string, body string) error
PostIssueComment posts a comment on a GitHub issue — used by the forward-sync subscriber to leave a visible trail explaining an automated close, per the "no silent automated action" convention (pitfalls research §7). Returns an error on any non-2xx response; callers treat a comment failure as best-effort (the close itself already succeeded by the time this is called).
type GitHubMetadataView ¶
type GitHubMetadataView struct {
PRNumber int
PRURL string
Owner string
Repo string
SourceRef string
ClonedRepoPath string
}
GitHubMetadataView is a read-only value object for GitHub session metadata. Constructed by Instance.GitHub() from the underlying fields. This is intentionally a value type (not a pointer) for safe concurrent reads.
func (GitHubMetadataView) IsEmpty ¶
func (gh GitHubMetadataView) IsEmpty() bool
IsEmpty returns true if no GitHub metadata is set.
func (GitHubMetadataView) IsGitHubSession ¶
func (gh GitHubMetadataView) IsGitHubSession() bool
IsGitHubSession returns true if owner and repo are both set.
func (GitHubMetadataView) IsPRSession ¶
func (gh GitHubMetadataView) IsPRSession() bool
IsPRSession returns true if this metadata represents a PR-based session.
func (GitHubMetadataView) PRDisplayInfo ¶
func (gh GitHubMetadataView) PRDisplayInfo() string
PRDisplayInfo returns human-readable PR description for UI display. Returns empty string if not a PR session.
func (GitHubMetadataView) RepoFullName ¶
func (gh GitHubMetadataView) RepoFullName() string
RepoFullName returns "owner/repo" format, or empty string if either is missing.
type GitHubPRsPlugin ¶ added in v1.35.0
type GitHubPRsPlugin struct{}
GitHubPRsPlugin fetches open pull requests from a GitHub repository.
func NewGitHubPRsPlugin ¶ added in v1.35.0
func NewGitHubPRsPlugin() *GitHubPRsPlugin
NewGitHubPRsPlugin returns a new GitHubPRsPlugin.
func (*GitHubPRsPlugin) Fetch ¶ added in v1.35.0
func (g *GitHubPRsPlugin) Fetch(ctx context.Context, config PluginConfig, cursor string) ([]ExternalItem, string, error)
Fetch retrieves open pull requests. Cursor is unused (full refresh each time). Returns empty list when token is absent.
func (*GitHubPRsPlugin) MapToBacklogItem ¶ added in v1.35.0
func (g *GitHubPRsPlugin) MapToBacklogItem(item ExternalItem, sourceID string) BacklogItemData
MapToBacklogItem converts a GitHub PR ExternalItem to a BacklogItemData.
func (*GitHubPRsPlugin) PluginID ¶ added in v1.35.0
func (g *GitHubPRsPlugin) PluginID() string
PluginID returns the unique identifier for this plugin.
type GitHubRef ¶
type GitHubRef struct {
Host string // GitHub host, e.g. "github.com" or a GHES hostname; "" means github.com
Owner string
Repo string
Branch string
PRNumber int
Type GitHubRefType
}
GitHubRef represents a parsed GitHub reference.
func ParseGitHubURL ¶
ParseGitHubURL parses a github.com URL and returns the components. Supported formats:
- https://github.com/owner/repo
- https://github.com/owner/repo.git
- https://github.com/owner/repo/tree/branch
- https://github.com/owner/repo/pull/123
- owner/repo (shorthand)
- owner/repo:branch (shorthand with branch)
func ParseGitHubURLWithHosts ¶ added in v1.41.0
ParseGitHubURLWithHosts parses a GitHub URL or shorthand, additionally recognizing URLs against any of the given GitHub Enterprise hostnames (in addition to github.com). Delegates the regex/host matching to the github package's ParseGitHubRefWithHosts, then re-validates Owner/Repo against isTraversalSegment: the github package's own parsing provides no path-traversal protection (its only validation, isValidGitHubName, is used solely for the shorthand path and explicitly permits "." and ".."), so this guard must be re-applied here before Owner/Repo are ever used to build a local filesystem path in GetRepoPath.
func ResolveGitHubInput ¶
ResolveGitHubInput is a convenience function using the default manager.
func ResolveGitHubInputCtx ¶ added in v1.41.0
func ResolveGitHubInputCtx(ctx context.Context, input string) (localPath string, ref *GitHubRef, err error)
ResolveGitHubInputCtx is a convenience function using the default manager, threading ctx down to the underlying git clone/fetch subprocess.
func ResolveGitHubInputCtxWithHosts ¶ added in v1.41.0
func ResolveGitHubInputCtxWithHosts(ctx context.Context, input string, enterpriseHosts []string) (localPath string, ref *GitHubRef, err error)
ResolveGitHubInputCtxWithHosts is a convenience function using the default manager, recognizing URLs against the given GitHub Enterprise hostnames in addition to github.com.
func (*GitHubRef) PRURL ¶ added in v1.42.0
PRURL returns the canonical URL of the PR this ref points at, or "" if PRNumber is not set (e.g. a plain repo or branch ref). This is the single source of truth for GitHub PR URL construction — session_service.go's CreateSession handler and the github_pr_url_backfill.go migration both call this instead of formatting the URL themselves, so a host-normalization or URL-shape fix only has to be made once.
type GitHubRefType ¶
type GitHubRefType int
GitHubRefType indicates what kind of GitHub reference this is.
const ( GitHubRefTypeRepo GitHubRefType = iota GitHubRefTypeBranch GitHubRefTypePR )
type GitManager ¶ added in v1.15.0
type GitManager interface {
HasWorktree() bool
GetWorktree() *git.GitWorktree
SetWorktree(*git.GitWorktree)
GetWorktreePath() string
GetRepoPath() string
GetRepoName() string
GetBranchName() string
GetBaseCommitSHA() string
Setup() error
Cleanup() error
Remove() error
Prune() error
IsDirty() (bool, error)
InvalidateDirtyCache()
CommitChanges(commitMsg string) error
PushChanges(commitMsg string, open bool) error
IsBranchCheckedOut() (bool, error)
OpenBranchURL() error
ComputeDiffIfReady() (stats *git.DiffStats, needsPause bool)
ComputeDiff() *git.DiffStats
UpdateDiffStats()
GetDiffStats() *git.DiffStats
SetDiffStats(*git.DiffStats)
ClearDiffStats()
GetCurrentCommitSHA() (string, error)
PrimeDirtyCacheJitter()
}
GitManager is the interface satisfied by *GitWorktreeManager. It covers all git worktree operations used by Instance and can be implemented by test doubles to avoid requiring a real git repository.
type GitWorktreeData ¶
type GitWorktreeData struct {
RepoPath string `json:"repo_path"`
WorktreePath string `json:"worktree_path"`
SessionName string `json:"session_name"`
BranchName string `json:"branch_name"`
BaseCommitSHA string `json:"base_commit_sha"`
}
GitWorktreeData represents the serializable data of a GitWorktree
type GitWorktreeManager ¶
type GitWorktreeManager struct {
// contains filtered or unexported fields
}
GitWorktreeManager owns the git worktree and diff-stats state that were previously bare fields on Instance.
Instance keeps thin wrapper methods that delegate here. GitWorktreeManager itself has no knowledge of Instance lifecycle; it only manages the worktree and diff operations.
worktree/diffStats are guarded by mu, not by Instance.stateMutex: setup (setupFirstTimeWorktree, called under Instance.startMu) and read-side callers (e.g. GetEffectiveRootDir) don't consistently hold stateMutex, so GitWorktreeManager protects its own fields directly.
func (*GitWorktreeManager) Cleanup ¶
func (gm *GitWorktreeManager) Cleanup() error
Cleanup removes the worktree from the filesystem and git metadata. Returns nil if no worktree is set.
func (*GitWorktreeManager) ClearDiffStats ¶
func (gm *GitWorktreeManager) ClearDiffStats()
ClearDiffStats sets diffStats to nil.
func (*GitWorktreeManager) CommitChanges ¶
func (gm *GitWorktreeManager) CommitChanges(commitMsg string) error
CommitChanges stages all changes and creates a commit.
func (*GitWorktreeManager) ComputeDiff ¶
func (gm *GitWorktreeManager) ComputeDiff() *git.DiffStats
ComputeDiff runs git diff and returns the result without storing it. Returns nil if no worktree is set.
func (*GitWorktreeManager) ComputeDiffIfReady ¶
func (gm *GitWorktreeManager) ComputeDiffIfReady() (stats *git.DiffStats, needsPause bool)
ComputeDiffIfReady checks if the worktree path exists and computes a new diff. Returns (stats, needsPause) where needsPause is true if the worktree directory is missing. This method performs I/O and should be called WITHOUT holding Instance.mu. Returns (nil, false) if no worktree is set.
func (*GitWorktreeManager) GetBaseCommitSHA ¶
func (gm *GitWorktreeManager) GetBaseCommitSHA() string
GetBaseCommitSHA returns the base commit SHA or "" if no worktree.
func (*GitWorktreeManager) GetBranchName ¶
func (gm *GitWorktreeManager) GetBranchName() string
GetBranchName returns the branch name or "" if no worktree.
func (*GitWorktreeManager) GetCurrentCommitSHA ¶
func (gm *GitWorktreeManager) GetCurrentCommitSHA() (string, error)
GetCurrentCommitSHA returns the current HEAD commit SHA for the worktree. Returns an empty string (not an error) if no worktree is set or the repo has no commits yet — this is safe to use in checkpoint creation.
func (*GitWorktreeManager) GetDiffStats ¶
func (gm *GitWorktreeManager) GetDiffStats() *git.DiffStats
GetDiffStats returns the most recently computed diff stats (may be nil).
func (*GitWorktreeManager) GetDirBaseSHA ¶ added in v1.37.0
func (gm *GitWorktreeManager) GetDirBaseSHA() string
GetDirBaseSHA returns the base commit SHA for directory-mode diff computation.
func (*GitWorktreeManager) GetRepoName ¶
func (gm *GitWorktreeManager) GetRepoName() string
GetRepoName returns the repository name or "" if no worktree.
func (*GitWorktreeManager) GetRepoPath ¶
func (gm *GitWorktreeManager) GetRepoPath() string
GetRepoPath returns the repo root path or "" if no worktree.
func (*GitWorktreeManager) GetWorktree ¶
func (gm *GitWorktreeManager) GetWorktree() *git.GitWorktree
GetWorktree returns the underlying GitWorktree (may be nil before Setup).
func (*GitWorktreeManager) GetWorktreePath ¶
func (gm *GitWorktreeManager) GetWorktreePath() string
GetWorktreePath returns the worktree path or "" if no worktree.
func (*GitWorktreeManager) HasWorktree ¶
func (gm *GitWorktreeManager) HasWorktree() bool
HasWorktree reports whether a git worktree has been initialized.
func (*GitWorktreeManager) InvalidateDirtyCache ¶ added in v1.35.0
func (gm *GitWorktreeManager) InvalidateDirtyCache()
InvalidateDirtyCache clears the IsDirty TTL cache so the next call re-runs git status. Call after transitions that may change worktree dirty state (Resume, Stop). No-op if no worktree is set.
func (*GitWorktreeManager) IsBranchCheckedOut ¶
func (gm *GitWorktreeManager) IsBranchCheckedOut() (bool, error)
IsBranchCheckedOut reports whether the branch is currently checked out.
func (*GitWorktreeManager) IsDirty ¶
func (gm *GitWorktreeManager) IsDirty() (bool, error)
IsDirty reports whether the worktree has uncommitted changes.
func (*GitWorktreeManager) OpenBranchURL ¶
func (gm *GitWorktreeManager) OpenBranchURL() error
OpenBranchURL opens the branch URL in the browser.
func (*GitWorktreeManager) PrimeDirtyCacheJitter ¶ added in v1.35.0
func (gm *GitWorktreeManager) PrimeDirtyCacheJitter()
PrimeDirtyCacheJitter staggers the dirty-cache TTL by setting the cache timestamp to a random point in [now-IsDirtyCacheTTL, now). Call this when adding a session to the poller so sessions added in a burst don't all run git-status subprocesses simultaneously when their caches expire.
func (*GitWorktreeManager) Prune ¶
func (gm *GitWorktreeManager) Prune() error
Prune cleans up stale worktree references.
func (*GitWorktreeManager) PushChanges ¶
func (gm *GitWorktreeManager) PushChanges(commitMsg string, open bool) error
PushChanges commits and pushes the worktree branch.
func (*GitWorktreeManager) Remove ¶
func (gm *GitWorktreeManager) Remove() error
Remove removes the worktree from git without pruning.
func (*GitWorktreeManager) SetDiffStats ¶
func (gm *GitWorktreeManager) SetDiffStats(stats *git.DiffStats)
SetDiffStats directly replaces the diff stats (used during deserialization).
func (*GitWorktreeManager) SetDirBaseSHA ¶ added in v1.37.0
func (gm *GitWorktreeManager) SetDirBaseSHA(sha string)
SetDirBaseSHA sets the base commit SHA for directory-mode diff computation.
func (*GitWorktreeManager) SetWorktree ¶
func (gm *GitWorktreeManager) SetWorktree(wt *git.GitWorktree)
SetWorktree replaces the underlying GitWorktree. Used during session start and by tests.
func (*GitWorktreeManager) Setup ¶
func (gm *GitWorktreeManager) Setup() error
Setup prepares the worktree (creates directories, checks out branch, etc.).
func (*GitWorktreeManager) UpdateDiffStats ¶
func (gm *GitWorktreeManager) UpdateDiffStats()
UpdateDiffStats computes a new diff and stores it. Returns nil and clears stats if worktree is not ready.
type HeadlessPoolClient ¶ added in v1.35.0
type HeadlessPoolClient interface {
CallBlocking(ctx context.Context, key headless.FeatureKey, systemPrompt string, userPrompt string, opts headless.CallOptions) (string, float64, error)
}
HeadlessPoolClient is the narrow interface AutonomousDriver needs from the headless pool. *headless.Pool satisfies this interface directly.
type HeadlessTriageResult ¶ added in v1.35.0
type HeadlessTriageResult struct {
Title string `json:"title"`
Summary string `json:"summary"`
// Priority is the LLM's assessed urgency/impact (1=P1 critical ... 5=P5
// trivial), applied to the item once triage completes — see
// applyTriageResultToUpdate (server/services/backlog_service_triage.go).
// Zero (omitted by the model) means "no assessment" and leaves the item's
// existing priority untouched, same convention as AcceptanceCriteria below.
Priority int `json:"priority,omitempty"`
// ItemCategory is the LLM's classification of what kind of work this is —
// one of session.BacklogCategory's values (bugfix/feature/chore/refactor).
// Named distinctly from TriageTask.Category (engineering area: backend/
// frontend/test/infra/docs) to avoid the two colliding in the same JSON
// object the model produces. Empty or invalid leaves the item's existing
// category untouched.
ItemCategory string `json:"item_category,omitempty"`
Suggestions []TriageSuggestion `json:"suggestions"`
Tasks []TriageTask `json:"tasks,omitempty"`
AcceptanceCriteria []AcCriterion `json:"acceptance_criteria,omitempty"`
// Iteration and Feedback are not part of the LLM's JSON output — the caller
// sets them after parsing, from server-tracked state, before persisting.
Iteration int `json:"iteration,omitempty"`
Feedback string `json:"feedback,omitempty"`
}
HeadlessTriageResult is the parsed output from a headless triage LLM call.
func ParseHeadlessTriageResult ¶ added in v1.35.0
func ParseHeadlessTriageResult(raw string) (HeadlessTriageResult, error)
ParseHeadlessTriageResult unmarshals an LLM JSON response into HeadlessTriageResult. Tolerates preamble text before the JSON block (e.g. "Here is the result:\n\n{...}") and stray unrelated braces earlier in the response (e.g. an illustrative snippet).
The triage prompt instructs the model to emit the JSON object last, so candidates are tried from the end of the response backwards — the first candidate (i.e. the last brace-delimited span in raw) that unmarshals cleanly wins. This correctly skips over any earlier decoy object that happens to also be syntactically valid JSON but isn't the real result.
Caps tasks at maxHeadlessTriageTasks.
type HealthCheckResult ¶
type HealthCheckResult struct {
InstanceTitle string
IsHealthy bool
Issues []string
Actions []string
RecoveryAttempted bool
RecoverySuccess bool
}
HealthCheckResult represents the result of a session health check
type HibernationSweeper ¶ added in v1.35.0
type HibernationSweeper struct {
// contains filtered or unexported fields
}
HibernationSweeper periodically checks all sessions and hibernates those that have been idle longer than the configured timeout or that are consuming memory while the system is under pressure.
func NewHibernationSweeper ¶ added in v1.35.0
func NewHibernationSweeper(storage *Storage, cfg *appconfig.Config, reader memory.Reader) *HibernationSweeper
NewHibernationSweeper creates a HibernationSweeper using the given storage, config, and memory reader.
func (*HibernationSweeper) GetCachedRSSMB ¶ added in v1.35.0
func (s *HibernationSweeper) GetCachedRSSMB(sessionUUID string) int64
GetCachedRSSMB returns the last-measured RSS in MB for the given session UUID. Returns 0 if not yet measured or entry expired. Implements MemoryCacheReader.
func (*HibernationSweeper) SetLiveProvider ¶ added in v1.35.0
func (s *HibernationSweeper) SetLiveProvider(p LiveInstancesProvider)
SetLiveProvider wires the fast-path instance source. Call this after constructing the ReviewQueuePoller so that sweep() uses live in-memory instances instead of calling LoadInstances() (which spawns PTY/tmux subprocesses).
func (*HibernationSweeper) Start ¶ added in v1.35.0
func (s *HibernationSweeper) Start(ctx context.Context)
Start runs the periodic sweep loop. Blocks until ctx is cancelled.
func (*HibernationSweeper) SystemMemoryPct ¶ added in v1.35.0
func (s *HibernationSweeper) SystemMemoryPct() (float64, error)
SystemMemoryPct returns the current system memory usage percentage. The result is cached for sysMemCacheTTL to avoid a syscall on every ListSessions request. The mutex is released before calling the reader to avoid holding the lock during /proc I/O. Implements MemoryCacheReader.
type HistoryAdapter ¶ added in v1.35.0
type HistoryAdapter interface {
Name() string
CanHandle(program string) bool
// Import reads this CLI's native format and returns canonical turns.
Import(ctx context.Context, inst *Instance) ([]CanonicalTurn, error)
// Export writes canonical turns into this CLI's native format so it can resume.
Export(ctx context.Context, turns []CanonicalTurn, inst *Instance) error
}
type HistoryEntry ¶
type HistoryEntry struct {
Command Command `json:"command"`
Result *ExecutionResult `json:"result,omitempty"`
Timestamp time.Time `json:"timestamp"`
SessionName string `json:"session_name"`
ExecutionTime time.Duration `json:"execution_time"`
}
HistoryEntry represents a single command execution in history.
type HistoryFileDetector ¶
type HistoryFileDetector struct {
// contains filtered or unexported fields
}
HistoryFileDetector detects Claude JSONL history files for a given process.
func NewHistoryFileDetector ¶
func NewHistoryFileDetector(inspector ProcessFileInspector) *HistoryFileDetector
NewHistoryFileDetector creates a new HistoryFileDetector.
func NewHistoryFileDetectorWithHomeDir ¶ added in v1.12.0
func NewHistoryFileDetectorWithHomeDir(inspector ProcessFileInspector, homeDir string) *HistoryFileDetector
NewHistoryFileDetectorWithHomeDir creates a HistoryFileDetector with a fixed home directory. Use this in tests to avoid writing to the real home dir.
func NewHistoryFileDetectorWithRealInspector ¶
func NewHistoryFileDetectorWithRealInspector() *HistoryFileDetector
NewHistoryFileDetectorWithRealInspector creates a HistoryFileDetector using the real gopsutil-based ProcessInspector on darwin.
func (*HistoryFileDetector) Detect ¶
func (d *HistoryFileDetector) Detect(pid int32) (*HistoryFileInfo, error)
Detect scans the open files of the given PID for Claude JSONL history files. Returns nil, nil if no matching file is found or the process is dead.
func (*HistoryFileDetector) DetectAllByPath ¶ added in v1.42.0
func (d *HistoryFileDetector) DetectAllByPath(projectPath string) ([]HistoryFileInfo, error)
DetectAllByPath scans ~/.claude/projects/<encoded-path>/ and returns every valid conversation JSONL file found, sorted most-recently-modified first. Unlike DetectByPath, it does not silently collapse multiple candidates down to "most recent" — callers that need to detect ambiguity (e.g. import correlation) use this variant; DetectByPath's existing single-result contract and callers (HistoryLinker) are unchanged.
Returns nil, nil if the project directory does not exist or contains no valid conversation files.
func (*HistoryFileDetector) DetectByPath ¶ added in v1.12.0
func (d *HistoryFileDetector) DetectByPath(projectPath string) (*HistoryFileInfo, error)
DetectByPath scans ~/.claude/projects/<encoded-path>/ for the most recently modified conversation JSONL file. It does NOT require a live process, making it suitable for sessions whose tmux session is dead (e.g. after a reboot).
Returns nil, nil if the project directory does not exist or contains no valid conversation files.
func (*HistoryFileDetector) ResolveFilePath ¶ added in v1.42.0
func (d *HistoryFileDetector) ResolveFilePath(projectPath, conversationUUID string) (string, error)
ResolveFilePath reconstructs the on-disk JSONL path for a known (projectPath, conversationUUID) pair, using the same home-dir resolution (including the test override) and path-encoding convention as Detect/DetectByPath/DetectAllByPath. Used by callers that already know the UUID (e.g. a Resolved CorrelationResult from CorrelateCandidate) and need the file path without re-scanning the directory.
type HistoryFileInfo ¶
type HistoryFileInfo struct {
ConversationUUID string
HistoryFilePath string
ProjectDir string
// ModTime is the on-disk mtime of the winning candidate. Populated by
// DetectByPath; left zero by Detect() since the PID-based fast path is
// process ground truth and doesn't need mtime gating.
ModTime time.Time
}
HistoryFileInfo contains information about a detected Claude history file.
type HistoryFileWatcher ¶
type HistoryFileWatcher struct {
// contains filtered or unexported fields
}
HistoryFileWatcher watches ~/.claude/projects/ for new JSONL files.
func NewHistoryFileWatcher ¶
func NewHistoryFileWatcher(watchDir string, callback func(filePath string)) *HistoryFileWatcher
NewHistoryFileWatcher creates a watcher for the given directory. If watchDir is empty, defaults to ~/.claude/projects/.
func (*HistoryFileWatcher) Start ¶
func (w *HistoryFileWatcher) Start(ctx context.Context) error
Start begins watching the directory. It returns without error even if the directory does not exist (degraded mode — polling fallback still works).
func (*HistoryFileWatcher) Stopped ¶ added in v1.35.0
func (w *HistoryFileWatcher) Stopped() <-chan struct{}
Stopped returns a channel that is closed when the watcher goroutine has exited.
type HistoryLinker ¶
type HistoryLinker struct {
// contains filtered or unexported fields
}
HistoryLinker is a background service that correlates running sessions with their Claude JSONL history files. It populates Instance.claudeSession.ConversationUUID and Instance.HistoryFilePath when a conversation file is detected.
Detection uses two complementary paths:
- Polling (every 5 s): scans all running sessions via proc_pidinfo open-files
- fsnotify (fast path): watcher callback fires as soon as a new JSONL is created
Both paths call the same correlateSession helper, which is idempotent. Sessions that repeatedly yield no JSONL file are throttled via exponential backoff to reduce subprocess spawn rate on idle worktrees.
func NewHistoryLinker ¶
func NewHistoryLinker(detector *HistoryFileDetector, watcher *HistoryFileWatcher) *HistoryLinker
NewHistoryLinker creates a HistoryLinker backed by the given detector and watcher. Call SetInstances (or AddInstance) to register sessions before starting.
func NewHistoryLinkerFromRealInspector ¶ added in v1.8.0
func NewHistoryLinkerFromRealInspector() *HistoryLinker
NewHistoryLinkerFromRealInspector creates a HistoryLinker backed by the real gopsutil-based process inspector and an fsnotify watcher on ~/.claude/projects/. This is the production constructor; use NewHistoryLinker in tests.
func (*HistoryLinker) AddInstance ¶
func (hl *HistoryLinker) AddInstance(instance *Instance)
AddInstance adds a single instance for monitoring. A no-op if an instance with the same ID is already registered: callers may legitimately invoke this more than once for the same instance (e.g. wireCallbacks runs on every loadInstancesWithWiring call, including the ListSessions fallback), and an unguarded append would leave duplicate entries in hl.instances, causing correlateSession to run twice per poll tick for the same session.
func (*HistoryLinker) Instances ¶ added in v1.8.0
func (hl *HistoryLinker) Instances() []*Instance
Instances returns a snapshot of the currently monitored instances. Used by shutdown hooks that need the live set (including externally added sessions).
func (*HistoryLinker) RegisterFileCallback ¶ added in v1.35.0
func (hl *HistoryLinker) RegisterFileCallback(cb func(filePath string))
RegisterFileCallback registers a callback that receives the file path whenever a JSONL history file is created or modified. Used to wire the TokenStore into the existing fsnotify infrastructure without creating a second watcher.
func (*HistoryLinker) RemoveInstance ¶
func (hl *HistoryLinker) RemoveInstance(title string)
RemoveInstance stops monitoring the named instance.
func (*HistoryLinker) ScanAll ¶
func (hl *HistoryLinker) ScanAll()
ScanAll triggers an immediate correlation pass over all monitored instances, including those already linked to a UUID. Exported for use by HistoryFileWatcher callbacks and called on startup. Resets backoffs and force-rechecks all sessions so that UUID changes (e.g., /clear creating a new conversation) are detected promptly rather than waiting for the next cold restore.
func (*HistoryLinker) SetInstances ¶
func (hl *HistoryLinker) SetInstances(instances []*Instance)
SetInstances replaces the full instance list.
func (*HistoryLinker) Start ¶
func (hl *HistoryLinker) Start(ctx context.Context)
Start performs an initial synchronous scan and then runs a background poll loop until ctx is cancelled. The fsnotify watcher is also started here so that new JSONL files trigger instant correlation.
type HistoryStatistics ¶
type HistoryStatistics struct {
TotalCommands int
SuccessfulCommands int
FailedCommands int
CancelledCommands int
AverageExecutionTime time.Duration
FirstCommandTime time.Time
LastCommandTime time.Time
}
HistoryStatistics provides summary statistics about command history.
type ImportSourceKind ¶ added in v1.42.0
type ImportSourceKind int
ImportSourceKind identifies which discovery mechanism produced an ExternalSessionCandidate. It determines which correlation inputs are available (socket+PID vs. pane-only) and which kill primitive applies.
PlainTmux is defined now (Phase 1) even though it is not yet produced by any discovery path — that arrives in Phase 2 — so that ExternalSessionCandidate does not need a breaking change when the second source is added.
const ( // MuxDiscovered indicates the candidate came from an ssq-mux-wrapped // session discovered via mux.Discovery.Scan(). MuxDiscovered ImportSourceKind = iota // PlainTmux indicates the candidate came from a plain tmux pane with no // ssq-mux wrapper (Phase 2; unused in Phase 1). PlainTmux )
func (ImportSourceKind) String ¶ added in v1.42.0
func (k ImportSourceKind) String() string
String returns a human-readable name for logging.
type InhibitionEngine ¶ added in v1.42.0
type InhibitionEngine struct {
// contains filtered or unexported fields
}
InhibitionEngine filters and redacts sensitive data from canonical turns.
func NewInhibitionEngine ¶ added in v1.42.0
func NewInhibitionEngine() *InhibitionEngine
NewInhibitionEngine creates a new InhibitionEngine with default security patterns.
func (*InhibitionEngine) SanitizeString ¶ added in v1.42.0
func (ie *InhibitionEngine) SanitizeString(s string) string
SanitizeString redacts any sensitive credentials or secrets found in text.
func (*InhibitionEngine) SanitizeTurn ¶ added in v1.42.0
func (ie *InhibitionEngine) SanitizeTurn(turn CanonicalTurn) CanonicalTurn
SanitizeTurn applies inhibition rules to all blocks within a CanonicalTurn.
func (*InhibitionEngine) SanitizeTurns ¶ added in v1.42.0
func (ie *InhibitionEngine) SanitizeTurns(turns []CanonicalTurn) []CanonicalTurn
SanitizeTurns applies inhibition rules to a slice of CanonicalTurns.
type Instance ¶
type Instance struct {
// ID is the stable, immutable identifier for this instance.
// Set once at creation; never changes even if Title is renamed.
// Falls back to Title when empty for backward compatibility.
ID string
// Title is the title of the instance.
Title string
// UUID is a stable unique identifier for this instance, generated at creation time.
// Unlike Title, UUID does not change when the session is renamed.
UUID string
// Path is the path to the workspace repository root.
Path string
// WorkingDir is the directory within the repository to start in.
WorkingDir string
// Branch is the branch of the instance.
Branch string
// Status is the status of the instance.
Status Status
// Program is the program to run in the instance.
Program string
// Height is the height of the instance.
Height int
// Width is the width of the instance.
Width int
// CreatedAt is the time the instance was created.
CreatedAt time.Time
// UpdatedAt is the time the instance was last updated.
UpdatedAt time.Time
// AutoYes is true if the instance should automatically press enter when prompted.
AutoYes bool
// AutoApprove is true if the launch command should get a per-agent CLI flag
// that skips permission/approval prompts entirely (e.g.
// --dangerously-skip-permissions for Claude). Independent of AutoYes (see
// its doc comment above) -- resolved via yoloFlagFor in instance_tmux.go.
AutoApprove bool
// Prompt is passed as a CLI argument to the program at process-spawn time (buildClaudeCommand),
// so it only takes effect on a truly fresh spawn (claudeSessionID == "", no --resume) or OneShot.
// Use for content that must exist before the process's first turn, e.g. backlog task context.
// See InitialPrompt for the tmux-typed alternative — the two are independent and can both be
// set on the same instance (e.g. Omnibar sends attachments via Prompt, typed text via InitialPrompt).
Prompt string
// InitialPrompt, unlike Prompt, is typed into the tmux pane as simulated keystrokes once the
// session reaches Ready state (session_driver.go) — the only delivery path that works for
// resuming/attaching to an already-running pane, where a CLI arg can't be injected after the
// fact. Replaces the static driverInitialPrompt when non-empty.
InitialPrompt string
// ExistingWorktree is an optional path to an existing worktree to reuse
ExistingWorktree string
// Category is used for organizing sessions into groups
Category string
// Note is a user-authored free-form markdown note attached to this session.
Note string
// IsExpanded indicates whether this session's category is expanded in the UI
IsExpanded bool
// SessionType determines the session workflow (directory, new_worktree, existing_worktree)
SessionType SessionType
// CreateIfMissing: when SessionTypeDirectory, create the directory and run git init
// if the path does not exist. Set from the request's create_if_missing field.
// Not persisted — only relevant during initial session start.
CreateIfMissing bool `json:"-"`
// TmuxPrefix is the prefix to use for tmux session names
TmuxPrefix string
// TmuxServerSocket is the server socket name for tmux isolation (used with -L flag)
// If empty, uses the default tmux server. For complete isolation (e.g., testing),
// set to a unique value like "test" or "teatest_123" to create separate tmux servers.
TmuxServerSocket string
// Tags are multi-valued labels for flexible session organization
// Sessions can have multiple tags and appear in multiple groups simultaneously
// Examples: ["frontend", "urgent", "client-work"]
Tags []string
// AutonomousMode enables autonomous Earpiece mode (crew autonomy).
// When true, the Fixer will inject correction prompts without user confirmation.
// When false (default), the session runs in supervised mode.
AutonomousMode bool `json:"autonomous_mode,omitempty"`
// AutonomousTurn is the current turn during an active autonomous run.
AutonomousTurn int32 `json:"autonomous_turn,omitempty"`
// AutonomousMaxTurns is the configured max turns for the current run.
AutonomousMaxTurns int32 `json:"autonomous_max_turns,omitempty"`
// AutonomousOutcome is the result of the last autonomous run: "", "done", or "stuck".
AutonomousOutcome string `json:"autonomous_outcome,omitempty"`
// GitHub integration fields for PR/URL-based session creation
// GitHubPRNumber is the PR number if this session was created from a PR URL
GitHubPRNumber int `json:"github_pr_number,omitempty"`
// GitHubPRURL is the full URL to the PR on GitHub
GitHubPRURL string `json:"github_pr_url,omitempty"`
// GitHubOwner is the repository owner (user or organization)
GitHubOwner string `json:"github_owner,omitempty"`
// GitHubRepo is the repository name
GitHubRepo string `json:"github_repo,omitempty"`
// GitHubSourceRef is the original URL or reference used to create this session
GitHubSourceRef string `json:"github_source_ref,omitempty"`
// ClonedRepoPath is the path where we cloned the repo (if cloned)
ClonedRepoPath string `json:"cloned_repo_path,omitempty"`
// MainRepoPath is the path to the main repository when Path is a worktree
// Detected automatically via `git rev-parse --git-common-dir`
MainRepoPath string `json:"main_repo_path,omitempty"`
// IsWorktree indicates whether Path is a git worktree (not the main repo)
IsWorktree bool `json:"is_worktree,omitempty"`
// GitHubIsFork is true when the remote repo is a fork (PR lookup uses upstream)
GitHubIsFork bool `json:"github_is_fork,omitempty"`
// PR status fields — populated by PRStatusPoller; not set on session creation
// GitHubPRState is the PR lifecycle state: "open", "closed", "merged"
GitHubPRState string `json:"github_pr_state,omitempty"`
// GitHubPRIsDraft is true when the PR is in draft mode
GitHubPRIsDraft bool `json:"github_pr_is_draft,omitempty"`
// GitHubPRPriority is the derived priority: blocking/ready/pending/draft/complete/no_pr
GitHubPRPriority string `json:"github_pr_priority,omitempty"`
// GitHubApprovedCount is the count of current non-dismissed APPROVED reviews
GitHubApprovedCount int `json:"github_approved_count,omitempty"`
// GitHubChangesReqCount is the count of current non-dismissed CHANGES_REQUESTED reviews
GitHubChangesReqCount int `json:"github_changes_req_count,omitempty"`
// GitHubCheckConclusion is the CI rollup: success/failure/pending/action_required/neutral/""
GitHubCheckConclusion string `json:"github_check_conclusion,omitempty"`
// GitHubPRStatusTerminal is true when the PR is merged/closed and polling should stop
GitHubPRStatusTerminal bool `json:"github_pr_status_terminal,omitempty"`
// LastPRStatusCheck is when the PR status was last successfully fetched
LastPRStatusCheck time.Time `json:"last_pr_status_check,omitempty"`
Checkpoints CheckpointList
ActiveCheckpoint string
ForkedFromID string
// OneShot runs claude in -p mode; the session exits after the task completes.
OneShot bool
// Hidden excludes this session from the default session list and review queue.
// Set true for system/background sessions (triage, validation) that should not
// appear in the user-facing session viewer.
Hidden bool
// ProjectID is the optional project this session belongs to.
ProjectID string
// HistoryFilePath is the path to the Claude conversation JSONL history file.
// Set by HistoryLinker when it correlates this session to an open JSONL file.
HistoryFilePath string
// MCPServerURL is the URL of the stapler-squad HTTP MCP endpoint.
// When set, passed as --mcp-config to claude on session start so no
// settings-file injection is needed.
MCPServerURL string `json:"mcp_server_url,omitempty"`
// AppendSystemPrompt, when non-empty and the program is claude, passes
// --append-system-prompt to inject extra instructions into the system prompt
// without modifying any file on disk. Survives context compaction.
AppendSystemPrompt string `json:"append_system_prompt,omitempty"`
// AllowedTools, when non-empty, passes --allowedTools to claude to pre-approve
// specific tool calls without requiring interactive permission prompts.
// Format: "Bash,Read,Edit" or "Bash(git commit *),Read".
AllowedTools string `json:"allowed_tools,omitempty"`
// PermissionMode, when non-empty, passes --permission-mode to claude.
// Values: "default", "acceptEdits", "bypassPermissions", "auto".
PermissionMode string `json:"permission_mode,omitempty"`
// CreationProgress holds a human-readable progress message during Creating state.
// Set by the async creation goroutine; cleared once the session becomes Active.
// Not persisted to the database — only meaningful in-memory during startup.
CreationProgress string `json:"-"`
// LaunchCommand is the full command passed to tmux on session start, including
// any injected flags (--resume, --mcp-config, -y, initial prompt). Set once on
// first start and updated on restart. Empty for external (mux-discovered) sessions.
LaunchCommand string `json:"launch_command,omitempty"`
// RateLimitAutoResume controls whether the rate-limit manager will automatically
// send recovery input when a rate limit expires. Persisted so the setting survives
// server restarts. Defaults to true (enabled) when zero value.
RateLimitAutoResume *bool `json:"rate_limit_auto_resume,omitempty"`
// PauseReason records why this session was paused. Use PauseReason* constants.
// Empty when session has never been paused.
PauseReason string `json:"pause_reason,omitempty"`
// ExitReason records why this session's pane exited when Status == Crashed
// (e.g. "signal SIGKILL (exit code 137)"). Empty otherwise. Set by
// SessionHealthChecker when it detects a dead pane (session/health.go).
ExitReason string `json:"exit_reason,omitempty"`
// WorkflowID is the UUID of the Workflow that spawned this session.
// Empty for manually-created sessions.
WorkflowID string `json:"workflow_id,omitempty"`
// EnvVars are session-level environment variables injected at tmux session creation.
EnvVars map[string]string `json:"env_vars,omitempty"`
// CLIFlags are additional CLI flags appended to the program launch command.
CLIFlags string `json:"cli_flags,omitempty"`
// ExtraArgs are additional argv elements appended verbatim (never whitespace-split) after
// CLIFlags at launch time. Populated by a selected launcher preset's argv[1:]; see
// buildLaunchCommand in instance_tmux.go for the shell-quoting boundary.
ExtraArgs []string `json:"extra_args,omitempty"`
// ArchivedAt is set when the session is archived. Nil means not archived.
ArchivedAt *time.Time `json:"archived_at,omitempty"`
// ReviewState holds all review queue and terminal activity timestamps.
// Fields are embedded (promoted) so external code can still access inst.LastViewed etc.
// Protected by mu (via sendSyncErr / Snapshot).
ReviewState
// Instance type and management metadata
// InstanceType indicates whether this is a squad-managed or external instance
InstanceType InstanceType
// IsManaged is true if this is a squad-managed session (backward compatible helper)
IsManaged bool
// ExternalMetadata contains additional information for externally discovered instances
ExternalMetadata *ExternalInstanceMetadata
// Permissions defines what operations are allowed on this instance
Permissions InstancePermissions
// Artifacts holds structured artifacts extracted from the session's JSONL history.
// Populated asynchronously by ArtifactExtractor. Protected by mu.
Artifacts *artifacts.SessionArtifactsBlob
// contains filtered or unexported fields
}
Instance is a running instance of claude code.
func CreateManagedInstance ¶ added in v1.42.0
func CreateManagedInstance(ctx context.Context, params CreateManagedInstanceParams) (*Instance, error)
CreateManagedInstance resolves path/session-type concerns, constructs a real *Instance via NewInstance, registers it in the live-handle registry (if provided), and persists it via Storage.AddInstance. It deliberately does NOT call instance.Start() -- starting tmux/the underlying process is left to the caller, which may want to do so asynchronously (as SessionService.CreateSession's handler does) or under additional preconditions (as CommitImportExternalSession does, e.g. after suspending the original process).
On any failure after a successful Registry.Register, the registration is rolled back via Registry.ForceRelease before returning the error, so no phantom live-handle entry is left behind.
func FromInstanceData ¶
func FromInstanceData(data InstanceData) (*Instance, error)
FromInstanceData creates a new Instance from serialized data FromInstanceData reconstructs an *Instance from persisted data, starting it synchronously (hot-attaching to an already-live tmux session or cold-restoring one) before returning. Use for on-demand single-instance loads (e.g. Registry.Acquire) where the caller needs a ready instance immediately.
Bulk startup loads should use fromInstanceData(data, true) via LoadInstances instead — starting every instance synchronously here is what made server startup block on restoring all sessions (including cold-relaunching every dead one) before the HTTP server could bind. See server/dependencies.go's "Step 6" background goroutine, which already exists to start un-started instances asynchronously once the deferred path skips Start() here.
func NewInstance ¶
func NewInstance(opts InstanceOptions) (*Instance, error)
func NewInstanceWithCleanup ¶
func NewInstanceWithCleanup(opts InstanceOptions) (*Instance, tmux.CleanupFunc, error)
NewInstanceWithCleanup creates a new Instance and returns it along with a cleanup function. Usage: instance, cleanup, err := NewInstanceWithCleanup(opts); if err == nil { defer cleanup() }
func SessionToInstance ¶
SessionToInstance converts a Session back to the legacy Instance type. This adapter enables interoperability during the migration period. Note: Some Session features (like CloudContext) don't have Instance equivalents.
func (*Instance) AddShellInMemory ¶ added in v1.35.0
AddShellInMemory registers a pre-built Shell directly into the in-memory registry without spawning a tmux process. Used by ReconcileShells and by tests that need to inject shells into an Instance without going through the full SpawnShell / tmux path.
func (*Instance) AddTag ¶
AddTag adds a tag to the instance. Delegates to TagManager.Add. Returns ErrTagTooLong if the tag exceeds MaxTagLength, or ErrDuplicateTag if it already exists.
func (*Instance) Approve ¶
Approve transitions the instance to Active (approval granted). Returns an error if the current state does not allow this transition.
func (*Instance) ArchiveWithStop ¶ added in v1.41.0
ArchiveWithStop sets ArchivedAt and transitions the instance to Stopped, in a single actor command. ArchiveSession previously only set ArchivedAt, which let a session sit with ArchivedAt set but Status still Active/Paused/Hibernated — archiving is meant to mean "this session is done," so the two must move together.
func (*Instance) CDPDisplayEnv ¶ added in v1.35.0
CDPDisplayEnv returns the extra environment variable strings to inject into the tmux session for CDP:
- "CDP_PORT=<N>" — the allocated CDP debugging port
- "PATH=<wrapperDir>:<original PATH>" — prepends the wrapper script dir so Chrome launcher scripts resolve to our wrappers, not the real binary
Returns nil if CDP is unavailable or if Allocate has not been called yet.
func (*Instance) CDPManager ¶ added in v1.35.0
func (i *Instance) CDPManager() CDPStreamManager
CDPManager returns the CDPStreamManager for this instance. Always non-nil after NewInstance() — returns a no-op manager when Chrome is unavailable.
func (*Instance) CaptureCurrentState ¶
CaptureCurrentState records the pane's current working directory into WorkingDir. Called during graceful shutdown so cold restore can restart in the right directory. No-op if the session is not started, paused, or the tmux session is dead.
For a worktree session, a captured path outside the worktree is refused rather than persisted (BUG-033): live-confirmed on an autonomous backlog session whose own isolated worktree was created successfully and never touched, while its agent ran real work — two feature commits and a branch checkout — directly in the shared parent repo checkout instead, apparently after `cd`-ing there mid-task. resolveStartPath already had a read-side backstop against a stale out-of-worktree WorkingDir, but that guard only fires when i.gitManager.HasWorktree() happens to already be true at the moment a session (re)starts — not guaranteed on every restart ordering — so a bad path could still be captured here, persisted, and later used unguarded. Gating the write itself closes the gap at its source instead of only defending against it on read.
func (*Instance) CapturePaneContent ¶
CapturePaneContent captures the current visible tmux pane content. This is a simple wrapper around TmuxSession.CapturePaneContent() for compatibility with the terminal WebSocket handlers.
func (*Instance) CapturePaneContentPriority ¶ added in v1.44.0
CapturePaneContentPriority captures the current visible tmux pane content via the resync exec-gate fast lane (Epic 4.2) when the instance is tmux-backed, falling back to the plain CapturePaneContent() call for non-tmux-backed instances (there is no fast lane to reach there, and the plain call is a correct, if unoptimized, behavior).
func (*Instance) CapturePaneContentRaw ¶
CapturePaneContentRaw captures pane content with ANSI codes preserved (no line joining). Essential for hybrid streaming where cursor positioning codes must be preserved.
func (*Instance) CleanupWorktree ¶
CleanupWorktree removes the git worktree, keeping session intact.
func (*Instance) ClearConversationState ¶ added in v1.35.0
func (i *Instance) ClearConversationState()
ClearConversationState removes the stored Claude conversation UUID and history file path so that the next Resume starts a fresh conversation rather than attempting --resume with a potentially stale or path-mismatched UUID.
func (*Instance) ClosePR ¶
ClosePR closes the PR without merging Returns an error if this is not a PR session or if the GitHub API call fails
func (*Instance) CreateCheckpoint ¶
func (i *Instance) CreateCheckpoint(label string, scrollbackSeq uint64) (*Checkpoint, error)
CreateCheckpoint captures a named state bookmark for this session. scrollbackSeq should be the current scrollback high-water mark (from ScrollbackManager); pass 0 if the caller does not have access to scrollback state. Thread-safe: routed through the actor mailbox. Returns an error if the instance is not started.
func (*Instance) CurrentBranch ¶ added in v1.35.0
CurrentBranch returns the branch the session is currently on. For worktree sessions, it returns the stored Branch field (set at creation and on worktree changes). For directory sessions, Branch is never stored, so it reads the branch live from the working directory via git. Returns "" if the branch cannot be determined.
func (*Instance) DeleteShell ¶ added in v1.35.0
DeleteShell stops a shell (if running), waits for active handlers to drain, then removes it from memory and the database.
func (*Instance) Deny ¶
Deny transitions the instance to Paused (approval denied). Returns an error if the current state does not allow this transition.
func (*Instance) Destroy ¶
Destroy completely destroys the instance - both tmux session and worktree. Fires EventStopped unconditionally (even if the instance was never started) so listeners tracking "is this session now gone" — e.g. BacklogLifecycleListener's ItemSession.EndedAt bookkeeping — see every deliberate stop, not just natural exits.
func (*Instance) DetectAndPopulateWorktreeInfo ¶
DetectAndPopulateWorktreeInfo detects if the instance path is a worktree and populates the IsWorktree, MainRepoPath, GitHubOwner, and GitHubRepo fields. NOTE: This method writes to GitHub fields (i.GitHubOwner, i.GitHubRepo) directly. A future pass could route writes through a setter method for encapsulation. This is useful for sessions created from existing worktrees where we want to display the actual repository information in the UI.
IMPORTANT: For sessions with git worktrees, we check BOTH paths: 1. The worktree path (gitWorktree.GetWorktreePath()) - to detect IsWorktree and MainRepoPath 2. The original path (i.Path) - as fallback for GitHub owner/repo if worktree detection fails
This is necessary because: - i.Path is the main repository path (e.g., ~/Documents/personal-wiki) - gitWorktree.GetWorktreePath() is the actual worktree (e.g., ~/.stapler-squad/worktrees/...) - The main repo has .git as a directory; the worktree has .git as a file pointing to the main repo
func (*Instance) FireLifecycleEventForTest ¶ added in v1.35.0
func (i *Instance) FireLifecycleEventForTest(event LifecycleEvent, reason string)
FireLifecycleEventForTest is the exported version of fireLifecycleEvent, used exclusively in cross-package tests that need to simulate an unexpected exit.
func (*Instance) ForceStatus ¶ added in v1.35.0
ForceStatus sets the instance status directly without state machine validation. Only call from error recovery paths where the normal transition would itself fail (e.g. the async-creation goroutine cannot cleanly call Stop() because the session was never fully started). Callers must hold no locks.
Routes through the actor mailbox (sendCtx) rather than taking i.mu directly: ForceStatus is invoked from ad hoc goroutines outside the actor (e.g. the async CreateSession goroutine in SessionService), not from inside an actor command. Funneling through sendCtx serializes this write with the actor's command loop when the instance is actor-owned (LiveInstance), and falls back to running synchronously in-place when it isn't (e.g. tests constructing a bare *Instance).
The write (loadStatus) and the buildSnapshot read are done under the SAME i.mu.Lock()/Unlock() critical section (not lock-write-then-unlock-then-read, which is what this used to do). buildSnapshot reads every mutable field, including ones mutated directly under i.mu by legacy setters (MarkViewed, SetLastMeaningfulOutput, MarkUserResponded, MarkAcknowledged, RecoverFromStopped) that bypass the actor entirely and run on arbitrary caller goroutines. Calling buildSnapshot() after releasing the lock left a window where one of those setters could mutate fields concurrently with this unguarded read — caught by -race via a concurrent MarkViewed()/ ForceStatus() pairing during CreateSession. See runActor's doc comment in actor.go for the matching fix on the read side.
func (*Instance) ForkFromCheckpoint ¶
func (i *Instance) ForkFromCheckpoint(checkpointID, newTitle string, configDir string) (*Instance, error)
ForkFromCheckpoint creates a new, unstarted Instance that is an independent branch of i, seeded from the state captured at the checkpoint identified by checkpointID.
func (*Instance) GeneratePRContextPrompt ¶
GeneratePRContextPrompt generates a context prompt for Claude based on PR information This can be used to initialize a Claude Code session with comprehensive PR context Returns an error if this is not a PR session or if the GitHub API call fails
func (*Instance) GetCategoryPath ¶
GetCategoryPath returns the category path as a slice of strings for nested category support Supports "Work/Frontend" syntax by splitting on "/" delimiter
func (*Instance) GetCheckpoints ¶
func (i *Instance) GetCheckpoints() CheckpointList
GetCheckpoints returns a snapshot copy of the checkpoint list, safe for concurrent reads from outside the instance's lock domain.
func (*Instance) GetClaudeConversationUUID ¶ added in v1.35.0
GetClaudeConversationUUID returns the stored Claude conversation UUID, empty if none. Thread-safe: acquires stateMutex read lock.
func (*Instance) GetClaudeSession ¶
func (i *Instance) GetClaudeSession() *ClaudeSessionData
GetClaudeSession returns the Claude session data for this instance. Thread-safe: acquires stateMutex read lock.
func (*Instance) GetController ¶
func (i *Instance) GetController() *ClaudeController
GetController returns the ClaudeController if one exists.
func (*Instance) GetConversationUUID ¶
GetConversationUUID returns the Claude conversation UUID, or "" if not linked. Thread-safe: acquires stateMutex read lock.
func (*Instance) GetCreatedAt ¶ added in v1.1.0
GetCreatedAt returns the time this instance was created. The field is immutable after creation.
func (*Instance) GetCurrentPaneContent ¶
GetCurrentPaneContent captures the current visible tmux pane content. Delegates to processManager.CaptureViewport.
func (*Instance) GetDetectedContext ¶ added in v1.35.0
GetDetectedContext returns the human-readable context string from the terminal detection layer. Returns an empty string when no controller is active or no context is available.
func (*Instance) GetDetectedStatus ¶ added in v1.35.0
func (i *Instance) GetDetectedStatus() detection.DetectedStatus
GetDetectedStatus returns the raw DetectedStatus from the terminal detection layer. Returns detection.StatusUnknown when no controller is active or no status has been detected. Use this for sub-status display; do not use for lifecycle decisions.
func (*Instance) GetDiffStats ¶
GetDiffStats returns the current git diff statistics.
func (*Instance) GetEffectiveRootDir ¶
GetEffectiveRootDir returns the root directory where this session operates. For worktree sessions, this is the worktree path. For directory sessions, this is Path. Used for injecting configuration files (e.g., .claude/settings.local.json).
This returns the worktree path as recorded, without checking whether it still exists on disk — callers that do path-string correlation (e.g. HistoryLinker matching against ~/.claude/projects/<hashed-path>) need the nominal path regardless of whether the directory is currently present. For callers that need to actually read from the filesystem, use Workspace(), which falls back to the repo root when the worktree is gone.
func (*Instance) GetEffectiveStatus ¶
GetEffectiveStatus returns the most accurate status for this instance, combining the lifecycle status with real-time terminal detection when available. Unlike Status (which only reflects lifecycle transitions), this consults the ClaudeController's detected terminal state to surface NeedsApproval, Idle, etc.
func (*Instance) GetEscapeParser ¶ added in v1.35.0
func (i *Instance) GetEscapeParser() *analytics.EscapeCodeParser
GetEscapeParser returns the escape code parser from the session's response stream. Returns nil if the controller is not running or has no response stream.
func (*Instance) GetExitContent ¶ added in v1.15.0
GetExitContent returns the last terminal bytes captured before the PTY exited. Returns nil if the controller is not running or no exit content was recorded.
func (*Instance) GetGitHubRepoFullName ¶
GetGitHubRepoFullName returns "owner/repo" format, or empty string. Delegates to GitHubMetadataView.RepoFullName.
func (*Instance) GetGitWorktree ¶
func (i *Instance) GetGitWorktree() (*git.GitWorktree, error)
GetGitWorktree returns the git worktree for the instance.
func (*Instance) GetLifecycleStatus ¶ added in v1.35.0
GetLifecycleStatus returns the current lifecycle status as a typed Status value.
func (*Instance) GetPRComments ¶
GetPRComments fetches all comments on the PR Returns an error if this is not a PR session or if the GitHub API call fails
func (*Instance) GetPRDiff ¶
GetPRDiff fetches the diff for the PR Returns an error if this is not a PR session or if the GitHub API call fails
func (*Instance) GetPRDisplayInfo ¶
GetPRDisplayInfo returns a human-readable PR description for UI display. Delegates to GitHubMetadataView.PRDisplayInfo.
func (*Instance) GetPTYReader ¶
GetPTYReader returns the PTY file handle for the tmux session.
func (*Instance) GetPaneCursorPosition ¶
GetPaneCursorPosition gets the current cursor position in the tmux pane. Returns cursor X (column) and Y (row) coordinates, both 0-based.
func (*Instance) GetPaneDimensions ¶
GetPaneDimensions gets the current dimensions of the tmux pane. Returns width (columns) and height (rows).
func (*Instance) GetPanePID ¶
GetPanePID returns the PID of the foreground process in the tmux pane. The DoesSessionExist guard is omitted here: TmuxSession.GetPanePID already uses the CM fast path (no subprocess) and falls back to display-message which returns an error if the session is gone. Avoiding a separate list-sessions call per instance keeps this cheap for HistoryLinker.ScanAll, which calls this sequentially (not fanned out) per session anyway -- and the display-message subprocess fallback is itself gated (session/tmux's exec gate), so there's no need for a second guard here even if that ever changes.
func (*Instance) GetPermissions ¶
func (i *Instance) GetPermissions() InstancePermissions
GetPermissions returns the permissions for this instance based on its type.
func (*Instance) GetProgram ¶ added in v1.41.0
GetProgram returns the program this instance runs (e.g. "claude", "aider").
Reads via Snapshot(), not a direct i.Program field access: actor commands (SetProgram/SwitchProgram and friends) write i.Program directly outside i.mu, publishing the change only by atomically storing a fresh snapshot at the end of the mutation — the same reasoning as GetStatus's doc comment. ClaudeController.Start reads this from a different goroutine than whatever last set it, so only the atomic snapshot read is synchronized with that write; a direct field read or an i.mu-guarded read would not be.
func (*Instance) GetRateLimitResetTime ¶ added in v1.35.0
GetRateLimitResetTime returns the time when the rate limit is expected to reset. Returns zero time if no controller is active or no reset time is known.
func (*Instance) GetRateLimitState ¶ added in v1.12.0
GetRateLimitState returns the current rate limit detection state.
func (*Instance) GetReviewItem ¶
func (i *Instance) GetReviewItem() (*ReviewItem, bool)
GetReviewItem returns the review item for this instance if it exists.
func (*Instance) GetReviewQueue ¶
func (i *Instance) GetReviewQueue() *ReviewQueue
GetReviewQueue returns the review queue for this instance.
func (*Instance) GetScrollbackHistory ¶
GetScrollbackHistory captures scrollback history from tmux using line ranges. Uses tmux's native scrollback capabilities instead of stored sequences. startLine and endLine follow tmux conventions: negative numbers go back from current position, use "-" for the start/end of history.
func (*Instance) GetSessionGoal ¶ added in v1.35.0
func (i *Instance) GetSessionGoal() *SessionGoalData
GetSessionGoal returns a thread-safe shallow copy of the current SessionGoalData (nil if not set). A copy is returned so callers cannot mutate the shared struct.
func (*Instance) GetShellExitCh ¶ added in v1.35.0
GetShellExitCh returns a channel that is closed when the shell exits. Multiple callers can select on it without coordination (closed-channel fan-out).
func (*Instance) GetShellPTYReader ¶ added in v1.35.0
GetShellPTYReader returns the PTY for streaming shell output. Lazily attaches if not yet attached (ADR-3: lazy PTY attach).
func (*Instance) GetShellTmuxSessionName ¶ added in v1.41.0
GetShellTmuxSessionName returns the sibling tmux session name for a running shell, for callers outside this package (e.g. the WebSocket streaming handler) that need to target the shell's isolated PTY instead of the parent session's.
func (*Instance) GetStableID ¶ added in v1.14.0
GetStableID returns a stable identifier for this instance. If UUID is set, returns it. Falls back to Title for backward compatibility with sessions that pre-date UUID assignment.
func (*Instance) GetStatus ¶ added in v1.12.0
GetStatus returns the current lifecycle status of this instance as an int. This is intentionally returns int to implement the SessionAccessor interface.
Reads via Snapshot(), not i.mu.RLock(): actor commands (transitionToLocked and friends) write i.Status directly while running inside the actor's own serialization, not under i.mu, and only publish the change by atomically storing a fresh snapshot. An RLock here doesn't synchronize with that write at all — caught by -race via a concurrent GetStatus() poll during Start(). Do not call this from within a sendSyncErr/send/sendCtx closure (see Snapshot's reentrancy note).
func (*Instance) GetStatusIconForType ¶
GetStatusIconForType returns the appropriate status icon based on instance type.
func (*Instance) GetStatusManager ¶
func (i *Instance) GetStatusManager() *InstanceStatusManager
GetStatusManager returns the status manager.
func (*Instance) GetTags ¶
GetTags returns a copy of the instance's tags. Delegates to TagManager.All.
func (*Instance) GetTimeSinceLastMeaningfulOutput ¶
GetTimeSinceLastMeaningfulOutput returns how long ago meaningful output was recorded. Fast path: reads the atomic shadow (no lock) once initialised via SyncAtomicTimestamps or UpdateTimestamps. Fallback: Snapshot() when the atomic is zero (before first write, or in tests that set LastMeaningfulOutput directly) — not a fresh i.mu-guarded read, since i.mu doesn't synchronize with actor commands' direct field writes (see GetStatus's doc comment).
func (*Instance) GetTimeSinceLastTerminalUpdate ¶
GetTimeSinceLastTerminalUpdate delegates to ReviewState.TimeSinceLastTerminalUpdate. Falls back to time since creation if no terminal output has been recorded. Reads via Snapshot(), not i.mu.RLock() — see GetTimeSinceLastMeaningfulOutput.
func (*Instance) GetTmuxSession ¶
func (i *Instance) GetTmuxSession() *tmux.TmuxSession
GetTmuxSession returns the underlying tmux session for direct access. Returns nil if the session hasn't been started yet or if the backend is not tmux.
func (*Instance) GetTmuxSessionName ¶ added in v1.15.0
GetTmuxSessionName returns the sanitized tmux session name for reconciliation. Returns empty string for external or uninitialized sessions.
func (*Instance) GetTotalBytesWritten ¶ added in v1.35.0
GetTotalBytesWritten returns the monotonic PTY byte offset from the session's circular buffer. This is the same counter used by Stage 1 analytics so Stage 2 session_seq values remain stable across WebSocket reconnections. Returns 0 if no controller is active or the buffer is unavailable.
func (*Instance) GetVCSInfo ¶
GetVCSInfo returns information about the VCS for this session
func (*Instance) GetWorkingDirectory ¶
GetWorkingDirectory returns the working directory for this instance.
func (*Instance) GitHub ¶
func (i *Instance) GitHub() GitHubMetadataView
GitHub returns a read-only view of the GitHub metadata for this instance.
func (*Instance) HasClaudeSession ¶
HasClaudeSession returns true if this instance has Claude session data. Thread-safe: acquires stateMutex read lock.
func (*Instance) HasGitHubPR ¶ added in v1.35.0
HasGitHubPR reports whether a GitHub PR has been associated with this session. Safe for use from any goroutine.
func (*Instance) HasGitWorktree ¶
HasGitWorktree returns true if the instance has a git worktree.
func (*Instance) HasTag ¶
HasTag returns true if the instance has the specified tag. Delegates to TagManager.Has.
func (*Instance) HasUpdated ¶
HasUpdated reports whether terminal content has changed since the last check. Returns (updated, hasPrompt) and side-effects terminal timestamps on change.
func (*Instance) Hibernate ¶ added in v1.35.0
Hibernate transitions an Active session to Hibernated. It transitions state and dispatches the heavy I/O to a goroutine.
func (*Instance) Hibernated ¶ added in v1.35.0
Hibernated returns true if the instance is hibernated.
func (*Instance) IsActive ¶ added in v1.35.0
IsActive returns true if the instance has a live AI process.
func (*Instance) IsCreating ¶ added in v1.35.0
IsCreating returns true if the instance is in the Creating state.
Reads via Snapshot(), not i.mu.RLock() — see GetStatus's doc comment for why an RLock here doesn't actually synchronize with the actor's status writes.
func (*Instance) IsGitHubSession ¶
IsGitHubSession returns true if this session has GitHub owner and repo set. Delegates to GitHubMetadataView.IsGitHubSession.
func (*Instance) IsHibernated ¶ added in v1.35.0
IsHibernated returns true if the instance has been hibernated (checkpoint written, tmux killed).
func (*Instance) IsPRSession ¶
IsPRSession returns true if this session was created from a GitHub PR URL. Delegates to GitHubMetadataView.IsPRSession.
func (*Instance) IsPaused ¶ added in v1.35.0
IsPaused returns true if the instance is paused (worktree removed, branch preserved).
func (*Instance) IsRateLimitEnabled ¶ added in v1.12.0
IsRateLimitEnabled returns whether rate limit auto-resume is enabled. Returns the persisted RateLimitAutoResume field (default: true when nil).
func (*Instance) IsStopped ¶ added in v1.35.0
IsStopped returns true if the instance is in the terminal Stopped state.
func (*Instance) Kill ¶
Kill terminates the instance and cleans up all resources Kill destroys both tmux session and worktree (legacy method)
func (*Instance) KillExternalSession ¶
KillExternalSession terminates an external mux session by killing its tmux session. This only works for external sessions that were started via ssq-mux with tmux integration. Returns an error if this is not an external instance or lacks tmux session name.
func (*Instance) KillSession ¶
KillSession terminates the tmux session only (leaves worktree intact).
func (*Instance) KillSessionKeepWorktree ¶
KillSessionKeepWorktree terminates tmux session but preserves worktree for recovery scenarios.
func (*Instance) LastMeaningfulOutputTime ¶ added in v1.1.0
LastMeaningfulOutputTime returns the time of the last meaningful terminal output.
Fast path: the atomic shadow (no lock), same as GetTimeSinceLastMeaningfulOutput. Fallback: Snapshot(), not a fresh i.mu-guarded read — i.mu doesn't synchronize with actor commands' direct field writes (see GetStatus's doc comment).
func (*Instance) ListAvailableTargets ¶
func (i *Instance) ListAvailableTargets() (*AvailableTargets, error)
ListAvailableTargets returns available switch targets (branches, bookmarks, worktrees)
func (*Instance) ListShellsInMemory ¶ added in v1.35.0
ListShellsInMemory returns in-memory shells sorted by OrderIndex.
func (*Instance) MarkAcknowledged ¶ added in v1.1.0
func (i *Instance) MarkAcknowledged()
MarkAcknowledged records that the user has acknowledged (dismissed) this session from the review queue.
func (*Instance) MarkCrashed ¶ added in v1.41.0
MarkCrashed kills the stale dead-pane tmux session and transitions the instance from Active to Crashed with exitReason recorded. Called by SessionHealthChecker when it detects an abnormally-exited dead pane (session/health.go) — remain-on-exit keeps the tmux session/pane around as a placeholder after the wrapped program exits, so TmuxAlive() alone never notices. Returns an error (and leaves the instance's Status/ExitReason untouched) if the instance is not Active when the actor executes this command. KillSession() itself still runs regardless of that check (see below) -- a truly-dead pane being killed is harmless even on the rare race where the status changed concurrently between health.go's detection and this call, and the in-actor guard below prevents that race from ever corrupting Status/ExitReason.
KillSession() runs on the CALLER's goroutine, outside the actor mailbox -- matching how the health checker called it directly before this status existed. KillSession() only touches i.pm() (no actor-owned state), so this is safe, and it keeps a slow/hung `tmux kill-session` from blocking every other operation on this instance's actor (writes, resizes, other RPCs) for its duration; only the transition + ExitReason write need the actor.
Fires EventExited on success (mirroring ReviewQueuePoller.reconcileSessions' "reconcile-session-missing" fire) so already-wired listeners -- the sessionExitedPublisher that pushes the new status to WatchSessions clients, and BacklogLifecycleListener (session/backlog_lifecycle.go) that notifies on EventExited/EventStopped -- pick this up without new plumbing. This is how a backlog automation session that crashes surfaces the failure instead of stalling silently. sessionSummaryListener also fires on this (it excludes only the reconciler's spurious "reconcile-session-missing" reason, per ADR-002 "natural exit and explicit stop dispatch identically") -- a crash is a real, confirmed exit (not a spurious signal), so generating a summary for it is consistent with every other genuine exit reason, not a new cost path.
func (*Instance) MarkExitedNormally ¶ added in v1.41.0
MarkExitedNormally kills the stale dead-pane tmux session and transitions the instance from Active to Stopped. Called by SessionHealthChecker when it detects a dead pane whose wrapped program exited normally (code 0, no signal) -- not a crash, so it must not be marked Crashed (see MarkCrashed). KillSession() runs outside the actor -- see MarkCrashed's doc comment. Fires EventExited on success -- see MarkCrashed's doc comment.
func (*Instance) MarkNeedsApproval ¶ added in v1.1.0
MarkNeedsApproval is a no-op: NeedsApproval is no longer a lifecycle state. Approval state is now tracked as sub-status via the detection layer. Deprecated: do not call from new code.
func (*Instance) MarkUserResponded ¶ added in v1.1.0
MarkUserResponded records that the user has responded to this session. Returns the timestamp that was set so callers can persist it without a second lock acquisition.
func (*Instance) MarkViewed ¶ added in v1.1.0
func (i *Instance) MarkViewed()
MarkViewed records that the user has viewed this session.
func (*Instance) MatchesID ¶ added in v1.18.0
MatchesID reports whether id refers to this instance. Accepts the stable UUID, the legacy Title, or the full tmux session name (e.g. "staplersquad_my-session") so that hook notifications sent from inside managed tmux sessions are correctly attributed to their human-readable session.
func (*Instance) MergePR ¶
MergePR merges the PR using the specified merge method method can be: "merge", "squash", or "rebase" Returns an error if this is not a PR session or if the GitHub API call fails
func (*Instance) NeedsReview ¶
NeedsReview returns true if this session is in the review queue.
func (*Instance) PaneExitInfo ¶ added in v1.41.0
PaneExitInfo reports whether the wrapped program's pane has exited (PaneProcessDead), along with its exit code and signal (empty string if none) when available. Used by SessionHealthChecker to distinguish a normal completion (exit code 0, no signal) from a genuine crash. code/signal are zero-valued when dead is false.
func (*Instance) PaneProcessDead ¶ added in v1.37.0
PaneProcessDead reports whether the tmux session is alive (TmuxAlive()==true) but the wrapped program running in the pane has already exited. remain-on-exit keeps the tmux session/pane around as a "Pane is dead (signal N, ...)" placeholder after the wrapped program is killed (e.g. OOM SIGKILL) or crashes, rather than tearing the session down -- so TmuxAlive() alone reports this session as healthy forever. Health checks must consult this in addition to TmuxAlive() to detect that failure mode. Returns false for non-tmux backends (e.g. native process manager), which have no equivalent placeholder state.
func (*Instance) Pause ¶
Pause stops the tmux session and removes the worktree, preserving the branch.
func (*Instance) PostComment ¶
PostComment posts a comment to the PR Returns an error if this is not a PR session or if the GitHub API call fails
func (*Instance) Preview ¶
Preview returns the current visible terminal content. Prefers tmux's own capture-pane (authoritative rendered screen) for tmux-backed instances; falls back to the in-memory PTY buffer from ClaudeController otherwise.
func (*Instance) PreviewContext ¶ added in v1.46.0
PreviewContext is Preview with an external context threaded onto the underlying tmux subprocess call itself (not just the exec-gate wait), so a caller that cancels ctx can kill an already-running capture-pane process rather than only giving up on waiting for it. Used by the SessionDriver polling loop (session_driver.go), whose stop channel needs to interrupt a capture already in flight so StopSessionDriver's bounded join can't stall on a stuck subprocess — see runGatedWith's doc comment in session/tmux/exec_gate.go for why the gate's own timeout doesn't cover this. Falls back to the plain, non-cancellable Preview() for backends that don't expose a context-aware capture (native backend, no active tmux session, or no controller).
func (*Instance) PreviewFullHistory ¶
PreviewFullHistory captures the entire tmux pane output including full scrollback history.
func (*Instance) ReconcileShells ¶ added in v1.35.0
ReconcileShells is called after an Instance is loaded from ent on startup. It queries ent for shells marked "running" and checks whether their sibling tmux sessions still exist. Live sessions are rebuilt in memory (without PTY attach — lazy). Dead sessions are marked stopped in ent.
The map lock is never held during I/O: all subprocess calls and DB writes happen outside any map operation; each final map insert is an independent Store call that holds only the bucket lock for nanoseconds.
func (*Instance) RecoverFromStopped ¶ added in v1.23.1
func (i *Instance) RecoverFromStopped()
RecoverFromStopped resets a stale Stopped status to Creating so the instance can be hot-restored via Start(false). Only call this during startup reconciliation when the tmux session is confirmed alive; it bypasses the state machine intentionally. Deprecated: prefer transitionTo(ctx, Active) on the Stopped→Active path.
func (*Instance) RefreshPRInfo ¶
RefreshPRInfo fetches the latest PR information from GitHub Returns an error if this is not a PR session or if the GitHub API call fails
func (*Instance) RefreshTmuxClient ¶
RefreshTmuxClient forces the tmux client to refresh, triggering a redraw of the process running inside. This is critical after resizing to ensure cursor positions and line wrapping are recalculated for the new dimensions.
func (*Instance) RefreshTmuxClientPriority ¶ added in v1.44.0
RefreshTmuxClientPriority forces the tmux client to refresh via the resync exec-gate fast lane (Epic 4.2) when the instance is tmux-backed, falling back to the plain RefreshTmuxClient() call otherwise. See CapturePaneContentPriority's doc comment for the fallback rationale.
func (*Instance) RegisterLifecycleListener ¶ added in v1.15.0
func (i *Instance) RegisterLifecycleListener(l LifecycleListener)
RegisterLifecycleListener adds a listener that will receive EventStarted, EventExited, and EventStopped notifications for this instance. The listener is called synchronously on the goroutine that fires the event; implementations must return quickly (no long blocking operations).
func (*Instance) RegisterStatusChangeCallback ¶ added in v1.35.0
func (i *Instance) RegisterStatusChangeCallback(fn func(detection.DetectedStatus, string))
RegisterStatusChangeCallback appends fn to the controller's fan-out listener set. Unlike SetStatusChangeCallback, it does not replace existing listeners. Safe to call before or after the controller is started.
func (*Instance) RemoveTag ¶
RemoveTag removes a tag from the instance. Delegates to TagManager.Remove.
func (*Instance) Rename ¶
Rename renames this session. Validates title constraints and updates UpdatedAt.
func (*Instance) RepoName ¶
RepoName returns the name of the git repository. Returns an error if the instance has not been started or has no worktree.
func (*Instance) ResizePTY ¶
ResizePTY resizes the terminal dimensions. This is used when clients resize their terminal windows.
func (*Instance) Restart ¶
Restart restarts the session by killing and recreating the tmux session. The git worktree is preserved during restart. If preserveOutput is true, captures terminal output before killing the session. For Claude sessions, uses --resume flag with the stored session ID.
func (*Instance) RestartShell ¶ added in v1.35.0
RestartShell stops a shell (if running) and relaunches it with the same command and workdir.
func (*Instance) ResumeFromCrash ¶ added in v1.41.0
ResumeFromCrash transitions a Crashed instance back to Active, relaunching the wrapped program. The tmux session was already killed by MarkCrashed, so Start(false) takes the cold-restore path and threads --resume automatically when a conversation UUID is known (see ClaudeCommandBuilder).
func (*Instance) ResumeFromHibernation ¶ added in v1.35.0
ResumeFromHibernation transitions a Hibernated session back to Active. The actual process re-launch happens asynchronously via resumeFromHibernationLocked.
func (*Instance) RunWithResume ¶ added in v1.35.0
RunWithResume spawns a new claude subprocess using --resume <uuid> and -p <message>, waits for completion, and returns the result text. Updates ConversationUUID on success.
func (*Instance) SendInputViaControlMode ¶ added in v1.35.0
SendInputViaControlMode sends raw bytes through the existing control mode connection, avoiding the subprocess spawn overhead and timeout risk of exec.CommandContext.
func (*Instance) SendPrompt ¶
SendPrompt sends a prompt to the tmux session. Delegates to processManager.SendPromptWithEnter.
func (*Instance) SetArchivedAt ¶ added in v1.35.0
SetArchivedAt sets or clears the ArchivedAt timestamp atomically. Pass nil to clear (unarchive).
func (*Instance) SetArchivedAtIfNil ¶ added in v1.35.0
SetArchivedAtIfNil sets ArchivedAt to t only if it is currently nil. Returns true if the value was set (CAS semantics). Now actor-routed.
func (*Instance) SetArchivedAtIfNilAndStop ¶ added in v1.41.0
SetArchivedAtIfNilAndStop is the CAS counterpart of ArchiveWithStop, used by ArchiveSessionByUUID (which must be safe to call unconditionally from a sweep). Returns true if ArchivedAt was set by this call (i.e. it was previously nil). No-ops the status transition if already Stopped.
func (*Instance) SetArtifacts ¶ added in v1.35.0
func (i *Instance) SetArtifacts(blob *artifacts.SessionArtifactsBlob)
SetArtifacts atomically updates the in-memory Artifacts cache.
func (*Instance) SetAutoApprove ¶ added in v1.42.0
SetAutoApprove sets the AutoApprove flag and, if the session is currently Active, restarts it so the flag takes effect immediately (the flag is baked into the launch command at spawn time, like Program -- not re-checked live, unlike AutonomousMode). persist is called before the restart so a crash between setting and restarting doesn't lose the change; the field mutation and persist survive even if the subsequent restart fails (matching SwitchProgram's documented ordering).
The full set->persist->restart sequence runs under restartTriggerMu, the same lock SwitchProgram holds for its own restart sequence, so a program switch and an auto-approve toggle firing near-simultaneously on the same instance serialize instead of both observing Status == Active and double-restarting the tmux session.
func (*Instance) SetAutoYes ¶ added in v1.35.0
SetAutoYes sets the AutoYes flag. Used by daemon.go to opt in automated sessions to non-interactive behaviour.
func (*Instance) SetAutonomousComplete ¶ added in v1.35.0
SetAutonomousComplete clears the autonomous-mode flag and turn counters, and records the outcome ("done" or "stuck") atomically.
func (*Instance) SetAutonomousMode ¶ added in v1.35.0
SetAutonomousMode sets the autonomous mode flag and outcome string atomically. Pass outcome="" to clear it when enabling; the existing value is preserved unless explicitly overwritten by the caller.
func (*Instance) SetAutonomousTurn ¶ added in v1.35.0
SetAutonomousTurn atomically updates the current turn counter and max-turns cap during an active autonomous run.
func (*Instance) SetCategory ¶ added in v1.35.0
SetCategory sets the session category.
func (*Instance) SetClaudeConversationUUID ¶ added in v1.35.0
SetClaudeConversationUUID stores the Claude conversation UUID so it is used in subsequent --resume flags. Fires the claudeSessionIDSavedCallback if set. No-op (including callback) if uuid is unchanged.
func (*Instance) SetClaudeSession ¶
func (i *Instance) SetClaudeSession(sessionData *ClaudeSessionData)
SetClaudeSession sets the Claude session data for this instance. Thread-safe: acquires stateMutex write lock.
func (*Instance) SetClaudeSessionIDSavedCallback ¶ added in v1.35.0
func (i *Instance) SetClaudeSessionIDSavedCallback(fn func())
SetClaudeSessionIDSavedCallback registers a callback that fires when SetClaudeConversationUUID is called. Used by the service layer to trigger a storage save when the session_id is discovered.
func (*Instance) SetCommitStatus ¶ added in v1.45.0
func (i *Instance) SetCommitStatus(state github.CommitStatusState, statusContext, description string) error
SetCommitStatus posts a commit status to the PR's current head commit via the GitHub Statuses API. It re-fetches PR info first so the status always lands on the current HEAD SHA rather than a stale one cached before a force-push or rebase. Returns an error if this is not a PR session or if the GitHub API call fails.
func (*Instance) SetCreationProgress ¶ added in v1.35.0
SetCreationProgress sets the human-readable creation progress message.
func (*Instance) SetDirBaseSHA ¶ added in v1.37.0
SetDirBaseSHA sets the base commit SHA used to compute diff stats for directory-mode sessions (sessions without an isolated git worktree).
func (*Instance) SetExitReason ¶ added in v1.41.0
SetExitReason sets (or, passed "", clears) the reason this session's pane crashed.
func (*Instance) SetGitHubPR ¶ added in v1.35.0
SetGitHubPR atomically sets the GitHub PR URL and PR number discovered after a RunOneShot or PR-discovery poll. Pass prNumber=0 if not yet known.
func (*Instance) SetGitHubPRNumber ¶ added in v1.35.0
SetGitHubPRNumber atomically updates the in-memory GitHubPRNumber field. Replaces the stateMutex-based implementation; now actor-routed so it is serialised with buildSnapshot.
func (*Instance) SetGitWorktree ¶
func (i *Instance) SetGitWorktree(worktree *git.GitWorktree)
SetGitWorktree sets the git worktree for testing purposes.
func (*Instance) SetHibernateReason ¶ added in v1.35.0
SetHibernateReason sets the reason string that will be recorded in the checkpoint. Must be called before Hibernate(). Values: "manual", "idle", "resource_pressure".
func (*Instance) SetHistoryInfo ¶
SetHistoryInfo updates the conversation UUID and history file path. Thread-safe: acquires stateMutex write lock. No-op if the UUID is already set to the same value. Fires the same claudeSessionIDSavedCallback as SetClaudeConversationUUID when the UUID actually changes, so a HistoryLinker-detected UUID is persisted to durable storage immediately rather than waiting on the next incidental full SaveInstances sweep (hibernation sweeper, health check) — a tmux pane killed before that sweep runs would otherwise resume with no conversation UUID to pass to --resume.
func (*Instance) SetLastAddedToQueue ¶ added in v1.35.0
SetLastAddedToQueue records when this session was last added to the review queue.
func (*Instance) SetLastMeaningfulOutput ¶ added in v1.1.0
SetLastMeaningfulOutput sets the time of the last meaningful terminal output.
func (*Instance) SetLastPRStatusCheck ¶ added in v1.35.0
SetLastPRStatusCheck records the time of the most recent PR-status fetch.
func (*Instance) SetMCPServerURL ¶ added in v1.35.0
SetMCPServerURL sets the MCP server URL on this instance.
func (*Instance) SetPauseReason ¶ added in v1.35.0
SetPauseReason sets the reason this session was paused.
func (*Instance) SetPreviewSize ¶
SetPreviewSize sets the detached terminal dimensions for preview rendering.
func (*Instance) SetProgram ¶ added in v1.35.0
SetProgram atomically updates the Program field during program-switch.
func (*Instance) SetRateLimitCallbacks ¶ added in v1.35.0
func (i *Instance) SetRateLimitCallbacks( onDetected func(sessionID string, resetTime time.Time), onRecovery func(sessionID string, success bool, errMsg string), )
SetRateLimitCallbacks registers server-layer callbacks for rate limit events. onDetected is called when a rate limit is detected; onRecovery is called when recovery completes. Both are invoked from goroutines in the ratelimit package. Safe to call before or after the controller is started; callbacks are wired at controller start time via wireRateLimitCallbacks.
func (*Instance) SetRateLimitEnabled ¶ added in v1.12.0
SetRateLimitEnabled enables or disables rate limit auto-resume. The setting is persisted in RateLimitAutoResume so it survives restarts, and is applied immediately to the running controller if one exists.
func (*Instance) SetReviewQueue ¶
func (i *Instance) SetReviewQueue(queue *ReviewQueue)
SetReviewQueue sets the review queue for this instance.
func (*Instance) SetSessionGoalCached ¶ added in v1.35.0
func (i *Instance) SetSessionGoalCached(g *SessionGoalData)
SetSessionGoalCached atomically updates the in-memory sessionGoal cache.
func (*Instance) SetShellRepository ¶ added in v1.35.0
func (i *Instance) SetShellRepository(repo ShellRepository)
SetShellRepository injects the shell persistence backend. Called by Storage after loading or creating an instance. Pass nil to disable persistence (e.g., in tests).
func (*Instance) SetStatusChangeCallback ¶ added in v1.35.0
func (i *Instance) SetStatusChangeCallback(fn func(detection.DetectedStatus, string))
SetStatusChangeCallback registers fn to be called on every terminal status change detected by the ClaudeController. Safe to call before or after the controller is started; the callback is wired at controller start time via wireStatusChangeCallback.
func (*Instance) SetStatusManager ¶
func (i *Instance) SetStatusManager(manager *InstanceStatusManager)
SetStatusManager sets the status manager for idle detection.
func (*Instance) SetTags ¶
SetTags replaces all tags with a new deduplicated set. Delegates to TagManager.Set. Returns ErrTagTooLong on the first tag that exceeds MaxTagLength.
func (*Instance) SetTitle ¶
SetTitle sets the title of the instance. Returns an error if the instance has started. We can't change the title once it's been used for a tmux session etc.
func (*Instance) SetTitleDirect ¶ added in v1.35.0
SetTitleDirect sets the Title field directly without tmux-session constraints. Use only from RPC handlers that have already validated uniqueness and title constraints (e.g. UpdateSession, RenameSession rollback).
func (*Instance) SetTmuxSession ¶
func (i *Instance) SetTmuxSession(session *tmux.TmuxSession)
SetTmuxSession sets the tmux session for testing purposes.
func (*Instance) SetWindowSize ¶
SetWindowSize propagates window size changes to the tmux session. This enables proper terminal resizing in environments like IntelliJ where SIGWINCH doesn't work.
func (*Instance) SetWorkingDir ¶ added in v1.35.0
SetWorkingDir sets the working directory for this session.
func (*Instance) Snapshot ¶ added in v1.35.0
func (i *Instance) Snapshot() *InstanceSnapshot
Snapshot returns the most recently published atomic snapshot of this Instance's mutable fields. The returned pointer is never nil. Callers must not mutate the returned struct.
On the first call for an Instance that bypassed finishInstanceConstruction (e.g. struct literals in tests), the snapshot is built lazily under stateMutex and stored via CAS so concurrent first-callers converge on one value.
func (*Instance) SpawnShell ¶ added in v1.35.0
SpawnShell creates and starts a new shell as an independent sibling tmux session. It persists the shell to the ent repository, registers it in memory, and launches the watchShellExit goroutine.
func (*Instance) Start ¶
Start starts the instance by routing through the actor mailbox. firstTimeSetup is true if this is a new instance. Otherwise, it's one loaded from storage.
func (*Instance) StartControlMode ¶ added in v1.15.0
StartControlMode starts the control mode stream on the underlying tmux session.
func (*Instance) StartController ¶
StartController creates and starts a ClaudeController for this instance. The controller enables automated idle detection and queue management.
func (*Instance) StartWithCleanup ¶
func (i *Instance) StartWithCleanup(firstTimeSetup bool) (tmux.CleanupFunc, error)
StartWithCleanup starts the instance and returns a cleanup function. Usage: cleanup, err := instance.StartWithCleanup(firstTimeSetup); if err == nil { defer cleanup() }
func (*Instance) StopControlMode ¶ added in v1.15.0
StopControlMode stops the control mode stream.
func (*Instance) StopController ¶
func (i *Instance) StopController()
StopController stops and cleans up the ClaudeController for this instance.
func (*Instance) StopShell ¶ added in v1.35.0
StopShell stops a running shell by setting status first (stop-while-streaming guard), then closing the handle.
func (*Instance) SubscribeControlModeUpdates ¶ added in v1.15.0
SubscribeControlModeUpdates returns a subscriber ID and a read-only output channel. Returns a pre-closed channel if the tmux session is not available.
func (*Instance) SwitchProgram ¶ added in v1.37.0
func (i *Instance) SwitchProgram(ctx context.Context, rawProgram string, persist func() error) (changed bool, resolvedProgram string, err error)
SwitchProgram atomically switches this instance's Program to rawProgram (resolving an empty string to the configured default), porting Claude<->Antigravity conversation history when crossing between those two and clearing stale conversation linkage (ClearConversationState) when the switch leaves that family entirely. If persist is non-nil it runs after the field mutation but before an Active-session restart, so callers can make the new program durable even if the subsequent restart fails.
The whole operation runs under a per-instance lock (restartTriggerMu, shared with SetAutoApprove) so a manual program-switch request, an automatic capacity-monitor fallback, and a post-creation auto-approve toggle firing near-simultaneously serialize instead of double-restarting or double-porting history. This is the single implementation shared by the UpdateSession RPC handler and the capacity-monitor auto-fallback path (SessionService.UpdateSessionProgram) so the two entry points can't drift.
changed reports whether the resolved program actually differed from the current one; a no-op skips persist/restart entirely. err is only ever a Restart failure — persist failures are logged, not returned, matching the pre-existing best-effort save semantics.
func (*Instance) SwitchWorkspace ¶
func (i *Instance) SwitchWorkspace(req WorkspaceSwitchRequest) (*WorkspaceSwitchResult, error)
SwitchWorkspace switches the session's workspace according to the request. For directory changes, this is a simple cd operation. For revision/worktree switches, this restarts Claude with --resume to preserve conversation.
func (*Instance) TapEnter ¶
func (i *Instance) TapEnter()
TapEnter sends an enter key press to the tmux session if AutoYes is enabled.
func (*Instance) TmuxAlive ¶
TmuxAlive returns true if the tmux session is alive. This is a sanity check before attaching. TmuxAlive intentionally does not special-case Hibernated or Crashed here (unlike Paused/Stopped): both rely on their tmux session having actually been killed (Hibernate()/MarkCrashed) to make !i.pm().HasSession() true. If that kill ever fails, TmuxAlive() can still report true for either status -- ReviewQueuePoller's reconcileSessions Hibernated-but-alive and Crashed-but-alive cases exist as the safety net for exactly that scenario.
func (*Instance) TmuxSessionExists ¶ added in v1.23.1
TmuxSessionExists reports whether the underlying tmux session is currently alive. Used at startup to reconcile stale Stopped status against live tmux sessions.
func (*Instance) ToInstanceData ¶
func (i *Instance) ToInstanceData() InstanceData
ToInstanceData converts an Instance to its serializable form
Builds a fresh snapshot via sendSyncErr rather than reading the cached Snapshot(): callers like Storage.UpdateInstance/SaveInstancesSync routinely mutate exported fields (Tags, Category, ...) directly and expect the very next ToInstanceData() call to reflect that — Snapshot()'s cache only refreshes when an actor command republishes it, so those direct-mutation callers would see stale data (caught by TestStorage_UpdateInstance / TestStorage_SaveInstancesSync). Routing the build through sendSyncErr gets a fresh buildSnapshot(s.inst) (current field values, actor-mutation or not) while still serializing against concurrent actor commands: with a live actor this blocks until the mailbox delivers it, matching every other actor command's ordering; with no live actor (tests, pre-NewLiveInstance) it runs synchronously on the calling goroutine, same as before. Do not call from within a sendSyncErr/send/sendCtx closure — see actor.go. LaunchCommand is not in the snapshot (set once during Start) and is read directly. gitManager and claudeSession sub-objects have their own synchronisation.
func (*Instance) ToSession ¶
ToSession converts this Instance to the new Session type. This is a convenience method that wraps InstanceToSession.
func (*Instance) UnsubscribeControlModeUpdates ¶ added in v1.15.0
UnsubscribeControlModeUpdates removes a subscriber by ID.
func (*Instance) UpdateDiffStats ¶
UpdateDiffStats updates the git diff statistics for this instance. Performs I/O (git diff) outside the lock, then updates state under the write lock.
func (*Instance) UpdatePRStatus ¶ added in v1.12.0
func (i *Instance) UpdatePRStatus(state, priority, checkConclusion string, approvedCount, changesReqCount int, isDraft, terminal bool) prUpdateResult
UpdatePRStatus atomically updates the PR status fields on this instance. Called by PRStatusPoller on each successful fetch. Returns prUpdateResult indicating whether the priority changed.
func (*Instance) UpdateTerminalTimestamps ¶
UpdateTerminalTimestamps is a coordinator method that bridges ProcessManager (I/O) with ReviewState (timestamp recording). It:
- Calls processManager.FilterBanners/HasMeaningfulContent (I/O-ish, done before touching the actor — same "no I/O inside the command" discipline as the other *Locked helpers)
- Routes the actual field mutation through the actor's send() — this is called from the PTY-read hot path (server/services/session_service.go's StreamTerminal), exactly the "callback that must not block the caller" case send() exists for (see actor.go's doc comment). Routing through the actor instead of i.mu means this mutation is serialized against every other actor command (transitionToLocked et al.), which don't take i.mu either — caught by -race via a concurrent StreamTerminal + StartController flow.
- Delegates to ReviewState.UpdateTimestamps
This method intentionally stays on Instance because it coordinates two sub-managers. The forceUpdate parameter bypasses meaningful content checking for user-initiated interactions.
func (*Instance) VNCDisplayEnv ¶ added in v1.35.0
VNCDisplayEnv returns the DISPLAY environment variable assignment for this session's display, e.g. "DISPLAY=:101" or "DISPLAY=:0". Returns "" if no display is available (VNC unavailable or StartDisplay not yet called).
func (*Instance) VNCManager ¶ added in v1.35.0
func (i *Instance) VNCManager() VNCProcessManager
VNCManager returns the VNCProcessManager for this instance. Always non-nil — returns a no-op manager on unsupported platforms.
func (*Instance) Workspace ¶ added in v1.12.0
Workspace returns where this session is operating. Use this as the single source of truth for path resolution instead of accessing inst.Path directly, which is wrong for worktree sessions.
Falls back to RepoRoot if the worktree path no longer exists on disk (e.g. a paused session's worktree was removed while its branch/metadata persisted) — otherwise filesystem-reading callers like ListFiles would try to read a directory that's gone and surface a bare "directory not found: ." with no indication why.
func (*Instance) WorkspaceKey ¶ added in v1.41.0
WorkspaceKey returns this instance's workspace identity. See the package-level WorkspaceKey function for the derivation rules.
type InstanceAcquirer ¶ added in v1.35.0
type InstanceAcquirer interface {
Acquire(sessionID string) (*LiveInstance, ReleaseFunc, error)
}
InstanceAcquirer is the narrowest interface for callers that only ever call Acquire. WorkspaceService, MCP tool handlers, and most RPC handlers should be typed against this rather than *Registry (Interface Segregation — matches WorkspaceService's existing LiveInstanceFinder convention).
type InstanceContext ¶ added in v1.1.0
type InstanceContext interface {
GetTitle() string
GetStableID() string
GetPTYReader() (*os.File, error)
Preview() (string, error)
LastMeaningfulOutputTime() time.Time
GetCreatedAt() time.Time
SetLastMeaningfulOutput(t time.Time)
GetStatus() int
WriteToPTY(data []byte) (int, error)
GetProgram() string
}
InstanceContext is the narrow interface ClaudeController needs from its owning Instance. Using an interface breaks the bidirectional Instance ↔ ClaudeController dependency.
type InstanceData ¶
type InstanceData struct {
Title string `json:"title"`
UUID string `json:"uuid,omitempty"`
Path string `json:"path"`
WorkingDir string `json:"working_dir"`
Branch string `json:"branch"`
Status Status `json:"status"`
Height int `json:"height"`
Width int `json:"width"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
AutoYes bool `json:"auto_yes"`
AutoApprove bool `json:"auto_approve"`
Prompt string `json:"prompt"`
InitialPrompt string `json:"initial_prompt,omitempty"`
Program string `json:"program"`
ExistingWorktree string `json:"existing_worktree,omitempty"`
Worktree GitWorktreeData `json:"worktree"`
DiffStats DiffStatsData `json:"diff_stats"`
// New fields for session organization and grouping
Category string `json:"category,omitempty"`
Note string `json:"note,omitempty"`
IsExpanded bool `json:"is_expanded,omitempty"`
Tags []string `json:"tags,omitempty"` // Multi-valued tags for flexible organization
// Session type determines the workflow (directory, new_worktree, existing_worktree)
SessionType SessionType `json:"session_type,omitempty"`
// GitHub integration fields for PR/URL-based session creation
GitHubPRNumber int `json:"github_pr_number,omitempty"`
GitHubPRURL string `json:"github_pr_url,omitempty"`
GitHubOwner string `json:"github_owner,omitempty"`
GitHubRepo string `json:"github_repo,omitempty"`
GitHubSourceRef string `json:"github_source_ref,omitempty"`
ClonedRepoPath string `json:"cloned_repo_path,omitempty"`
// Worktree detection fields
MainRepoPath string `json:"main_repo_path,omitempty"` // Path to main repo when this is a worktree
IsWorktree bool `json:"is_worktree,omitempty"` // True if path is a git worktree
GitHubIsFork bool `json:"github_is_fork,omitempty"` // True when remote repo is a fork
// PR status fields — populated by PRStatusPoller
GitHubPRState string `json:"github_pr_state,omitempty"`
GitHubPRIsDraft bool `json:"github_pr_is_draft,omitempty"`
GitHubPRPriority string `json:"github_pr_priority,omitempty"`
GitHubApprovedCount int `json:"github_approved_count,omitempty"`
GitHubChangesReqCount int `json:"github_changes_req_count,omitempty"`
GitHubCheckConclusion string `json:"github_check_conclusion,omitempty"`
GitHubPRStatusTerminal bool `json:"github_pr_status_terminal,omitempty"`
LastPRStatusCheck time.Time `json:"last_pr_status_check,omitempty"`
// Crew autonomy mode — when true, the Fixer injects correction prompts without user confirmation.
AutonomousMode bool `json:"autonomous_mode,omitempty"`
// Claude Code session persistence
ClaudeSession ClaudeSessionData `json:"claude_session,omitempty"`
// Tmux session prefix for isolation
TmuxPrefix string `json:"tmux_prefix,omitempty"`
// Tmux server socket name for isolation (used with tmux -L flag)
TmuxServerSocket string `json:"tmux_server_socket,omitempty"`
// Terminal update timestamps for activity tracking
LastTerminalUpdate time.Time `json:"last_terminal_update,omitempty"`
LastMeaningfulOutput time.Time `json:"last_meaningful_output,omitempty"`
// Content signature for detecting actual terminal changes vs restarts
// This is a SHA256 hash of the terminal content used to prevent false "new activity"
// notifications when app restarts but terminal content hasn't changed
LastOutputSignature string `json:"last_output_signature,omitempty"`
// Review queue spam prevention
LastAddedToQueue time.Time `json:"last_added_to_queue,omitempty"`
// User interaction tracking
// LastViewed tracks when the user last viewed this session (terminal, session details, etc.)
// Used for smarter review queue notifications (don't notify if just viewed)
LastViewed time.Time `json:"last_viewed,omitempty"`
// Review queue snooze tracking
// LastAcknowledged tracks when the user last dismissed this session from review queue
// Sessions acknowledged after their last update won't appear in the queue until they update again
LastAcknowledged time.Time `json:"last_acknowledged,omitempty"`
// Prompt detection and interaction tracking for smart review queue behavior
LastPromptDetected time.Time `json:"last_prompt_detected,omitempty"`
LastPromptSignature string `json:"last_prompt_signature,omitempty"`
LastUserResponse time.Time `json:"last_user_response,omitempty"`
ProcessingGraceUntil time.Time `json:"processing_grace_until,omitempty"`
// Checkpoint metadata for session state bookmarking (session resumption)
Checkpoints CheckpointList `json:"checkpoints,omitempty"`
ActiveCheckpoint string `json:"active_checkpoint,omitempty"`
ForkedFromID string `json:"forked_from_id,omitempty"`
// History file linkage for cold restore
HistoryFilePath string `json:"history_file_path,omitempty"`
// OneShot runs claude in -p mode; session exits after task completes.
OneShot bool `json:"one_shot,omitempty"`
// Hidden excludes this session from the default session list and review queue.
Hidden bool `json:"hidden,omitempty"`
// ProjectID is the optional project this session belongs to.
ProjectID string `json:"project_id,omitempty"`
// LaunchCommand is the full command passed to tmux on session start, including
// any injected flags (--resume, --mcp-config, -y, initial prompt).
LaunchCommand string `json:"launch_command,omitempty"`
// MCPServerURL is the stapler-squad HTTP MCP endpoint passed to claude via
// --mcp-config on session start. Persisted so restarts re-inject the flag.
MCPServerURL string `json:"mcp_server_url,omitempty"`
// PauseReason records why this session was paused.
// Values: "manual", "auto:inactivity", "auto:session_limit", "auto:resource".
// Empty when session has never been paused.
PauseReason string `json:"pause_reason,omitempty"`
// ExitReason records why this session's pane crashed (Status == Crashed).
// Empty otherwise. Set by SessionHealthChecker (session/health.go).
ExitReason string `json:"exit_reason,omitempty"`
// WorkflowID is the UUID of the Workflow that spawned this session.
// Empty for manually-created sessions.
WorkflowID string `json:"workflow_id,omitempty"`
// ArchivedAt is set when the session is archived. Nil means not archived.
ArchivedAt *time.Time `json:"archived_at,omitempty"`
}
InstanceData represents the serializable data of an Instance
func (InstanceData) GetStableID ¶ added in v1.35.0
func (d InstanceData) GetStableID() string
GetStableID mirrors Instance.GetStableID for InstanceData: returns UUID when set, Title otherwise. Used by Registry.AcquireAll and ListInstanceIDs to produce stable per-session keys without constructing live Instance objects.
func (InstanceData) MatchesID ¶ added in v1.35.0
func (d InstanceData) MatchesID(id string) bool
MatchesID reports whether id refers to this InstanceData. Unlike Instance.MatchesID, there is no tmux-name arm because InstanceData has no GetTmuxSessionName (that method requires the live processManager). For tmux-name matching, call Instance.MatchesID.
func (InstanceData) WorkspaceKey ¶ added in v1.41.0
func (d InstanceData) WorkspaceKey() string
WorkspaceKey returns this instance data's workspace identity. See the package-level WorkspaceKey function for the derivation rules.
type InstanceOptions ¶
type InstanceOptions struct {
// Title is the title of the instance.
Title string
// Path is the path to the workspace repository root.
Path string
// WorkingDir is the directory within the repository to start in.
// If empty, defaults to repository root.
WorkingDir string
// Branch is the git branch name to use when creating a new worktree.
// If empty and SessionType is SessionTypeNewWorktree, a branch name is derived from the title.
Branch string
// Program is the program to run in the instance (e.g. "claude", "aider --model ollama_chat/gemma3:1b")
Program string
// If AutoYes is true, automatically accept prompts
AutoYes bool
// AutoApprove mirrors Instance.AutoApprove — see its doc comment.
AutoApprove bool
// Prompt is passed as a CLI argument at process-spawn time — only takes effect on a fresh
// spawn or OneShot. See InitialPrompt for the tmux-typed alternative; the two are independent
// and may both be set (see Instance.Prompt/Instance.InitialPrompt for the full explanation).
Prompt string
// InitialPrompt, when non-empty, is typed into the tmux pane once the session reaches Ready state,
// replacing the static "Please proceed..." fallback. Use for resume/attach flows where a CLI
// arg can no longer be injected.
InitialPrompt string
// ExistingWorktree is an optional path to an existing worktree to reuse
ExistingWorktree string
// Category is used for organizing sessions into groups
Category string
// Note is a user-authored free-form markdown note attached to this session.
Note string
// Tags are multi-valued labels for flexible organization
Tags []string
// SessionType determines the session workflow (directory, new_worktree, existing_worktree)
SessionType SessionType
// TmuxPrefix is the prefix to use for tmux session names (e.g., "staplersquad_")
TmuxPrefix string
// TmuxServerSocket is the server socket name for tmux isolation (used with -L flag)
// If empty, uses the default tmux server. For complete isolation (e.g., testing),
// set to a unique value like "test" or "teatest_123" to create separate tmux servers.
TmuxServerSocket string
// GitHub integration fields for PR/URL-based session creation
GitHubPRNumber int // PR number if created from PR URL
GitHubPRURL string // Full URL to the PR
GitHubOwner string // Repository owner
GitHubRepo string // Repository name
GitHubSourceRef string // Original URL/reference used to create session
ClonedRepoPath string // Path where repo was cloned (if cloned)
// ResumeId is the Claude conversation ID to resume (from history browser).
// When set, the session will start with --resume <id> flag.
ResumeId string
// OneShot runs claude in -p mode; the session exits after the task completes.
OneShot bool
// Hidden excludes the session from the default session list and review queue.
Hidden bool
// ProjectID associates the session with a project.
ProjectID string
// MCPServerURL, when non-empty and the program is claude, passes
// --mcp-config '{"stapler-squad":{"type":"http","url":"<MCPServerURL>"}}' so the
// session can call back into stapler-squad without any file injection.
MCPServerURL string
// AppendSystemPrompt, when non-empty and the program is claude, passes
// --append-system-prompt so extra instructions are injected into the system
// prompt without touching any file on disk.
AppendSystemPrompt string
// AllowedTools pre-approves specific Claude Code tool calls (--allowedTools).
AllowedTools string
// PermissionMode sets Claude Code's permission handling mode (--permission-mode).
PermissionMode string
// CreateIfMissing: when SessionTypeDirectory, create the directory and run git init
// if the path does not exist. Only set when the user has confirmed the action.
CreateIfMissing bool
// AutonomousMode, when true, starts an AutonomousDriver after session creation
// so the session runs to completion without manual steering.
AutonomousMode bool
// WorkflowID is the UUID of the Workflow that spawned this session.
// Set by the scheduler; empty for manually-created sessions.
WorkflowID string
// EnvVars are session-level environment variables injected at tmux session creation time.
EnvVars map[string]string
// CLIFlags are additional CLI flags appended to the program launch command.
CLIFlags string
// ExtraArgs are additional argv elements appended verbatim (never whitespace-split) after
// CLIFlags at launch time.
ExtraArgs []string
}
Options for creating a new instance
type InstancePermissions ¶
type InstancePermissions struct {
// View operations
CanView bool
// Attach to the terminal session
CanAttach bool
// Send commands to the terminal
CanSendCommand bool
// Pause the session (stop tmux, keep worktree)
CanPause bool
// Resume a paused session
CanResume bool
// Destroy the session completely
CanDestroy bool
// Perform git operations (commit, push, worktree management)
CanModifyGit bool
// Add to review queue
CanAddToQueue bool
// RequiresConfirmation maps operation names to whether they need confirmation
// Used for high-risk operations on external instances
RequiresConfirmation map[string]bool
}
InstancePermissions defines what operations are allowed on an instance
func GetExternalPermissions ¶
func GetExternalPermissions(allowAttach bool) InstancePermissions
GetExternalPermissions returns limited permissions for external instances allowAttach controls whether attach operations are permitted (power user mode)
func GetManagedPermissions ¶
func GetManagedPermissions() InstancePermissions
GetManagedPermissions returns full permissions for squad-managed instances
func GetMuxExternalPermissions ¶
func GetMuxExternalPermissions() InstancePermissions
GetMuxExternalPermissions returns permissions for mux-enabled external instances. Mux instances support full bidirectional terminal access and can be destroyed since they're explicitly opted-in by launching through ssq-mux with tmux session.
type InstanceReader ¶ added in v1.35.0
type InstanceReader interface {
// Identity
GetTitle() string
GetStableID() string
// Descriptive metadata
GetWorkingDirectory() string
// GetStatus returns the current lifecycle status as int.
// Deprecated: use GetLifecycleStatus() or the typed predicates below.
GetStatus() int
// GetLifecycleStatus returns the current lifecycle status as a typed Status value.
GetLifecycleStatus() Status
// Typed state predicates — prefer these over comparing GetStatus() against constants.
IsActive() bool
IsPaused() bool
IsHibernated() bool
IsStopped() bool
// Git / diff
GetDiffStats() *git.DiffStats
// Activity timestamps
GetTimeSinceLastMeaningfulOutput() time.Duration
}
InstanceReader exposes a minimal read-only view of an Instance for server-layer code that only needs to observe session state. It is not yet used at every call site (some helpers still take *Instance directly for field access); adopt it incrementally as call sites are converted to use getter methods.
*Instance satisfies this interface automatically. Use it to supply lightweight test doubles without starting a real tmux session.
type InstanceSnapshot ¶ added in v1.35.0
type InstanceSnapshot struct {
// Identity / config
ID string
UUID string
Title string
Path string
WorkingDir string
Branch string
CreatedAt time.Time
UpdatedAt time.Time
Status Status
Program string
Height int
Width int
AutoYes bool
AutoApprove bool
IsExpanded bool
Prompt string
InitialPrompt string
Category string
Note string
SessionType SessionType
TmuxPrefix string
TmuxServerSocket string
Tags []string // defensive deep copy — see buildSnapshot
// Autonomous mode (grouped — access as snap.Autonomous.AutonomousMode)
Autonomous AutonomousModeState
// GitHub PR / URL integration (grouped — access as snap.GitHub.GitHubPRURL)
GitHub GitHubIntegration
// Checkpoints
Checkpoints CheckpointList // defensive deep copy — see buildSnapshot
ActiveCheckpoint string
ForkedFromID string
// Misc config
OneShot bool
Hidden bool
ProjectID string
HistoryFilePath string
MCPServerURL string
AppendSystemPrompt string
AllowedTools string
PermissionMode string
RateLimitAutoResume *bool // copy of pointee — see buildSnapshot
PauseReason string
ExitReason string
WorkflowID string
EnvVars map[string]string // defensive deep copy — see buildSnapshot
CLIFlags string
ArchivedAt *time.Time // copy of pointee — see buildSnapshot
// Review queue / activity state (embedded value — copied by value)
ReviewState
// Instance type and management metadata
InstanceType InstanceType
IsManaged bool
ExternalMetadata *ExternalInstanceMetadata // copy of pointee — see buildSnapshot
Permissions InstancePermissions // RequiresConfirmation map deep-copied
Artifacts *artifacts.SessionArtifactsBlob
}
InstanceSnapshot is a point-in-time, read-safe copy of all mutable Instance fields. Published via Instance.snapshot (atomic.Pointer) inside stateMutex at the end of every mutator so lock-free readers always see consistent state.
Excluded: manager/dependency objects (gitManager, vncManager, cdpManager, processManager, controllerManager, tagManager, shellRepo, historyDetector) and callback registrations (lifecycleListeners, onRateLimitDetected, onStatusChange). Those are behavior, not data; callers needing them go through dedicated accessors or mailbox round-trips (Epic 3).
type InstanceStatusInfo ¶
type InstanceStatusInfo struct {
BasicStatus Status // Creating, Active, Paused, Stopped, Hibernated
ClaudeStatus detection.DetectedStatus // If ClaudeController is active
StatusContext string // Context/details about current status (e.g., error message)
PendingApprovals int // Number of pending approvals
QueuedCommands int // Number of queued commands
LastCommandStatus string // Status of last command
IsControllerActive bool // Whether ClaudeController is running
IdleState detection.IdleStateInfo // NEW: Idle state information
// SubagentCount is the count of background agents/shells/monitors from the
// WaitingForAgent detector; 0 unless ClaudeStatus == detection.StatusWaitingForAgent.
SubagentCount int
}
InstanceStatusInfo provides extended status information for an instance.
func (InstanceStatusInfo) GetColorCode ¶
func (info InstanceStatusInfo) GetColorCode() string
GetColorCode returns a color code for the status (for lipgloss styling).
func (InstanceStatusInfo) GetStatusDescription ¶
func (info InstanceStatusInfo) GetStatusDescription() string
GetStatusDescription returns a human-readable status description.
func (InstanceStatusInfo) GetStatusIcon ¶
func (info InstanceStatusInfo) GetStatusIcon() string
GetStatusIcon returns an icon representing the instance status.
func (InstanceStatusInfo) HasPendingWork ¶
func (info InstanceStatusInfo) HasPendingWork() bool
HasPendingWork returns true if the instance has pending commands or approvals.
func (InstanceStatusInfo) IsWaitingForUser ¶
func (info InstanceStatusInfo) IsWaitingForUser() bool
IsWaitingForUser returns true if the instance is waiting for user input.
func (InstanceStatusInfo) NeedsAttention ¶
func (info InstanceStatusInfo) NeedsAttention() bool
NeedsAttention returns true if the instance requires user attention.
type InstanceStatusManager ¶
type InstanceStatusManager struct {
// contains filtered or unexported fields
}
InstanceStatusManager manages status information for instances.
func NewInstanceStatusManager ¶
func NewInstanceStatusManager() *InstanceStatusManager
NewInstanceStatusManager creates a new status manager.
func (*InstanceStatusManager) GetAllControllers ¶
func (ism *InstanceStatusManager) GetAllControllers() map[string]*ClaudeController
GetAllControllers returns all registered controllers.
func (*InstanceStatusManager) GetController ¶
func (ism *InstanceStatusManager) GetController(instanceTitle string) (*ClaudeController, bool)
GetController retrieves a controller for an instance.
func (*InstanceStatusManager) GetStatus ¶
func (ism *InstanceStatusManager) GetStatus(instance *Instance) InstanceStatusInfo
GetStatus retrieves comprehensive status for an instance.
func (*InstanceStatusManager) RegisterController ¶
func (ism *InstanceStatusManager) RegisterController(instanceTitle string, controller *ClaudeController)
RegisterController registers a controller for an instance.
func (*InstanceStatusManager) UnregisterController ¶
func (ism *InstanceStatusManager) UnregisterController(instanceTitle string)
UnregisterController removes a controller for an instance.
type InstanceStore ¶ added in v1.1.0
type InstanceStore interface {
LoadInstances() ([]*Instance, error)
// ListInstanceData returns raw persisted InstanceData without constructing Instance
// objects or spawning PTY processes. Use this for read-only existence/title checks
// where calling LoadInstances() would create unnecessary side effects.
ListInstanceData() ([]InstanceData, error)
SaveInstances([]*Instance) error
AddInstance(*Instance) error
DeleteInstance(title string) error
UpdateInstanceLastUserResponse(title string, t time.Time) error
UpdateInstanceMetadata(currentTitle string, newTitle, category, note, workingDir *string) error
}
InstanceStore is the minimal interface the server layer needs for session persistence. Defining it here (alongside the concrete Storage) allows test fakes to be built without depending on the full Storage implementation.
type InstanceType ¶
type InstanceType int
InstanceType represents the type of session instance
const ( // InstanceTypeManaged represents a session fully managed by stapler-squad // with complete lifecycle control, git worktrees, and all features InstanceTypeManaged InstanceType = iota // InstanceTypeExternal represents a Claude instance discovered externally // (not created by stapler-squad) with limited interaction capabilities InstanceTypeExternal )
func (InstanceType) String ¶
func (it InstanceType) String() string
type ItemChangePublisher ¶ added in v1.41.0
type ItemChangePublisher interface {
PublishItemChanged(item *BacklogItemData, change BacklogItemChange)
}
ItemChangePublisher publishes a backlog item mutation to interested subscribers (typically the event bus). Implemented outside this package (typically a thin adapter over the event bus, in server/services) since this package cannot import pkg/events directly — pkg/events imports session, so the reverse import would be a cycle. Mirrors the Notifier interface's cross-package adapter pattern (see Notifier, above in backlog_lifecycle.go).
Publish is always best-effort: implementations must never block or panic into the caller, and callers must nil-check before invoking since a publisher may not be wired (e.g. in tests, or a repository that hasn't had SetItemChangePublisher called on it).
type ItemSessionBacklogEntry ¶ added in v1.37.0
type ItemSessionBacklogEntry struct {
SessionUUID string
SessionRole string
ItemID string
ItemTitle string
ItemStatus string
}
ItemSessionBacklogEntry is a lightweight join record linking a tmux session UUID to its parent backlog item's metadata. Returned by GetAllItemSessionsWithBacklogInfo.
type ItemSessionData ¶ added in v1.35.0
type ItemSessionData struct {
ItemID string // BacklogItem UUID
SessionUUID string
SessionRole string
AcSnapshot AcCriteriaJSON
// PipelineModeSnapshot/PipelineModeSnapshotHash freeze the resolved
// PipelineMode slug and its content hash at the moment this session
// first starts — see ItemSessionSummary.PipelineModeSnapshot(Hash).
PipelineModeSnapshot string
PipelineModeSnapshotHash string
TriageResult string
VerificationNotes string // Freeform verification evidence reported via request_review
EstimatedCostUsd float64 // Only set for headless sessions where cost is known at creation time
// ClaimantHostID is the claiming/attaching process's own stable host identifier
// (Config.GetOrCreateClaimantHostID), never anything derived from the session being
// claimed/attached. See ItemSession.claimant_host_id's schema comment for the full
// disambiguation against STAPLER_SQUAD_INSTANCE and session/contexts.go's CloudContext.InstanceID.
ClaimantHostID string
}
ItemSessionData is the input data for creating a new ItemSession.
type ItemSessionSummary ¶ added in v1.37.0
type ItemSessionSummary struct {
ID string
BacklogItemID string
SessionUUID string
Role string
AcSnapshot AcCriteriaJSON
PipelineModeSnapshot string
PipelineModeSnapshotHash string
// BaseCommitSha is the worktree's pre-work HEAD, captured once at spawn —
// the base of the review gate's base..HEAD diff, and by construction always
// already an ancestor of main. Never use it as evidence that this session's
// work shipped; that is LastCommitSha's job. See the ItemSession ent
// schema's field comments for the full BUG-047 rationale.
BaseCommitSha string
// LastCommitSha is the session's current tip commit, refreshed each
// reconciliation tick while the session is active (see
// BacklogLifecycleListener.refreshWorkSessionGitActivity).
LastCommitSha string
LastCommitMessage string
CommitCountSinceSpawn int
StartedAt *time.Time
EndedAt *time.Time
EndReason string // set alongside EndedAt for a headless call; see ItemSession.end_reason schema comment
FailureCapturePath string // absolute path to a durable raw-output capture; see ItemSession.failure_capture_path schema comment
LastCommitAt *time.Time
LastFileTouchAt *time.Time
LastProgressAt *time.Time
CreatedAt time.Time
EstimatedCostUsd float64
TriageResult string // raw JSON stored in triage_result column
TriageResultSummary string // summary field parsed from TriageResult
VerificationNotes string // freeform verification evidence reported via request_review
OverallOutcome string // from linked review_verdict (empty if none)
ReviewVerdict *ReviewVerdictSummary
// ClaimantHostID identifies the physical stapler-squad process/host that claimed or
// attached this session. See ItemSession.claimant_host_id's schema comment for the
// full disambiguation against STAPLER_SQUAD_INSTANCE and CloudContext.InstanceID.
ClaimantHostID string
}
ItemSessionSummary is the domain DTO replacing *ent.ItemSession in Storage returns. Note: item_sessions table has NO status, triage_result_summary, or overall_outcome columns.
- EndedAt == nil means the session is still running
- TriageResultSummary: parsed from the triage_result JSON column
- OverallOutcome: from the review_verdicts table (populated via ReviewVerdict edge)
- ReviewVerdict: eagerly loaded when the query uses WithReviewVerdict()
func MostRecentCompletedWorkSession ¶ added in v1.42.0
func MostRecentCompletedWorkSession(sessions []ItemSessionSummary) *ItemSessionSummary
MostRecentCompletedWorkSession returns the most recent completed (EndedAt != nil) work-role ItemSession from sessions, or nil if none exists. sessions must be ordered oldest-first, as Storage.ListItemSessions returns. Exported: called from server/services at review-verdict-save time to resolve which work session's diff a given verdict is reviewing (feeds the DiffHash computation IsFlakyVerdictFlipFlop consumes), and by the stuck-item reconciler to build IsTestOnlyReworkCycle's per-attempt file lists. Only considers completed sessions — reading a still-in-progress session's commit range risks racing an in-flight write (see validation.md).
func RecordDegradedReviewVerdict ¶ added in v1.38.0
func RecordDegradedReviewVerdict(storage *Storage, itemID string, acSnapshot AcCriteriaJSON, uuidPrefix, summary string) (ItemSessionSummary, error)
RecordDegradedReviewVerdict persists a synthetic UNVERIFIABLE verdict for a review that could not actually be attempted or completed (capability self-check failure, codebase-read timeout/cancellation). Thin wrapper around recordTerminalReviewVerdict that fixes the outcome to UNVERIFIABLE and the session UUID convention (uuidPrefix + a fresh random UUID) shared by every "degraded, not a real failure" call site — see recordTerminalReviewVerdict's doc comment for the full rationale.
type ItemSourceData ¶ added in v1.35.0
type ItemSourceData struct {
ID string
PluginID string
DisplayName string
Config string // JSON, may contain encrypted token
Enabled bool
ForwardSyncEnabled bool
BackwardSyncEnabled bool
ForwardSyncCloseLabel string
TokenConfigured bool
LastSyncedAt *time.Time
CreatedAt time.Time
UpdatedAt time.Time
}
ItemSourceData is the domain model for an external item source.
type ItemSourcePlugin ¶ added in v1.35.0
type ItemSourcePlugin interface {
// PluginID returns the unique identifier for this plugin (e.g., "github_issues").
PluginID() string
// Fetch retrieves new and updated items since the cursor. Returns items and the new cursor.
Fetch(ctx context.Context, config PluginConfig, cursor string) ([]ExternalItem, string, error)
// MapToBacklogItem converts an external item to a BacklogItemData.
MapToBacklogItem(item ExternalItem, sourceID string) BacklogItemData
}
ItemSourcePlugin is the interface all external source integrations must implement.
type ItemSourceUpdate ¶ added in v1.35.0
type ItemSourceUpdate struct {
DisplayName *string
Enabled *bool
ForwardSyncEnabled *bool
BackwardSyncEnabled *bool
ForwardSyncCloseLabel *string
Config *string
}
ItemSourceUpdate carries the mutable fields for UpdateItemSource.
type KillOutcome ¶ added in v1.42.0
type KillOutcome struct {
Status KillOutcomeStatus
Err error
}
KillOutcome is the domain-level result of KillExternalOriginalProcess.
func KillExternalOriginalProcess ¶ added in v1.42.0
func KillExternalOriginalProcess(checker AliveChecker, pid int32, createTimeMs int64, tmuxSession string) KillOutcome
KillExternalOriginalProcess re-verifies pid/createTimeMs are still the same process (guarding against PID reuse in the window between commit and this call) and, if so, resumes it (it was SIGSTOP'd at commit time -- a stopped process ignores signals sent by "tmux kill-session" until it's running again) and kills its tmux session via a throwaway InstanceTypeExternal Instance's KillExternalSession. On success, the caller is responsible for removing the SuspendedProcessRecord -- this function only performs the resume+kill, it does not touch persisted state.
type KillOutcomeStatus ¶ added in v1.42.0
type KillOutcomeStatus int
KillOutcomeStatus enumerates the possible results of ConfirmKillExternalSession. This is a Go domain type, not a proto message -- the RPC handler maps it onto sessionv1.KillStatus.
const ( // KillOutcomeUnspecified is the zero value and is never returned by // KillExternalOriginalProcess. KillOutcomeUnspecified KillOutcomeStatus = iota // KillOutcomeKilled means the tmux session was killed successfully. KillOutcomeKilled // KillOutcomeAlreadyGone means IsAlive re-verification failed (PID reuse // or the process already exited) -- no signal was sent. KillOutcomeAlreadyGone // KillOutcomeFailed means the kill primitive itself (tmux kill-session) // failed. The original process is left SIGSTOP'd; it is never // auto-resumed on this path. KillOutcomeFailed )
type LifecycleEvent ¶ added in v1.15.0
type LifecycleEvent int
LifecycleEvent is a notification type emitted by an Instance when key state transitions occur (e.g., the session starts, or the program exits unexpectedly).
const ( // EventStarted fires at the end of start() when the instance has successfully // transitioned to Running and the controller is up. EventStarted LifecycleEvent = iota // EventExited fires when the underlying program exits unexpectedly (not via an // operator-initiated Kill/Stop). Callers may use this to drive auto-restart logic. EventExited // EventStopped fires when Destroy() tears down the instance via an explicit // operator-initiated Kill/Stop (e.g. the stop_session MCP tool, DeleteSession // RPC, or backlog stale-work remediation). Kept distinct from EventExited so a // future auto-restart listener can still ignore deliberate stops, while // listeners that only care "is this session now gone" (e.g. // BacklogLifecycleListener's ItemSession.EndedAt bookkeeping) can subscribe to // both. EventStopped )
type LifecycleListener ¶ added in v1.15.0
type LifecycleListener interface {
OnLifecycleEvent(event LifecycleEvent, reason string)
}
LifecycleListener is implemented by any component that wants to receive Instance lifecycle notifications. Implementations must be non-blocking; use a goroutine or channel if the handler needs to do significant work.
type LiveInstance ¶ added in v1.35.0
type LiveInstance struct {
*Instance
// contains filtered or unexported fields
}
LiveInstance is the actor-owning handle for a session. It wraps *Instance with lifecycle fields for the actor goroutine (IAC Epic 3). Supported construction paths from outside this package:
- Registry.Acquire(sessionID) — load-or-construct for an existing persisted session
- Registry.Register(inst) — for brand-new sessions in CreateSession (R2.18a)
- NewLiveInstance(inst) — direct wrap when the caller already holds *Instance
The actor goroutine (runActor in actor.go) is started by NewLiveInstance via finishLiveInstanceConstruction and exits when Stop()/stopActor() cancels the ctx.
func NewLiveInstance ¶ added in v1.35.0
func NewLiveInstance(inst *Instance) *LiveInstance
NewLiveInstance wraps an already-constructed *Instance in a LiveInstance and starts its actor goroutine. Use Registry.Acquire or Registry.Register where possible; call this directly only when the caller already holds a freshly- constructed *Instance (e.g. CreateSession, which builds its own via NewInstance and then passes it to Registry.Register).
func (*LiveInstance) Stop ¶ added in v1.35.0
func (l *LiveInstance) Stop()
Stop signals this instance's actor to exit and waits for it to drain. Idempotent: safe to call multiple times; the second and subsequent calls return immediately once the first call's <-done wait completes.
type LiveInstancesProvider ¶ added in v1.35.0
type LiveInstancesProvider interface {
GetInstances() []*Instance
}
LiveInstancesProvider is satisfied by ReviewQueuePoller. It returns the live in-memory instances without constructing new Instance objects or spawning PTY processes. HibernationSweeper uses this as a fast path to avoid LoadInstances().
type LoadOptions ¶
type LoadOptions struct {
// LoadWorktree controls whether git worktree data is loaded
LoadWorktree bool
// LoadDiffStats controls whether diff statistics (added/removed counts) are loaded
LoadDiffStats bool
// LoadDiffContent controls whether full diff content is loaded
// Note: This implies LoadDiffStats=true, as we need counts to interpret content
LoadDiffContent bool
// LoadTags controls whether session tags are loaded
LoadTags bool
// LoadClaudeSession controls whether Claude Code session data is loaded
LoadClaudeSession bool
}
LoadOptions controls what child data is loaded for sessions. This allows selective loading to optimize performance by avoiding unnecessary data retrieval.
func (LoadOptions) WithDiffContent ¶
func (o LoadOptions) WithDiffContent() LoadOptions
WithDiffContent returns a copy of options with diff content loading enabled.
func (LoadOptions) WithTags ¶
func (o LoadOptions) WithTags() LoadOptions
WithTags returns a copy of options with tag loading enabled.
func (LoadOptions) WithoutDiffContent ¶
func (o LoadOptions) WithoutDiffContent() LoadOptions
WithoutDiffContent returns a copy of options with diff content loading disabled.
func (LoadOptions) WithoutTags ¶
func (o LoadOptions) WithoutTags() LoadOptions
WithoutTags returns a copy of options with tag loading disabled.
type Locked ¶ added in v1.35.0
type Locked[T any] struct { // contains filtered or unexported fields }
Locked bundles a value T with a RWMutex, enforcing lock discipline by only exposing the value through Read/Write callbacks.
This is the Go equivalent of Rust's RwLock<T>: the data and its lock are inseparable, making it structurally impossible to access the value without correct lock discipline. Instead of a mutex sitting next to a field (which the compiler cannot enforce is held on access), callers receive or mutate the value only through Read or Write.
var listeners Locked[[]StatusChangeListener]
// Add a listener — write lock taken automatically
listeners.Write(func(ls *[]StatusChangeListener) {
*ls = append(*ls, fn)
})
// Read all listeners — read lock taken automatically
var snapshot []StatusChangeListener
listeners.Read(func(ls []StatusChangeListener) {
snapshot = append(snapshot, ls...)
})
type MemoryCacheReader ¶ added in v1.35.0
type MemoryCacheReader interface {
GetCachedRSSMB(sessionUUID string) int64
SystemMemoryPct() (float64, error)
}
MemoryCacheReader is implemented by HibernationSweeper so SessionService can read cached RSS values without importing the sweeper concretely.
type MigrationOptions ¶
type MigrationOptions struct {
// JSONPath is the path to the existing JSON state file
JSONPath string
// SQLitePath is the path where the SQLite database will be created
SQLitePath string
// BackupPath is the path where the JSON backup will be saved
BackupPath string
// ForceOverwrite allows overwriting existing SQLite database
ForceOverwrite bool
// DryRun performs validation without actually migrating
DryRun bool
}
MigrationOptions configures the migration from JSON to SQLite
type MigrationResult ¶
type MigrationResult struct {
TotalSessions int
MigratedSessions int
SkippedSessions int
Errors []string
Duration time.Duration
BackupCreated bool
BackupPath string
SQLiteDatabasePath string
}
MigrationResult contains the results of the migration process
func MigrateJSONToEnt ¶
func MigrateJSONToEnt(opts MigrationOptions) (*MigrationResult, error)
MigrateJSONToEnt migrates session data from JSON to Ent ORM storage.
type NativeProcessManager ¶ added in v1.35.0
type NativeProcessManager struct {
// contains filtered or unexported fields
}
NativeProcessManager implements ProcessManager using a raw PTY and process supervision. It launches the configured program directly under a PTY master fd (via creack/pty) and restarts it with exponential backoff when it exits unexpectedly.
Phase 2 implementation: Start(), Close(), IsAlive(), GetPTY(), GetPanePID(), GetSessionIdentifier(), SetWindowSize(), GetPaneDimensions(), SendKeys(), TapEnter(), SetOnExitCallback(), SubscribeToControlModeUpdates(), and GetCurrentWorkingDirectory() are fully functional. Content capture (CapturePaneContent variants) and precise CWD via lsof/proc are deferred to Phase 3.
func NewNativeProcessManager ¶ added in v1.35.0
func NewNativeProcessManager(opts ProcessManagerOptions) *NativeProcessManager
NewNativeProcessManager creates a NativeProcessManager with the given options. Call Start() to launch the process.
func (*NativeProcessManager) Attach ¶ added in v1.35.0
func (n *NativeProcessManager) Attach() (chan struct{}, error)
Attach is not supported for the native backend; returns an error. Interactive TUI attach requires a proper terminal multiplexer.
func (*NativeProcessManager) CapturePaneContent ¶ added in v1.35.0
func (n *NativeProcessManager) CapturePaneContent() (string, error)
CapturePaneContent returns an empty string until scrollback capture is implemented.
func (*NativeProcessManager) CapturePaneContentRaw ¶ added in v1.35.0
func (n *NativeProcessManager) CapturePaneContentRaw() (string, error)
CapturePaneContentRaw returns an empty string until scrollback capture is implemented.
func (*NativeProcessManager) CapturePaneContentWithOptions ¶ added in v1.35.0
func (n *NativeProcessManager) CapturePaneContentWithOptions(_, _ string) (string, error)
CapturePaneContentWithOptions returns an empty string until scrollback capture is implemented.
func (*NativeProcessManager) CaptureViewport ¶ added in v1.35.0
func (n *NativeProcessManager) CaptureViewport(_ int) (string, error)
CaptureViewport returns an empty string until scrollback capture is implemented.
func (*NativeProcessManager) Close ¶ added in v1.35.0
func (n *NativeProcessManager) Close() error
Close terminates the supervised process and stops the restart loop. Implements NM-3 (SIGTERM before context cancel) and NM-5 (goroutines exit).
func (*NativeProcessManager) DetachSafely ¶ added in v1.35.0
func (n *NativeProcessManager) DetachSafely() error
DetachSafely is a no-op for the native backend.
func (*NativeProcessManager) FilterBanners ¶ added in v1.35.0
func (n *NativeProcessManager) FilterBanners(content string) (string, int)
FilterBanners returns content unchanged; banner detection is tmux-specific.
func (*NativeProcessManager) GetCurrentWorkingDirectory ¶ added in v1.35.0
func (n *NativeProcessManager) GetCurrentWorkingDirectory() (string, error)
GetCurrentWorkingDirectory returns the directory passed to the most recent Start() call. Phase 3 follow-on: replace with /proc/<pid>/cwd on Linux or lsof on macOS for the true current working directory of the running process.
func (*NativeProcessManager) GetCursorPosition ¶ added in v1.35.0
func (n *NativeProcessManager) GetCursorPosition() (x, y int, err error)
GetCursorPosition returns (0, 0) for the native backend. There are zero callers in the server/ package that require real cursor position from the native backend (confirmed in plan.md).
func (*NativeProcessManager) GetPTY ¶ added in v1.35.0
func (n *NativeProcessManager) GetPTY() (*os.File, error)
GetPTY returns the PTY master file descriptor.
func (*NativeProcessManager) GetPaneDimensions ¶ added in v1.35.0
func (n *NativeProcessManager) GetPaneDimensions() (width, height int, err error)
GetPaneDimensions returns the last window size set via SetWindowSize. Tracks the value in memory to avoid a TIOCGWINSZ syscall on the hot path (GetPaneDimensions is called 5× per resize event in connectrpc_websocket.go).
func (*NativeProcessManager) GetPanePID ¶ added in v1.35.0
func (n *NativeProcessManager) GetPanePID() (int32, error)
GetPanePID returns the PID of the supervised process.
func (*NativeProcessManager) GetSessionIdentifier ¶ added in v1.35.0
func (n *NativeProcessManager) GetSessionIdentifier() string
GetSessionIdentifier returns the stable session name set at construction.
func (*NativeProcessManager) HasMeaningfulContent ¶ added in v1.35.0
func (n *NativeProcessManager) HasMeaningfulContent(_ string) bool
HasMeaningfulContent always returns false until content analysis is implemented.
func (*NativeProcessManager) HasSession ¶ added in v1.35.0
func (n *NativeProcessManager) HasSession() bool
HasSession reports whether a process has been started at least once. Alias for IsAlive() on the native backend.
func (*NativeProcessManager) HasUpdated ¶ added in v1.35.0
func (n *NativeProcessManager) HasUpdated() (updated bool, hasPrompt bool, content string)
HasUpdated always returns (false, false, "") until content diffing is implemented.
func (*NativeProcessManager) IsAlive ¶ added in v1.35.0
func (n *NativeProcessManager) IsAlive() bool
IsAlive reports whether the supervised process is currently running.
func (*NativeProcessManager) RefreshClient ¶ added in v1.35.0
func (n *NativeProcessManager) RefreshClient() error
RefreshClient is a no-op for the native backend (no tmux client to refresh).
func (*NativeProcessManager) ResetExitOnce ¶ added in v1.35.0
func (n *NativeProcessManager) ResetExitOnce()
ResetExitOnce is a no-op for the native backend; the restart loop does not use a sync.Once guard.
func (*NativeProcessManager) RestoreWithWorkDir ¶ added in v1.35.0
func (n *NativeProcessManager) RestoreWithWorkDir(_ string) error
RestoreWithWorkDir is a no-op for the native backend; the process is already running after Start() and does not need re-attachment.
func (*NativeProcessManager) SendInputViaControlMode ¶ added in v1.35.0
func (n *NativeProcessManager) SendInputViaControlMode(_ context.Context, data []byte) error
SendInputViaControlMode writes raw bytes directly to the PTY master. The native backend has no concept of tmux control mode; bytes are written directly.
func (*NativeProcessManager) SendKeys ¶ added in v1.35.0
func (n *NativeProcessManager) SendKeys(keys string) (int, error)
SendKeys writes the given string to the PTY master.
func (*NativeProcessManager) SendPromptWithEnter ¶ added in v1.35.0
func (n *NativeProcessManager) SendPromptWithEnter(prompt string) error
SendPromptWithEnter sends text followed by Enter.
func (*NativeProcessManager) SetDetachedSize ¶ added in v1.35.0
func (n *NativeProcessManager) SetDetachedSize(width, height int, _ string) error
SetDetachedSize updates the stored window size without requiring an active PTY. The instanceTitle parameter is ignored; it exists only for interface compatibility.
func (*NativeProcessManager) SetOnExitCallback ¶ added in v1.35.0
func (n *NativeProcessManager) SetOnExitCallback(fn func(string))
SetOnExitCallback registers a callback invoked when the supervised process exits unexpectedly (before the restart loop relaunches it).
func (*NativeProcessManager) SetWindowSize ¶ added in v1.35.0
func (n *NativeProcessManager) SetWindowSize(cols, rows int) error
SetWindowSize resizes the PTY to the given columns and rows.
func (*NativeProcessManager) Start ¶ added in v1.35.0
func (n *NativeProcessManager) Start(dir string) error
Start launches the configured program under a PTY in the given directory. If the process is already running, Start is a no-op. Start resets the stop signal so it is safe to call after Close().
func (*NativeProcessManager) StartControlMode ¶ added in v1.35.0
func (n *NativeProcessManager) StartControlMode() error
StartControlMode is a no-op for the native backend; raw PTY reads replace control mode.
func (*NativeProcessManager) StopControlMode ¶ added in v1.35.0
func (n *NativeProcessManager) StopControlMode() error
StopControlMode is a no-op for the native backend.
func (*NativeProcessManager) SubscribeToControlModeUpdates ¶ added in v1.35.0
func (n *NativeProcessManager) SubscribeToControlModeUpdates() (string, chan []byte)
SubscribeToControlModeUpdates adds a subscriber that receives raw PTY output bytes. Returns the subscription ID and a channel that receives byte slices.
func (*NativeProcessManager) TapEnter ¶ added in v1.35.0
func (n *NativeProcessManager) TapEnter() error
TapEnter sends a carriage return + newline sequence to the PTY.
func (*NativeProcessManager) UnsubscribeFromControlModeUpdates ¶ added in v1.35.0
func (n *NativeProcessManager) UnsubscribeFromControlModeUpdates(id string)
UnsubscribeFromControlModeUpdates removes a subscriber by ID and closes its channel.
type NotificationDecisionLister ¶ added in v1.41.0
type NotificationDecisionLister interface {
ListDecisionRecords(ctx context.Context, sessionID string) ([]DecisionRecord, error)
}
NotificationDecisionLister is a small consumer-defined interface, scoped to exactly what BuildDecisionsSnapshot needs, satisfied by a thin adapter over *notifications.NotificationHistoryStore.List (wired in Phase 2). Defined here, next to its consumer, per .claude/rules/interface-pollution-checklist.md's "define the interface where it's consumed".
type Notifier ¶ added in v1.37.0
Notifier publishes an operator-facing notification. Implemented outside this package (typically a thin adapter over the event bus) since this package cannot import pkg/events directly — pkg/events imports session, so the reverse import would be a cycle. notificationType and priority are int32 values matching sessionv1.NotificationType / sessionv1.NotificationPriority; this package stays free of the proto dependency and just passes the raw values through.
type OneShotShipRunner ¶ added in v1.39.0
type OneShotShipRunner interface {
RunOneShotForSession(ctx context.Context, sessionID, prompt string, timeoutSeconds int32) (string, error)
}
OneShotShipRunner runs a one-shot LLM prompt against a session's worktree, returning the PR URL the prompt produced (or "" if none was found in its output). Defined here — the consumer — per this repo's anti-interface- pollution convention (.claude/rules/interface-pollution-checklist.md); *services.SessionService satisfies it via RunOneShotForSession, wired in production via SetOneShotShipRunner from server/dependencies.go. Mirrors services.PRRunner (server/services/backlog_service_ship.go), which the same method also satisfies for the manual "Ship PR" self-service action — intentionally not shared/exported from that package, since importing it here would pull server/services (which imports this package) into an import cycle.
Used by shipViaAgentOrFallback to close the gap flagged in PR #189's "deliberately out of scope" section: when the work session that earned a PASS verdict has already exited, the only PR-creation mechanism available was pushAndCreatePR's mechanical `git push` + `gh pr create` — no CI reaction, no merge-conflict resolution. RunOneShotForSession lets us run the same agent-driven ship flow a still-live session would have run itself (see /backlog/ship's ship.md, which drives /github:pr-ship) as a headless one-shot against the ended session's worktree — it only needs the session's Instance/worktree to still be resolvable, not a live tmux process (see RunOneShot's use of findInstance + GetEffectiveRootDir).
type OpenStuckStateData ¶ added in v1.38.0
type OpenStuckStateData struct {
ID string
ItemID string
Reason domain.StuckReason
FirstDetectedAt time.Time
LastCheckedAt time.Time
NotifiedAt *time.Time
Context string
ItemTitle string
ItemStatus BacklogStatus
PrNumber int
PrURL string
RemediationAttempts int32
NextRemediationAt *time.Time
GraceBootTime *time.Time
PlanArtifactsPath string
}
OpenStuckStateData is a projected, already-filtered (open + un-snoozed) BacklogStuckState row joined with its parent item's rendering-relevant fields. Returned only by FindOpenStuckStates, which applies the "open" (resolved_at IS NULL) and "not currently snoozed" filters at the query boundary — callers never need to re-check ResolvedAt/SnoozedUntil nullability themselves (parse-don't-validate at the repository boundary).
type OutputConsumer ¶
type OutputConsumer func(data []byte)
OutputConsumer is a callback that receives terminal output from external sessions.
type PRFixSpawner ¶ added in v1.37.0
type PRFixSpawner interface {
AutoReopenForPRFix(ctx context.Context, itemID string, fixContext string) error
}
PRFixSpawner can reopen a pr_pending item for rework when CI checks fail or reviewers request changes. The fixContext string contains a summary of the failures/comments to pass as context to the new work session.
type PRReassignmentGuard ¶ added in v1.42.0
type PRReassignmentGuard struct {
// OverrideReason must be non-empty — the caller's own already-validated
// reason for the reassignment.
OverrideReason string
// CurrentPRMerged must reflect the caller's verified state of the
// CURRENTLY tracked PR. true hard-blocks the reassignment
// unconditionally — a merged PR's association must never be silently
// swapped, even with OverrideReason set.
CurrentPRMerged bool
// NewPRAuthorVerified must be true only when the caller has verified the
// new PR's GitHub author matches the caller's own verified identity.
NewPRAuthorVerified bool
}
PRReassignmentGuard carries the caller-verified preconditions required before SetBacklogItemPRAndTransition (session/storage.go) will accept a reassignment — a call where the observed item is already pr_pending with a DIFFERENT PR number than the one being recorded now. This function itself never calls GitHub; a caller supplies this guard to attest it already did that verification. A caller with no way to produce a valid guard (e.g. the manual-override RPC in server/services/backlog_service_lifecycle.go, which by design never calls GitHub) passes nil and gets a clear rejection instead of silently reassigning an unverified PR.
type PRStatusPoller ¶ added in v1.12.0
type PRStatusPoller struct {
// contains filtered or unexported fields
}
PRStatusPoller polls GitHub PR status for all sessions at a shared interval. Uses a single workspace-level ticker (not per-session goroutines) and an ETag cache so unchanged PRs return HTTP 304 and cost zero rate-limit quota.
func NewPRStatusPoller ¶ added in v1.12.0
func NewPRStatusPoller(storage *Storage) *PRStatusPoller
NewPRStatusPoller creates a new poller with default configuration.
func NewPRStatusPollerWithConfig ¶ added in v1.12.0
func NewPRStatusPollerWithConfig(storage *Storage, config PRStatusPollerConfig) *PRStatusPoller
NewPRStatusPollerWithConfig creates a poller with custom configuration.
func (*PRStatusPoller) AddInstance ¶ added in v1.12.0
func (p *PRStatusPoller) AddInstance(inst *Instance)
AddInstance adds a single instance to monitor.
func (*PRStatusPoller) ETagCache ¶ added in v1.42.0
func (p *PRStatusPoller) ETagCache() *github.ETagCache
ETagCache returns the poller's shared *github.ETagCache, so other pollers (e.g. WorktreePRPoller) reuse the same conditional-request cache instead of each maintaining their own — per ADR-022, a separate cache would double GitHub API call volume for repos both pollers hit.
func (*PRStatusPoller) GetInstances ¶ added in v1.35.0
func (p *PRStatusPoller) GetInstances() []*Instance
GetInstances returns a defensive copy of the currently monitored instances. Callers must not modify the returned slice elements.
func (*PRStatusPoller) PollInterval ¶ added in v1.41.0
func (p *PRStatusPoller) PollInterval() time.Duration
PollInterval returns the poller's configured poll interval, so other components (e.g. ApprovalHandler's CI-status staleness guard) can bound freshness against the same live-configured value rather than a disconnected duplicate literal.
func (*PRStatusPoller) RemoveInstance ¶ added in v1.12.0
func (p *PRStatusPoller) RemoveInstance(title string)
RemoveInstance removes an instance from monitoring.
func (*PRStatusPoller) SetInstances ¶ added in v1.12.0
func (p *PRStatusPoller) SetInstances(instances []*Instance)
SetInstances replaces the full list of monitored instances.
func (*PRStatusPoller) SetOnUpdated ¶ added in v1.12.0
func (p *PRStatusPoller) SetOnUpdated(fn func(*Instance))
SetOnUpdated registers a callback called when a session's PR priority changes. The callback is invoked from a goroutine; it must be concurrency-safe.
func (*PRStatusPoller) Start ¶ added in v1.12.0
func (p *PRStatusPoller) Start(ctx context.Context)
Start begins the polling loop. Safe to call multiple times; subsequent calls are no-ops.
func (*PRStatusPoller) Stop ¶ added in v1.12.0
func (p *PRStatusPoller) Stop()
Stop gracefully shuts down the poller and waits for in-flight requests.
type PRStatusPollerConfig ¶ added in v1.12.0
type PRStatusPollerConfig struct {
// PollInterval controls how often all sessions are checked.
PollInterval time.Duration
// ConcurrentFetches limits simultaneous gh CLI calls (respects secondary rate limits).
ConcurrentFetches int
// CallTimeout is the maximum time for a single gh API call.
CallTimeout time.Duration
// AuthCacheDuration controls how long a successful auth check is cached.
AuthCacheDuration time.Duration
// NoPRBackoff is how long to wait before re-checking a session after ErrNoPR.
// Zero disables the backoff (always re-check).
NoPRBackoff time.Duration
}
PRStatusPollerConfig contains configuration for the PR status poller.
func DefaultPRStatusPollerConfig ¶ added in v1.12.0
func DefaultPRStatusPollerConfig() PRStatusPollerConfig
DefaultPRStatusPollerConfig returns sensible defaults.
type PTYAccess ¶
type PTYAccess struct {
// contains filtered or unexported fields
}
PTYAccess provides thread-safe access to a tmux session's PTY for reading and writing. It wraps the PTY file descriptor with synchronization primitives to enable concurrent access from multiple goroutines (e.g., command execution, response streaming, status monitoring).
func NewPTYAccess ¶
func NewPTYAccess(sessionName string, pty *os.File, buffer *CircularBuffer) *PTYAccess
NewPTYAccess creates a new PTYAccess wrapper for a PTY file descriptor. The buffer parameter specifies the circular buffer for storing PTY output history.
func (*PTYAccess) Close ¶
Close marks the PTY access as closed and prevents further operations. It does NOT close the underlying PTY file descriptor - that's handled by the tmux session.
func (*PTYAccess) GetBuffer ¶
GetBuffer returns the most recent output from the circular buffer. This provides access to historical PTY output without blocking. Returns a copy of the buffer contents to prevent concurrent modification issues.
func (*PTYAccess) GetFile ¶ added in v1.35.0
GetFile returns the underlying PTY *os.File and whether the PTY has been closed. Returns (f, false) when open, (nil, false) when not yet initialized, (nil, true) when closed. The returned file must not be used after a subsequent UpdatePTY or Close call.
func (*PTYAccess) GetRecentHash ¶ added in v1.37.0
GetRecentHash returns the murmur3-64 hash of the last n bytes without copying. Returns (0, false) when no data is available.
func (*PTYAccess) GetRecentOutput ¶
GetRecentOutput returns the last n bytes from the circular buffer. This is useful for status detection and response streaming.
func (*PTYAccess) GetRecentOutputInto ¶ added in v1.37.0
GetRecentOutputInto copies the last n bytes into dst and returns the number of bytes written. dst must have length >= n. Prefer over GetRecentOutput when the caller can provide a pooled buffer.
func (*PTYAccess) GetSessionName ¶
GetSessionName returns the name of the session this PTY access is for.
func (*PTYAccess) Read ¶
Read reads data from the PTY in a thread-safe manner. This is a blocking call that will wait for data to be available. Returns the number of bytes read and any error encountered.
type PTYCategory ¶
type PTYCategory int
PTYCategory represents grouping of PTYs
const ( PTYCategorySquad PTYCategory = iota // Squad-managed sessions PTYCategoryOrphaned // Unmanaged Claude instances PTYCategoryOther // Other tools (aider, etc.) )
func (PTYCategory) String ¶
func (c PTYCategory) String() string
type PTYConnection ¶
type PTYConnection struct {
Path string // /dev/pts/12
PID int // Process ID
Command string // "claude" or "aider"
SessionName string // Associated squad session (if any)
Status PTYStatus // Current status
LastActivity time.Time // Last activity timestamp
Controller *ClaudeController // Connected controller (if any)
// Ownership and management metadata
IsManaged bool // True if this is a squad-managed session
TmuxSocket string // Which tmux server socket (empty = default)
TmuxSessionName string // Full tmux session name
CanAttach bool // Whether attach operations are allowed
CanDestroy bool // Whether destroy operations are allowed
Owner string // "squad" for managed, "external" for discovered
}
PTYConnection represents a discovered PTY
func (*PTYConnection) GetDisplayName ¶
func (conn *PTYConnection) GetDisplayName() string
GetDisplayName returns a human-readable name for the PTY
func (*PTYConnection) GetPTYBasename ¶
func (conn *PTYConnection) GetPTYBasename() string
GetPTYBasename returns just the PTY number (e.g., "12" from "/dev/pts/12")
func (*PTYConnection) GetStatusColor ¶
func (conn *PTYConnection) GetStatusColor() string
GetStatusColor returns a color code for PTY status
func (*PTYConnection) GetStatusIcon ¶
func (conn *PTYConnection) GetStatusIcon() string
GetStatusIcon returns a visual indicator for PTY status
type PTYDiscovery ¶
type PTYDiscovery struct {
// contains filtered or unexported fields
}
PTYDiscovery manages PTY discovery and monitoring
func NewPTYDiscovery ¶
func NewPTYDiscovery(opts ...PTYDiscoveryOption) *PTYDiscovery
NewPTYDiscovery creates a new PTY discovery service with default configuration. Optional PTYDiscoveryOption values are applied after initialization.
func NewPTYDiscoveryWithConfig ¶
func NewPTYDiscoveryWithConfig(config PTYDiscoveryConfig, opts ...PTYDiscoveryOption) *PTYDiscovery
NewPTYDiscoveryWithConfig creates a new PTY discovery service with custom configuration. Optional PTYDiscoveryOption values are applied after initialization.
func (*PTYDiscovery) GetConnection ¶
func (pd *PTYDiscovery) GetConnection(path string) *PTYConnection
GetConnection returns a specific PTY connection by path
func (*PTYDiscovery) GetConnections ¶
func (pd *PTYDiscovery) GetConnections() []*PTYConnection
GetConnections returns all discovered PTY connections
func (*PTYDiscovery) GetConnectionsByCategory ¶
func (pd *PTYDiscovery) GetConnectionsByCategory() map[PTYCategory][]*PTYConnection
GetConnectionsByCategory returns PTYs grouped by category
func (*PTYDiscovery) Refresh ¶
func (pd *PTYDiscovery) Refresh() error
Refresh performs a full PTY discovery scan
func (*PTYDiscovery) SetSessions ¶
func (pd *PTYDiscovery) SetSessions(sessions []*Instance)
SetSessions updates the session map for correlation
func (*PTYDiscovery) Stop ¶
func (pd *PTYDiscovery) Stop()
Stop halts PTY discovery monitoring and blocks until monitorLoop has fully exited, so callers (notably tests tearing down a t.TempDir()-backed config dir) can rely on monitorLoop no longer touching any shared state — e.g. the tmux exec-gate directory — once Stop returns. The wait is bounded: a monitorLoop stuck mid-refresh (blocked on a contended exec-gate slot) logs a warning and lets the caller proceed rather than hanging teardown indefinitely. stopOnce guards against a double-close panic if Stop is ever called more than once on the same instance.
type PTYDiscoveryConfig ¶
type PTYDiscoveryConfig struct {
// Primary tmux server socket for squad-managed sessions
// Empty string means use the default tmux server
PrimarySocket string
// ExternalSockets are additional tmux servers to scan for external instances
// Only used when Mode is Extended or Full
ExternalSockets []string
// Mode controls discovery scope and permissions
Mode DiscoveryMode
// ManagedPrefix is the tmux session prefix for squad-managed sessions
// Default: "staplersquad_"
ManagedPrefix string
// DiscoverExternal enables discovery of non-prefixed Claude instances
// Automatically enabled for Extended and Full modes
DiscoverExternal bool
// AllowExternalAttach permits attaching to external instances
// Only effective in Full mode
AllowExternalAttach bool
// RequireConfirmation requires user confirmation for external operations
// Recommended to keep true for safety
RequireConfirmation bool
// DiscoveryInterval controls how often to refresh discovery
DiscoveryInterval time.Duration
// ParallelDiscovery enables parallel scanning of multiple tmux servers
ParallelDiscovery bool
}
PTYDiscoveryConfig controls PTY discovery scope and behavior
func DefaultPTYDiscoveryConfig ¶
func DefaultPTYDiscoveryConfig() PTYDiscoveryConfig
DefaultPTYDiscoveryConfig returns the default discovery configuration
func (*PTYDiscoveryConfig) CanAttachExternal ¶
func (c *PTYDiscoveryConfig) CanAttachExternal() bool
CanAttachExternal returns true if attaching to external instances is allowed
func (*PTYDiscoveryConfig) ShouldDiscoverExternal ¶
func (c *PTYDiscoveryConfig) ShouldDiscoverExternal() bool
ShouldDiscoverExternal returns true if external instances should be discovered
type PTYDiscoveryOption ¶ added in v1.18.0
type PTYDiscoveryOption func(*PTYDiscovery)
PTYDiscoveryOption is a functional option for PTYDiscovery construction.
func WithSessionLister ¶ added in v1.18.0
func WithSessionLister(l tmux.SessionLister) PTYDiscoveryOption
WithSessionLister injects a SessionLister; used in tests to avoid exec.Command forks.
type PTYSubscriber ¶ added in v1.35.0
type PTYSubscriber interface {
// Push appends data to the buffer. Must be goroutine-safe and never block.
// Returns ErrSubscriberFull if the buffer is at capacity; the caller should
// then close the subscriber and force the consumer to reconnect.
Push(data []byte) error
// Chan returns the receive-only channel from which the consumer reads buffered
// data. The channel is closed when Close is called and all queued data is drained.
Chan() <-chan []byte
// Close signals that no more data will be pushed and releases resources.
Close()
}
PTYSubscriber is a lossless, ordered buffer for raw PTY bytes from a single session. fanOut calls Push; the consumer reads from Chan. Implementations must be goroutine-safe.
The interface is intentionally minimal so that alternative backends (e.g. a memory-mapped circular file for large or persistent sessions) can be substituted without changing callers.
type PaginatedFetcher ¶ added in v1.41.0
type PaginatedFetcher interface {
// FetchAll retrieves items across multiple pages up to an
// implementation-defined cap, returning the aggregated items, the newest
// cursor value seen, and possiblyIncomplete=true if the cap was hit
// while more results may still exist beyond it.
FetchAll(ctx context.Context, config PluginConfig, cursor string) (items []ExternalItem, newCursor string, possiblyIncomplete bool, err error)
}
PaginatedFetcher is an optional capability an ItemSourcePlugin can implement for retrieving its complete result set across all pages, rather than Fetch's single-page/incremental-sync behavior. Consumers that need the full current state regardless of page size (e.g. SyncLoop.PreviewBackwardSyncImpact) should type-assert for this interface and prefer FetchAll when a plugin implements it, falling back to a plain Fetch call otherwise.
type PendingApproval ¶
type PendingApproval struct {
Request *detection.ApprovalRequest
Decision *PolicyDecision
ReceivedAt time.Time
ExpiresAt time.Time
Status PendingApprovalStatus
UserResponse *detection.ApprovalResponse
}
PendingApproval represents an approval request awaiting action.
type PendingApprovalStatus ¶
type PendingApprovalStatus string
PendingApprovalStatus tracks the state of a pending approval.
const ( PendingStatusAwaiting PendingApprovalStatus = "awaiting" PendingStatusProcessed PendingApprovalStatus = "processed" PendingStatusExpired PendingApprovalStatus = "expired" PendingStatusCancelled PendingApprovalStatus = "cancelled" )
type PipelineEngine ¶ added in v1.38.0
type PipelineEngine interface {
// SlashCommandSet returns the filename→rendered-content map that
// WriteSlashCommands writes to .claude/commands/backlog/ for item.
SlashCommandSet(item *BacklogItemData) (map[string]string, error)
// TriagePromptFor builds the headless-triage prompt for item.
TriagePromptFor(item *BacklogItemData, artifactAbsPath string) string
// ReviewPromptFor builds the headless-review prompt for item.
ReviewPromptFor(item *BacklogItemData, acSnapshot []AcCriterion, diff string, diffTruncated bool, verificationNotes string, extras ReviewContextExtras) string
// InteractiveReviewPromptFor builds the tool-call-style review prompt used
// by the automatic review gate's real, hidden session.Instance (see
// ReviewGateRunner.Run) — the review path most items actually go through.
// Unlike ReviewPromptFor (JSON-output style, for headless callers with no
// tool access), this asks the reviewer to call submit_review_verdict.
// PipelineModeDefault renders BuildReviewPrompt; a resolved custom mode
// renders the same ReviewPromptTemplate field ReviewPromptFor uses — mode
// authors must include verdict-tool-call instructions in that template if
// they want it to drive the real review gate too.
InteractiveReviewPromptFor(item *BacklogItemData, acSnapshot []AcCriterion, diff string, diffTruncated bool, itemSessionID string, verificationNotes string) string
// InitialPromptFor builds the interactive/autonomous session's initial
// prompt (inst.Prompt).
InitialPromptFor(item *BacklogItemData, priorSessions []ItemSessionSummary) string
// ContentHashFor returns the content hash of a resolved mode's 9 raw
// content-template fields. ok is false for PipelineModeDefault (code-
// backed content can't drift without a redeploy — nothing to hash) or an
// unresolved slug.
ContentHashFor(mode PipelineMode) (hash string, ok bool)
}
PipelineEngine is the narrow (5-method) seam described in the package doc comment above. CachingPipelineEngine is its single concrete implementation.
The method count (5) intentionally exceeds the usual 1-3-method interface- segregation guidance: each method shares the same resolve-and-render mechanism and has a genuine, independently-verified caller elsewhere in the codebase (see plan.md's Pattern Decisions row on method count) — splitting them into multiple interfaces would be exactly the kind of speculative interface-pollution .claude/rules/interface-pollution-checklist.md warns against, not less of it.
type PipelineMode ¶ added in v1.38.0
type PipelineMode string
PipelineMode identifies which PipelineMode definition (by slug) drives a backlog item's triage/work/review content.
const PipelineModeDefault PipelineMode = ""
PipelineModeDefault is the sentinel PipelineMode value meaning "no mode chosen — use the pre-existing hardcoded pipeline." Resolving this value is guaranteed, by construction, to never touch pipelineModeCache or PipelineModeRepository — this is the concrete mechanism that keeps "no uncached DB read on the hot path for the common case" true without needing runtime feature-flagging.
type PipelineModeContentFields ¶ added in v1.38.0
type PipelineModeContentFields struct {
Slug string
ValidateSlug bool
StatusCommandTemplate string
DoneCommandTemplate string
FailCommandTemplate string
ReviewCommandTemplate string
ShipCommandTemplate string
HelpCommandTemplate string
TriagePromptTemplate string
ReviewPromptTemplate string
InitialPromptTemplate string
}
PipelineModeContentFields groups the slug and the 9 content-template fields ValidatePipelineModeContent checks.
ValidateSlug should be true only for a Create-style call: slug is required and format-checked there. Update never sets it, because UpdatePipelineModeRequest has no slug field at all — slug is immutable after creation (see proto/session/v1/backlog.proto) — so there is nothing for an Update call to validate.
Any content-template field left as "" (e.g. a field a partial Update request didn't set) trivially passes both content checks below (an empty string contains no shell metacharacters and no placeholder tokens), so callers building this struct for an Update only need to populate whichever fields the request actually sets — omitted fields never fail validation they didn't ask for.
type PipelineModeCreateInput ¶ added in v1.38.0
type PipelineModeCreateInput struct {
Slug string
Name string
Description string
Enabled bool
StatusCommandTemplate string
DoneCommandTemplate string
FailCommandTemplate string
ReviewCommandTemplate string
ShipCommandTemplate string
HelpCommandTemplate string
TriagePromptTemplate string
ReviewPromptTemplate string
InitialPromptTemplate string
}
PipelineModeCreateInput holds the fields for creating a new pipeline mode.
type PipelineModeRepository ¶ added in v1.38.0
type PipelineModeRepository interface {
Create(ctx context.Context, m PipelineModeCreateInput) (*ent.PipelineMode, error)
Update(ctx context.Context, id uuid.UUID, m PipelineModeUpdateInput) (*ent.PipelineMode, error)
Delete(ctx context.Context, id uuid.UUID) error
GetByID(ctx context.Context, id uuid.UUID) (*ent.PipelineMode, error)
GetBySlug(ctx context.Context, slug string) (*ent.PipelineMode, error)
ListAll(ctx context.Context) ([]*ent.PipelineMode, error)
ListEnabled(ctx context.Context) ([]*ent.PipelineMode, error)
}
PipelineModeRepository defines persistence operations for pipeline mode definitions.
type PipelineModeUpdateInput ¶ added in v1.38.0
type PipelineModeUpdateInput struct {
Name *string
Description *string
Enabled *bool
StatusCommandTemplate *string
DoneCommandTemplate *string
FailCommandTemplate *string
ReviewCommandTemplate *string
ShipCommandTemplate *string
HelpCommandTemplate *string
TriagePromptTemplate *string
ReviewPromptTemplate *string
InitialPromptTemplate *string
}
PipelineModeUpdateInput holds optional fields for updating an existing pipeline mode. Pointer fields are only applied when non-nil (partial update).
type PluginConfig ¶ added in v1.35.0
type PluginConfig struct {
Raw string // JSON
}
PluginConfig is opaque config passed to a plugin. Plugins decode their own fields.
type PluginRegistry ¶ added in v1.35.0
type PluginRegistry struct {
// contains filtered or unexported fields
}
PluginRegistry holds registered source plugins.
func NewDefaultRegistry ¶ added in v1.35.0
func NewDefaultRegistry() *PluginRegistry
NewDefaultRegistry returns a registry with all built-in plugins registered.
func NewPluginRegistry ¶ added in v1.35.0
func NewPluginRegistry() *PluginRegistry
NewPluginRegistry creates a new empty PluginRegistry.
func (*PluginRegistry) Get ¶ added in v1.35.0
func (r *PluginRegistry) Get(id string) (ItemSourcePlugin, bool)
Get retrieves a plugin by ID.
func (*PluginRegistry) Register ¶ added in v1.35.0
func (r *PluginRegistry) Register(p ItemSourcePlugin)
Register adds a plugin to the registry.
type PolicyAction ¶
type PolicyAction string
PolicyAction specifies what to do when a policy matches.
const ( ActionAutoApprove PolicyAction = "auto_approve" ActionAutoReject PolicyAction = "auto_reject" ActionPrompt PolicyAction = "prompt" ActionLog PolicyAction = "log_only" )
type PolicyAuditEntry ¶
type PolicyAuditEntry struct {
Timestamp time.Time `json:"timestamp"`
RequestID string `json:"request_id"`
PolicyID string `json:"policy_id"`
PolicyName string `json:"policy_name"`
Action PolicyAction `json:"action"`
MatchedRequest *detection.ApprovalRequest `json:"matched_request"`
Reason string `json:"reason"`
}
PolicyAuditEntry records policy evaluation results.
type PolicyCondition ¶
type PolicyCondition struct {
Field string `json:"field"` // Field to check (e.g., "command", "file_path")
Operator string `json:"operator"` // "equals", "contains", "regex", "not_contains"
Value string `json:"value"` // Value to compare against
// contains filtered or unexported fields
}
PolicyCondition represents a single condition that must be met.
type PolicyDecision ¶
type PolicyDecision struct {
Request *detection.ApprovalRequest `json:"request"`
Timestamp time.Time `json:"timestamp"`
Decision PolicyAction `json:"decision"`
Matched bool `json:"matched"`
MatchedPolicy *ApprovalPolicy `json:"matched_policy,omitempty"`
Reason string `json:"reason"`
}
PolicyDecision represents the result of policy evaluation.
type PolicyEngine ¶
type PolicyEngine struct {
// contains filtered or unexported fields
}
PolicyEngine manages approval policies and evaluates approval requests.
func NewPolicyEngine ¶
func NewPolicyEngine() *PolicyEngine
NewPolicyEngine creates a new approval policy engine.
func (*PolicyEngine) AddPolicy ¶
func (pe *PolicyEngine) AddPolicy(policy *ApprovalPolicy) error
AddPolicy adds a new approval policy.
func (*PolicyEngine) ClearAuditLog ¶
func (pe *PolicyEngine) ClearAuditLog()
ClearAuditLog removes all audit log entries.
func (*PolicyEngine) Evaluate ¶
func (pe *PolicyEngine) Evaluate(request *detection.ApprovalRequest) (*PolicyDecision, error)
Evaluate evaluates an approval request against all policies.
func (*PolicyEngine) GetAuditLog ¶
func (pe *PolicyEngine) GetAuditLog(limit int) []PolicyAuditEntry
GetAuditLog returns recent audit log entries.
func (*PolicyEngine) GetPolicy ¶
func (pe *PolicyEngine) GetPolicy(id string) *ApprovalPolicy
GetPolicy retrieves a policy by ID.
func (*PolicyEngine) GetStatistics ¶
func (pe *PolicyEngine) GetStatistics() PolicyStatistics
GetStatistics returns statistics about policy usage.
func (*PolicyEngine) ListPolicies ¶
func (pe *PolicyEngine) ListPolicies() []*ApprovalPolicy
ListPolicies returns all policies, sorted by priority.
func (*PolicyEngine) RemovePolicy ¶
func (pe *PolicyEngine) RemovePolicy(id string) bool
RemovePolicy removes a policy by ID.
func (*PolicyEngine) SetMaxAuditLog ¶
func (pe *PolicyEngine) SetMaxAuditLog(max int)
SetMaxAuditLog sets the maximum number of audit log entries to keep.
func (*PolicyEngine) UpdatePolicy ¶
func (pe *PolicyEngine) UpdatePolicy(updated *ApprovalPolicy) error
UpdatePolicy updates an existing policy.
type PolicyStatistics ¶
type PolicyStatistics struct {
TotalPolicies int
EnabledPolicies int
TotalEvaluations int
AutoApprovals int
AutoRejections int
PromptedApprovals int
LoggedOnly int
}
PolicyStatistics provides summary statistics.
type ProcessFileInspector ¶
type ProcessFileInspector interface {
OpenFiles(pid int32) ([]string, error)
IsAlive(pid int32, expectedCreateTimeMs int64) bool
}
ProcessFileInspector is the interface used by HistoryFileDetector. This allows mocking in tests.
type ProcessManager ¶ added in v1.35.0
type ProcessManager interface {
// Lifecycle
Start(dir string) error
RestoreWithWorkDir(workDir string) error
Close() error
IsAlive() bool
// Identification
GetSessionIdentifier() string
// Existence / state
HasSession() bool
// Working directory (via pane or process introspection)
GetCurrentWorkingDirectory() (string, error)
// Terminal I/O
GetPTY() (*os.File, error)
SendKeys(keys string) (int, error)
TapEnter() error
SendPromptWithEnter(prompt string) error
SendInputViaControlMode(ctx context.Context, data []byte) error
// Terminal state
CapturePaneContent() (string, error)
CapturePaneContentRaw() (string, error)
CapturePaneContentWithOptions(startLine, endLine string) (string, error)
CaptureViewport(lines int) (string, error)
GetCursorPosition() (x, y int, err error)
GetPaneDimensions() (width, height int, err error)
SetWindowSize(cols, rows int) error
SetDetachedSize(width, height int, instanceTitle string) error
RefreshClient() error
// Process metadata
GetPanePID() (int32, error)
// Content helpers
HasUpdated() (updated bool, hasPrompt bool, content string)
FilterBanners(content string) (string, int)
HasMeaningfulContent(content string) bool
// Streaming (control mode)
StartControlMode() error
StopControlMode() error
// SubscribeToControlModeUpdates returns a subscription ID and a bidirectional channel.
// The channel must be bidirectional (chan []byte, not <-chan []byte) because some callers
// write synthetic frames for testing. Implementations must not write to the channel themselves.
SubscribeToControlModeUpdates() (string, chan []byte)
UnsubscribeFromControlModeUpdates(id string)
// Attach (interactive TUI)
Attach() (chan struct{}, error)
DetachSafely() error
// Exit notifications
SetOnExitCallback(fn func(string))
ResetExitOnce()
}
ProcessManager abstracts terminal process lifecycle and I/O. Implementations: TmuxBackend (wraps TmuxProcessManager), NativeProcessManager (Phase 2).
func NewProcessManager ¶ added in v1.35.0
func NewProcessManager(_ context.Context, defaultBackend ProcessManagerBackend, opts ProcessManagerOptions) ProcessManager
NewProcessManager returns the ProcessManager implementation selected by the registered backend. Falls back to TmuxBackend for unknown values.
type ProcessManagerBackend ¶ added in v1.35.0
type ProcessManagerBackend string
ProcessManagerBackend identifies the backend implementation.
const ( BackendTmux ProcessManagerBackend = "tmux" BackendNative ProcessManagerBackend = "native" )
type ProcessManagerOptions ¶ added in v1.35.0
type ProcessManagerOptions struct {
SessionName string
Prefix string
ServerSocket string
Program string
Args []string
}
ProcessManagerOptions holds constructor parameters for NewProcessManager.
type ProgressNoteData ¶ added in v1.38.0
type ProgressNoteData struct {
ID string
CriterionIndex int
Note string
Status string
CreatedAt time.Time
}
ProgressNoteData is the domain DTO replacing *ent.BacklogProgressNote in Storage returns. Unlike the current-note-per-criterion stored on BacklogItem.AcceptanceCriteria, this represents a single append-only history entry from one report_progress call.
type ProjectData ¶ added in v1.23.0
type ProjectData struct {
// ID is the unique project name (used as string external identifier)
ID string
Name string
Description string
CreatedAt time.Time
UpdatedAt time.Time
}
ProjectData is the domain model for a project that groups sessions.
type QueueDequeuer ¶ added in v1.41.0
QueueDequeuer claims and spawns as many queued (and, by default, "ready" — config.Config.AutoSpawnReadyItemsOrDefault) backlog items as there are free WIP slots, highest-priority first. Called the moment a slot frees up (onSessionExited) and by the periodic ReconcileStuck sweep as a safety net for a missed exit hook, a concurrency limit raised while items were waiting, or an item reaching "ready" between ticks.
type Registry ¶ added in v1.35.0
type Registry struct {
// contains filtered or unexported fields
}
Registry owns the sessionID → live-actor mapping plus refcounts. Its mutex guards map membership only — not per-field Instance state. Construct with NewRegistry; the zero value is not usable.
func NewRegistry ¶ added in v1.35.0
func NewRegistry(storage *Storage, onConstruct func(*LiveInstance)) *Registry
NewRegistry constructs a Registry. onConstruct may be nil (e.g. daemon.go's own Registry, which has no SessionService to wire callbacks for); Acquire nil-checks before calling it.
func (*Registry) Acquire ¶ added in v1.35.0
func (r *Registry) Acquire(sessionID string) (*LiveInstance, ReleaseFunc, error)
Acquire returns the live handle for sessionID, constructing its actor on first access. On success, the caller MUST call the returned ReleaseFunc exactly once — prefer WithInstance for synchronous single-call-stack callers to avoid forgetting it.
Three outcomes:
- Not in storage → ErrSessionNotFound
- Not in map, construction succeeds → new entry, refcount=1
- Already in map (or races with concurrent Acquire) → refcount++
func (*Registry) AcquireAll ¶ added in v1.35.0
func (r *Registry) AcquireAll() ([]*LiveInstance, ReleaseFunc, error)
AcquireAll acquires every session known to Storage in one call; returns one release closing over all of them. Sugar for sweep-style callers (health.go, hibernation_sweeper.go). Sessions that fail to Acquire are logged and skipped, not returned.
func (*Registry) ForceRelease ¶ added in v1.35.0
ForceRelease tears down sessionID's actor and map entry immediately, regardless of refcount (R2.18 — DeleteSession's force-invalidate; also used by CreateSession to abort a Register()'d entry when the immediately-following storage.AddInstance fails).
Other holders' *LiveInstance pointers stay valid Go values; their next command must return a typed error (Story 2.5.9c's contract, implemented in Epic 3), never hang.
For CreateSession's abort path: use ForceRelease (not the release() closure Register returned) because a concurrent Acquire racing between Register and the abort would bump refcount to 2, making plain release() decrement 2→1 and leave the phantom entry alive. ForceRelease deletes unconditionally, regardless of current refcount.
func (*Registry) List ¶ added in v1.35.0
func (r *Registry) List() []*LiveInstance
List returns a snapshot of all currently-live instances, holding the lock only for the copy.
func (*Registry) Register ¶ added in v1.35.0
func (r *Registry) Register(instance *LiveInstance) (ReleaseFunc, error)
Register is the construction-time counterpart to Acquire (R2.18a). CreateSession builds a brand-new *LiveInstance via NewInstance (no persisted row exists yet for Acquire to look up) and hands it to Register before calling storage.AddInstance.
Register deliberately does NOT invoke onConstruct: CreateSession already performs its own explicit post-construction wiring, so routing Register through onConstruct would wire the same callbacks twice. onConstruct exists solely to backfill wiring for the Acquire-from-storage path (sessions loaded on server restart), which has no other caller positioned to do it.
No double-checked locking here (unlike Acquire): Register has no storage I/O to release the lock around, so the whole check-then-insert runs under one lock acquisition.
func (*Registry) Shutdown ¶ added in v1.35.0
func (r *Registry) Shutdown()
Shutdown force-stops every actor regardless of refcount. Register this as a shutdownHooks entry (Story 2.5.5d) so it fires on server shutdown.
func (*Registry) Storage ¶ added in v1.35.0
Storage returns the Registry's backing storage. Used by callers (e.g. daemon.go) that need access to storage through a Registry reference.
func (*Registry) WithInstance ¶ added in v1.35.0
func (r *Registry) WithInstance(ctx context.Context, sessionID string, fn func(*LiveInstance) error) error
WithInstance is the preferred entry point for synchronous, single-call-stack callers (RPC handlers, one-shot lookups) where forgetting release() is the common failure mode. Reserve raw Acquire/release() for genuinely long-lived holders (WebSocket streams, poller caches, background goroutines).
type RegistryInspector ¶ added in v1.35.0
type RegistryInspector interface {
List() []*LiveInstance
Count() int
}
RegistryInspector is the narrowest interface for callers that only enumerate live instances without acquiring individual handles.
type ReleaseFunc ¶ added in v1.35.0
type ReleaseFunc func()
ReleaseFunc is the refcount-gated teardown closure returned by Acquire and Register. It is idempotent (safe to call more than once via an internal sync.Once) and must be called exactly once per successful Acquire/Register to avoid refcount leaks. Distinct from ForceReleaseFunc so that future callers storing it generically cannot silently conflate refcount-gated and unconditional teardown (type-driven-audit finding B).
type RepoPathManager ¶
type RepoPathManager struct {
// contains filtered or unexported fields
}
RepoPathManager handles GOPATH-style repository path management. Repositories are stored in a consistent location based on their URL:
- ~/.stapler-squad/repos/github.com/owner/repo (main clone)
- Worktrees are created relative to the main repo as needed
func NewRepoPathManager ¶
func NewRepoPathManager() *RepoPathManager
NewRepoPathManager creates a new RepoPathManager with the default base directory.
func NewRepoPathManagerWithBase ¶
func NewRepoPathManagerWithBase(baseDir string) *RepoPathManager
NewRepoPathManagerWithBase creates a RepoPathManager with a custom base directory.
func (*RepoPathManager) EnsureRepoCloned ¶
EnsureRepoCloned ensures the repository is cloned to the local path. If already cloned, it fetches the latest changes. Returns the path to the cloned repository. ctx bounds the underlying git fetch/clone subprocess (via safeexec.CommandContext) with a hard per-operation timeout, so callers that cancel ctx actually kill the subprocess rather than merely abandoning the RPC while it keeps running.
func (*RepoPathManager) GetCloneURL ¶
func (m *RepoPathManager) GetCloneURL(ref *GitHubRef) string
GetCloneURL returns the git clone URL for a GitHub ref, injecting a stored keychain token for the ref's host when one is available (required for private repos on GitHub Enterprise hosts, where an unauthenticated clone would otherwise fail).
func (*RepoPathManager) GetRepoPath ¶
func (m *RepoPathManager) GetRepoPath(ref *GitHubRef) string
GetRepoPath returns the local path where a GitHub repo should be stored. Format: ~/.stapler-squad/repos/<host>/owner/repo
func (*RepoPathManager) ResolveGitHubInput ¶
func (m *RepoPathManager) ResolveGitHubInput(input string) (localPath string, ref *GitHubRef, err error)
ResolveGitHubInput takes a GitHub URL/shorthand and returns a resolved path. It clones the repo if necessary and returns the local path. Also returns the parsed GitHubRef for storing metadata.
func (*RepoPathManager) ResolveGitHubInputCtx ¶ added in v1.41.0
func (m *RepoPathManager) ResolveGitHubInputCtx(ctx context.Context, input string) (localPath string, ref *GitHubRef, err error)
ResolveGitHubInputCtx takes a GitHub URL/shorthand and returns a resolved path, threading ctx down to EnsureRepoCloned so the underlying git clone/fetch subprocess is actually cancelled if ctx is cancelled or times out.
func (*RepoPathManager) ResolveGitHubInputCtxWithHosts ¶ added in v1.41.0
func (m *RepoPathManager) ResolveGitHubInputCtxWithHosts(ctx context.Context, input string, enterpriseHosts []string) (localPath string, ref *GitHubRef, err error)
ResolveGitHubInputCtxWithHosts takes a GitHub URL/shorthand and returns a resolved path, recognizing URLs against the given GitHub Enterprise hostnames in addition to github.com, and threading ctx down to EnsureRepoCloned so the underlying git clone/fetch subprocess is actually cancelled if ctx is cancelled or times out.
type Repository ¶
type Repository interface {
// Create inserts a new session into storage
Create(ctx context.Context, data InstanceData) error
// Update modifies an existing session in storage
Update(ctx context.Context, data InstanceData) error
// Delete removes a session from storage by title
Delete(ctx context.Context, title string) error
// Get retrieves a single session by title with full child data
// For selective loading, use GetWithOptions instead
Get(ctx context.Context, title string) (*InstanceData, error)
// GetWithOptions retrieves a single session with selective child data loading
// Use LoadOptions presets (LoadMinimal, LoadSummary, LoadFull) or custom options
GetWithOptions(ctx context.Context, title string, options LoadOptions) (*InstanceData, error)
// List retrieves all sessions with summary child data (no diff content)
// For selective loading, use ListWithOptions instead
List(ctx context.Context) ([]InstanceData, error)
// ListWithOptions retrieves all sessions with selective child data loading
// Use LoadOptions presets (LoadMinimal, LoadSummary, LoadFull) or custom options
ListWithOptions(ctx context.Context, options LoadOptions) ([]InstanceData, error)
// ListByStatus retrieves sessions filtered by status with summary child data
// For selective loading, use ListByStatusWithOptions instead
ListByStatus(ctx context.Context, status Status) ([]InstanceData, error)
// ListByStatusWithOptions retrieves sessions filtered by status with selective loading
ListByStatusWithOptions(ctx context.Context, status Status, options LoadOptions) ([]InstanceData, error)
// ListByTag retrieves sessions with a specific tag with summary child data
// For selective loading, use ListByTagWithOptions instead
ListByTag(ctx context.Context, tag string) ([]InstanceData, error)
// ListByTagWithOptions retrieves sessions with a specific tag with selective loading
ListByTagWithOptions(ctx context.Context, tag string, options LoadOptions) ([]InstanceData, error)
// UpdateTimestamps efficiently updates only timestamp fields for a session
// This is optimized for frequent updates from WebSocket terminal streaming
UpdateTimestamps(ctx context.Context, title string, lastTerminalUpdate, lastMeaningfulOutput time.Time, lastOutputSignature string) error
// UpdateReviewQueueState efficiently updates the review-queue interaction fields
// (LastUserResponse, ProcessingGraceUntil, LastPromptDetected, LastPromptSignature)
// without the read-modify-write overhead of a full Get+Update cycle.
UpdateReviewQueueState(ctx context.Context, title string, lastUserResponse, processingGraceUntil, lastPromptDetected time.Time, lastPromptSignature string) error
// UpdateLastAddedToQueue sets only the last_added_to_queue field for a session.
// Issues a single UPDATE WHERE title=? without a prior SELECT.
UpdateLastAddedToQueue(ctx context.Context, title string, t time.Time) error
// UpdateLastAcknowledged sets only the last_acknowledged field for a session.
// Issues a single UPDATE WHERE title=? without a prior SELECT.
UpdateLastAcknowledged(ctx context.Context, title string, t time.Time) error
// UpdateLastViewed sets only the last_viewed field for a session.
// Issues a single UPDATE WHERE title=? without a prior SELECT.
UpdateLastViewed(ctx context.Context, title string, t time.Time) error
// UpdateSessionMetadata efficiently updates only title/category/note/working_dir
// fields for a session, issuing a single UPDATE WHERE title=? without a prior SELECT
// and without touching worktree/diffstats/tags/claude_session rows (unlike Update).
// currentTitle must be the row's title from before any rename applied in this same
// call — see the EntRepository implementation for why. A nil field pointer leaves
// that field untouched; Note is written whenever non-nil (including "") since an
// empty note is a meaningful cleared state, not "unset".
UpdateSessionMetadata(ctx context.Context, currentTitle string, newTitle, category, note, workingDir *string) error
// Close performs cleanup and releases resources
Close() error
// GetSession retrieves a session using the new Session domain model.
// Use ContextOptions to control which optional contexts are loaded.
// Returns nil if session not found.
GetSession(ctx context.Context, title string, opts ContextOptions) (*Session, error)
// ListSessions retrieves all sessions using the new Session domain model.
// Use ContextOptions to control which optional contexts are loaded.
ListSessions(ctx context.Context, opts ContextOptions) ([]*Session, error)
// CreateSession creates a new session from the Session domain model.
CreateSession(ctx context.Context, session *Session) error
// UpdateSession updates an existing session using the Session domain model.
UpdateSession(ctx context.Context, session *Session) error
// AllRules returns all auto-approval rules.
AllRules(ctx context.Context) ([]ApprovalRuleData, error)
// UpsertRule creates or updates an auto-approval rule.
UpsertRule(ctx context.Context, rule ApprovalRuleData) error
// DeleteRule removes an auto-approval rule by ID.
DeleteRule(ctx context.Context, id string) error
// RecordAnalytics logs a classification decision.
RecordAnalytics(ctx context.Context, data AnalyticsData) error
// ListAnalytics retrieves recent classification decisions.
ListAnalytics(ctx context.Context, limit int) ([]AnalyticsData, error)
// ListAnalyticsSince retrieves analytics entries with created_at >= since.
// Replaces the in-Go date filter in LoadWindow. Implements AC-1.
// Pass limit=0 for no limit.
ListAnalyticsSince(ctx context.Context, since time.Time, limit int) ([]AnalyticsData, error)
// ListAnalyticsByProgramSince retrieves entries for a specific program since a time.
// Uses the compound index (command_program, created_at). Implements AC-3.
// Pass limit=0 for no limit.
ListAnalyticsByProgramSince(ctx context.Context, program string, since time.Time, limit int) ([]AnalyticsData, error)
// GetSubcommandBreakdown returns per-(subcommand, decision) counts for a program
// in the given time window. Uses SQL GROUP BY via ent Aggregate. Implements AC-4.
GetSubcommandBreakdown(ctx context.Context, program string, since time.Time) ([]SubcommandDecisionCount, error)
// ListRecentCommandsByProgram returns the most recent n command_preview strings
// for (program, subcommand). Pass subcommand="" to match all subcommands.
// Implements AC-5.
ListRecentCommandsByProgram(ctx context.Context, program, subcommand string, since time.Time, n int) ([]string, error)
// GetSubcommandTrend returns raw analytics rows for (program, subcommand) since
// a given time. The caller buckets these using ComputeDailyBuckets. Implements AC-6.
// Pass subcommand="" to match all subcommands for the program.
GetSubcommandTrend(ctx context.Context, program, subcommand string, since time.Time) ([]AnalyticsData, error)
// CreateProject inserts a new project.
CreateProject(ctx context.Context, data ProjectData) (*ProjectData, error)
// ListProjects returns all projects.
ListProjects(ctx context.Context) ([]ProjectData, error)
// UpdateProject modifies an existing project.
UpdateProject(ctx context.Context, data ProjectData) (*ProjectData, error)
// DeleteProject removes a project by name; sessions are unassigned.
DeleteProject(ctx context.Context, name string) error
// AssignSessionsToProject links sessions (by title) to a project (by name).
AssignSessionsToProject(ctx context.Context, projectName string, sessionTitles []string) error
// CreateBacklogItem inserts a new backlog item.
CreateBacklogItem(ctx context.Context, data BacklogItemData) (*BacklogItemData, error)
// GetBacklogItem retrieves a backlog item by UUID string.
GetBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
// ListBacklogItems returns backlog items with optional filtering.
ListBacklogItems(ctx context.Context, filter BacklogItemFilter) ([]BacklogItemData, error)
// UpdateBacklogItem modifies an existing backlog item with optional precondition check.
UpdateBacklogItem(ctx context.Context, id string, update BacklogItemUpdate, precondition *BacklogItemPrecondition) (*BacklogItemData, error)
// ArchiveBacklogItem sets the archived_at timestamp on a backlog item.
ArchiveBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
// UnarchiveBacklogItem clears archived_at and restores the item to "idea".
UnarchiveBacklogItem(ctx context.Context, id string) (*BacklogItemData, error)
// DeleteBacklogItem permanently removes an item and all its child records.
DeleteBacklogItem(ctx context.Context, id string) error
// TransitionBacklogItemStatus changes the status of a backlog item with optional precondition.
// triggeredBy records who/what caused the transition (TriggeredByUser or TriggeredBySystem)
// in the resulting BacklogStatusEvent audit row.
TransitionBacklogItemStatus(ctx context.Context, id string, toStatus BacklogStatus, precondition *BacklogItemPrecondition, triggeredBy string) (*BacklogItemData, error)
// GetAllItemSessionsWithBacklogInfo returns all item sessions joined with their parent backlog item metadata.
// Used by the Insights dashboard to annotate sessions with backlog context.
GetAllItemSessionsWithBacklogInfo(ctx context.Context) ([]ItemSessionBacklogEntry, error)
// ListBacklogItemSummaries returns lightweight summaries for the list view.
// Unlike ListBacklogItems it omits Description/plan fields and eagerly loads
// ItemSessions (with ReviewVerdict) without over-fetching status events.
ListBacklogItemSummaries(ctx context.Context, filter BacklogItemFilter) ([]BacklogItemSummary, error)
// AddBacklogItemDependency records that edge.BlockedID may not be
// dequeued/started until edge.BlockerID reaches a resolved status
// (done). Upserts against the unique (blocker_id, blocked_id) index —
// adding an existing pair is a no-op. Returns an error if the new edge
// would create a cycle.
AddBacklogItemDependency(ctx context.Context, edge BacklogItemDependencyEdge) error
// UnresolvedBlockerItemIDs returns the subset of itemIDs that have at
// least one BacklogItemDependency whose blocker has not reached done.
// Batched by blocked_id so callers (DequeueNextQueuedItems,
// transitionWithGuard) avoid an N+1 per-candidate query.
UnresolvedBlockerItemIDs(ctx context.Context, itemIDs []string) (map[string]bool, error)
// UnresolvedBlockerIDs returns the specific blocker item IDs still
// unresolved for a single blocked item, for stuck-reason messaging.
UnresolvedBlockerIDs(ctx context.Context, itemID string) ([]string, error)
// CreateItemSource registers a new external item source.
CreateItemSource(ctx context.Context, data ItemSourceData) (*ItemSourceData, error)
// ListItemSources returns all registered item sources.
ListItemSources(ctx context.Context) ([]ItemSourceData, error)
// UpdateItemSource modifies an existing item source.
UpdateItemSource(ctx context.Context, id string, update ItemSourceUpdate) (*ItemSourceData, error)
// DeleteItemSource removes an item source by UUID string.
DeleteItemSource(ctx context.Context, id string) error
}
Repository defines the interface for session persistence operations. This abstraction allows multiple storage backends (SQLite, JSON, etc.) while maintaining a consistent API for session management.
type RepositoryOption ¶
type RepositoryOption func(interface{}) error
RepositoryOption is a function that configures a repository
func WithDatabasePath ¶
func WithDatabasePath(path string) RepositoryOption
WithDatabasePath sets the database file path for the repository
type ResponseChunk ¶
ResponseChunk represents a chunk of output from the Claude instance.
type ResponseStream ¶
type ResponseStream struct {
OnEOF func() // Called when the PTY exits unexpectedly (program exit, not Stop())
// contains filtered or unexported fields
}
ResponseStream manages real-time streaming of Claude instance responses to multiple subscribers. It reads from the PTY access layer and broadcasts output to all active subscribers.
func NewResponseStream ¶
func NewResponseStream(sessionName string, ptyAccess *PTYAccess) *ResponseStream
NewResponseStream creates a new response stream for the given session. The bufferSize parameter determines how many chunks can be buffered per subscriber.
func NewResponseStreamWithBuffer ¶
func NewResponseStreamWithBuffer(sessionName string, ptyAccess *PTYAccess, bufferSize int) *ResponseStream
NewResponseStreamWithBuffer creates a response stream with a custom buffer size.
func (*ResponseStream) GetBufferSize ¶
func (rs *ResponseStream) GetBufferSize() int
GetBufferSize returns the current buffer size setting.
func (*ResponseStream) GetEscapeParser ¶ added in v1.35.0
func (rs *ResponseStream) GetEscapeParser() *analytics.EscapeCodeParser
GetEscapeParser returns the escape code parser for this stream. Used by the WebSocket handler for Stage 2 analytics observations. Returns nil if no parser is configured.
func (*ResponseStream) GetExitTail ¶ added in v1.15.0
func (rs *ResponseStream) GetExitTail() []byte
GetExitTail returns the last exitTailSize bytes from the circular buffer. The circular buffer already holds this data; no separate rolling copy is needed.
func (*ResponseStream) GetSubscriberCount ¶
func (rs *ResponseStream) GetSubscriberCount() int
GetSubscriberCount returns the number of active subscribers.
func (*ResponseStream) GetSubscriberIDs ¶
func (rs *ResponseStream) GetSubscriberIDs() []string
GetSubscriberIDs returns the IDs of all active subscribers.
func (*ResponseStream) GetSubscriberInfo ¶
func (rs *ResponseStream) GetSubscriberInfo(subscriberID string) (created time.Time, exists bool)
GetSubscriberInfo returns information about a specific subscriber.
func (*ResponseStream) GetTotalBytesWritten ¶ added in v1.35.0
func (rs *ResponseStream) GetTotalBytesWritten() int64
GetTotalBytesWritten returns the monotonic PTY byte offset from the circular buffer. This is the same counter used by Stage 1 (Parse) so Stage 2 (ParseStage2) session_seq values are stable across WebSocket reconnections. Returns 0 if no buffer is available.
func (*ResponseStream) IsStarted ¶
func (rs *ResponseStream) IsStarted() bool
IsStarted returns whether the stream is currently active.
func (*ResponseStream) SetBufferSize ¶
func (rs *ResponseStream) SetBufferSize(size int)
SetBufferSize sets the buffer size for future subscribers. Does not affect existing subscribers.
func (*ResponseStream) SetOnOutput ¶ added in v1.9.0
func (rs *ResponseStream) SetOnOutput(fn func())
SetOnOutput registers a callback invoked each time PTY bytes arrive. Used by ClaudeController to drive event-based activity tracking in IdleDetector. Must be called before Start().
func (*ResponseStream) SetStableSessionID ¶ added in v1.35.0
func (rs *ResponseStream) SetStableSessionID(id string)
SetStableSessionID switches the escape parser's recorded session identifier from the tmux session name (used at construction time, before the owning Instance's stable UUID is available) to the stable UUID. This only affects how escape_event rows are tagged — it does not change rs.sessionName, which is still used for logging, PTY naming, and history keyed off the tmux name.
func (*ResponseStream) Start ¶
func (rs *ResponseStream) Start(ctx context.Context) error
Start begins streaming responses from the PTY to all subscribers. This is a non-blocking call that starts a background goroutine. Use the provided context to stop the stream.
func (*ResponseStream) Stop ¶
func (rs *ResponseStream) Stop() error
Stop stops the response stream and closes all subscriber channels. This is a blocking call that waits for the streaming goroutine to finish.
func (*ResponseStream) Subscribe ¶
func (rs *ResponseStream) Subscribe(subscriberID string) (<-chan ResponseChunk, error)
Subscribe registers a new subscriber and returns a channel for receiving response chunks. The subscriber ID should be unique. Returns an error if the ID is already in use.
func (*ResponseStream) Unsubscribe ¶
func (rs *ResponseStream) Unsubscribe(subscriberID string) error
Unsubscribe removes a subscriber and closes their channel.
type RestartState ¶
type RestartState struct {
// Working directory to restore
WorkingDir string
// Claude session ID for --resume flag
ClaudeSessionID string
// Environment variables to restore
Environment map[string]string
// Original command/program
Program string
// AutoYes flag
AutoYes bool
// Original prompt
Prompt string
}
RestartState holds the state needed to restart a session
type ReviewContextExtras ¶ added in v1.38.0
type ReviewContextExtras struct {
// PriorSessions is the full ItemSession history for this backlog item (as returned
// by Storage.ListItemSessions). Used to render "## Prior Review Attempts" — only
// review-role sessions with a non-nil ReviewVerdict contribute to that section.
PriorSessions []ItemSessionSummary
// ProgressNotes is the full append-only report_progress history for this item (as
// returned by Storage.ListProgressNotesForItem). Used to render "## Full Notes
// History", which supersedes the single latest-note-per-criterion view already
// shown in "## Acceptance Criteria" with the complete timeline.
ProgressNotes []ProgressNoteData
// ItemDescription is the backlog item's Description, rendered in "## Item Context"
// alongside StatusEvents. Kept separate from the *BacklogItemData already passed to
// BuildHeadlessReviewPrompt because that pointer may not have StatusEvents loaded —
// callers typically populate both fields together from a single freshly-loaded
// GetBacklogItem(..., WithStatusEvents) call.
ItemDescription string
// StatusEvents is the item's status transition history, used to render "## Item
// Context" alongside ItemDescription.
StatusEvents []BacklogStatusEventData
// TranscriptRelPath is the path (relative to codebaseWorkDir) of a searchable
// session transcript file written by WriteReviewTranscriptFile, or "" when no
// transcript is available (e.g. scrollback fetch failed or was empty — best-effort
// enrichment, never required). Rendered as an instruction in "## Session
// Transcript".
TranscriptRelPath string
}
ReviewContextExtras bundles the additional context sources available to the empty-diff codebase-read review path (prior review verdicts, full progress-notes history, item goal/status history, and a searchable session transcript file). Passed as a single struct to BuildHeadlessReviewPrompt rather than as separate positional parameters because Go has no named/optional parameters and this prompt builder already has five — the same rationale headless.CallOptions uses for its own set of optional per-call knobs. Every field is a zero-value-safe optional: an unset field simply omits the corresponding prompt section rather than requiring a distinct code path per caller. Only rendered when diff == "" (see BuildHeadlessReviewPrompt) — this is deliberately more expensive context than the normal diff-review path carries.
type ReviewGateRunner ¶ added in v1.37.0
type ReviewGateRunner struct {
// contains filtered or unexported fields
}
ReviewGateRunner encapsulates the spawnReviewGate logic into a testable value type. BacklogLifecycleListener holds one as a field and delegates to it.
getAutoReopener, getNotifier, and getSessionCreator are getter functions rather than stored values so that the runner always observes the latest reopener/notifier/spawner even when SetAutoReopener / SetNotifier / SetSessionCreator are called after construction.
func NewReviewGateRunner ¶ added in v1.37.0
func NewReviewGateRunner( storage *Storage, getAutoReopener func() AutoReopenSpawner, getNotifier func() Notifier, getSessionCreator func() ReviewGateSpawner, pipelineEngine PipelineEngine, ) *ReviewGateRunner
NewReviewGateRunner constructs a ReviewGateRunner. getAutoReopener, getNotifier, and getSessionCreator are getter functions (typically method values from BacklogLifecycleListener) so the runner sees the latest values when dynamic setters are called after construction. pipelineEngine may be nil — see the field's doc comment for the fallback.
func (*ReviewGateRunner) Run ¶ added in v1.37.0
func (r *ReviewGateRunner) Run( ctx context.Context, item *BacklogItemData, is ItemSessionSummary, onPass func(ctx context.Context, item *BacklogItemData, is ItemSessionSummary), )
Run executes the review gate for a backlog item session. ctx should be the listener's shutdownCtx so long-running calls are cancelled on shutdown. onPass is retained for signature compatibility with existing callers (BacklogLifecycleListener.pushAndCreatePR) but is no longer invoked directly from Run: since review now always happens in a real, hidden session.Instance, the PASS/FAIL/PARTIAL/UNVERIFIABLE outcome is only known once that session exits and calls submit_review_verdict — handled by BacklogLifecycleListener.handleReviewSessionExited, not here.
type ReviewGateSpawner ¶ added in v1.35.0
type ReviewGateSpawner interface {
// SpawnReviewSession creates a one-shot review session for item using prompt.
// itemSessionID is the UUID of the work ItemSession being reviewed.
SpawnReviewSession(ctx context.Context, item *BacklogItemData, itemSessionID string, prompt string) (*Instance, error)
}
ReviewGateSpawner can create a short-lived review session for a backlog item. Deprecated: use headless.Pool via NewBacklogLifecycleListenerWithSpawner instead. Retained for backward compatibility with existing tests and callers.
type ReviewOutcome ¶ added in v1.37.0
type ReviewOutcome = domain.ReviewOutcome
ReviewOutcome is a typed verdict outcome value (PASS, FAIL, PARTIAL, UNVERIFIABLE). Type alias — session.ReviewOutcome and domain.ReviewOutcome are identical types.
type ReviewQueueLookup ¶ added in v1.41.0
type ReviewQueueLookup interface {
// ReviewQueueResolvedCount returns the count of resolved and still-open review
// queue items linked to sessionID. Returns (0, 0, nil) if no linked backlog item
// exists (FR-6's "no backlog item" first-class empty case).
ReviewQueueResolvedCount(ctx context.Context, sessionID string) (resolved, stillOpen int, err error)
}
ReviewQueueLookup is a small consumer-defined interface, scoped to exactly what BuildDecisionsSnapshot needs, satisfied by existing ItemSession/ReviewVerdict query code. Defined here, next to its consumer, per .claude/rules/interface-pollution-checklist.md's "define the interface where it's consumed".
type ReviewQueueObserver ¶
type ReviewQueueObserver = queue.ReviewQueueObserver
ReviewQueueObserver re-export
type ReviewQueuePoller ¶
type ReviewQueuePoller struct {
// contains filtered or unexported fields
}
ReviewQueuePoller automatically monitors sessions and adds them to the review queue when they become idle or need attention.
func NewReviewQueuePoller ¶
func NewReviewQueuePoller(queue *ReviewQueue, statusManager StatusProvider, storage *Storage) *ReviewQueuePoller
NewReviewQueuePoller creates a new poller for automatically managing the review queue. The storage parameter is optional (can be nil) but required for persisting LastAddedToQueue timestamps.
func NewReviewQueuePollerWithConfig ¶
func NewReviewQueuePollerWithConfig(queue *ReviewQueue, statusManager StatusProvider, storage *Storage, config ReviewQueuePollerConfig) *ReviewQueuePoller
NewReviewQueuePollerWithConfig creates a poller with custom configuration. The storage parameter is optional (can be nil) but required for persisting LastAddedToQueue timestamps.
func (*ReviewQueuePoller) AddInstance ¶
func (rqp *ReviewQueuePoller) AddInstance(instance *Instance)
AddInstance adds a single instance to monitor.
func (*ReviewQueuePoller) CheckSession ¶
func (rqp *ReviewQueuePoller) CheckSession(inst *Instance)
CheckSession checks a single session immediately (exported for ReactiveQueueManager). This allows external components to trigger immediate re-evaluation without waiting for the next poll cycle, providing <100ms feedback on user interactions. Fetches a fresh pane activity snapshot for accurate cache invalidation.
func (*ReviewQueuePoller) FindInstance ¶
func (rqp *ReviewQueuePoller) FindInstance(sessionID string) *Instance
FindInstance finds an instance by session ID (exported for ReactiveQueueManager). Returns nil if the instance is not found in the monitored list.
func (*ReviewQueuePoller) ForceReconcile ¶ added in v1.24.0
func (rqp *ReviewQueuePoller) ForceReconcile()
ForceReconcile immediately runs session reconciliation outside the normal 30s cadence. Safe to call concurrently; typically used by the fork pressure monitor to rapidly clean up dead sessions when subprocess failures indicate stale Active states.
func (*ReviewQueuePoller) GetConfig ¶
func (rqp *ReviewQueuePoller) GetConfig() ReviewQueuePollerConfig
GetConfig returns the current configuration.
func (*ReviewQueuePoller) GetInstances ¶
func (rqp *ReviewQueuePoller) GetInstances() []*Instance
GetInstances returns a snapshot of all live in-memory instances held by the poller. Use this instead of LoadInstances() for read-only operations to avoid the side effect of FromInstanceData() calling Start() on every non-paused instance.
func (*ReviewQueuePoller) GetMonitoredCount ¶
func (rqp *ReviewQueuePoller) GetMonitoredCount() int
GetMonitoredCount returns the number of instances being monitored.
func (*ReviewQueuePoller) IsRunning ¶
func (rqp *ReviewQueuePoller) IsRunning() bool
IsRunning returns true if the poller is currently running.
func (*ReviewQueuePoller) RemoveInstance ¶
func (rqp *ReviewQueuePoller) RemoveInstance(instanceTitle string)
RemoveInstance removes an instance from monitoring.
func (*ReviewQueuePoller) SetActivityChannel ¶ added in v1.23.0
func (rqp *ReviewQueuePoller) SetActivityChannel(ch <-chan struct{})
SetActivityChannel wires an external signal channel to the poll loop. When a signal arrives on ch, the loop snaps back to the fast interval (PollInterval). Must be called before Start(); subsequent calls have no effect once the loop is running.
func (*ReviewQueuePoller) SetApprovalProvider ¶
func (rqp *ReviewQueuePoller) SetApprovalProvider(provider ApprovalMetadataProvider)
SetApprovalProvider sets the approval metadata provider for enriching review queue items.
func (*ReviewQueuePoller) SetInstances ¶
func (rqp *ReviewQueuePoller) SetInstances(instances []*Instance)
SetInstances sets the list of instances to monitor.
func (*ReviewQueuePoller) Start ¶
func (rqp *ReviewQueuePoller) Start(ctx context.Context)
Start begins polling for idle sessions.
func (*ReviewQueuePoller) UpdateConfig ¶
func (rqp *ReviewQueuePoller) UpdateConfig(config ReviewQueuePollerConfig)
UpdateConfig updates the poller configuration.
type ReviewQueuePollerConfig ¶
type ReviewQueuePollerConfig struct {
PollInterval time.Duration // How often to check sessions (fast path, default 2s)
SlowPollInterval time.Duration // Interval when review queue is empty (default 8s); 0 = no backoff
IdleThreshold time.Duration // Duration before considering session idle and adding to queue
InputWaitDuration time.Duration // Time waiting for input before flagging
StalenessThreshold time.Duration // Duration since last meaningful output before considering stale
ReconcileInterval time.Duration // How often to reconcile in-memory state against tmux reality (0 = disabled)
}
ReviewQueuePollerConfig contains configuration for the review queue poller.
func DefaultReviewQueuePollerConfig ¶
func DefaultReviewQueuePollerConfig() ReviewQueuePollerConfig
DefaultReviewQueuePollerConfig returns sensible defaults for polling.
type ReviewQueueStatistics ¶
type ReviewQueueStatistics = queue.ReviewQueueStatistics
ReviewQueueStatistics re-export
type ReviewQueueWriter ¶ added in v1.35.0
type ReviewQueueWriter interface {
Add(item *ReviewItem) bool
}
ReviewQueueWriter is the write-side interface for the review queue. It is satisfied by *ReviewQueue and can be used in place of the concrete type wherever only Add is required, making it easy to supply a test double.
type ReviewRespawner ¶ added in v1.39.0
ReviewRespawner can automatically re-trigger the review gate for a backlog item stuck in review with no active session in flight (the StuckReasonAbandonedReview condition — see markAbandonedReview). Before this existed, such items were detected and notified but nothing ever respawned work on them, so they sat forever until a human noticed (see docs/tasks/backlog-feature-improvement.md, 2026-07-17 update — 4 real items went stale this way, several with nearly all acceptance criteria already marked complete, just never actually re-reviewed).
type ReviewState ¶
type ReviewState struct {
// LastAcknowledged tracks when the user last acknowledged this session in the review queue.
// Sessions acknowledged after their last update won't appear in the queue until they update again.
LastAcknowledged time.Time
// LastAddedToQueue tracks when this session was last added to the review queue.
// Used to prevent notification spam by enforcing a minimum re-add interval.
LastAddedToQueue time.Time
// LastTerminalUpdate is the timestamp of the last output received from the terminal (any output).
LastTerminalUpdate time.Time
// LastMeaningfulOutput is the timestamp of the last meaningful output (excludes tmux status banners).
// Used by the review queue to determine session staleness.
LastMeaningfulOutput time.Time
// LastOutputSignature is a hash of the terminal content, used to detect actual changes
// vs app restarts with unchanged content (prevents false "new activity" notifications).
LastOutputSignature string
// LastViewed tracks when the user last interacted with this session
// (viewing the terminal, attaching via tmux, or viewing session details).
// Used for smarter review queue notifications (don't notify if just viewed).
LastViewed time.Time
// LastPromptDetected is the timestamp when we last detected a prompt requiring user input.
// Used to distinguish new prompts from the same prompt re-appearing.
LastPromptDetected time.Time
// LastPromptSignature is a hash of the prompt content (last 10 lines before cursor).
// Used to determine if this is the same prompt or a new one.
LastPromptSignature string
// LastUserResponse is the timestamp when the user last provided input/interaction.
// Used to determine if user responded AFTER a prompt was detected.
LastUserResponse time.Time
// ProcessingGraceUntil is the deadline for waiting for the session to respond after
// user interaction. If the session shows no activity by this time, it may be re-added
// to the review queue.
ProcessingGraceUntil time.Time
// contains filtered or unexported fields
}
ReviewState holds all timestamps and state related to the review queue and terminal activity tracking for a session. It is embedded in Instance so all field accesses remain unchanged.
Fields are NOT protected by Instance.mu. Mutation is serialized through the actor's send()/sendSyncErr() closures instead (see UpdateTerminalTimestamps in instance_approval.go, which routes through i.send() rather than i.mu.Lock()) - the same "no locking, serialize via the actor's own command queue" discipline used by transitionToLocked et al. Methods on ReviewState are intentionally non-locking - callers must be running inside an actor command closure (or otherwise be the sole writer) if concurrent access is possible.
Direct field access via Go embedding promotion (inst.LastMeaningfulOutput etc.) is used by:
- session/review_queue_poller.go: reads LastMeaningfulOutput, LastAcknowledged, LastAddedToQueue, ProcessingGraceUntil, LastPromptDetected, LastPromptSignature, LastUserResponse, LastViewed, LastTerminalUpdate, LastOutputSignature
- server/dependencies.go: reads LastMeaningfulOutput, LastTerminalUpdate, LastAddedToQueue, LastAcknowledged
- server/adapters/instance_adapter.go: reads LastTerminalUpdate, LastMeaningfulOutput
- server/review_queue_manager.go: writes LastUserResponse directly
All access is either within the session package (via the actor's serialized closures) or through Instance methods that route through i.send()/sendSyncErr().
TODO: Migrate cross-package field accesses (server/) to accessor methods to enable future encapsulation of ReviewState as a composed (non-embedded) field.
func (*ReviewState) ComputePromptSignature ¶
func (rs *ReviewState) ComputePromptSignature(content string) string
ComputePromptSignature computes a hash of the prompt content using the last 10 lines. Returns "" if content is empty. Caller may call this without holding any lock.
func (*ReviewState) DetectAndTrackPrompt ¶
func (rs *ReviewState) DetectAndTrackPrompt(content string, statusInfo InstanceStatusInfo, sessionTitle string) bool
DetectAndTrackPrompt detects whether the current status represents a new user-facing prompt and records it. Returns true only when a NEW prompt is detected (signature changed or first). Caller must hold Instance.mu when writing prompt fields.
func (*ReviewState) IsAcknowledgedAfterOutput ¶
func (rs *ReviewState) IsAcknowledgedAfterOutput() bool
IsAcknowledgedAfterOutput returns true if the user acknowledged this session more recently than the last meaningful terminal output — meaning no new output has occurred since the user last dismissed the session from the review queue. Returns false when no meaningful output has been recorded yet: the acknowledgment cannot logically be "after" output that never happened, so the session is not snoozed.
Lock-free: reads both atomic shadows (lastMeaningfulOutputNs, lastAcknowledgedNs) rather than the plain LastMeaningfulOutput/LastAcknowledged fields, so this method is safe to call from any goroutine — including outside the actor's serialized command closures (e.g. review_queue_determiner.go's Determine(), called directly on the live *Instance from ReviewQueuePoller's own independent background goroutine).
func (*ReviewState) IsInProcessingGracePeriod ¶
func (rs *ReviewState) IsInProcessingGracePeriod() bool
IsInProcessingGracePeriod returns true if the session is within its processing grace window. Caller must hold the relevant mutex if concurrent access is possible.
func (*ReviewState) SyncAtomicTimestamps ¶ added in v1.35.0
func (rs *ReviewState) SyncAtomicTimestamps()
SyncAtomicTimestamps initialises atomic shadow fields from their time.Time counterparts. Must be called once after constructing ReviewState from persisted or restored data so that lock-free readers see the correct initial value immediately.
func (*ReviewState) TimeSinceLastMeaningfulOutput ¶
func (rs *ReviewState) TimeSinceLastMeaningfulOutput(createdAt time.Time) time.Duration
TimeSinceLastMeaningfulOutput returns how long ago meaningful terminal output was received. If no meaningful output has been recorded yet, returns the duration since the given createdAt time.
Lock-free: reads the atomic shadow (lastMeaningfulOutputNs) via loadLastMeaningfulOutputNs() rather than the plain LastMeaningfulOutput field, so this method is safe to call from any goroutine — including outside the actor's serialized command closures (e.g. HibernationSweeper.sweep(), which runs on its own independent background goroutine).
func (*ReviewState) TimeSinceLastTerminalUpdate ¶
func (rs *ReviewState) TimeSinceLastTerminalUpdate(createdAt time.Time) time.Duration
TimeSinceLastTerminalUpdate returns how long ago any terminal output was received. If LastTerminalUpdate is zero, returns the duration since the given createdAt time. Caller must hold the relevant mutex if concurrent access is possible.
func (*ReviewState) UpdateTimestamps ¶
func (rs *ReviewState) UpdateTimestamps(rawContent, filteredContent string, shouldUpdateMeaningful bool, sessionTitle string) bool
UpdateTimestamps updates terminal activity timestamps based on processed content.
- rawContent: original captured output, used for the LastTerminalUpdate non-blank check.
- filteredContent: rawContent with tmux banners stripped, used for signature computation.
- shouldUpdateMeaningful: true when the content carries meaningful signal (not just banners).
- sessionTitle: used only for structured debug logging.
Caller must be running inside the actor's serialized command closure (via i.send()/ sendSyncErr()), not holding Instance.mu - see UpdateTerminalTimestamps in instance_approval.go, the sole caller. Returns true when any field was updated (caller should rebuild the snapshot).
func (*ReviewState) UserRespondedAfterPrompt ¶
func (rs *ReviewState) UserRespondedAfterPrompt() bool
UserRespondedAfterPrompt returns true if the user responded (LastUserResponse) after a prompt was detected (LastPromptDetected), indicating the session is no longer waiting. Caller must hold the relevant mutex if concurrent access is possible.
type ReviewVerdictData ¶ added in v1.35.0
type ReviewVerdictData struct {
ItemSessionID string
OverallOutcome ReviewOutcome
PerCriterion string // JSON
Summary string
DiffHash string
PromptHash string
DiffTokenCount int
DiffTruncated bool
OverrideBy string
OverrideReason string
OverrideAt *time.Time
}
ReviewVerdictData is the input data for saving a ReviewVerdict.
type ReviewVerdictSummary ¶ added in v1.37.0
type ReviewVerdictSummary struct {
ID string
OverallOutcome string
PerCriterion string // JSON []CriterionVerdict
Summary string
DiffHash string
DiffTokenCount int
DiffTruncated bool
OverrideBy string
OverrideReason string
OverrideAt *time.Time
CreatedAt time.Time
}
ReviewVerdictSummary is a domain DTO for a review verdict embedded in ItemSessionSummary.
type RevisionTarget ¶
type RevisionTarget struct {
ID string
ShortID string
Description string
Author string
Timestamp time.Time
IsCurrent bool
}
RevisionTarget represents a revision as a switch target
type ReworkBlockStaleResolver ¶ added in v1.41.0
type ReworkBlockStaleResolver interface {
// ResolveReworkBlockedStaleIfRecovered no-ops (nil error) if the item
// still has an open, still-stale blocking work session. Best-effort: a
// storage error is logged by the caller, never returned to the reconcile
// tick as a hard failure.
ResolveReworkBlockedStaleIfRecovered(ctx context.Context, itemID string) error
}
ReworkBlockStaleResolver re-checks whether a review-status item's open StuckReasonReworkBlockedStale row (server/services/backlog_service_triage.go's notifyIfActiveWorkSessionStale) should resolve — because the blocking work session has produced output again, has ended, or the item has left review — and clears the row via storage.ResolveStuck if so. Implemented outside this package (BacklogService owns the live SessionStopper needed to re-check liveness/staleness) and wired via SetReworkBlockStaleResolver, mirroring StaleWorkRemediator/SetStaleWorkRemediator exactly: session-package orchestration (reconcileReworkBlockedStaleResolution) needs a server/services-layer, liveness-aware action, and this narrow interface — not a direct sessionStopper-shaped dependency added to BacklogLifecycleListener — is this codebase's established pattern for that. No automated remediation counterpart exists for this reason (unlike StaleWorkRemediator's RemediateStaleWorkSession) — see StuckReasonReworkBlockedStale's doc comment (session/domain/backlog.go) for why that's intentional, not a gap.
type Session ¶
type Session struct {
// Identity
ID string `json:"id"`
Title string `json:"title"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
// Process
Status Status `json:"status"`
Program string `json:"program"`
// Configuration
AutoYes bool `json:"auto_yes,omitempty"`
Prompt string `json:"prompt,omitempty"`
// Optional contexts (nil = not loaded or not applicable)
Git *GitContext `json:"git,omitempty"`
Filesystem *FilesystemContext `json:"filesystem,omitempty"`
Terminal *TerminalContext `json:"terminal,omitempty"`
UI *UIPreferences `json:"ui,omitempty"`
Activity *ActivityTracking `json:"activity,omitempty"`
Cloud *CloudContext `json:"cloud,omitempty"`
}
Session represents the core domain entity for an AI agent session. It contains only universally required fields, with optional contexts for deployment-specific functionality.
Context types are defined in contexts.go: - GitContext: Git repository, branch, PR integration - FilesystemContext: Paths, working directories, worktree detection - TerminalContext: Terminal dimensions, tmux configuration - UIPreferences: Categories, tags, display preferences - ActivityTracking: Timestamps, output signatures, queue tracking - CloudContext: Cloud provider, API configuration
func InstanceToSession ¶
InstanceToSession converts a legacy Instance to the new Session type. This adapter enables gradual migration while maintaining backward compatibility. It populates all relevant contexts from the Instance fields.
func NewSession ¶
NewSession creates a new Session with the required fields. Optional contexts can be added using the With* methods.
func (*Session) GetBranch ¶
GetBranch returns the Git branch name, or empty string if no Git context.
func (*Session) GetCategory ¶
GetCategory returns the UI category, or empty string if no UI preferences.
func (*Session) GetLastMeaningfulOutput ¶
GetLastMeaningfulOutput returns when the session had meaningful output, or zero time if no activity tracking.
func (*Session) GetLastViewed ¶
GetLastViewed returns when the session was last viewed, or zero time if no activity tracking.
func (*Session) GetPath ¶
GetPath returns the filesystem project path, or empty string if no filesystem context.
func (*Session) GetTerminalDimensions ¶
GetTerminalDimensions returns the terminal width and height, or 0,0 if no terminal context.
func (*Session) GetTmuxSessionName ¶
GetTmuxSessionName returns the tmux session name, or empty string if no terminal context.
func (*Session) GetWorkingDir ¶
GetWorkingDir returns the working directory, or empty string if no filesystem context.
func (*Session) HasActivityTracking ¶
HasActivityTracking returns true if activity tracking is available.
func (*Session) HasCloudContext ¶
HasCloudContext returns true if cloud context is available.
func (*Session) HasFilesystemContext ¶
HasFilesystemContext returns true if filesystem context is available.
func (*Session) HasGitContext ¶
HasGitContext returns true if Git context is available.
func (*Session) HasTerminalContext ¶
HasTerminalContext returns true if terminal context is available.
func (*Session) HasUIPreferences ¶
HasUIPreferences returns true if UI preferences are available.
func (*Session) IsCloudConfigured ¶
IsCloudConfigured returns true if the cloud context is properly configured.
func (*Session) NeedsReviewQueueAttention ¶
NeedsReviewQueueAttention returns true if session has unacknowledged output.
func (*Session) WithActivityTracking ¶
func (s *Session) WithActivityTracking(activity *ActivityTracking) *Session
WithActivityTracking adds activity tracking to the session.
func (*Session) WithCloudContext ¶
func (s *Session) WithCloudContext(cloud *CloudContext) *Session
WithCloudContext adds cloud context to the session.
func (*Session) WithFilesystemContext ¶
func (s *Session) WithFilesystemContext(fs *FilesystemContext) *Session
WithFilesystemContext adds filesystem context to the session.
func (*Session) WithGitContext ¶
func (s *Session) WithGitContext(git *GitContext) *Session
WithGitContext adds Git context to the session.
func (*Session) WithTerminalContext ¶
func (s *Session) WithTerminalContext(terminal *TerminalContext) *Session
WithTerminalContext adds terminal context to the session.
func (*Session) WithUIPreferences ¶
func (s *Session) WithUIPreferences(ui *UIPreferences) *Session
WithUIPreferences adds UI preferences to the session.
type SessionArchiver ¶ added in v1.39.0
type SessionArchiver interface {
// ArchiveSessionByUUID soft-archives the session, if found and not already
// archived. No-op (not an error) if the session is not tracked.
ArchiveSessionByUUID(ctx context.Context, sessionUUID string) error
// KillTmuxPaneOnly closes the session's live tmux pane, if any, leaving its
// worktree intact (worktree cleanup is handled separately — see
// cleanupItemWorktreesExcept). No-op if the session isn't tracked live.
// Without this, ArchiveSessionByUUID alone only hides a terminal item's work
// session from the default list — the underlying tmux/claude process keeps
// running indefinitely, accumulating memory across every completed backlog
// item (root cause of the 2026-07-29 OOM: dozens of `done` items' work
// sessions still live, each with its own MCP server subprocess fleet).
KillTmuxPaneOnly(ctx context.Context, sessionUUID string) error
}
SessionArchiver soft-archives a session by UUID so it stops accumulating in the default session list, and can also kill its live tmux pane. Implemented by server/services.SessionService (it owns the live in-memory Instance registry both operations must go through — see ArchivedAt's doc comment on session.Instance); wired via SetSessionArchiver from server/dependencies.go, same pattern as SetNotifier/SetSessionCreator below. Used by the archive_terminal_sessions detector in ReconcileStuck as a periodic safety net for work sessions belonging to backlog items that reached done/archived without their sessions being archived/stopped by the (also newly added) transition hook — e.g. pre-existing terminal items from before this detector existed, or a race/crash mid-transition. Nil-safe: the detector no-ops when unset.
type SessionGoalData ¶ added in v1.35.0
type SessionGoalData struct {
UUID string `json:"uuid"`
SessionUUID string `json:"session_uuid"`
Goal string `json:"goal"`
Status string `json:"status"`
Tasks []TaskNode `json:"tasks,omitempty"`
SetBy string `json:"set_by,omitempty"`
UpdatedAt time.Time `json:"updated_at"`
}
SessionGoalData holds the goal state for a session, including the task tree.
func (*SessionGoalData) TasksDone ¶ added in v1.35.0
func (g *SessionGoalData) TasksDone() int
TasksDone returns the count of all tasks with status "done" (including nested children).
func (*SessionGoalData) TasksTotal ¶ added in v1.35.0
func (g *SessionGoalData) TasksTotal() int
TasksTotal returns the total count of all tasks (including nested children) in the goal.
type SessionHealthChecker ¶
type SessionHealthChecker struct {
// contains filtered or unexported fields
}
SessionHealthChecker manages session health validation and recovery
func NewSessionHealthChecker ¶
func NewSessionHealthChecker(storage *Storage) *SessionHealthChecker
NewSessionHealthChecker creates a new session health checker
func (*SessionHealthChecker) CheckAllSessions ¶
func (h *SessionHealthChecker) CheckAllSessions() ([]HealthCheckResult, error)
CheckAllSessions performs a health check on all active sessions
func (*SessionHealthChecker) RecoverUnhealthySessions ¶
func (h *SessionHealthChecker) RecoverUnhealthySessions() error
RecoverUnhealthySessions attempts to recover all unhealthy sessions
func (*SessionHealthChecker) ScheduledHealthCheck ¶
func (h *SessionHealthChecker) ScheduledHealthCheck(interval time.Duration, stopChan <-chan struct{})
ScheduledHealthCheck runs health checks at regular intervals
type SessionSummaryGenerator ¶ added in v1.41.0
type SessionSummaryGenerator struct {
// contains filtered or unexported fields
}
SessionSummaryGenerator is the domain-level orchestrator that owns the headless pool, the ent client, and the FR-7 in-process dedup map. GenerateAndPersist runs the full async pipeline (assemble deterministic snapshots, call the LLM for a narrative, render markdown, persist) and satisfies the summaryGenerator interface consumed by sessionSummaryListener structurally — no explicit "implements" declaration needed.
func NewSessionSummaryGenerator ¶ added in v1.41.0
func NewSessionSummaryGenerator(entClient *ent.Client, pool headless.PoolClient, notifLister NotificationDecisionLister, tokenStore tokens.TokenStoreReader, reviewLookup ReviewQueueLookup) *SessionSummaryGenerator
NewSessionSummaryGenerator creates a SessionSummaryGenerator. notifLister and reviewLookup may be nil in a degraded/partial deployment — BuildDecisionsSnapshot nil-checks both.
func (*SessionSummaryGenerator) FindRowBySessionID ¶ added in v1.41.0
func (g *SessionSummaryGenerator) FindRowBySessionID(ctx context.Context, sessionID string) (*ent.SessionSummary, error)
FindRowBySessionID queries the SessionSummary row for sessionID directly — never via the Session-keyed live-instance machinery (AC-3: a summary must remain retrievable after its Session row is gone). Wraps ent's not-found error as ErrNotFound so callers in server/services (which must not import session/ent's error-handling helpers directly — see .golangci.yml's no_ent_in_services/forbidigo rules) can check it with errors.Is instead.
func (*SessionSummaryGenerator) GenerateAndPersist ¶ added in v1.41.0
func (g *SessionSummaryGenerator) GenerateAndPersist(ctx context.Context, sessionUUID, sessionTitle string, createdAt time.Time, diff DiffSnapshot, diffContent string, sessionGoal *SessionGoalData, reason string)
GenerateAndPersist runs the full session-completion-summary pipeline: build deterministic snapshots, generate (or skip/fallback) a narrative, render markdown, and persist via a single final status-transitioning upsert. Always invoked as a detached goroutine (`go g.GenerateAndPersist(...)`) by sessionSummaryListener or (Phase 2) RegenerateSessionSummary — never call this synchronously.
diff/diffContent/sessionGoal are synchronous, in-memory-only reads captured by the caller at dispatch time (no I/O) — see sessionSummaryListener.OnLifecycleEvent. diff is the already-derived DiffSnapshot (callers that have a live *git.DiffStats build it via BuildDiffSnapshot; RegenerateSessionSummary's no-live-instance fallback builds it directly from the persisted row's diff_* columns, since BuildDiffSnapshot's FilesChanged derivation depends on diff Content, which isn't persisted). diffContent is the raw diff text forwarded into the LLM narrative prompt — empty when unavailable (e.g. the no-live-instance fallback).
func (*SessionSummaryGenerator) ReconcileStaleness ¶ added in v1.41.0
func (g *SessionSummaryGenerator) ReconcileStaleness(ctx context.Context, row *ent.SessionSummary) *ent.SessionSummary
ReconcileStaleness flips a row stuck in GENERATING for longer than staleGenerationTimeout to ERROR, unless this process's in-memory guard is still held for that session (a long-running call, not a genuinely stuck row). Called from the RPC read path (Phase 2's GetSessionSummary), not a background sweep — see plan.md's Pattern Decisions "FR-7 restart-survival dedup" row for the accepted v1 gap (a never-revisited session stays stuck in GENERATING forever). Exported so server/services.SessionSummaryService (a different package) can call it directly from the RPC handler.
func (*SessionSummaryGenerator) SetNotificationLister ¶ added in v1.41.0
func (g *SessionSummaryGenerator) SetNotificationLister(l NotificationDecisionLister)
SetNotificationLister wires notifLister after construction. Needed because server/dependencies.go constructs SessionSummaryGenerator early (alongside the headless pool, so it can be wired to every instance in the same loop that wires backlogLifecycleListener) but the NotificationHistoryStore it needs isn't built until later, in server.go's RunServer — the same "Set* called long after construction" ordering constraint documented on SessionService.SetHeadlessPool. Safe to call with nil; BuildDecisionsSnapshot nil-checks notifLister. Guarded by lateBindMu because GenerateAndPersist (always dispatched via a goroutine) reads notifLister concurrently with this call.
func (*SessionSummaryGenerator) SetTokenStore ¶ added in v1.41.0
func (g *SessionSummaryGenerator) SetTokenStore(t tokens.TokenStoreReader)
SetTokenStore wires tokenStore after construction, for the same reason and timing as SetNotificationLister — the token store is also constructed after SessionSummaryGenerator during server startup. Safe to call with nil; BuildCostSnapshot nil-checks tokenStore. Guarded by lateBindMu because GenerateAndPersist (always dispatched via a goroutine) reads tokenStore concurrently with this call.
type SessionSummaryStatus ¶ added in v1.41.0
type SessionSummaryStatus string
SessionSummaryStatus is the lifecycle status of a SessionSummary row.
const ( // SessionSummaryStatusPending means a row exists but generation has not started. SessionSummaryStatusPending SessionSummaryStatus = "pending" // SessionSummaryStatusGenerating means the async pipeline is actively running. SessionSummaryStatusGenerating SessionSummaryStatus = "generating" // SessionSummaryStatusReady means generation completed successfully. SessionSummaryStatusReady SessionSummaryStatus = "ready" // SessionSummaryStatusError means generation failed at some stage. SessionSummaryStatusError SessionSummaryStatus = "error" )
func (SessionSummaryStatus) IsValid ¶ added in v1.41.0
func (s SessionSummaryStatus) IsValid() bool
IsValid reports whether s is a recognized SessionSummaryStatus.
type SessionType ¶
type SessionType = config.SessionType
SessionType is an alias for config.SessionType so callers can use either package.
type Shell ¶ added in v1.35.0
type Shell struct {
// ID is the stable UUID for this shell. Also the fragment used in the tmux session name.
ID string
// Name is the user-visible label for the shell tab.
Name string
// Command is the command running in the shell (e.g. "bash", "python").
Command string
// WorkingDir is the working directory for the shell process.
WorkingDir string
// TmuxSessionName is the full computed tmux session name:
// "{parentPrefix}_shell_{shellID}"
TmuxSessionName string
// Status is the current lifecycle status.
Status ShellStatus
// ExitCode is the process exit code (meaningful when Status != ShellStatusRunning).
ExitCode int
// OrderIndex controls tab display order.
OrderIndex int
// StartedAt is when the shell was spawned.
StartedAt time.Time
// contains filtered or unexported fields
}
Shell represents a custom shell attached to a session. It is the in-memory projection of the ent Shell entity; changes are written back through the repository.
type ShellData ¶ added in v1.35.0
type ShellData struct {
// ID is the UUID for the shell. If empty, the caller must populate it.
ID string
// Name is the user-visible label.
Name string
// Command is the command to run in the shell.
Command string
// WorkingDir is the starting directory for the shell process.
WorkingDir string
// TmuxSessionName is the full sibling tmux session name.
TmuxSessionName string
// OrderIndex is the display order for the tab.
OrderIndex int
}
ShellData carries the input fields for creating a new shell.
type ShellHandle ¶ added in v1.35.0
type ShellHandle interface {
// GetPTY returns the PTY file for reading terminal output.
// Returns ErrShellStopped if the shell has been closed.
GetPTY() (*os.File, error)
// Resize updates the PTY dimensions.
Resize(cols, rows int) error
// Close stops the shell process and releases resources.
Close() error
}
ShellHandle is the interface for managing a single shell PTY. It is implemented by session/tmux.ShellTmuxHandle for real sessions and can be mocked in tests.
type ShellRegistry ¶ added in v1.35.0
type ShellRegistry struct {
// contains filtered or unexported fields
}
ShellRegistry is a concurrent shell+handle store. Zero value is not usable; create with newShellRegistry(). The exported API intentionally has no Lock/Unlock methods — mutations go through named operations that hold the bucket lock only for fast in-memory work.
func (*ShellRegistry) Add ¶ added in v1.35.0
func (r *ShellRegistry) Add(sh *Shell, handle *tmux.ShellTmuxHandle)
Add stores a new shell+handle pair. Overwrites any existing entry for the same ID.
func (*ShellRegistry) AddStopped ¶ added in v1.35.0
func (r *ShellRegistry) AddStopped(sh *Shell)
AddStopped stores a shell that has no live handle (already stopped or error).
func (*ShellRegistry) Get ¶ added in v1.35.0
func (r *ShellRegistry) Get(shellID string) (*Shell, bool)
Get returns the Shell for shellID, or (nil, false) if absent.
func (*ShellRegistry) GetBoth ¶ added in v1.35.0
func (r *ShellRegistry) GetBoth(shellID string) (*Shell, *tmux.ShellTmuxHandle, bool)
GetBoth returns both shell and handle in one atomic load.
func (*ShellRegistry) GetHandle ¶ added in v1.35.0
func (r *ShellRegistry) GetHandle(shellID string) (*tmux.ShellTmuxHandle, bool)
GetHandle returns the ShellTmuxHandle for shellID, or (nil, false) if absent.
func (*ShellRegistry) Len ¶ added in v1.35.0
func (r *ShellRegistry) Len() int
Len returns the number of shells in the registry.
func (*ShellRegistry) List ¶ added in v1.35.0
func (r *ShellRegistry) List() []*Shell
List returns all shells sorted by OrderIndex.
func (*ShellRegistry) Remove ¶ added in v1.35.0
func (r *ShellRegistry) Remove(shellID string)
Remove deletes the entry for shellID. No-op if not present.
func (*ShellRegistry) SetHandle ¶ added in v1.35.0
func (r *ShellRegistry) SetHandle(shellID string, handle *tmux.ShellTmuxHandle)
SetHandle atomically replaces the handle for shellID without changing the shell.
func (*ShellRegistry) UpdateForRestart ¶ added in v1.35.0
func (r *ShellRegistry) UpdateForRestart(shellID string, newHandle *tmux.ShellTmuxHandle, newSessionName string, exitCh, watcherDone chan struct{})
UpdateForRestart atomically replaces a shell's mutable restart fields with new values (Status=Running, ExitCode=0, new TmuxSessionName, new exitCh/watcherDone). If the shellID is not found it stores a brand-new entry built from newShell.
func (*ShellRegistry) UpdateStatus ¶ added in v1.35.0
func (r *ShellRegistry) UpdateStatus(shellID string, status ShellStatus, exitCode *int) bool
UpdateStatus atomically updates Shell.Status and Shell.ExitCode for shellID. Returns true if the entry was found and updated.
type ShellRepository ¶ added in v1.35.0
type ShellRepository interface {
// CreateShell persists a new shell record under the given session title.
CreateShell(ctx context.Context, sessionTitle string, data ShellData) (*ent.Shell, error)
// ListShells returns all shell records for the given session title, ordered by order_index.
ListShells(ctx context.Context, sessionTitle string) ([]*ent.Shell, error)
// UpdateShellStatus sets the status (and optionally exit code) for the shell with the given ID.
UpdateShellStatus(ctx context.Context, shellID, status string, exitCode *int) error
// DeleteShell removes the shell record with the given ID.
DeleteShell(ctx context.Context, shellID string) error
}
ShellRepository is the minimal persistence interface for per-session shell management. It is implemented by EntRepository; pass nil to disable persistence (e.g., tests).
type ShellStatus ¶ added in v1.35.0
type ShellStatus string
ShellStatus represents the lifecycle status of a custom shell.
const ( // ShellStatusRunning means the shell process is alive and the PTY is open. ShellStatusRunning ShellStatus = "running" // ShellStatusStopped means the shell exited cleanly (via exit command or StopShell). ShellStatusStopped ShellStatus = "stopped" // ShellStatusError means the shell exited with a non-zero status unexpectedly. ShellStatusError ShellStatus = "error" )
type SourceSyncEventData ¶ added in v1.37.0
type SourceSyncEventData struct {
ID string
ItemsCreated int
ItemsUpdated int
ItemsSkipped int
ItemsErrored int
ErrorMessage string
CursorAfter string
StartedAt time.Time
FinishedAt *time.Time
}
SourceSyncEventData is the domain DTO replacing *ent.SourceSyncEvent in Storage returns.
type SpawnShellRequest ¶ added in v1.35.0
type SpawnShellRequest struct {
// Name is the optional user-visible label. Defaults to the command base name.
Name string
// Command is the command to run. Defaults to $SHELL or /bin/sh.
Command string
// WorkingDir is the starting directory. Defaults to the session's WorkingDir.
WorkingDir string
}
SpawnShellRequest carries the parameters for Instance.SpawnShell.
type StaleWorkRemediator ¶ added in v1.39.0
type StaleWorkRemediator interface {
// RemediateStaleWorkSession ends the item's current stale work session
// (killing its tmux pane but keeping the worktree so uncommitted work
// survives) and spawns a fresh one with a new turn budget. No-op (nil
// error) if the item already moved off in_progress or its work session
// already ended by the time this runs.
RemediateStaleWorkSession(ctx context.Context, itemID string) error
}
StaleWorkRemediator can clean up and respawn an in_progress backlog item whose active work session has gone stale (StuckReasonStaleWork — no progress reported for over maxWorkSessionStaleness) but is NOT a zombie: the underlying tmux session and pane process are still alive (Instance.TmuxAlive/PaneProcessDead), so the generic tmux health check never flags it — the agent inside simply finished its own work and is idle at an interactive prompt instead of properly closing out. Before this existed, reconcileStaleWorkSessions was detection-only (MarkStuck + notify), so such an item sat "in_progress" forever once the agent went idle (docs/tasks/backlog-stuck-item-auto-remediation.md Phase B; live repro 2026-07-20, item 9264efe7-b4c2-455a-9e2a-ab0196a63ecd, rework suffix -r14). Implemented outside this package (BacklogService owns the live Instance registry needed to kill the stale tmux pane) and wired via SetStaleWorkRemediator, same pattern as AutoReopenSpawner/PRFixSpawner.
type StartupScanner ¶ added in v1.35.0
type StartupScanner struct {
// contains filtered or unexported fields
}
StartupScanner scans running sessions for pre-existing approval prompts and adds matches to the review queue immediately, before the first regular poll cycle.
func NewStartupScanner ¶ added in v1.35.0
func NewStartupScanner(statusManager StatusProvider, contentProvider ContentProvider) *StartupScanner
NewStartupScanner creates a StartupScanner using the provided status and content providers.
func (*StartupScanner) Scan ¶ added in v1.35.0
func (ss *StartupScanner) Scan(instances []*Instance, queue ReviewQueueWriter) int
Scan iterates over instances and adds any that need attention to the queue. Returns the number of sessions added to the queue.
type Status ¶
type Status int
const ( // Creating is the status when the instance is being initialized. Creating Status = 0 // Active is the status when the instance has a live AI process (running or ready). Active Status = 1 // Paused is if the instance is paused (worktree removed but branch preserved). Paused Status = 2 // Stopped is a terminal state: the instance has been shut down and cannot transition further. Stopped Status = 3 // Hibernated is the status when the instance has been checkpointed and the tmux session killed. Hibernated Status = 4 // Restoring is the transient startup state when a hibernated session is being restored. // Never persisted to the database — transitions to Active or Creating on completion. Restoring Status = 5 // Crashed is a terminal state distinct from Stopped: the wrapped program exited // abnormally (non-zero exit code or signal) and tmux's remain-on-exit left a dead // pane placeholder, detected by SessionHealthChecker's polling (see session/health.go). // Unlike Stopped, a Crashed session is not auto-recovered by the health checker — // it surfaces to the user/automation for an explicit resume (see ExitReason). Crashed Status = 6 // Deprecated: use Active. Running = Active // Deprecated: use Active. Ready = Active // Deprecated: use Creating. Loading = Creating )
func StatusFromDetected ¶
func StatusFromDetected(detected detection.DetectedStatus) Status
StatusFromDetected maps a DetectedStatus to the corresponding lifecycle Status. All detected states map to Active because the instance process is still executing. NeedsApproval, InputRequired, Error, and TestsFailing are sub-status signals surfaced via GetEffectiveStatus() — they do not change the lifecycle state.
type StatusChange ¶
type StatusChange struct {
Timestamp time.Time
Status detection.DetectedStatus
Context string
}
StatusChange represents a change in detected status during execution.
type StatusChangeListener ¶ added in v1.35.0
type StatusChangeListener func(newStatus detection.DetectedStatus, sessionName string)
StatusChangeListener is called when the controller detects a terminal status transition. Always invoked from the controller's own background goroutine, outside any lock.
type StatusDeterminer ¶ added in v1.35.0
type StatusDeterminer interface {
Determine(
inst *Instance,
content string,
statusInfo InstanceStatusInfo,
detector detection.TerminalDetector,
) DetectionResult
}
StatusDeterminer evaluates whether a session should be added to, removed from, or left unchanged in the review queue. It is a pure function — no queue operations.
type StatusProvider ¶ added in v1.35.0
type StatusProvider interface {
GetStatus(inst *Instance) InstanceStatusInfo
GetController(instanceTitle string) (*ClaudeController, bool)
}
StatusProvider is the interface ReviewQueuePoller uses to fetch session status. Defined at the consumption point (the poller), not the production point.
type Storage ¶
type Storage struct {
// contains filtered or unexported fields
}
Storage handles saving and loading instances via the repository backend.
func NewStorageWithRepository ¶
func NewStorageWithRepository(repo Repository) (*Storage, error)
NewStorageWithRepository creates a Storage backed by a Repository.
func (*Storage) AddBacklogItemDependency ¶ added in v1.43.0
func (s *Storage) AddBacklogItemDependency(ctx context.Context, edge BacklogItemDependencyEdge) error
AddBacklogItemDependency records a blocker/blocked dependency edge.
func (*Storage) AddInstance ¶
AddInstance adds a new instance to storage. Unlike SaveInstances, this does not require instance.Started() to be true.
func (*Storage) AllRules ¶ added in v1.12.0
func (s *Storage) AllRules(ctx context.Context) ([]ApprovalRuleData, error)
AllRules returns all auto-approval rules from the repository.
func (*Storage) AppendActivityNote ¶ added in v1.46.0
func (s *Storage) AppendActivityNote(ctx context.Context, itemID, authorSessionUUID, authorSessionTitle, message string) error
AppendActivityNote records a single post_backlog_update call as an immutable, append-only history entry — the ungated sibling to AppendProgressNote (ADR-001).
func (*Storage) AppendProgressNote ¶ added in v1.38.0
func (s *Storage) AppendProgressNote(ctx context.Context, itemID string, criterionIndex int, note, status string) error
AppendProgressNote records a single report_progress call as an immutable history entry, in addition to the current-note-per-criterion updated by UpdateAcCriterionStatus.
func (*Storage) ArchiveBacklogItem ¶ added in v1.35.0
ArchiveBacklogItem sets the archived_at timestamp.
func (*Storage) AssignSessionsToProject ¶ added in v1.23.0
func (s *Storage) AssignSessionsToProject(ctx context.Context, projectName string, sessionTitles []string) error
AssignSessionsToProject links sessions to a project in storage.
func (*Storage) BulkResetStuckRemediation ¶ added in v1.39.0
func (s *Storage) BulkResetStuckRemediation(ctx context.Context, reason *domain.StuckReason, onlyParked bool) (int, error)
BulkResetStuckRemediation is a thin passthrough to *EntRepository, same rationale as RecordRemediationAttempt above.
func (*Storage) ComputeCurrentDiffHash ¶ added in v1.42.0
ComputeCurrentDiffHash resolves itemID's most recent completed work session's base..head commit range (via GetRepoPathAndLatestCompletedWorkSessionCommits — two bounded, no-edge queries, not GetBacklogItem/ListItemSessions' unbounded eager-loaded fetch) and returns a content hash of that diff (git.DiffHashBetween), for stamping onto a review verdict's DiffHash at save time — see stuck_decisions.go's IsFlakyVerdictFlipFlop, which the hash feeds.
Best-effort: any resolution failure (item/session lookup, missing SHAs, a git error) returns "" rather than propagating an error, matching this codebase's "best-effort, never blocks the write it's attached to" convention (see e.g. SaveReviewVerdict's publish-hook comments) — a missing DiffHash just means IsFlakyVerdictFlipFlop treats that verdict as unknown, never as a false match.
func (*Storage) CreateBacklogItem ¶ added in v1.35.0
func (s *Storage) CreateBacklogItem(ctx context.Context, data BacklogItemData) (*BacklogItemData, error)
CreateBacklogItem inserts a new backlog item.
func (*Storage) CreateItemSession ¶ added in v1.35.0
func (s *Storage) CreateItemSession(ctx context.Context, data ItemSessionData) (ItemSessionSummary, error)
CreateItemSession creates a new ItemSession linked to a BacklogItem.
func (*Storage) CreateItemSessionWithVerdict ¶ added in v1.35.0
func (s *Storage) CreateItemSessionWithVerdict(ctx context.Context, isData ItemSessionData, verdict ReviewVerdictData) (ItemSessionSummary, error)
CreateItemSessionWithVerdict atomically creates an ItemSession and its initial ReviewVerdict in a single transaction. Falls back gracefully if the backend is not ent-based.
func (*Storage) CreateItemSource ¶ added in v1.35.0
func (s *Storage) CreateItemSource(ctx context.Context, data ItemSourceData) (*ItemSourceData, error)
CreateItemSource registers a new external item source.
func (*Storage) CreateProject ¶ added in v1.23.0
func (s *Storage) CreateProject(ctx context.Context, data ProjectData) (*ProjectData, error)
CreateProject inserts a new project into storage.
func (*Storage) CreateSourceSyncEvent ¶ added in v1.35.0
func (s *Storage) CreateSourceSyncEvent(ctx context.Context, sourceID, cursorAfter string, created, updated, skipped, errored int, errMsg string, startedAt, finishedAt time.Time) error
CreateSourceSyncEvent records a sync run for an item source. Direct EntRepository delegation, like ListSourceSyncEvents above.
func (*Storage) DeleteAllInstances ¶
DeleteAllInstances removes all stored instances.
func (*Storage) DeleteBacklogItem ¶ added in v1.35.0
DeleteBacklogItem permanently removes an item and all its child records.
func (*Storage) DeleteInstance ¶
DeleteInstance removes an instance from storage.
func (*Storage) DeleteItemSource ¶ added in v1.35.0
DeleteItemSource removes an item source by UUID string.
func (*Storage) DeleteProject ¶ added in v1.23.0
DeleteProject removes a project from storage (sessions are unassigned).
func (*Storage) DeleteRule ¶ added in v1.12.0
DeleteRule removes an auto-approval rule from the repository.
func (*Storage) FindDoneItemsOlderThan ¶ added in v1.39.0
func (s *Storage) FindDoneItemsOlderThan(ctx context.Context, cutoff time.Time) ([]BacklogItemData, error)
FindDoneItemsOlderThan returns backlog items in "done" status whose most recent done-transition happened at/before cutoff. Thin passthrough to the ent-backed repository (same rationale as MarkStuck below) — returns nil, nil for backends that don't support it (e.g. an in-memory test double), never an error.
func (*Storage) FindInstanceDataByID ¶ added in v1.35.0
func (s *Storage) FindInstanceDataByID(id string) (*InstanceData, error)
FindInstanceDataByID finds the first InstanceData whose stable ID or title matches id. Returns ErrInstanceDataNotFound when no match exists.
func (*Storage) FindOpenStuckStates ¶ added in v1.38.0
func (s *Storage) FindOpenStuckStates(ctx context.Context) ([]OpenStuckStateData, error)
FindOpenStuckStates returns every open (unresolved, un-snoozed) BacklogStuckState row, joined with rendering-relevant item fields. Returns an empty slice (no error) when the backend does not support stuck-state queries (e.g. an in-memory test double).
func (*Storage) GetAllInstanceArtifacts ¶ added in v1.35.0
GetAllInstanceArtifacts returns a map of title → raw artifacts JSON for all sessions that have stored artifacts. Single bulk query (M-4 fix).
func (*Storage) GetAllItemSessionsWithBacklogInfo ¶ added in v1.37.0
func (s *Storage) GetAllItemSessionsWithBacklogInfo(ctx context.Context) ([]ItemSessionBacklogEntry, error)
GetAllItemSessionsWithBacklogInfo returns all item sessions joined with backlog item metadata. Delegates to EntRepository; returns an error for non-ent backends.
func (*Storage) GetBacklogItem ¶ added in v1.35.0
GetBacklogItem retrieves a backlog item by UUID string.
func (*Storage) GetBaseCommitSHAsForSessions ¶ added in v1.37.0
func (s *Storage) GetBaseCommitSHAsForSessions(ctx context.Context, uuids []string) (map[string]string, error)
GetBaseCommitSHAsForSessions returns a sessionUUID→base_commit_sha map for the given UUIDs.
func (*Storage) GetClaudeConversationUUIDBySessionUUID ¶ added in v1.37.0
func (s *Storage) GetClaudeConversationUUIDBySessionUUID(ctx context.Context, sessionUUID string) (string, error)
GetClaudeConversationUUIDBySessionUUID returns the Claude conversation UUID for the session whose title matches the given UUID. Returns "" when the session has no ClaudeSession, and ErrNotFound when no session matches.
func (*Storage) GetEntClient ¶ added in v1.35.0
GetEntClient returns the *ent.Client from the underlying EntRepository, or nil when the repository is not ent-backed (e.g. in-memory test doubles).
func (*Storage) GetInstanceArtifacts ¶ added in v1.35.0
GetInstanceArtifacts loads the raw JSON-encoded artifact blob for a session. Returns ("", nil) if the session exists but has no artifacts yet.
func (*Storage) GetItemSession ¶ added in v1.35.0
GetItemSession looks up an ItemSession by entity UUID (loads BacklogItem edge).
func (*Storage) GetItemSessionBySessionAndItem ¶ added in v1.35.0
func (s *Storage) GetItemSessionBySessionAndItem(ctx context.Context, sessionUUID string, itemID string) (ItemSessionSummary, error)
GetItemSessionBySessionAndItem looks up an ItemSession by both sessionUUID and backlog item ID. Returns ErrNotFound if no matching record exists.
func (*Storage) GetItemSessionBySessionUUID ¶ added in v1.35.0
func (s *Storage) GetItemSessionBySessionUUID(ctx context.Context, sessionUUID string) (ItemSessionSummary, error)
GetItemSessionBySessionUUID looks up the ItemSession for a given session UUID (loads BacklogItem edge).
func (*Storage) GetItemSourceByID ¶ added in v1.41.0
GetItemSourceByID retrieves a single item source's domain data by UUID string. Used by the GitHub forward-sync EventBus subscriber (see server/services/backlog_github_forward_sync.go) to look up a backlog item's source (ForwardSyncEnabled, ForwardSyncCloseLabel, PluginID, Config) without needing an *EntRepository handle of its own.
func (*Storage) GetMostRecentReviewVerdictForItem ¶ added in v1.35.0
func (s *Storage) GetMostRecentReviewVerdictForItem(ctx context.Context, itemID string) (ReviewOutcome, error)
GetMostRecentReviewVerdictForItem returns the OverallOutcome of the most recent ReviewVerdict linked to any ItemSession for itemID. Returns "" when none exists.
func (*Storage) GetRecentReviewVerdictSummaries ¶ added in v1.39.0
func (s *Storage) GetRecentReviewVerdictSummaries(ctx context.Context, itemID string, limit int) ([]ReviewVerdictSummary, error)
GetRecentReviewVerdictSummaries returns up to limit ReviewVerdicts for itemID, most recent first. Returns nil (not an error) when the repo isn't ent-backed.
func (*Storage) GetSession ¶
func (s *Storage) GetSession(ctx context.Context, title string, opts ContextOptions) (*Session, error)
GetSession retrieves a session by title using the Session domain model. Use ContextOptions presets (ContextMinimal, ContextUIView, etc.) to control what is loaded.
func (*Storage) GetSessionGoal ¶ added in v1.35.0
GetSessionGoal retrieves the goal for a session by session UUID. Returns ErrNotFound if no goal has been set for the session.
func (*Storage) GetSubcommandBreakdown ¶ added in v1.35.0
func (s *Storage) GetSubcommandBreakdown(ctx context.Context, program string, since time.Time) ([]SubcommandDecisionCount, error)
GetSubcommandBreakdown returns per-(subcommand, decision) counts for a program.
func (*Storage) GetSubcommandTrend ¶ added in v1.35.0
func (s *Storage) GetSubcommandTrend(ctx context.Context, program, subcommand string, since time.Time) ([]AnalyticsData, error)
GetSubcommandTrend returns raw analytics rows for (program, subcommand) since a time.
func (*Storage) GetWorktreeDataBySessionUUID ¶ added in v1.37.0
func (s *Storage) GetWorktreeDataBySessionUUID(ctx context.Context, sessionUUID string) (GitWorktreeData, error)
GetWorktreeDataBySessionUUID returns the git worktree data for the Session with the given UUID. Returns empty GitWorktreeData for directory-mode sessions or if the session is not found.
func (*Storage) ListActivityNotesForItem ¶ added in v1.46.0
func (s *Storage) ListActivityNotesForItem(ctx context.Context, itemID string) ([]ActivityNoteData, error)
ListActivityNotesForItem returns the full append-only activity-note history for a backlog item, ordered by created_at ascending.
func (*Storage) ListAnalytics ¶ added in v1.12.0
ListAnalytics retrieves recent classification decisions from the repository.
func (*Storage) ListAnalyticsByProgramSince ¶ added in v1.35.0
func (s *Storage) ListAnalyticsByProgramSince(ctx context.Context, program string, since time.Time, limit int) ([]AnalyticsData, error)
ListAnalyticsByProgramSince retrieves entries for a specific program since a time.
func (*Storage) ListAnalyticsSince ¶ added in v1.35.0
func (s *Storage) ListAnalyticsSince(ctx context.Context, since time.Time, limit int) ([]AnalyticsData, error)
ListAnalyticsSince retrieves analytics entries with created_at >= since.
func (*Storage) ListBacklogItemSummaries ¶ added in v1.37.0
func (s *Storage) ListBacklogItemSummaries(ctx context.Context, filter BacklogItemFilter) ([]BacklogItemSummary, error)
ListBacklogItemSummaries returns lightweight summaries for list views.
func (*Storage) ListBacklogItems ¶ added in v1.35.0
func (s *Storage) ListBacklogItems(ctx context.Context, filter BacklogItemFilter) ([]BacklogItemData, error)
ListBacklogItems returns backlog items with optional filtering.
func (*Storage) ListInstanceData ¶ added in v1.18.0
func (s *Storage) ListInstanceData() ([]InstanceData, error)
ListInstanceData returns raw InstanceData from the repository without constructing Instance objects. This avoids the side effect of FromInstanceData() calling Start() (which spawns PTY processes). Use for read-only existence and title checks.
func (*Storage) ListInstanceDataWithWorktree ¶ added in v1.44.0
func (s *Storage) ListInstanceDataWithWorktree() ([]InstanceData, error)
ListInstanceDataWithWorktree returns raw InstanceData with the Worktree edge eager-loaded. Use this instead of ListInstanceData whenever a read-only pass needs Worktree.WorktreePath/ RepoPath/BranchName/BaseCommitSHA (e.g. to stat the worktree or check dirty status) — plain ListInstanceData uses LoadMinimal, which never populates Worktree, so any such field will silently read as its zero value under that call.
func (*Storage) ListInstanceIDs ¶ added in v1.35.0
ListInstanceIDs returns the stable ID (UUID if set, else Title) for every stored InstanceData. Used by Registry.AcquireAll to seed the initial live-handle set.
func (*Storage) ListItemSessions ¶ added in v1.35.0
func (s *Storage) ListItemSessions(ctx context.Context, itemID string) ([]ItemSessionSummary, error)
ListItemSessions returns all ItemSessions for a given BacklogItem UUID string.
func (*Storage) ListItemSources ¶ added in v1.35.0
func (s *Storage) ListItemSources(ctx context.Context) ([]ItemSourceData, error)
ListItemSources returns all registered item sources.
func (*Storage) ListProgressNotesForItem ¶ added in v1.38.0
func (s *Storage) ListProgressNotesForItem(ctx context.Context, itemID string) ([]ProgressNoteData, error)
ListProgressNotesForItem returns the full append-only history of report_progress calls for a backlog item, ordered by created_at ascending.
func (*Storage) ListProjects ¶ added in v1.23.0
func (s *Storage) ListProjects(ctx context.Context) ([]ProjectData, error)
ListProjects returns all projects from storage.
func (*Storage) ListRecentCommandsByProgram ¶ added in v1.35.0
func (s *Storage) ListRecentCommandsByProgram(ctx context.Context, program, subcommand string, since time.Time, n int) ([]string, error)
ListRecentCommandsByProgram returns the most recent n command_preview strings.
func (*Storage) ListSessionRecords ¶ added in v1.35.0
func (s *Storage) ListSessionRecords() []tokens.SessionRecord
ListSessionRecords returns a snapshot of all sessions as SessionRecords, for use by the tokens.Associator to match JSONL files to stapler-squad sessions.
func (*Storage) ListSessions ¶
ListSessions retrieves all sessions using the Session domain model. Use ContextOptions presets (ContextMinimal, ContextUIView, etc.) to control what is loaded.
func (*Storage) ListSourceSyncEvents ¶ added in v1.35.0
func (s *Storage) ListSourceSyncEvents(ctx context.Context, sourceID string) ([]SourceSyncEventData, bool, error)
ListSourceSyncEvents returns sync history events for an item source, most recent first. Direct EntRepository delegation, like GetItemSession below.
func (*Storage) ListWorkspacePeers ¶ added in v1.41.0
func (s *Storage) ListWorkspacePeers(ctx context.Context, workspaceKey string, excludeSessionUUID string) ([]WorkspacePeer, error)
ListWorkspacePeers returns other sessions sharing workspaceKey, excluding excludeSessionUUID. Returns an empty slice (not an error) when workspaceKey is empty or no peers exist. Goal-less peers are still returned (Goal is nil) since AC0 only requires "other active sessions", not "sessions with a goal set".
func (*Storage) LoadInstances ¶
LoadInstances loads the list of instances from the repository.
func (*Storage) MarkStuck ¶ added in v1.38.0
func (s *Storage) MarkStuck(ctx context.Context, itemID string, reason domain.StuckReason, expectedStatus BacklogStatus, stuckContext string) (bool, error)
MarkStuck opens/refreshes/reopens a durable BacklogStuckState row for (itemID, reason). Thin passthrough so callers outside package session (e.g. server/services, which cannot reach the unexported repo field) can write stuck state. Returns false, nil when the backend does not support stuck-state writes — never an error for an unsupported backend.
func (*Storage) MarkStuckNotified ¶ added in v1.38.0
func (s *Storage) MarkStuckNotified(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
MarkStuckNotified sets notified_at=now on an open, not-yet-notified stuck row for (itemID, reason). Thin passthrough, same rationale as MarkStuck above.
func (*Storage) RecordAnalytics ¶ added in v1.12.0
func (s *Storage) RecordAnalytics(ctx context.Context, data AnalyticsData) error
RecordAnalytics logs a classification decision to the repository.
func (*Storage) RecordManualRemediationAttempt ¶ added in v1.39.0
func (s *Storage) RecordManualRemediationAttempt(ctx context.Context, itemID string, reason domain.StuckReason) (justParked bool, err error)
RecordManualRemediationAttempt implements the operator-triggered "Retry now" path (TriggerRemediationNow RPC): it requires an already-open row for (itemID, reason) — mirroring SnoozeStuckItem's validation, there must be something currently stuck to retry — and rejects with ErrRemediationParked once the row already exhausted its attempt budget, rather than silently un-parking it (reset is a separate, explicit operator action). On success it records exactly the same accounting a normal dispatcher-triggered attempt would (this IS a real attempt, just operator- instead of timer-initiated), so it counts toward the same 5-attempt cap. Callers invoke the reason-specific remediation action themselves once this returns successfully — this method only owns the gate/accounting, not the action dispatch (which differs by caller: BacklogService methods directly for the RPC handler, vs the interfaces the periodic sweep uses).
func (*Storage) RecordRemediationAttempt ¶ added in v1.39.0
func (s *Storage) RecordRemediationAttempt(ctx context.Context, itemID string, reason domain.StuckReason, attempts int32, nextAt *time.Time) (bool, error)
RecordRemediationAttempt is a thin passthrough to *EntRepository, mirroring MarkStuck/ResolveStuck's rationale: callers outside package session cannot reach the unexported repo field. Returns false, nil (never an error) when the backend does not support stuck-state writes.
func (*Storage) RecordRemediationRestartGrace ¶ added in v1.39.0
func (s *Storage) RecordRemediationRestartGrace(ctx context.Context, itemID string, reason domain.StuckReason, bootTime time.Time) (bool, error)
RecordRemediationRestartGrace is a thin passthrough to *EntRepository, same rationale as RecordRemediationAttempt above.
func (*Storage) RecordSourceSyncFailure ¶ added in v1.41.0
RecordSourceSyncFailure records a forward-sync failure (e.g. CloseIssue erroring) as a queryable sync-history row. Direct EntRepository delegation, like CreateSourceSyncEvent above.
func (*Storage) RemediationBlocked ¶ added in v1.41.0
func (s *Storage) RemediationBlocked(ctx context.Context, itemID string, reason domain.StuckReason) (blocked bool, err error)
RemediationBlocked is a read-only peek at whether reason's own remediation gate is currently closed (parked at the attempt cap, or mid-backoff) for itemID — unlike RemediationDue, it never mutates the row or consumes an attempt. Built for callers whose remediation action's entire value depends on a DIFFERENT reason's gate letting a downstream step through: producing a fresh review verdict is pointless if the reopen that verdict would trigger (autoReopenWithBackoffGate, gated on StuckReasonBouncing) is itself closed right now — the diff hasn't changed, so a respawn's outcome would be identical to the last one. Spending an attempt on a foregone conclusion wastes that budget for zero forward progress and, once it repeats enough times, silently parks the caller's OWN reason with a "use Reset to retry" notification that doesn't mention the real blocker (BUG-043) — the caller should check this FIRST and skip the attempt entirely when true, logging why, rather than spend it on a call that cannot help.
Returns false (not blocked) when no open row exists for (itemID, reason): nothing is gating yet, same "ungated until first detected" default RemediationDue documents.
func (*Storage) RemediationDue ¶ added in v1.39.0
func (s *Storage) RemediationDue(ctx context.Context, itemID string, reason domain.StuckReason) (due bool, justParked bool, err error)
RemediationDue is the shared backoff gate every automated remediation action — inside package session (BacklogLifecycleListener) or outside it (server/services' AutonomousOrchestrationService) — must call before invoking its reason-specific respawn action. It reports whether the caller should proceed, and atomically records the resulting accounting (a normal attempt, or a restart-grace pass) BEFORE returning true — so a caller that dispatches its actual action asynchronously (e.g. bounded by a semaphore, taking minutes) can never double-count across overlapping sweep ticks or concurrent event callbacks.
due=true, justParked=false: caller should invoke its action now. due=true, justParked=true: caller should invoke its action now AND send a one-time "auto-remediation exhausted, this was the last automated attempt" notification — this attempt is the one that pushed remediation_attempts to the cap. due=false: caller must not invoke its action this tick (parked or not yet due). Not an error.
Returns (true, false, nil) — ungated — when no open row exists for (itemID, reason) yet: the reason hasn't been detected as stuck at all, so there is nothing to gate against. This preserves today's behavior for the first review-failure/driver-turn-cap/etc. before the corresponding detector has had a chance to MarkStuck a row.
func (*Storage) ResetStuckRemediation ¶ added in v1.39.0
func (s *Storage) ResetStuckRemediation(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
ResetStuckRemediation is a thin passthrough to *EntRepository, same rationale as RecordRemediationAttempt above.
func (*Storage) ResolveStuck ¶ added in v1.38.0
func (s *Storage) ResolveStuck(ctx context.Context, itemID string, reason domain.StuckReason) (bool, error)
ResolveStuck atomically, idempotently closes an open BacklogStuckState row for (itemID, reason). Thin passthrough, same rationale as MarkStuck above.
func (*Storage) SaveInstances ¶
SaveInstances upserts each started instance into the repository.
func (*Storage) SaveInstancesSync ¶
SaveInstancesSync saves instances synchronously (same as SaveInstances for the repo backend).
func (*Storage) SaveReviewVerdict ¶ added in v1.35.0
func (s *Storage) SaveReviewVerdict(ctx context.Context, itemSessionID string, verdict ReviewVerdictData) error
SaveReviewVerdict upserts a ReviewVerdict for a given ItemSession UUID.
func (*Storage) SaveSession ¶
SaveSession upserts a session using the Session domain model. If the session exists it is updated; otherwise it is created. Deprecated InstanceData-based methods (SaveInstances, LoadInstances) remain for backward compatibility.
func (*Storage) SetBacklogItemPRAndTransition ¶ added in v1.41.0
func (s *Storage) SetBacklogItemPRAndTransition(ctx context.Context, observed *BacklogItemData, prURL string, prNumber int, summary string, guard *PRReassignmentGuard) error
SetBacklogItemPRAndTransition is the shared primary-write path for recording a PR that genuinely exists on GitHub against a backlog item and moving it review -> pr_pending, or — when observed is already pr_pending — correcting an already-recorded PR to a different one (a reassignment, e.g. the tracked branch was polluted and the real PR was opened from a clean one instead). Used by both the agent-initiated report_pr_created MCP tool (server/mcp/tools_backlog.go, Epic 3.1), the reconciliation backstop detector (BacklogLifecycleListener.reconcileOrphanedAgentPRs, Epic 3.2), and the manual-override RPC (server/services/backlog_service_lifecycle.go) — see "PR Metadata Capture Fix", project_plans/backlog-agent-communication/implementation/plan.md.
observed must be the caller's own, already-fetched snapshot of the item — this function never re-fetches it. That's load-bearing, not an optimization: the CAS precondition below pins to observed.Status and observed.UpdatedAt exactly as the caller read them. A prior version of this function did its own internal GetBacklogItem call and derived the precondition from THAT fresh read; under a real race, that internal read could land after a concurrent winner's write had already committed, so it would see the winner's (already valid) post-write state and re-derive a precondition that ALSO matched — letting a second, policy-unvalidated call silently succeed as an accidental "reassignment" the caller never actually decided to allow (its override_reason/merged-PR/author checks all ran against the caller's original, now-stale read). Pinning to the caller's own observed snapshot closes that: any state change since the caller's read — including a second call winning first — is guaranteed to fail this call's CAS, rather than being silently reinterpreted.
Only observed.Status == "review" or "pr_pending" is accepted; anything else is rejected outright (ErrPreconditionFailed) rather than attempted against an arbitrary starting status.
Unlike AppendProgressNote's best-effort discipline, a failure persisting the PR fields or performing the transition is returned to the caller, not merely logged — BUG-040's root cause #1 was exactly this class of silent failure (a write whose result was never checked against the invariant it protects), and this is the primitive that must not repeat it.
Idempotent: if observed is already pr_pending with this exact prNumber, this is a no-op success — a retried report_pr_created call (network blip) or the reconciliation backstop re-scanning an item it already fixed on a prior tick must not error.
guard is required whenever observed is already pr_pending (a reassignment): the caller must supply a PRReassignmentGuard attesting it already verified override_reason, the currently-tracked PR's merged state, and the new PR's author — nil (or a guard failing any of those checks) is rejected outright with ErrPRReassignmentNotAllowed. This function does not itself call GitHub — that verification stays in the caller (server/mcp/tools_backlog.go's reportPRCreated is the only caller today with that machinery) — but centralizing the *requirement* here means every caller of this shared primitive gets the same guarantee: a caller with no way to produce a valid guard (e.g. the manual-override RPC in server/services/backlog_service_lifecycle.go, which by design never calls GitHub — see its own doc comment) simply cannot reassign, rather than silently succeeding because the check only lived in one handler. guard is ignored (may be nil) when observed.Status is review — a first-time recording never needs one.
func (*Storage) SetCallbackDispatcher ¶ added in v1.43.0
func (s *Storage) SetCallbackDispatcher(d CallbackDispatcher)
SetCallbackDispatcher forwards to the concrete *EntRepository's SetCallbackDispatcher, mirroring SetItemChangePublisher above — same reasoning: server/dependencies.go only has a *Storage value in scope. When the repository is not ent-backed, the dispatcher is simply never wired (no panic).
func (*Storage) SetItemChangePublisher ¶ added in v1.41.0
func (s *Storage) SetItemChangePublisher(p ItemChangePublisher)
SetItemChangePublisher wires p into the underlying repository when it is ent-backed, following the same type-assertion-forwarding precedent as GetEntClient above. server/dependencies.go only has a *Storage value in scope (Storage.repo is a Repository interface field, not a concrete *EntRepository), so this forwarding method is the entry point it uses to reach ItemChangePublisher wiring. When the repository is not ent-backed (e.g. an in-memory test double), the type assertion fails gracefully and the publisher is simply never wired — no panic, matching GetEntClient's nil-on-mismatch behavior.
func (*Storage) SetItemSessionBaseCommit ¶ added in v1.41.0
SetItemSessionBaseCommit records the pre-work base commit SHA on an ItemSession. See the EntRepository method for why this is separate from git activity.
func (*Storage) SetSessionGoal ¶ added in v1.35.0
func (s *Storage) SetSessionGoal(ctx context.Context, sessionUUID string, goal string, status string, tasks []TaskNode, setBy string, workspaceKey string) (*SessionGoalData, error)
SetSessionGoal upserts the goal for a session (1:1 per session_uuid). If a goal already exists for the session, it is replaced. workspaceKey, when non-empty, is stamped in the same upsert as the goal write (rather than a separate follow-up UPDATE) so the two never diverge on a crash between writes.
func (*Storage) SetSessionGoalWorkspaceKey ¶ added in v1.41.0
func (s *Storage) SetSessionGoalWorkspaceKey(ctx context.Context, sessionUUID, workspaceKey string) error
SetSessionGoalWorkspaceKey stamps the workspace_key column on an existing goal row. Best-effort/no-op if no goal row exists yet for sessionUUID. Kept separate from SetSessionGoal so that method's signature (and existing tests/call sites) stay unchanged.
func (*Storage) SnoozeStuckState ¶ added in v1.38.0
func (s *Storage) SnoozeStuckState(ctx context.Context, itemID string, reason domain.StuckReason, until time.Time) (bool, error)
SnoozeStuckState sets snoozed_until on an open BacklogStuckState row for (itemID, reason). Returns false, nil when the backend does not support stuck-state writes or no matching open row exists — never an error for a missing row.
func (*Storage) TransitionBacklogItemStatus ¶ added in v1.35.0
func (s *Storage) TransitionBacklogItemStatus(ctx context.Context, id string, toStatus BacklogStatus, precondition *BacklogItemPrecondition, triggeredBy string) (*BacklogItemData, error)
TransitionBacklogItemStatus changes the status of a backlog item.
func (*Storage) UnarchiveBacklogItem ¶ added in v1.44.0
UnarchiveBacklogItem clears archived_at and restores the item to "idea".
func (*Storage) UnresolvedBlockerIDs ¶ added in v1.43.0
UnresolvedBlockerIDs returns the specific blocker item IDs still unresolved for a single item.
func (*Storage) UnresolvedBlockerItemIDs ¶ added in v1.43.0
func (s *Storage) UnresolvedBlockerItemIDs(ctx context.Context, itemIDs []string) (map[string]bool, error)
UnresolvedBlockerItemIDs returns the subset of itemIDs blocked by an unresolved dependency.
func (*Storage) UpdateAcCriterionStatus ¶ added in v1.35.0
func (s *Storage) UpdateAcCriterionStatus(ctx context.Context, itemID string, criterionIndex int, status string, note string) error
UpdateAcCriterionStatus updates a single acceptance criterion's status by index.
func (*Storage) UpdateBacklogItem ¶ added in v1.35.0
func (s *Storage) UpdateBacklogItem(ctx context.Context, id string, update BacklogItemUpdate, precondition *BacklogItemPrecondition) (*BacklogItemData, error)
UpdateBacklogItem modifies an existing backlog item.
func (*Storage) UpdateInstance ¶
UpdateInstance updates an existing instance in storage.
func (*Storage) UpdateInstanceAcknowledged ¶ added in v1.18.0
UpdateInstanceAcknowledged sets the LastAcknowledged timestamp to now for a specific instance. Used by AcknowledgeSession when the instance is not available in the live poller.
func (*Storage) UpdateInstanceArtifacts ¶ added in v1.35.0
UpdateInstanceArtifacts persists the JSON-encoded artifact blob for a session. Only the session_artifacts column is touched; all other fields are unchanged.
func (*Storage) UpdateInstanceForkFlag ¶ added in v1.12.0
UpdateInstanceForkFlag is intentionally a no-op: fork status is not persisted in the ent schema. Callers (e.g. PRStatusPoller) call this as a persistence hook, but no DB write occurs.
func (*Storage) UpdateInstanceLastAddedToQueue ¶
UpdateInstanceLastAddedToQueue updates ONLY the LastAddedToQueue field for a specific instance.
func (*Storage) UpdateInstanceLastUserResponse ¶
UpdateInstanceLastUserResponse persists the LastUserResponse timestamp for a session. Uses a direct UPDATE (no read round-trip) via UpdateReviewQueueState.
func (*Storage) UpdateInstanceMetadata ¶ added in v1.42.0
func (s *Storage) UpdateInstanceMetadata(currentTitle string, newTitle, category, note, workingDir *string) error
UpdateInstanceMetadata persists a narrow set of session metadata fields (title rename, category, note, working dir) via a single UPDATE, avoiding the full-row rewrite (and worktree/diffstats/tags/claude_session churn) that SaveInstances performs for every started session. currentTitle must be the title from before any rename already applied in-memory by the caller — see EntRepository.UpdateSessionMetadata for why. A nil field pointer leaves that field untouched.
func (*Storage) UpdateInstancePRNumber ¶ added in v1.12.0
UpdateInstancePRNumber persists the discovered PR number for a session so it survives restarts and avoids repeated branch-name lookups in PRStatusPoller.
func (*Storage) UpdateInstancePRStatus ¶ added in v1.12.0
UpdateInstancePRStatus updates the PR status fields for a specific instance. PR fields are not stored in the ent schema — they live in memory and are re-populated by PRStatusPoller on each poll cycle. No DB write is needed.
func (*Storage) UpdateInstanceProcessingGrace ¶
UpdateInstanceProcessingGrace persists the ProcessingGraceUntil timestamp. Uses a direct UPDATE (no read round-trip) via UpdateReviewQueueState.
func (*Storage) UpdateInstanceTimestampsOnly ¶
func (s *Storage) UpdateInstanceTimestampsOnly(title string, lastTerminalUpdate, lastMeaningfulOutput time.Time, lastOutputSignature string, lastViewed time.Time) error
UpdateInstanceTimestampsOnly updates ONLY the timestamp fields in storage without creating Instance objects. This preserves in-memory state like controllers. This is critical for WebSocket terminal streaming which updates timestamps frequently.
func (*Storage) UpdateItemSessionEnded ¶ added in v1.35.0
UpdateItemSessionEnded records the end time for an ItemSession.
func (*Storage) UpdateItemSessionEndedWithReason ¶ added in v1.41.0
func (s *Storage) UpdateItemSessionEndedWithReason(ctx context.Context, id string, endedAt time.Time, reason string) error
UpdateItemSessionEndedWithReason records the end time for an ItemSession alongside classifyHeadlessCallError's bucket (or "" for a successful end).
func (*Storage) UpdateItemSessionFailureCapture ¶ added in v1.42.0
func (s *Storage) UpdateItemSessionFailureCapture(ctx context.Context, id string, path string) error
UpdateItemSessionFailureCapture records the absolute path to a durable raw-output capture file for a headless triage/review call that errored or produced unparseable output. See EntRepository.UpdateItemSessionFailureCapture.
func (*Storage) UpdateItemSessionGitActivity ¶ added in v1.37.0
func (s *Storage) UpdateItemSessionGitActivity(ctx context.Context, id string, sha, msg string, commitAt time.Time, commitCount int) error
UpdateItemSessionGitActivity records the session's current tip commit and related fields on an ItemSession. For the spawn-time baseline, use SetItemSessionBaseCommit.
func (*Storage) UpdateItemSessionSessionUUID ¶ added in v1.35.0
func (s *Storage) UpdateItemSessionSessionUUID(ctx context.Context, id string, sessionUUID string) error
UpdateItemSessionSessionUUID updates the session_uuid on an existing ItemSession record.
func (*Storage) UpdateItemSessionStarted ¶ added in v1.35.0
func (s *Storage) UpdateItemSessionStarted(ctx context.Context, id string, startedAt time.Time) error
UpdateItemSessionStarted records the start time for an ItemSession.
func (*Storage) UpdateItemSessionTriageResult ¶ added in v1.35.0
func (s *Storage) UpdateItemSessionTriageResult(ctx context.Context, id string, triageResult string) error
UpdateItemSessionTriageResult stores the triage result JSON payload on an ItemSession.
func (*Storage) UpdateItemSessionVerificationNotes ¶ added in v1.37.0
func (s *Storage) UpdateItemSessionVerificationNotes(ctx context.Context, id string, verificationNotes string) error
UpdateItemSessionVerificationNotes stores verification evidence (commands run, manual checks performed) reported via request_review on an ItemSession.
func (*Storage) UpdateItemSource ¶ added in v1.35.0
func (s *Storage) UpdateItemSource(ctx context.Context, id string, update ItemSourceUpdate) (*ItemSourceData, error)
UpdateItemSource modifies an existing item source.
func (*Storage) UpdateProject ¶ added in v1.23.0
func (s *Storage) UpdateProject(ctx context.Context, data ProjectData) (*ProjectData, error)
UpdateProject modifies an existing project in storage.
func (*Storage) UpdateSessionTaskStatus ¶ added in v1.35.0
func (s *Storage) UpdateSessionTaskStatus(ctx context.Context, sessionUUID string, taskID string, newStatus string) (*SessionGoalData, error)
UpdateSessionTaskStatus loads the goal for a session, finds the task by ID, updates its status, and saves the goal back. Returns ErrNotFound if no goal exists, or an error if task_id is not found in the tree. The read-modify-write is wrapped in a transaction to prevent concurrent update races.
func (*Storage) UpsertRule ¶ added in v1.12.0
func (s *Storage) UpsertRule(ctx context.Context, rule ApprovalRuleData) error
UpsertRule creates or updates an auto-approval rule in the repository.
func (*Storage) WireChainFirer ¶ added in v1.43.0
func (s *Storage) WireChainFirer(workflows WorkflowRepository, fireEvents TriggerFireEventRepository, firer TriggerFirer, cfg *config.Config) *ChainFirer
WireChainFirer constructs a ChainFirer bound to the underlying *EntRepository (so its ListItemSessions/UpdateBacklogItem calls share the exact same callbackDispatcher/itemChangePublisher wiring as every other backlog mutation) and wires it as that repository's own chain-fire dispatcher (EntRepository.SetChainFirer — the happy-path caller from TransitionBacklogItemStatus, webhook-triggers Phase 6). Returns nil when the repository is not ent-backed, mirroring GetEntClient's nil-on-mismatch behavior — callers should skip TriggerChainReconciler wiring in that case.
type SubcommandDecisionCount ¶ added in v1.35.0
SubcommandDecisionCount holds a (subcommand, decision) aggregate count. Returned by GetSubcommandBreakdown.
type Subscriber ¶
type Subscriber struct {
ID string
Ch chan ResponseChunk
// contains filtered or unexported fields
}
Subscriber represents a client that is receiving response chunks.
type SuspendedProcessRecord ¶ added in v1.42.0
type SuspendedProcessRecord struct {
PID int32 `json:"pid"`
CreateTimeMs int64 `json:"create_time_ms"`
Candidate ExternalSessionCandidate `json:"candidate"`
InstanceID string `json:"instance_id"`
}
SuspendedProcessRecord durably records an external process this server SIGSTOP'd during CommitImportExternalSession, so a server restart can reconcile it (see ReconcileSuspendedProcesses) instead of leaving it frozen forever.
type SuspendedProcessStore ¶ added in v1.42.0
type SuspendedProcessStore struct {
// contains filtered or unexported fields
}
SuspendedProcessStore persists SuspendedProcessRecord entries to suspended_processes.json using the same exclusive-flock + write-tmp-then-rename idiom as config/state.go.
func NewSuspendedProcessStore ¶ added in v1.42.0
func NewSuspendedProcessStore() (*SuspendedProcessStore, error)
NewSuspendedProcessStore creates a store rooted at the same config directory config.GetConfigDir() resolves (workspace-isolated in tests via STAPLER_SQUAD_TEST_DIR, matching config/state.go's behavior).
func (*SuspendedProcessStore) Add ¶ added in v1.42.0
func (s *SuspendedProcessStore) Add(record SuspendedProcessRecord) error
Add persists a SuspendedProcessRecord under an exclusive lock, using upsert semantics: any existing record for the same InstanceID is replaced rather than duplicated. CommitImportExternalSession's compensating-delete path can retry Add for the same InstanceID (e.g. after a transient write failure), and without this dedup a retry would leave two records for one instance, which would make ReconcileSuspendedProcesses/Remove behavior ambiguous.
func (*SuspendedProcessStore) Get ¶ added in v1.42.0
func (s *SuspendedProcessStore) Get(instanceID string) (SuspendedProcessRecord, bool, error)
Get returns the SuspendedProcessRecord for instanceID, if one exists.
func (*SuspendedProcessStore) List ¶ added in v1.42.0
func (s *SuspendedProcessStore) List() ([]SuspendedProcessRecord, error)
List returns every currently-persisted SuspendedProcessRecord.
func (*SuspendedProcessStore) Remove ¶ added in v1.42.0
func (s *SuspendedProcessStore) Remove(instanceID string) error
Remove deletes the record for instanceID, if present. Removing a non-existent record is not an error -- callers call this on every resume/kill path, some of which race with reconciliation.
type SyncLoop ¶ added in v1.35.0
type SyncLoop struct {
// contains filtered or unexported fields
}
SyncLoop drives periodic sync of all enabled ItemSources.
func NewSyncLoop ¶ added in v1.35.0
func NewSyncLoop(storage *Storage, registry *PluginRegistry) *SyncLoop
NewSyncLoop creates a SyncLoop with the default interval and no key provider.
func NewSyncLoopWithKeyProvider ¶ added in v1.35.0
func NewSyncLoopWithKeyProvider(storage *Storage, registry *PluginRegistry, keyFunc func() ([]byte, error)) *SyncLoop
NewSyncLoopWithKeyProvider creates a SyncLoop with a key provider for decryption.
func (*SyncLoop) DecryptConfigToken ¶ added in v1.41.0
DecryptConfigToken decrypts an encrypted token in config JSON if needed. If the config has "encrypted":true, it decrypts the token field using the provided key function. If decryption is not available or not needed, returns the raw config unchanged. Exported so package server/services (holding a *SyncLoop handle) can call it cross-package for the forward-sync subscriber.
func (*SyncLoop) PreviewBackwardSyncImpact ¶ added in v1.41.0
func (sl *SyncLoop) PreviewBackwardSyncImpact(ctx context.Context, source *ent.ItemSource) (itemCount int, sampleTitles []string, possiblyIncomplete bool, err error)
PreviewBackwardSyncImpact reports how many already-imported items for source would immediately transition — per determineBackwardSyncTarget, ADR-002 — if backward sync were enabled for it right now. Used to gate the Settings UI's first-enable confirmation dialog (Epic 4.4, resolving Unresolved Question #3) so a user can see the blast radius of already-closed linked issues before opting in, rather than a silent bulk-archive on the same tick the toggle flips.
Read-only: reuses the same decrypted-token/plugin-Fetch path SyncOne uses, but does not advance source.SyncCursor, does not record a SourceSyncEvent, and does not itself gate on source.BackwardSyncEnabled — the entire point is to preview what WOULD happen if it were enabled. Deliberately fetches with an empty cursor rather than source.SyncCursor: the preview needs the full current state of already-imported items' issues (a source may have been forward-syncing for a while before backward sync is ever considered, advancing the cursor well past issues that are now closed but haven't changed since).
If the plugin implements PaginatedFetcher, the full result set is fetched across all pages (bounded by the plugin's own cap) rather than just the newest page — GitHub's Issues API sorts by `created` descending by default, so a single-page Fetch would silently miss older closed issues on repos with more than one page of history, undercounting the blast radius. possiblyIncomplete is true when the underlying fetch hit its page cap, meaning the count/titles returned are a lower bound, not exhaustive.
func (*SyncLoop) PreviewBackwardSyncImpactByID ¶ added in v1.41.0
func (sl *SyncLoop) PreviewBackwardSyncImpactByID(ctx context.Context, sourceID string) (itemCount int, sampleTitles []string, possiblyIncomplete bool, err error)
PreviewBackwardSyncImpactByID looks up an ItemSource by ID and previews the impact of enabling backward sync for it — see PreviewBackwardSyncImpact.
func (*SyncLoop) Start ¶ added in v1.35.0
Start runs the sync loop until ctx is cancelled or Stop is called.
func (*SyncLoop) Stop ¶ added in v1.35.0
func (sl *SyncLoop) Stop()
Stop gracefully shuts down the sync loop. Safe to call multiple times.
func (*SyncLoop) SyncByID ¶ added in v1.35.0
SyncByID looks up an ItemSource by ID and syncs it, regardless of its Enabled flag — unlike the periodic loop (runAllSources), which only syncs enabled sources, this is for an explicit manual/on-demand trigger where the caller already decided to sync this specific source.
type TagManager ¶
type TagManager struct {
// contains filtered or unexported fields
}
TagManager provides CRUD operations for session tags. It is a pure data structure with no I/O or external dependencies. Thread safety is provided by Instance.mu -- callers must hold the lock when calling TagManager methods.
TagManager stores a pointer to the Instance.Tags slice so that mutations are automatically visible via inst.Tags (used by instance_adapter.go, review_queue_poller.go, and ToInstanceData for serialization).
func NewTagManager ¶
func NewTagManager(tags *[]string) TagManager
NewTagManager creates a TagManager backed by the given slice pointer.
func (*TagManager) Add ¶
func (tm *TagManager) Add(tag string) error
Add adds a tag if it does not already exist and does not exceed MaxTagLength. Returns ErrTagTooLong if the tag exceeds MaxTagLength. Returns ErrDuplicateTag if the tag already exists.
func (*TagManager) Has ¶
func (tm *TagManager) Has(tag string) bool
Has returns true if the tag exists.
func (*TagManager) Remove ¶
func (tm *TagManager) Remove(tag string)
Remove removes a tag by value. No-op if the tag does not exist.
func (*TagManager) Set ¶
func (tm *TagManager) Set(tags []string) error
Set replaces all tags with a new deduplicated set. Returns ErrTagTooLong on the first tag that exceeds MaxTagLength. Returns ErrTooManyTags if the deduplicated count exceeds MaxTagCount.
type TaskNode ¶ added in v1.35.0
type TaskNode struct {
ID string `json:"id"`
Title string `json:"title"`
Status string `json:"status"`
Children []TaskNode `json:"children,omitempty"`
}
TaskNode represents a single task in the goal's task tree.
func DecodeTasks ¶ added in v1.35.0
DecodeTasks deserializes a JSON string to a task tree.
type TerminalContext ¶
type TerminalContext struct {
// Height is the terminal height in rows
Height int `json:"height,omitempty"`
// Width is the terminal width in columns
Width int `json:"width,omitempty"`
// TmuxSessionName is the name of the tmux session
TmuxSessionName string `json:"tmux_session_name,omitempty"`
// TmuxPrefix is the prefix used for tmux session naming
TmuxPrefix string `json:"tmux_prefix,omitempty"`
// TmuxServerSocket is the path to the tmux server socket
TmuxServerSocket string `json:"tmux_server_socket,omitempty"`
// TerminalType indicates the terminal backend type
// Possible values: "tmux", "mux", "pty", "web"
TerminalType string `json:"terminal_type,omitempty"`
}
TerminalContext represents the terminal-related context for a session. This includes terminal dimensions, tmux configuration, and terminal type.
func (*TerminalContext) IsEmpty ¶
func (t *TerminalContext) IsEmpty() bool
IsEmpty returns true if the TerminalContext has no meaningful data
type TimeRestriction ¶
type TimeRestriction struct {
DaysOfWeek []time.Weekday `json:"days_of_week"` // Empty = all days
StartHour int `json:"start_hour"` // 0-23
EndHour int `json:"end_hour"` // 0-23
}
TimeRestriction limits when a policy is active.
type TimelineSnapshot ¶ added in v1.41.0
TimelineSnapshot captures the start/stop times of a session.
func BuildTimelineSnapshot ¶ added in v1.41.0
func BuildTimelineSnapshot(createdAt time.Time, stoppedAt time.Time) TimelineSnapshot
BuildTimelineSnapshot builds a TimelineSnapshot from a session's creation time and the time it was captured as stopped (both captured at dispatch time by the caller, not re-derived later).
func (TimelineSnapshot) Duration ¶ added in v1.41.0
func (t TimelineSnapshot) Duration() time.Duration
Duration returns the elapsed time between StartedAt and StoppedAt.
type TmuxBackend ¶ added in v1.35.0
type TmuxBackend struct {
// contains filtered or unexported fields
}
TmuxBackend implements ProcessManager by delegating to TmuxManager. It is the default backend used when process_manager_backend = "tmux" (or empty).
func NewTmuxBackend ¶ added in v1.35.0
func NewTmuxBackend(mgr TmuxManager) *TmuxBackend
NewTmuxBackend creates a TmuxBackend wrapping the given TmuxManager.
func (*TmuxBackend) Attach ¶ added in v1.35.0
func (b *TmuxBackend) Attach() (chan struct{}, error)
func (*TmuxBackend) CapturePaneContent ¶ added in v1.35.0
func (b *TmuxBackend) CapturePaneContent() (string, error)
func (*TmuxBackend) CapturePaneContentRaw ¶ added in v1.35.0
func (b *TmuxBackend) CapturePaneContentRaw() (string, error)
func (*TmuxBackend) CapturePaneContentWithOptions ¶ added in v1.35.0
func (b *TmuxBackend) CapturePaneContentWithOptions(start, end string) (string, error)
func (*TmuxBackend) CaptureViewport ¶ added in v1.35.0
func (b *TmuxBackend) CaptureViewport(lines int) (string, error)
func (*TmuxBackend) Close ¶ added in v1.35.0
func (b *TmuxBackend) Close() error
func (*TmuxBackend) DetachSafely ¶ added in v1.35.0
func (b *TmuxBackend) DetachSafely() error
func (*TmuxBackend) FilterBanners ¶ added in v1.35.0
func (b *TmuxBackend) FilterBanners(content string) (string, int)
func (*TmuxBackend) GetCurrentWorkingDirectory ¶ added in v1.35.0
func (b *TmuxBackend) GetCurrentWorkingDirectory() (string, error)
GetCurrentWorkingDirectory returns the current working directory of the pane. Delegates to the underlying Session().GetPaneCurrentPath() via type assertion.
func (*TmuxBackend) GetCursorPosition ¶ added in v1.35.0
func (b *TmuxBackend) GetCursorPosition() (x, y int, err error)
func (*TmuxBackend) GetPaneDimensions ¶ added in v1.35.0
func (b *TmuxBackend) GetPaneDimensions() (width, height int, err error)
func (*TmuxBackend) GetPanePID ¶ added in v1.35.0
func (b *TmuxBackend) GetPanePID() (int32, error)
func (*TmuxBackend) GetSessionIdentifier ¶ added in v1.35.0
func (b *TmuxBackend) GetSessionIdentifier() string
GetSessionIdentifier implements ProcessManager by delegating to GetTmuxSessionName. This is the name-mapping method: backend-agnostic callers use GetSessionIdentifier, but the value is identical to what GetTmuxSessionName returns for the tmux backend.
func (*TmuxBackend) HasMeaningfulContent ¶ added in v1.35.0
func (b *TmuxBackend) HasMeaningfulContent(content string) bool
func (*TmuxBackend) HasSession ¶ added in v1.35.0
func (b *TmuxBackend) HasSession() bool
func (*TmuxBackend) HasUpdated ¶ added in v1.35.0
func (b *TmuxBackend) HasUpdated() (updated bool, hasPrompt bool, content string)
func (*TmuxBackend) IsAlive ¶ added in v1.35.0
func (b *TmuxBackend) IsAlive() bool
func (*TmuxBackend) RefreshClient ¶ added in v1.35.0
func (b *TmuxBackend) RefreshClient() error
func (*TmuxBackend) ResetExitOnce ¶ added in v1.35.0
func (b *TmuxBackend) ResetExitOnce()
func (*TmuxBackend) RestoreWithWorkDir ¶ added in v1.35.0
func (b *TmuxBackend) RestoreWithWorkDir(w string) error
func (*TmuxBackend) SendInputViaControlMode ¶ added in v1.35.0
func (b *TmuxBackend) SendInputViaControlMode(ctx context.Context, data []byte) error
func (*TmuxBackend) SendKeys ¶ added in v1.35.0
func (b *TmuxBackend) SendKeys(keys string) (int, error)
func (*TmuxBackend) SendPromptWithEnter ¶ added in v1.35.0
func (b *TmuxBackend) SendPromptWithEnter(p string) error
func (*TmuxBackend) SetDetachedSize ¶ added in v1.35.0
func (b *TmuxBackend) SetDetachedSize(w, h int, title string) error
func (*TmuxBackend) SetOnExitCallback ¶ added in v1.35.0
func (b *TmuxBackend) SetOnExitCallback(fn func(string))
func (*TmuxBackend) SetWindowSize ¶ added in v1.35.0
func (b *TmuxBackend) SetWindowSize(cols, rows int) error
func (*TmuxBackend) Start ¶ added in v1.35.0
func (b *TmuxBackend) Start(dir string) error
func (*TmuxBackend) StartControlMode ¶ added in v1.35.0
func (b *TmuxBackend) StartControlMode() error
func (*TmuxBackend) StopControlMode ¶ added in v1.35.0
func (b *TmuxBackend) StopControlMode() error
func (*TmuxBackend) SubscribeToControlModeUpdates ¶ added in v1.35.0
func (b *TmuxBackend) SubscribeToControlModeUpdates() (string, chan []byte)
func (*TmuxBackend) TapEnter ¶ added in v1.35.0
func (b *TmuxBackend) TapEnter() error
func (*TmuxBackend) TmuxManager ¶ added in v1.35.0
func (b *TmuxBackend) TmuxManager() TmuxManager
TmuxManager returns the underlying TmuxManager for type assertions in reconciliation paths that need tmux-specific operations (e.g. Session(), SetSession()).
func (*TmuxBackend) UnsubscribeFromControlModeUpdates ¶ added in v1.35.0
func (b *TmuxBackend) UnsubscribeFromControlModeUpdates(id string)
type TmuxManager ¶ added in v1.15.0
type TmuxManager interface {
HasSession() bool
Session() *tmux.TmuxSession
SetSession(*tmux.TmuxSession)
GetTmuxSessionName() string
IsAlive() bool
Close() error
DetachSafely() error
DoesSessionExist() bool
SetDetachedSize(width, height int, instanceTitle string) error
Attach() (chan struct{}, error)
CapturePaneContent() (string, error)
CapturePaneContentPriority() (string, error)
CapturePaneContentRaw() (string, error)
CapturePaneContentWithOptions(startLine, endLine string) (string, error)
GetPaneDimensions() (width, height int, err error)
GetCursorPosition() (x, y int, err error)
GetPTY() (*os.File, error)
SendKeys(keys string) (int, error)
SetWindowSize(cols, rows int) error
RefreshClient() error
RefreshClientPriority() error
TapEnter() error
HasUpdated() (updated bool, hasPrompt bool, content string)
RestoreWithWorkDir(workDir string) error
Start(dir string) error
FilterBanners(content string) (string, int)
HasMeaningfulContent(content string) bool
CaptureViewport(lines int) (string, error)
SendPromptWithEnter(prompt string) error
GetPanePID() (int32, error)
SetOnExitCallback(fn func(string))
ResetExitOnce()
StartControlMode() error
StopControlMode() error
SubscribeToControlModeUpdates() (string, chan []byte)
UnsubscribeFromControlModeUpdates(id string)
SendInputViaControlMode(ctx context.Context, data []byte) error
// PaneExitStatus reports whether the pane's wrapped program has already
// exited even though the tmux session object itself is still alive.
// remain-on-exit keeps a "Pane is dead" placeholder pane around after the
// wrapped program is killed (e.g. OOM SIGKILL) instead of tearing the
// session down, so IsAlive()/HasSession() alone cannot detect this state.
PaneExitStatus() (code int, signal string, dead bool)
}
TmuxManager is the interface satisfied by *TmuxProcessManager. It covers all tmux session operations used by Instance and can be implemented by test doubles to avoid requiring a real tmux server.
type TmuxProcessManager ¶
type TmuxProcessManager struct {
// contains filtered or unexported fields
}
TmuxProcessManager owns the tmux session and preview-size tracking state that were previously scattered as bare fields on Instance.
Instance keeps thin wrapper methods (with started/paused guards) that delegate here. TmuxProcessManager itself has no knowledge of Instance lifecycle; it only manages the tmux session and the preview-resize bookkeeping.
func (*TmuxProcessManager) Attach ¶
func (tm *TmuxProcessManager) Attach() (chan struct{}, error)
Attach returns a channel that closes when the user detaches from the session.
func (*TmuxProcessManager) CapturePaneContent ¶
func (tm *TmuxProcessManager) CapturePaneContent() (string, error)
CapturePaneContent returns the current visible pane content. Results are cached for capturePaneCacheTTL to reduce subprocess/forkLock contention when called per-session on every poll tick.
func (*TmuxProcessManager) CapturePaneContentContext ¶ added in v1.46.0
func (tm *TmuxProcessManager) CapturePaneContentContext(ctx context.Context) (string, error)
CapturePaneContentContext mirrors CapturePaneContent (including its capturePaneCacheTTL cache) but threads ctx onto the underlying subprocess call on a cache miss, so a caller that cancels ctx can kill an in-flight capture-pane process rather than only abandoning the exec-gate wait. Used by Instance.PreviewContext for the SessionDriver polling path, whose stop channel needs to interrupt a capture already underway — see session/session_driver.go's stop/join mechanism.
func (*TmuxProcessManager) CapturePaneContentPriority ¶ added in v1.44.0
func (tm *TmuxProcessManager) CapturePaneContentPriority() (string, error)
CapturePaneContentPriority mirrors CapturePaneContent but routes the subprocess call through the resync exec-gate fast lane instead of the default pool (Epic 4.2, terminal:resync-exec-gate-fast-lane), and always captures fresh — it deliberately bypasses the capturePaneCacheTTL cache since resync's whole point is an up-to-date snapshot, not the cached value a concurrent poll tick may have populated.
func (*TmuxProcessManager) CapturePaneContentRaw ¶
func (tm *TmuxProcessManager) CapturePaneContentRaw() (string, error)
CapturePaneContentRaw returns pane content with ANSI escape codes preserved.
func (*TmuxProcessManager) CapturePaneContentWithOptions ¶
func (tm *TmuxProcessManager) CapturePaneContentWithOptions(startLine, endLine string) (string, error)
CapturePaneContentWithOptions captures pane content between startLine and endLine.
func (*TmuxProcessManager) CaptureViewport ¶
func (tm *TmuxProcessManager) CaptureViewport(lines int) (string, error)
CaptureViewport captures the last N lines of the pane. If lines <= 0, captures the current viewport height.
func (*TmuxProcessManager) Close ¶
func (tm *TmuxProcessManager) Close() error
Close terminates the tmux session.
func (*TmuxProcessManager) DetachSafely ¶
func (tm *TmuxProcessManager) DetachSafely() error
DetachSafely detaches the current tmux client from the session without closing it.
func (*TmuxProcessManager) DoesSessionExist ¶
func (tm *TmuxProcessManager) DoesSessionExist() bool
DoesSessionExist returns true if the tmux session name is registered with the server.
func (*TmuxProcessManager) FilterBanners ¶
func (tm *TmuxProcessManager) FilterBanners(content string) (string, int)
FilterBanners strips banner/header content from terminal output.
func (*TmuxProcessManager) GetCursorPosition ¶
func (tm *TmuxProcessManager) GetCursorPosition() (x, y int, err error)
GetCursorPosition returns the current cursor column and row (0-based).
func (*TmuxProcessManager) GetPTY ¶
func (tm *TmuxProcessManager) GetPTY() (*os.File, error)
GetPTY returns the PTY master file for reading terminal output.
func (*TmuxProcessManager) GetPaneDimensions ¶
func (tm *TmuxProcessManager) GetPaneDimensions() (width, height int, err error)
GetPaneDimensions returns the current pane width and height.
func (*TmuxProcessManager) GetPanePID ¶
func (tm *TmuxProcessManager) GetPanePID() (int32, error)
GetPanePID returns the PID of the foreground process in the pane. The pane PID is stable for the lifetime of a tmux pane, so the result is cached after the first successful lookup to avoid repeated subprocess calls.
func (*TmuxProcessManager) GetTmuxSessionName ¶ added in v1.15.0
func (tm *TmuxProcessManager) GetTmuxSessionName() string
GetTmuxSessionName returns the sanitized tmux session name for reconciliation. Returns empty string when no session has been initialized.
func (*TmuxProcessManager) HasMeaningfulContent ¶
func (tm *TmuxProcessManager) HasMeaningfulContent(content string) bool
HasMeaningfulContent reports whether the terminal output contains substantive content.
func (*TmuxProcessManager) HasSession ¶
func (tm *TmuxProcessManager) HasSession() bool
HasSession reports whether a tmux session has been initialized.
func (*TmuxProcessManager) HasUpdated ¶
func (tm *TmuxProcessManager) HasUpdated() (updated bool, hasPrompt bool, content string)
HasUpdated reports whether the pane content has changed since the last check.
func (*TmuxProcessManager) IsAlive ¶
func (tm *TmuxProcessManager) IsAlive() bool
IsAlive reports whether the tmux session process is still running.
func (*TmuxProcessManager) PaneExitStatus ¶ added in v1.37.0
func (tm *TmuxProcessManager) PaneExitStatus() (code int, signal string, dead bool)
PaneExitStatus reports the wrapped program's exit code/signal for a dead pane whose tmux session is otherwise still alive (remain-on-exit keeps the placeholder pane around after the wrapped program exits/is killed). Returns dead=false if there is no session, or the pane is still running.
func (*TmuxProcessManager) RefreshClient ¶
func (tm *TmuxProcessManager) RefreshClient() error
RefreshClient forces the tmux client to redraw.
func (*TmuxProcessManager) RefreshClientPriority ¶ added in v1.44.0
func (tm *TmuxProcessManager) RefreshClientPriority() error
RefreshClientPriority mirrors RefreshClient but routes the subprocess call through the resync exec-gate fast lane instead of the default pool (Epic 4.2, terminal:resync-exec-gate-fast-lane).
func (*TmuxProcessManager) ResetExitOnce ¶ added in v1.15.0
func (tm *TmuxProcessManager) ResetExitOnce()
ResetExitOnce resets the sync.Once guard so that the exit callback can fire again on the next start cycle (e.g., after a restart). No-op if no session.
func (*TmuxProcessManager) RestoreWithWorkDir ¶
func (tm *TmuxProcessManager) RestoreWithWorkDir(workDir string) error
RestoreWithWorkDir re-attaches to an existing session in the given directory.
func (*TmuxProcessManager) SendInputViaControlMode ¶ added in v1.35.0
func (tm *TmuxProcessManager) SendInputViaControlMode(ctx context.Context, data []byte) error
SendInputViaControlMode sends raw bytes through the existing control mode connection.
func (*TmuxProcessManager) SendKeys ¶
func (tm *TmuxProcessManager) SendKeys(keys string) (int, error)
SendKeys sends a string of keys to the tmux session and returns the number of bytes written.
func (*TmuxProcessManager) SendPromptWithEnter ¶
func (tm *TmuxProcessManager) SendPromptWithEnter(prompt string) error
SendPromptWithEnter sends text to the session followed by Enter key. Includes a brief pause between text and Enter to prevent interpretation issues.
func (*TmuxProcessManager) Session ¶
func (tm *TmuxProcessManager) Session() *tmux.TmuxSession
Session returns the underlying tmux session (may be nil before Start).
func (*TmuxProcessManager) SetDetachedSize ¶
func (tm *TmuxProcessManager) SetDetachedSize(width, height int, instanceTitle string) error
SetDetachedSize updates the tmux window dimensions without attaching. Rate-limits PTY-not-initialized warnings to avoid log spam.
func (*TmuxProcessManager) SetOnExitCallback ¶ added in v1.15.0
func (tm *TmuxProcessManager) SetOnExitCallback(fn func(string))
SetOnExitCallback registers a callback that fires when the tmux session exits unexpectedly. No-op if no session is initialized.
func (*TmuxProcessManager) SetSession ¶
func (tm *TmuxProcessManager) SetSession(s *tmux.TmuxSession)
SetSession replaces the underlying tmux session. Used by tests and by Instance.start() when reusing a pre-created session.
func (*TmuxProcessManager) SetWindowSize ¶
func (tm *TmuxProcessManager) SetWindowSize(cols, rows int) error
SetWindowSize resizes the tmux window to the given columns and rows.
func (*TmuxProcessManager) Start ¶
func (tm *TmuxProcessManager) Start(dir string) error
Start creates and starts the tmux session in the given directory.
func (*TmuxProcessManager) StartControlMode ¶ added in v1.15.0
func (tm *TmuxProcessManager) StartControlMode() error
StartControlMode starts the tmux control mode stream. Returns nil if no session is initialized.
func (*TmuxProcessManager) StopControlMode ¶ added in v1.15.0
func (tm *TmuxProcessManager) StopControlMode() error
StopControlMode stops the tmux control mode stream. Returns nil if no session is initialized.
func (*TmuxProcessManager) SubscribeToControlModeUpdates ¶ added in v1.15.0
func (tm *TmuxProcessManager) SubscribeToControlModeUpdates() (string, chan []byte)
SubscribeToControlModeUpdates registers a new subscriber for real-time terminal output. Returns a pre-closed channel if no session is initialized.
func (*TmuxProcessManager) TapEnter ¶
func (tm *TmuxProcessManager) TapEnter() error
TapEnter sends an Enter key to the session.
func (*TmuxProcessManager) UnsubscribeFromControlModeUpdates ¶ added in v1.15.0
func (tm *TmuxProcessManager) UnsubscribeFromControlModeUpdates(id string)
UnsubscribeFromControlModeUpdates removes a subscriber by ID. No-op if no session is initialized.
type TmuxSocketQuerier ¶ added in v1.37.0
type TmuxSocketQuerier interface {
// ListSessions returns the set of live tmux session names on serverSocket.
ListSessions(serverSocket string) (map[string]bool, error)
// IsServerDown reports whether the tmux server on serverSocket is unreachable.
IsServerDown(serverSocket string) bool
}
TmuxSocketQuerier abstracts read-only tmux server-socket queries so that callers needing to know "which sessions are alive on this socket" or "is this socket's server down" can be exercised in tests without a real tmux server. Production code backs this with the real tmux package (via realTmuxSocketQuerier); tests substitute a fake.
Both ReviewQueuePoller.reconcileSessions and SessionHealthChecker.CheckAllSessions need this: instances can be spread across multiple tmux server sockets (the default socket for ordinary sessions, isolated sockets for some worktree/test scenarios), so every query must be scoped to a specific socket rather than assumed to be shared across all instances.
func NewRealTmuxSocketQuerier ¶ added in v1.42.0
func NewRealTmuxSocketQuerier() TmuxSocketQuerier
NewRealTmuxSocketQuerier returns the production TmuxSocketQuerier backed by the real tmux package, for callers outside this package (e.g. server/services) that need to query live tmux state without depending on the unexported realTmuxSocketQuerier type directly.
type TransitionDef ¶ added in v1.35.0
type TransitionDef struct {
From Status
To Status
// Guard is called before the status is updated. Return non-nil to abort.
// nil means unconditionally allowed.
Guard func(ctx context.Context, i *Instance) error
// After is called once the status has been updated (side-effects: process
// management, worktree ops, scrollback restore, etc.).
// nil means no post-transition side-effect.
After func(ctx context.Context, i *Instance)
}
TransitionDef describes a single valid state machine transition with optional guard (pre-condition) and after (post-transition side-effect) hooks.
type TriageRespawner ¶ added in v1.41.0
type TriageRespawner interface {
// AutoRespawnTriage re-triggers triage for itemID. No-op (nil error) if
// the item already moved off "idea" by the time this runs (e.g. a human
// already re-triggered triage manually, or the item was otherwise
// resolved) — mirrors AutoRespawnReview's identical staleness guard.
AutoRespawnTriage(ctx context.Context, itemID string) error
// IsTriageLive reports whether the implementer (BacklogService) itself still
// has a headless triage call genuinely in flight for itemID. Added for
// reconcileOrphanedTriageItems' shape-1 staleness gate (BUG-055): a headless
// triage session has no live tmux instance to query, so before this existed
// that gate had no way to tell a session still open past
// maxHeadlessTriageSessionStaleness because it's genuinely still running
// apart from one that's actually dead — it assumed the latter unconditionally.
IsTriageLive(itemID string) bool
}
TriageRespawner can automatically re-trigger triage for a backlog item whose most recent triage-role ItemSession orphaned (StuckReasonOrphanedTriage — reconcileOrphanedTriageItems found the session still open long after it should have finished, tombstoned it, and marked the item stuck). Before this existed, orphaned_triage was detection-and-notify only, exactly like StuckReasonAbandonedReview before ReviewRespawner: a human had to notice the one-time notification and manually re-trigger triage (confirmed live 2026-07-27, docs/tasks/backlog-feature-improvement.md — items 4f03de7b and 505fb733 sat in "idea" for 2 days this way). Implemented outside this package (BacklogService owns TriggerTriage, the RPC-shaped entry point that already knows how to tombstone/re-trigger triage safely) and wired via SetTriageRespawner, mirroring StaleWorkRemediator/ReviewRespawner exactly.
type TriageSuggestion ¶ added in v1.35.0
TriageSuggestion is a canonical suggestion entry shared by the headless triage path and the submit_triage_result MCP tool.
type TriageTask ¶ added in v1.35.0
type TriageTask struct {
Text string `json:"text"`
Estimate string `json:"estimate"`
Category string `json:"category"`
}
TriageTask is a canonical implementation task shared by the headless triage path and the submit_triage_result MCP tool.
type TriggerChainReconciler ¶ added in v1.43.0
type TriggerChainReconciler struct {
// contains filtered or unexported fields
}
TriggerChainReconciler completes pipeline chain-fires interrupted by a crash between the "done" transition committing (EntRepository.dispatchChainFire) and the chained session actually being created — AC5's restart-recovery scenario. ReconcileChains is invoked from BacklogLifecycleListener's existing 60s reconcile tick (ReconcileStuck), mirroring reconcileStaleWorkSessions' idiom exactly: no dedicated goroutine/ticker of its own (plan.md's explicit instruction — reuse the existing ticker infra, don't create a second one).
func NewTriggerChainReconciler ¶ added in v1.43.0
func NewTriggerChainReconciler(firer *ChainFirer) *TriggerChainReconciler
NewTriggerChainReconciler constructs a TriggerChainReconciler bound to firer — the same ChainFirer instance wired as the happy-path dispatcher (EntRepository.SetChainFirer), so both paths share one semaphore and one "fire exactly once" implementation (ChainFirer.Fire).
func (*TriggerChainReconciler) ReconcileChains ¶ added in v1.43.0
func (r *TriggerChainReconciler) ReconcileChains(ctx context.Context, er *EntRepository)
ReconcileChains scans done items for a pending, unfired chain (NextWorkflowID != nil && !ChainFired) and re-dispatches each through ChainFirer.Dispatch — the same async, semaphore-bounded path the happy-path caller uses, so a tick with many pending chains can't block the reconcile sweep itself on a burst of expensive CreateSession calls.
type TriggerFireEventInput ¶ added in v1.43.0
type TriggerFireEventInput struct {
// WorkflowID is nil when the request was rejected before a Workflow could be
// resolved (e.g. an unknown webhook slug).
WorkflowID *uuid.UUID
Outcome string // "fired_success" | "fired_failed" | "no_match" | "rejected"
DeliveryID string // "" when not applicable (e.g. cron fires) — left unset, not stored as ""
SessionID string
ErrorMessage string
}
TriggerFireEventInput holds the fields for recording a single trigger evaluation attempt (fired/no-match/rejected).
type TriggerFireEventRepository ¶ added in v1.43.0
type TriggerFireEventRepository interface {
// Create inserts a new TriggerFireEvent row. Returns ErrDuplicateDelivery when the
// (workflow_id, delivery_id) unique constraint is violated by a concurrent duplicate
// delivery.
Create(ctx context.Context, input TriggerFireEventInput) error
// ListByWorkflow returns the most recent TriggerFireEvent rows for workflowID,
// newest first, capped at limit.
ListByWorkflow(ctx context.Context, workflowID uuid.UUID, limit int) ([]*ent.TriggerFireEvent, error)
// UpdateOutcome transitions an existing row — identified by its (workflow_id,
// delivery_id) composite key, the same key Create claims atomically — to a final
// outcome, optionally setting sessionID/errMsg (empty strings leave those fields
// untouched). Used by the webhook handlers (Epic 2.2/2.3) to move a freshly-claimed
// "pending" row to "fired_success"/"fired_failed" after the fire attempt completes.
// Returns an error if no row matches the key.
UpdateOutcome(ctx context.Context, workflowID uuid.UUID, deliveryID, outcome, sessionID, errMsg string) error
}
TriggerFireEventRepository defines persistence operations for the trigger-fire audit trail.
type TriggerFirer ¶ added in v1.43.0
type TriggerFirer interface {
// FireTriggerChained fires wf as the next hop in a pipeline chain:
// priorItemSummary (built via BuildSessionInitialPrompt over the
// just-completed item) is interpolated into wf's own prompt template, and
// chainDepth is threaded onto the created session's
// TriggeredByChainDepth attribution field. Returns the created session ID.
FireTriggerChained(ctx context.Context, wf *ent.Workflow, priorItemSummary string, chainDepth int32) (sessionID string, err error)
}
TriggerFirer is the narrow interface ChainFirer needs to fire the next workflow in a pipeline chain. Defined here (consumer-defined), not in server/workflows, to avoid a session -> server/workflows import cycle (server/workflows already imports session for WorkflowRepository, TriggerFireEventRepository, etc. — see server/workflows/scheduler.go). Satisfied by *workflows.Scheduler's FireTriggerChained method — per .claude/rules/interface-pollution-checklist.md, this is a genuine cross-package boundary (unlike ChainFirer's other collaborators below, which live in this same package and are referenced by concrete type).
type TurnCallback ¶ added in v1.35.0
TurnCallback is called after each successful turn injection.
type UIPreferences ¶
type UIPreferences struct {
// Category is the organizational category for the session
Category string `json:"category,omitempty"`
// IsExpanded indicates if the session is expanded in grouped views
IsExpanded bool `json:"is_expanded,omitempty"`
// Tags are the user-defined tags for multi-dimensional organization
Tags []string `json:"tags,omitempty"`
// GroupingStrategy is the current grouping mode (e.g., "category", "tag", "branch")
GroupingStrategy string `json:"grouping_strategy,omitempty"`
// SortOrder is the preferred sort order (e.g., "name", "date", "status")
SortOrder string `json:"sort_order,omitempty"`
}
UIPreferences represents the UI-related preferences for a session. This includes categorization, tags, and display preferences.
func (*UIPreferences) HasTag ¶
func (u *UIPreferences) HasTag(tag string) bool
HasTag returns true if the UIPreferences contains the specified tag
func (*UIPreferences) IsEmpty ¶
func (u *UIPreferences) IsEmpty() bool
IsEmpty returns true if the UIPreferences has no meaningful data
type UsageLimit ¶
type UsageLimit struct {
MaxUses int `json:"max_uses"` // 0 = unlimited
TimeWindow time.Duration `json:"time_window"` // 0 = no time window
PerApproval bool `json:"per_approval"` // Track per approval type vs globally
}
UsageLimit restricts how many times a policy can be used.
type VCSInfo ¶
type VCSInfo struct {
// VCSType is "jj" or "git"
VCSType string
// HasJJ indicates if JJ is available
HasJJ bool
// HasGit indicates if Git is available
HasGit bool
// IsColocated indicates if this is a JJ+Git colocated repo
IsColocated bool
// RepoPath is the repository root path
RepoPath string
// CurrentBookmark is the current branch/bookmark name
CurrentBookmark string
// CurrentRevision is the current revision (short ID)
CurrentRevision string
// HasUncommittedChanges indicates if there are uncommitted changes
HasUncommittedChanges bool
// ModifiedFileCount is the count of modified/added/deleted files
ModifiedFileCount int
}
VCSInfo contains version control information for a session
type VNCProcessManager ¶ added in v1.35.0
type VNCProcessManager = vnc.VNCProcessManager
VNCProcessManager is a local alias for the vnc package interface so that files within the session package can reference it without importing vnc directly.
type WorkflowCreateInput ¶ added in v1.35.0
type WorkflowCreateInput struct {
Slug string
Name string
Description string
Command string
TargetDirectory string
InputTemplate string
SessionType string
Model string
AgentType string
CronExpression string
CronEnabled bool
// Enabled is the generic per-trigger enable gate. Pointer, not a bare bool: the
// schema default (true) only applies when the column is never explicitly set, so
// a bare bool would silently create a disabled workflow for any caller that
// forgets to set it (the zero value is false) — nil here means "use the schema
// default," matching every other optional-with-a-meaningful-default field's shape.
Enabled *bool
KeepSessions *int // nil = use default (0, disabled); 0 = keep all
ArchiveAfterHours *int // nil = use default (0, disabled); 0 = disabled
// Trigger fields (webhook-triggers Epic 1.1).
TriggerType string // "cron" | "github_push" | "webhook" | "manual"; "" -> ent default "manual"
GitHubRepo string
GitHubBranch string
WebhookSlug string
WebhookSecretEncrypted string
EventFilter string
LabelFilter string
PromptTemplate string
}
WorkflowCreateInput holds the fields for creating a new workflow.
type WorkflowEngine ¶ added in v1.35.0
type WorkflowEngine interface {
// CanTransition returns true if transitioning from → to is structurally allowed.
CanTransition(from, to BacklogStatus) bool
// ValidateGates runs guard rules for the transition. Returns nil if gates pass.
ValidateGates(item BacklogItemTransitionInput, to BacklogStatus) error
// AllowedTransitions returns the set of statuses reachable from from.
AllowedTransitions(from BacklogStatus) []BacklogStatus
}
WorkflowEngine is the policy object that governs which backlog status transitions are permitted and what guards must pass.
type WorkflowRepository ¶ added in v1.35.0
type WorkflowRepository interface {
Create(ctx context.Context, w WorkflowCreateInput) (*ent.Workflow, error)
Update(ctx context.Context, id uuid.UUID, w WorkflowUpdateInput) (*ent.Workflow, error)
// UpdateConditional applies a partial update only if the row's current updated_at
// exactly matches expectedUpdatedAt (optimistic-concurrency CAS, mirrors
// EntRepository.TransitionBacklogItemStatus's precondition pattern). Returns
// ErrPreconditionFailed if the row has been modified since expectedUpdatedAt was
// read, or ErrNotFound if the row doesn't exist.
UpdateConditional(ctx context.Context, id uuid.UUID, w WorkflowUpdateInput, expectedUpdatedAt time.Time) (*ent.Workflow, error)
Delete(ctx context.Context, id uuid.UUID) error
GetByID(ctx context.Context, id uuid.UUID) (*ent.Workflow, error)
GetBySlug(ctx context.Context, slug string) (*ent.Workflow, error)
// GetByWebhookSlug retrieves a workflow by its webhook_slug (the routing key for
// POST /webhooks/{slug}). Only meaningful when TriggerType == "webhook".
GetByWebhookSlug(ctx context.Context, slug string) (*ent.Workflow, error)
ListAll(ctx context.Context) ([]*ent.Workflow, error)
ListEnabled(ctx context.Context) ([]*ent.Workflow, error) // cron_enabled=true
// ListByTriggerType returns all workflows with the given trigger_type, regardless
// of cron_enabled — callers (e.g. GitHubWebhookHandler) filter further by
// repo/branch/enabled in application code. Narrowing by trigger_type at the DB
// layer first avoids a linear decrypt-and-check over every workflow in the system
// (webhook-triggers Task 2.2.1b).
ListByTriggerType(ctx context.Context, triggerType string) ([]*ent.Workflow, error)
}
WorkflowRepository defines persistence operations for workflow definitions.
type WorkflowUpdateInput ¶ added in v1.35.0
type WorkflowUpdateInput struct {
Name *string
Description *string
Command *string
TargetDirectory *string
InputTemplate *string
SessionType *string
Model *string
AgentType *string
CronExpression *string
CronEnabled *bool
Enabled *bool // generic per-trigger enable gate
KeepSessions *int // nil = do not update; 0 = keep all (disabled)
ArchiveAfterHours *int // nil = do not update; 0 = disabled
// Trigger fields (webhook-triggers Epic 1.1).
TriggerType *string
GitHubRepo *string
GitHubBranch *string
WebhookSlug *string
WebhookSecretEncrypted *string
EventFilter *string
LabelFilter *string
PromptTemplate *string
LastFiredAt *time.Time
}
WorkflowUpdateInput holds optional fields for updating an existing workflow. Pointer fields are only applied when non-nil (partial update).
type Workspace ¶ added in v1.12.0
type Workspace struct {
// EffectivePath is the directory where the session process runs.
// For worktree sessions: the worktree directory.
// For directory sessions: the session's Path field.
EffectivePath string
// RepoRoot is the git repository root (the main checkout, not the worktree).
// For directory sessions, this is the same as EffectivePath.
RepoRoot string
}
Workspace describes where a session is operating. Use Instance.Workspace() to obtain this value; do not construct directly.
type WorkspacePath ¶ added in v1.35.0
type WorkspacePath string
WorkspacePath represents a cleaned, resolved workspace root path.
func NewWorkspacePath ¶ added in v1.35.0
func NewWorkspacePath(s string) (WorkspacePath, error)
NewWorkspacePath resolves symlinks and cleans a path to guarantee a single canonical representation.
type WorkspacePeer ¶ added in v1.41.0
type WorkspacePeer struct {
SessionUUID string
Title string
Branch string
Path string
Status Status
Goal *SessionGoalData // nil when the peer has never set a goal
// InstanceLive is a best-effort liveness signal: false when the session's Status is
// Stopped. Callers wanting an authoritative "process confirmed dead" signal should
// cross-reference LiveTmuxSessionUUIDs and override this field.
InstanceLive bool
// StaleGoal is true when the peer is live but its goal hasn't been updated within
// goalStaleThreshold — independent of InstanceLive, per the two-signal requirement.
StaleGoal bool
}
WorkspacePeer is another session sharing the caller's workspace (repo), regardless of which worktree/branch it's on.
func (WorkspacePeer) Lifecycle ¶ added in v1.41.0
func (p WorkspacePeer) Lifecycle() string
Lifecycle returns a coarse peer status label derived from the two independent signals: "gone" (process confirmed dead), "stuck" (alive but goal stale), or "active".
type WorkspaceSwitchRequest ¶
type WorkspaceSwitchRequest struct {
// Type is the type of switch operation
Type WorkspaceSwitchType
// Target is the destination (directory path, revision/branch, or worktree path)
Target string
// ChangeStrategy determines how to handle uncommitted changes
ChangeStrategy vcs.ChangeStrategy
// CreateIfMissing creates the bookmark/branch/worktree if it doesn't exist
CreateIfMissing bool
// BaseRevision is the base for new bookmark creation (empty = current)
BaseRevision string
// VCSPreference overrides the default VCS preference for this operation
VCSPreference vcs.VCSPreference
}
WorkspaceSwitchRequest represents a request to switch the workspace
type WorkspaceSwitchResult ¶
type WorkspaceSwitchResult struct {
// Success indicates if the switch was successful
Success bool
// Error contains any error that occurred
Error error
// PreviousRevision is the revision before the switch
PreviousRevision string
// CurrentRevision is the revision after the switch
CurrentRevision string
// VCSType is the VCS that was used
VCSType vcs.VCSType
// ChangesHandled describes how uncommitted changes were handled
ChangesHandled string
}
WorkspaceSwitchResult contains the result of a workspace switch operation
type WorkspaceSwitchType ¶
type WorkspaceSwitchType int
WorkspaceSwitchType defines the type of workspace switch operation
const ( // SwitchTypeDirectory is a simple directory change (no VCS, no restart) SwitchTypeDirectory WorkspaceSwitchType = iota // SwitchTypeRevision switches to a different revision/branch SwitchTypeRevision // SwitchTypeWorktree switches to or creates a different worktree SwitchTypeWorktree )
func (WorkspaceSwitchType) String ¶
func (t WorkspaceSwitchType) String() string
type WorktreeInfo ¶
type WorktreeInfo struct {
// IsWorktree is true if the path is a git worktree (not the main repo)
IsWorktree bool
// MainRepoPath is the path to the main repository's .git directory
// For a worktree at ~/.stapler-squad/worktrees/foo, this might be /path/to/main/repo/.git
MainRepoPath string
// MainRepoRoot is the working directory root of the main repository
MainRepoRoot string
// RemoteURL is the git remote origin URL (e.g., https://github.com/owner/repo.git)
RemoteURL string
// GitHubOwner is the owner extracted from a GitHub remote URL
GitHubOwner string
// GitHubRepo is the repo name extracted from a GitHub remote URL
GitHubRepo string
}
WorktreeInfo contains information about a git worktree
func DetectWorktree ¶
func DetectWorktree(path string) (*WorktreeInfo, error)
DetectWorktree checks if the given path is a git worktree and extracts relevant info. Results are cached per-path for 5 minutes to avoid repeated git subprocess calls on every LoadInstances invocation for sessions whose GitHubOwner was never resolved. Returns WorktreeInfo with IsWorktree=false if it's not a worktree or not a git repo.
type WorktreePRPoller ¶ added in v1.35.0
type WorktreePRPoller struct {
// contains filtered or unexported fields
}
WorktreePRPoller polls GitHub PR status for worktrees that have no active session. It is the counterpart to PRStatusPoller (which covers session-backed worktrees). The two pollers divide the worktree space: PRStatusPoller owns worktrees with a running session; WorktreePRPoller owns the rest.
Concurrency design:
- data cache is a sync.Map — lock-free reads in the steady state
- auth state is an atomic.Value (pollerAuthResult) — same pattern as PRStatusPoller
- onUpdated callback is an atomic.Value — writers Store, readers Load, no lock
- rateLimitedUntil and noPRPollAfter are guarded by mu
func NewWorktreePRPoller ¶ added in v1.35.0
func NewWorktreePRPoller(etagCache *github.ETagCache, prPoller *PRStatusPoller) *WorktreePRPoller
NewWorktreePRPoller creates a WorktreePRPoller with default configuration. source may be nil at construction time; call SetSource before Start.
func NewWorktreePRPollerWithConfig ¶ added in v1.35.0
func NewWorktreePRPollerWithConfig(etagCache *github.ETagCache, prPoller *PRStatusPoller, cfg WorktreePRPollerConfig) *WorktreePRPoller
NewWorktreePRPollerWithConfig creates a WorktreePRPoller with custom configuration.
func (*WorktreePRPoller) GetPRData ¶ added in v1.35.0
func (p *WorktreePRPoller) GetPRData(repoPath, branch string) *github.PRInfo
GetPRData returns cached PR info for a worktree, or nil if not yet known.
func (*WorktreePRPoller) SetOnUpdated ¶ added in v1.35.0
func (p *WorktreePRPoller) SetOnUpdated(fn func(repoPath, branch string, info *github.PRInfo))
SetOnUpdated registers a callback invoked whenever cached PR data changes. Safe to call before or after Start; the callback is replaced atomically.
func (*WorktreePRPoller) SetSource ¶ added in v1.35.0
func (p *WorktreePRPoller) SetSource(src WorktreeSource)
SetSource sets the worktree data source. Safe to call before Start.
func (*WorktreePRPoller) Start ¶ added in v1.35.0
func (p *WorktreePRPoller) Start(ctx context.Context)
Start begins the polling loop. It is a no-op if already started.
func (*WorktreePRPoller) Stop ¶ added in v1.35.0
func (p *WorktreePRPoller) Stop()
Stop gracefully shuts down the poller and waits for in-flight requests.
type WorktreePRPollerConfig ¶ added in v1.35.0
type WorktreePRPollerConfig struct {
PollInterval time.Duration
CallTimeout time.Duration
AuthCacheDuration time.Duration
// NoPRBackoff is how long to skip a branch after its PR list returns empty.
// Zero disables the backoff.
NoPRBackoff time.Duration
}
WorktreePRPollerConfig controls polling cadence and auth caching.
func DefaultWorktreePRPollerConfig ¶ added in v1.35.0
func DefaultWorktreePRPollerConfig() WorktreePRPollerConfig
DefaultWorktreePRPollerConfig returns sensible defaults matching PRStatusPoller.
type WorktreeScanItem ¶ added in v1.35.0
WorktreeScanItem is the minimal worktree info the poller needs from the unfinished-work scanner. Using a local struct avoids an import cycle:
session → session/unfinished → pkg/events → session
The server layer bridges the two packages via WorktreeSource (adapter pattern).
type WorktreeSource ¶ added in v1.35.0
type WorktreeSource interface {
// ScanDone returns a channel that receives the scan time after every scan pass.
ScanDone() <-chan time.Time
// GetWorktrees returns a snapshot of all currently-known worktrees.
GetWorktrees() []WorktreeScanItem
}
WorktreeSource provides the set of currently-known worktrees. Implement this interface by wrapping an *unfinished.Scanner in the server layer.
Source Files
¶
- actor.go
- agy_adapter.go
- approval_automation.go
- approval_policy.go
- artifact_lookup.go
- autonomous_driver.go
- backend_factory.go
- backlog.go
- backlog_commands.go
- backlog_context.go
- backlog_crypto.go
- backlog_item_change.go
- backlog_item_updated_at_utc_migration.go
- backlog_lifecycle.go
- backlog_lifecycle_archive.go
- backlog_lifecycle_pr.go
- backlog_lifecycle_review.go
- backlog_lifecycle_stale.go
- backlog_lifecycle_triage.go
- backlog_plugin.go
- backlog_plugin_github.go
- backlog_plugin_github_prs.go
- backlog_remediation.go
- backlog_review.go
- backlog_sync.go
- backlog_triage.go
- callback_dispatcher.go
- canonical.go
- chain_firer.go
- checkpoint.go
- circular_buffer.go
- claude_adapter.go
- claude_command_builder.go
- claude_controller.go
- claude_session_manager.go
- command_executor.go
- command_history.go
- command_queue.go
- context_options.go
- contexts.go
- controller_manager.go
- create_managed_instance.go
- ent_pipeline_mode_repository.go
- ent_repository.go
- ent_repository_backlog.go
- ent_workflow_repository.go
- external_approval.go
- external_discovery.go
- external_streamer.go
- external_tmux_streamer.go
- feature_controller.go
- git_worktree_manager.go
- github_metadata.go
- github_pr_url_backfill.go
- headless_failure_capture.go
- health.go
- hibernation_sweeper.go
- history.go
- history_adapter.go
- history_detector.go
- history_fork.go
- history_linker.go
- history_transfer.go
- history_watcher.go
- import_cancel.go
- import_candidate.go
- import_collision.go
- import_commit.go
- import_correlate.go
- import_kill.go
- import_reconcile.go
- inhibition.go
- instance.go
- instance_actor_setters.go
- instance_approval.go
- instance_cdp.go
- instance_checkpoint.go
- instance_claude.go
- instance_controller.go
- instance_crash.go
- instance_hibernate.go
- instance_program.go
- instance_serialization.go
- instance_shells.go
- instance_snapshot.go
- instance_state.go
- instance_status.go
- instance_tags.go
- instance_terminal.go
- instance_tmux.go
- instance_vnc.go
- instance_workspace.go
- instance_worktree.go
- interfaces.go
- live_instance.go
- load_options.go
- locked.go
- migrate.go
- native_process_manager.go
- nudge_dedup.go
- orphan_sweep.go
- pipeline_engine.go
- pipeline_mode_repository.go
- pipeline_mode_seed.go
- pipeline_mode_validation.go
- pr_status_poller.go
- pr_tracking.go
- process_manager.go
- process_suspend.go
- pty_access.go
- pty_discovery.go
- pty_subscriber.go
- registry.go
- repo_path.go
- repository.go
- response_stream.go
- review_gate.go
- review_queue.go
- review_queue_determiner.go
- review_queue_poller.go
- review_state.go
- review_transcript.go
- session.go
- session_driver.go
- session_goal.go
- session_summary_listener.go
- session_summary_markdown.go
- session_summary_service.go
- session_summary_snapshot.go
- session_summary_types.go
- shell.go
- shell_registry.go
- startup_scanner.go
- state_machine.go
- status_mapping.go
- status_remap.go
- storage.go
- storage_backlog.go
- stuck_decisions.go
- suspended_process_store.go
- tag_manager.go
- testing.go
- tmux_backend.go
- tmux_process_manager.go
- tmux_socket_querier.go
- trigger_fire_event_repository.go
- types.go
- workflow_enabled_field_migration.go
- workflow_engine.go
- workflow_repository.go
- workflow_slug.go
- workflow_updated_at_utc_migration.go
- workspace_peers.go
- worktree_pr_poller.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cdp provides per-session Chrome DevTools Protocol (CDP) browser streaming.
|
Package cdp provides per-session Chrome DevTools Protocol (CDP) browser streaming. |
|
Package detection: this file implements the TOML→DTO parsing layer for detector plugins (schema v1, see project_plans/detector-plugins/decisions/ADR-003-plugin-toml-schema-v1.md).
|
Package detection: this file implements the TOML→DTO parsing layer for detector plugins (schema v1, see project_plans/detector-plugins/decisions/ADR-003-plugin-toml-schema-v1.md). |
|
binaries
Package binaries provides per-binary BinaryDetector implementations.
|
Package binaries provides per-binary BinaryDetector implementations. |
|
dtypes
Package dtypes contains shared types for the detection package and its sub-packages.
|
Package dtypes contains shared types for the detection package and its sub-packages. |
|
Package domain contains pure domain types for the backlog subsystem.
|
Package domain contains pure domain types for the backlog subsystem. |
|
Package headless provides a subprocess-based interface for running claude -p headlessly.
|
Package headless provides a subprocess-based interface for running claude -p headlessly. |
|
Package hibernation provides checkpoint writing and cleanup for hibernated sessions.
|
Package hibernation provides checkpoint writing and cleanup for hibernated sessions. |
|
Package memory provides session memory measurement for the hibernation sweeper.
|
Package memory provides session memory measurement for the hibernation sweeper. |
|
memorytest
Package memorytest provides test doubles for the memory package.
|
Package memorytest provides test doubles for the memory package. |
|
Package mux provides PTY multiplexing functionality for external Claude sessions.
|
Package mux provides PTY multiplexing functionality for external Claude sessions. |
|
Package scanbuf provides a pooled buffer for bufio.Scanner instances that need to handle large JSONL lines (base64-encoded tool output, etc.).
|
Package scanbuf provides a pooled buffer for bufio.Scanner instances that need to handle large JSONL lines (base64-encoded tool output, etc.). |
|
Package tokens provides JSONL-based token usage parsing and aggregation for Claude Code sessions.
|
Package tokens provides JSONL-based token usage parsing and aggregation for Claude Code sessions. |
|
Package unfinished provides background scanning for git worktrees that have uncommitted changes, commits ahead of the default branch, or commits behind.
|
Package unfinished provides background scanning for git worktrees that have uncommitted changes, commits ahead of the default branch, or commits behind. |
|
gogitstore
mmapindex.go implements the mmap-backed .idx loader described in session/unfinished/design/pluggable-gitstore.md §5 ("mmap for the index — designed, not built").
|
mmapindex.go implements the mmap-backed .idx loader described in session/unfinished/design/pluggable-gitstore.md §5 ("mmap for the index — designed, not built"). |
|
Package vcs provides an abstraction layer over version control systems.
|
Package vcs provides an abstraction layer over version control systems. |
|
Package vnc provides per-session virtual display and VNC server lifecycle management.
|
Package vnc provides per-session virtual display and VNC server lifecycle management. |
|
Package workspace provides workspace tracking and status management for stapler-squad sessions.
|
Package workspace provides workspace tracking and status management for stapler-squad sessions. |