Documentation
¶
Overview ¶
Package state manages the SQLite state database for The Forge.
The database lives at ~/.forge/state.db and tracks:
- workers: active and historical Smith worker processes
- prs: pull requests created by Forge across anvils
- events: timestamped log of all significant actions
Index ¶
- Constants
- Variables
- func DefaultPath() (string, error)
- type AnvilPollStatus
- type AssayRun
- type DB
- func (db *DB) ActiveDispatchWorkers() ([]Worker, error)
- func (db *DB) ActiveDispatchWorkersByAnvil(anvil string) ([]Worker, error)
- func (db *DB) ActiveFindings(anvil string, prNumber int) ([]Finding, error)
- func (db *DB) ActiveWorkerByBead(beadID string) (*Worker, error)
- func (db *DB) ActiveWorkerByBeadAndAnvil(beadID, anvil string) (*Worker, error)
- func (db *DB) ActiveWorkers() ([]Worker, error)
- func (db *DB) AddBeadCost(beadID, anvil string, input, output, cacheRead, cacheWrite int, cost float64) error
- func (db *DB) AddCopilotRequest(date string, multiplier float64) error
- func (db *DB) AddDailyCost(date string, input, output, cacheRead, cacheWrite int, cost float64) error
- func (db *DB) AddPendingOrphan(beadID, anvil, title, branch string) error
- func (db *DB) AddProviderDailyCost(date, prov string, input, output, cacheRead, cacheWrite int, cost float64) error
- func (db *DB) AllWorkers(limit int) ([]Worker, error)
- func (db *DB) AppendForgeSessionMessage(m ForgeSessionMessage) (ForgeSessionMessage, error)
- func (db *DB) AppendForgeSessionMessageRaw(sessionID int64, role, kind, content, metadata string) (ForgeSessionMessage, error)
- func (db *DB) AssayCostUSDSince(since time.Time) (float64, error)
- func (db *DB) BeadTitle(beadID, anvil string) string
- func (db *DB) ClarificationNeededBeadIDSet() (map[string]struct{}, error)
- func (db *DB) ClarificationNeededBeads() ([]RetryRecord, error)
- func (db *DB) ClearNeedsAttention(beadID, anvil string) error
- func (db *DB) ClearRetry(beadID, anvil string) error
- func (db *DB) Close() error
- func (db *DB) CompleteWorkersByBead(beadID string) error
- func (db *DB) CompletedWorkers(limit int) ([]Worker, error)
- func (db *DB) Conn() *sql.DB
- func (db *DB) CountAssayRuns(anvil string, prNumber int) (int, error)
- func (db *DB) CountForgeSessionMessages(sessionID int64) (int, error)
- func (db *DB) CountWicketIssues(opts ListWicketIssuesOpts) (int, error)
- func (db *DB) CreateForgeSession(s ForgeSession) (ForgeSession, error)
- func (db *DB) CreateWebSession(s WebSession) error
- func (db *DB) DeleteForgeSession(id int64) error
- func (db *DB) DeletePendingRules(ids []int) error
- func (db *DB) DeletePipelineBellowsWorker(beadID, anvil string) error
- func (db *DB) DeleteWebSession(tokenHash string) error
- func (db *DB) DismissExhaustedPR(id int) error
- func (db *DB) DismissRetry(beadID, anvil string) error
- func (db *DB) EventsByBead(beadID, anvil string, limit int) ([]Event, error)
- func (db *DB) EventsSince(lastID, limit int) ([]Event, error)
- func (db *DB) ExhaustedPRs(maxCI, maxRev, maxRebase int) ([]ExhaustedPR, error)
- func (db *DB) GetAllProviderQuotas() (map[string]provider.Quota, error)
- func (db *DB) GetCopilotRequestsOn(date string) (float64, error)
- func (db *DB) GetDailyCost(date string) (inputTokens, outputTokens, cacheRead, cacheWrite int, cost, limit float64, ...)
- func (db *DB) GetForgeSession(id int64) (*ForgeSession, error)
- func (db *DB) GetPRByID(id int) (*PR, error)
- func (db *DB) GetPRByNumber(anvil string, number int) (*PR, error)
- func (db *DB) GetProviderDailyCosts(date string) ([]ProviderDailyCost, error)
- func (db *DB) GetProviderQuota(pv string) (*provider.Quota, error)
- func (db *DB) GetRetry(beadID, anvil string) (*RetryRecord, error)
- func (db *DB) GetTodayCopilotRequests() (float64, error)
- func (db *DB) GetTodayCost() (float64, error)
- func (db *DB) GetTodayCostOn(date string) (float64, error)
- func (db *DB) GetWebSession(tokenHash string) (*WebSession, error)
- func (db *DB) GetWicketIssue(repo string, number int) (*WicketIssue, error)
- func (db *DB) GetWicketIssueByBeadID(beadID string) (*WicketIssue, error)
- func (db *DB) GetWicketSummary() ([]WicketAnvilSummary, error)
- func (db *DB) GetWorker(id string) (*Worker, error)
- func (db *DB) HasEventForDate(eventType EventType, date string) (bool, error)
- func (db *DB) HasEventWithin(eventType EventType, d time.Duration) (bool, error)
- func (db *DB) HasOpenPRForBead(beadID, anvil string) (bool, error)
- func (db *DB) HasWorkerRecord(beadID, anvil string) (bool, error)
- func (db *DB) IncrementConsecutiveMiss(findingHash string) error
- func (db *DB) IncrementDispatchFailures(beadID, anvil string, maxFailures int, reason string) (int, bool, error)
- func (db *DB) IncrementRecoveryFailures(beadID, anvil, reason string) (int, bool, error)
- func (db *DB) InsertFinding(f Finding) error
- func (db *DB) InsertPR(pr *PR) error
- func (db *DB) InsertPendingRule(anvil, ruleYAML, sourcePR string) error
- func (db *DB) InsertWicketIssue(issue WicketIssue) error
- func (db *DB) InsertWorker(w *Worker) error
- func (db *DB) InsertWorkerIfMissing(w *Worker) error
- func (db *DB) IsIssueTracked(repo string, number int) (bool, error)
- func (db *DB) IsPRReadyToMerge(id int) (bool, error)
- func (db *DB) IsPendingOrphan(beadID, anvil string) (bool, error)
- func (db *DB) LastAssayRunAt(anvil string, prNumber int) (time.Time, error)
- func (db *DB) LastEventTime(eventType EventType) (time.Time, bool, error)
- func (db *DB) LastPollAllAnvils() ([]AnvilPollStatus, error)
- func (db *DB) LastPollPerAnvil(anvilNames []string) ([]AnvilPollStatus, error)
- func (db *DB) LastReviewedSHA(anvil string, prNumber int) (string, error)
- func (db *DB) LastWicketScanAt() (*time.Time, error)
- func (db *DB) LastWorkerBranchForBead(beadID, anvil string) (string, error)
- func (db *DB) LastWorkerLogPath(beadID string) (string, error)
- func (db *DB) LatestWardenRejectMessage(beadID string) string
- func (db *DB) ListForgeSessionMessages(sessionID int64) ([]ForgeSessionMessage, error)
- func (db *DB) ListForgeSessions(createdBy string, limit int) ([]ForgeSession, error)
- func (db *DB) ListForgeSessionsMissingAnvil() ([]ForgeSession, error)
- func (db *DB) ListForgeSessionsWithCounts(createdBy string, limit int) ([]ForgeSessionWithCount, error)
- func (db *DB) ListPendingOrphans() ([]PendingOrphan, error)
- func (db *DB) ListWicketIssues(opts ListWicketIssuesOpts) ([]WicketIssue, error)
- func (db *DB) LogEvent(typ EventType, message, beadID, anvil string) error
- func (db *DB) LogEventAt(typ EventType, message, beadID, anvil string, at time.Time) error
- func (db *DB) MarkFindingPosted(findingHash string, ghCommentID int64) error
- func (db *DB) MarkNeedsHuman(beadID, anvil, reason string) error
- func (db *DB) MarkResolved(findingHash string) error
- func (db *DB) MarkWorkerStalled(id string) error
- func (db *DB) MergedPRs() ([]PR, error)
- func (db *DB) NeedsAttentionBeads(maxCI, maxRev, maxRebase int) ([]NeedsAttentionBead, error)
- func (db *DB) NeedsHumanBeadIDSet() (map[string]struct{}, error)
- func (db *DB) NeedsHumanBeads() ([]RetryRecord, error)
- func (db *DB) OpenPRs() ([]PR, error)
- func (db *DB) OpenPRsWithDetail() ([]OpenPRDetail, error)
- func (db *DB) OpenPostedFindings(anvil string, prNumber int) ([]Finding, error)
- func (db *DB) OrphanedMonitoringBellowsWorkers() ([]OrphanedWorkerInfo, error)
- func (db *DB) PRByNumber(number int) (*PR, error)
- func (db *DB) Path() string
- func (db *DB) PendingRetries() ([]RetryRecord, error)
- func (db *DB) PostedFindingHashes(anvil string, prNumber int) (map[string]bool, error)
- func (db *DB) PurgeExpiredWebSessions() (int64, error)
- func (db *DB) QueryPendingRulesByAnvil() (map[string][]PendingRule, error)
- func (db *DB) QueueCache() ([]QueueItem, error)
- func (db *DB) QueueCount() (int, error)
- func (db *DB) ReadyToMergePRs() ([]ReadyToMergePR, error)
- func (db *DB) RecentDailyCosts(n int) ([]struct{ ... }, error)
- func (db *DB) RecentEvents(n int) ([]Event, error)
- func (db *DB) RecentEventsExcluding(n int, excludeTypes []EventType) ([]Event, error)
- func (db *DB) RecentEventsMatching(pattern string, n int, excludeTypes []EventType) ([]Event, error)
- func (db *DB) RecordAssayRun(r *AssayRun) error
- func (db *DB) RemovePendingOrphan(beadID, anvil string) error
- func (db *DB) ReplaceQueueCacheForAnvils(anvils []string, items []QueueItem) error
- func (db *DB) ResetConsecutiveMiss(findingHash string) error
- func (db *DB) ResetDispatchFailures(beadID, anvil string) error
- func (db *DB) ResetPRFixCounts(id int) error
- func (db *DB) ResetRecoveryFailures(beadID, anvil string) error
- func (db *DB) ResetRetry(beadID, anvil string) error
- func (db *DB) SetClarificationNeeded(beadID, anvil string, needed bool, reason string) error
- func (db *DB) SetDailyCostLimit(date string, limit float64) error
- func (db *DB) SetFindingThreadID(findingHash, threadID string) error
- func (db *DB) StalledWorkers(staleThreshold time.Duration) ([]Worker, error)
- func (db *DB) TotalCostSince(sinceDate string) (float64, error)
- func (db *DB) TouchForgeSession(id int64) error
- func (db *DB) TouchWebSession(tokenHash string, ttl time.Duration) (time.Time, error)
- func (db *DB) UpdateForgeSession(id int64, title, status *string) error
- func (db *DB) UpdateForgeSessionAnvil(id int64, anvil string) error
- func (db *DB) UpdateForgeSessionStageAndPlan(id int64, stage, plan *string) (*ForgeSession, error)
- func (db *DB) UpdatePRBellowsAssignment(id int, managed, manuallyAssigned bool) error
- func (db *DB) UpdatePRBellowsManaged(id int, managed bool) error
- func (db *DB) UpdatePRLifecycle(id int, ciFixCount, reviewFixCount, rebaseCount int, ciPassing bool) error
- func (db *DB) UpdatePRMergeability(id int, ...) error
- func (db *DB) UpdatePRStatus(id int, status PRStatus) error
- func (db *DB) UpdatePRStatusIfNeedsFix(id int, status PRStatus) error
- func (db *DB) UpdatePRTitle(id int, title string) error
- func (db *DB) UpdateWicketIssue(issue WicketIssue) error
- func (db *DB) UpdateWorkerLogPath(id string, logPath string) error
- func (db *DB) UpdateWorkerPID(id string, pid int) error
- func (db *DB) UpdateWorkerPhase(id string, phase string) error
- func (db *DB) UpdateWorkerStatus(id string, status WorkerStatus) error
- func (db *DB) UpdateWorkerTitle(id string, title string) error
- func (db *DB) UpsertProviderQuota(pv string, q *provider.Quota) error
- func (db *DB) UpsertRetry(r *RetryRecord) error
- func (db *DB) WorkersByAnvil(anvil string) ([]Worker, error)
- func (db *DB) WorkersByBead(beadID, anvil string, limit int) ([]Worker, error)
- type Event
- type EventType
- type ExhaustedPR
- type Finding
- type ForgeSession
- type ForgeSessionMessage
- type ForgeSessionWithCount
- type ListWicketIssuesOpts
- type NeedsAttentionBead
- type OpenPRDetail
- type OrphanedWorkerInfo
- type PR
- type PRStatus
- type PendingOrphan
- type PendingRule
- type ProviderDailyCost
- type QueueItem
- type QueueSection
- type ReadyToMergePR
- type RetryRecord
- type WebSession
- type WicketAnvilSummary
- type WicketIssue
- type Worker
- type WorkerStatus
Constants ¶
const ( DefaultMaxCIFixAttempts = 5 DefaultMaxReviewFixAttempts = 5 DefaultMaxRebaseAttempts = 3 )
Default lifecycle thresholds. These are used by NeedsAttentionBeads and can be overridden via config (settings.max_ci_fix_attempts, etc.).
const ( ForgeSessionStatusDraft = "draft" ForgeSessionStatusArchived = "archived" )
Permitted forge session status values. Other beads may add more — the DB stores TEXT so this is an advisory list rather than a strict enum.
const ( ForgeMessageRoleUser = "user" ForgeMessageRoleAssistant = "assistant" ForgeMessageRoleSystem = "system" )
Permitted forge session message roles.
const ( // ForgeStageDrafting is the default open-chat stage. The user and claude // converse freely; the user can ask claude to emit a plan at any point. ForgeStageDrafting = "drafting" // ForgeStageGrilling is the structured Q&A stage. Claude relentlessly // asks questions (with options + a recommendation) until the design tree // is exhausted. The user picks an option or writes a free-form answer. ForgeStageGrilling = "grilling" // ForgeStageReady marks a session whose plan and answers are settled // enough to be turned into beads in the next bead. The current bead // stops at producing the plan + answered grilling tree; the actual // bd-create flow lives in a follow-on bead. ForgeStageReady = "ready" )
Beads-Forge stages. The session moves through these as the user iterates on the design with claude. The DB stores TEXT so future beads can extend the set without a schema migration.
const ( // ForgeMessageKindText is a plain conversational turn. ForgeMessageKindText = "text" // ForgeMessageKindPlan is a markdown plan emitted by the assistant. The // content holds the plan body; metadata is unused. ForgeMessageKindPlan = "plan" // ForgeMessageKindQuestion is a structured grilling-stage question. The // metadata holds the JSON-encoded options + recommendation; the content // is the question text. ForgeMessageKindQuestion = "question" // ForgeMessageKindAnswer is the user's response to a question. Metadata // pins the parent question_id and the chosen option_id (when an option // was picked); content is the human-readable answer (option label or // free-form text). ForgeMessageKindAnswer = "answer" // ForgeMessageKindStatus is a system-emitted status note (e.g. "Stage // changed to grilling"). Rendered as italic in the chat view. ForgeMessageKindStatus = "status" // ForgeMessageKindBeadsCreated is the receipt for a successful // bead-emission turn. Content holds a chat-friendly recap; metadata // holds the JSON list of created beads (bead_id, anvil, title) so the // UI can render clickable links and replays survive page reloads. ForgeMessageKindBeadsCreated = "beads_created" )
Forge message kinds. Like roles, this is a TEXT column so callers can add new kinds without a migration.
Variables ¶
var ErrForgeSessionNotFound = errors.New("forge session not found")
ErrForgeSessionNotFound is returned when an UPDATE targets a session row that doesn't exist. Callers can map this to a 404 instead of treating a silent no-op as success.
Functions ¶
Types ¶
type AnvilPollStatus ¶ added in v0.5.0
type AnvilPollStatus struct {
Anvil string
Timestamp time.Time
OK bool // true if last poll succeeded, false on error
Message string // e.g. "5 ready" or error message
}
AnvilPollStatus holds the last poll outcome for an anvil.
type AssayRun ¶ added in v0.20.0
type AssayRun struct {
ID int
Anvil string
PRNumber int
HeadSHA string
StartedAt time.Time
FinishedAt *time.Time
DurationMs int64
CostUSD float64
FindingsCount int
SkippedReason string
ShadowMode bool
PostedCount int
Error string
}
AssayRun records a single Assay review pass over a PR head SHA.
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
DB wraps a SQLite connection for Forge state.
func Open ¶
Open opens or creates the state database at the given path. If path is empty, DefaultPath() is used.
func (*DB) ActiveDispatchWorkers ¶
ActiveDispatchWorkers returns active workers that are running primary dispatch pipeline phases (schematic, smith, temper, warden). All phases in backgroundPhases are excluded so they don't consume dispatch capacity slots. Stalled workers are included so they continue to count against capacity and prevent the daemon from over-subscribing while stalled processes are still running.
func (*DB) ActiveDispatchWorkersByAnvil ¶
ActiveDispatchWorkersByAnvil returns active dispatch workers for a given anvil, excluding all phases in backgroundPhases. Stalled workers are included so they continue to count against per-anvil capacity.
func (*DB) ActiveFindings ¶ added in v0.20.0
ActiveFindings returns every non-resolved finding for the given anvil and PR, regardless of posted state. Assay uses this for cross-run similarity-based dedup: a finding the model rewords on a subsequent run has a new canonical body and therefore a new Finding.Hash, so the hash-only guards (INSERT OR IGNORE, PostedFindingHashes) would otherwise let the reworded version through as a fresh row. The returned slice is never nil.
func (*DB) ActiveWorkerByBead ¶
ActiveWorkerByBead returns the non-terminal worker for a given bead ID.
func (*DB) ActiveWorkerByBeadAndAnvil ¶
ActiveWorkerByBeadAndAnvil returns the non-terminal worker for a given bead scoped to a specific anvil. Use this instead of ActiveWorkerByBead when iterating per-anvil to avoid false positives when two anvils share bead IDs.
func (*DB) ActiveWorkers ¶
ActiveWorkers returns all workers with non-terminal status (including stalled).
func (*DB) AddBeadCost ¶
func (db *DB) AddBeadCost(beadID, anvil string, input, output, cacheRead, cacheWrite int, cost float64) error
AddBeadCost adds token usage to a bead's cumulative cost.
func (*DB) AddCopilotRequest ¶ added in v0.2.0
AddCopilotRequest records a Copilot premium request for today, weighted by the model's multiplier (e.g. opus 4.6 = 3x per invocation).
func (*DB) AddDailyCost ¶
func (db *DB) AddDailyCost(date string, input, output, cacheRead, cacheWrite int, cost float64) error
AddDailyCost adds token usage to today's aggregate.
func (*DB) AddPendingOrphan ¶ added in v0.4.0
AddPendingOrphan records a bead that needs user review before recovery. If the bead is already pending, it is a no-op.
func (*DB) AddProviderDailyCost ¶ added in v0.2.0
func (db *DB) AddProviderDailyCost(date, prov string, input, output, cacheRead, cacheWrite int, cost float64) error
AddProviderDailyCost adds token usage to a specific provider's daily aggregate.
func (*DB) AllWorkers ¶
AllWorkers returns all workers ordered by most recent first.
func (*DB) AppendForgeSessionMessage ¶ added in v0.16.0
func (db *DB) AppendForgeSessionMessage(m ForgeSessionMessage) (ForgeSessionMessage, error)
AppendForgeSessionMessage inserts a new message and bumps the parent session's updated_at in a single transaction. Returns the persisted message with its assigned ID and timestamp.
func (*DB) AppendForgeSessionMessageRaw ¶ added in v0.16.0
func (db *DB) AppendForgeSessionMessageRaw(sessionID int64, role, kind, content, metadata string) (ForgeSessionMessage, error)
AppendForgeSessionMessageRaw is a thin wrapper that lets tests inject a message with explicit role + kind + metadata in one call. Production code should prefer AppendForgeSessionMessage with a fully-populated struct.
func (*DB) AssayCostUSDSince ¶ added in v0.20.0
AssayCostUSDSince returns the total cost_usd of all assay_runs started at or after the given time. The cutoff is formatted with dbTimeLayout, whose lexicographic ordering matches chronological ordering, so a TEXT comparison is valid. Used by the Bellows Assay trigger gate to enforce a daily cost cap.
func (*DB) BeadTitle ¶
BeadTitle returns the display title for a bead, consulting queue_cache first then falling back to the most recent workers entry. Returns an empty string if no title is found.
func (*DB) ClarificationNeededBeadIDSet ¶
ClarificationNeededBeadIDSet returns a set of "beadID\x00anvil" keys for all beads needing clarification. This allows callers to do a single query and then O(1) membership checks.
func (*DB) ClarificationNeededBeads ¶
func (db *DB) ClarificationNeededBeads() ([]RetryRecord, error)
ClarificationNeededBeads returns all beads that need human clarification before work can start.
func (*DB) ClearNeedsAttention ¶ added in v0.16.0
ClearNeedsAttention zeroes the needs-attention flags on a bead's retry row without scheduling a re-dispatch. Unlike ResetRetry it leaves clarification_needed, retry_count, and next_retry untouched, and unlike DismissRetry it preserves the row. It is idempotent: running it on an already-clean row (or a bead with no retry row at all) is a no-op success.
func (*DB) ClearRetry ¶
ClearRetry removes the retry record for a bead (typically after success).
func (*DB) CompleteWorkersByBead ¶
CompleteWorkersByBead marks all non-terminal workers for a bead as Done.
func (*DB) CompletedWorkers ¶
CompletedWorkers returns workers in terminal states (done, failed, timeout), ordered by most recently completed first. Limit 0 means no limit.
func (*DB) CountAssayRuns ¶ added in v0.21.0
CountAssayRuns returns the number of executed Assay review passes for the given anvil and PR — rows whose skipped_reason is empty (a skipped run, e.g. a failed diff fetch, never reviewed the diff and so does not count toward the per-PR run cap). Used by the Bellows Assay trigger gate to stop the Assay→Burnish→new-head→Assay loop after a configured number of real reviews.
func (*DB) CountForgeSessionMessages ¶ added in v0.16.0
CountForgeSessionMessages returns the number of persisted messages for a session. Used by the API to populate the sidebar preview without sending the full message list.
func (*DB) CountWicketIssues ¶ added in v0.11.0
func (db *DB) CountWicketIssues(opts ListWicketIssuesOpts) (int, error)
CountWicketIssues returns the number of wicket_issues rows matching the given options using a DB-side COUNT(*) query (no full-row fetch).
func (*DB) CreateForgeSession ¶ added in v0.16.0
func (db *DB) CreateForgeSession(s ForgeSession) (ForgeSession, error)
CreateForgeSession inserts a new session row, returning the assigned ID and the canonical timestamps. CreatedBy may be empty when the caller is unable to attribute the session to a user.
func (*DB) CreateWebSession ¶ added in v0.16.0
func (db *DB) CreateWebSession(s WebSession) error
CreateWebSession inserts a new web session row.
func (*DB) DeleteForgeSession ¶ added in v0.16.0
DeleteForgeSession removes a session and its messages. Foreign keys are declared with ON DELETE CASCADE in the schema, but SQLite only honours that when foreign_keys = ON, so we delete messages explicitly to be independent of the connection-level pragma.
func (*DB) DeletePendingRules ¶ added in v0.10.0
DeletePendingRules removes pending warden rules by their IDs.
func (*DB) DeletePipelineBellowsWorker ¶ added in v0.8.0
DeletePipelineBellowsWorker removes any worker row for bead+anvil that was repurposed from the pipeline (no "bellows-" prefix in ID). Called by the bellows poll loop before inserting the canonical bellows-{anvil}-{prNum} row so Hearth never shows two workers for the same PR.
func (*DB) DeleteWebSession ¶ added in v0.16.0
DeleteWebSession removes a session by token hash.
func (*DB) DismissExhaustedPR ¶
DismissExhaustedPR marks an exhausted PR as closed so it no longer appears in the Needs Attention panel.
func (*DB) DismissRetry ¶
DismissRetry removes the retry record entirely, clearing the bead from the Needs Attention list without resetting for a retry.
func (*DB) EventsByBead ¶ added in v0.19.0
EventsByBead returns the most recent events for a bead, newest-first (ordered by id descending so the ordering is stable even when several events share the same second-resolution timestamp). When anvil is non-empty the lookup is scoped to that anvil. limit <= 0 returns all matching rows. Used by the needs-attention surface to derive a bead's escalation type from its latest relevant lifecycle event.
func (*DB) EventsSince ¶ added in v0.16.0
EventsSince returns events with ID greater than lastID, ordered oldest-first. Used by the web SSE activity stream to deliver new events to connected clients; the oldest-first ordering and id > lastID predicate make it trivial to drive an EventSource Last-Event-ID resume.
func (*DB) ExhaustedPRs ¶
func (db *DB) ExhaustedPRs(maxCI, maxRev, maxRebase int) ([]ExhaustedPR, error)
ExhaustedPRs returns non-terminal PRs where any fix/rebase counter has reached its threshold. The thresholds are passed as parameters so the caller can source them from config or constants. Non-positive threshold values are normalized to their intended defaults to avoid matching all PRs.
func (*DB) GetAllProviderQuotas ¶
GetAllProviderQuotas returns all known provider quotas.
func (*DB) GetCopilotRequestsOn ¶ added in v0.2.0
GetCopilotRequestsOn returns the total weighted premium requests for the given date (YYYY-MM-DD). Returns 0 if no row exists yet.
func (*DB) GetDailyCost ¶
func (db *DB) GetDailyCost(date string) (inputTokens, outputTokens, cacheRead, cacheWrite int, cost, limit float64, err error)
GetDailyCost returns cost data for a specific date.
func (*DB) GetForgeSession ¶ added in v0.16.0
func (db *DB) GetForgeSession(id int64) (*ForgeSession, error)
GetForgeSession returns the session with the given ID, or nil if no row exists. Errors other than ErrNoRows are returned to the caller.
func (*DB) GetPRByNumber ¶
GetPRByNumber returns a PR by its anvil and number.
func (*DB) GetProviderDailyCosts ¶ added in v0.2.0
func (db *DB) GetProviderDailyCosts(date string) ([]ProviderDailyCost, error)
GetProviderDailyCosts returns per-provider cost data for a given date.
func (*DB) GetProviderQuota ¶
GetProviderQuota returns the quota for a specific provider.
func (*DB) GetRetry ¶
func (db *DB) GetRetry(beadID, anvil string) (*RetryRecord, error)
GetRetry returns the retry record for a bead, or nil if none exists.
func (*DB) GetTodayCopilotRequests ¶ added in v0.2.0
GetTodayCopilotRequests returns the total weighted premium requests used today. Returns 0 if no row exists yet.
func (*DB) GetTodayCost ¶
GetTodayCost returns today's estimated cost total. Returns 0 if no row exists yet.
func (*DB) GetTodayCostOn ¶
GetTodayCostOn returns the estimated cost total for the given date (YYYY-MM-DD). Returns 0 if no row exists yet for that date.
func (*DB) GetWebSession ¶ added in v0.16.0
func (db *DB) GetWebSession(tokenHash string) (*WebSession, error)
GetWebSession looks up a session by its token hash, returning nil if not found or expired.
func (*DB) GetWicketIssue ¶ added in v0.11.0
func (db *DB) GetWicketIssue(repo string, number int) (*WicketIssue, error)
GetWicketIssue returns the wicket_issues row for the given repo and issue number, or nil if no such row exists.
func (*DB) GetWicketIssueByBeadID ¶ added in v0.11.0
func (db *DB) GetWicketIssueByBeadID(beadID string) (*WicketIssue, error)
GetWicketIssueByBeadID returns the wicket_issues row for the given bead ID, or nil if no such row exists. Used for lifecycle event bridging.
func (*DB) GetWicketSummary ¶ added in v0.11.0
func (db *DB) GetWicketSummary() ([]WicketAnvilSummary, error)
GetWicketSummary returns per-repo open and needs-human issue counts from the wicket_issues table. Only repos that have at least one open issue (state NOT IN ('closed', 'merged')) are returned, sorted by repo name.
func (*DB) GetWorker ¶ added in v0.13.0
GetWorker returns the worker record for the given worker ID, regardless of status. Returns an error if no worker with that ID exists.
func (*DB) HasEventForDate ¶ added in v0.4.0
HasEventForDate reports whether any event of the given type was logged on the specified date (YYYY-MM-DD). It is used to deduplicate notifications that should fire at most once per calendar day (e.g. cost_limit_hit) even across daemon restarts, which would otherwise reset in-memory guards.
func (*DB) HasEventWithin ¶ added in v0.11.1
HasEventWithin reports whether any event of the given type was logged within the past duration d from now. Any DB error is returned to the caller.
func (*DB) HasOpenPRForBead ¶
HasOpenPRForBead returns true if there is a non-terminal PR for the given bead in the given anvil, OR if there is a recently-merged PR still within the grace period (mergedPRGracePeriod). The grace window prevents orphan recovery from falsely reclaiming beads whose PR just merged but whose bd close has not yet completed.
func (*DB) HasWorkerRecord ¶
HasWorkerRecord returns true if Forge has ever had a worker for the given bead in the given anvil (any status). This is used by orphan recovery to distinguish beads that Forge previously claimed from beads that are in_progress because a human or external tool is working on them.
func (*DB) IncrementConsecutiveMiss ¶ added in v0.20.0
IncrementConsecutiveMiss bumps the consecutive_misses counter for the finding identified by findingHash.
func (*DB) IncrementDispatchFailures ¶
func (db *DB) IncrementDispatchFailures(beadID, anvil string, maxFailures int, reason string) (int, bool, error)
IncrementDispatchFailures atomically increments the dispatch_failures counter for a bead within a transaction. If the counter reaches maxFailures, sets needs_human=1 with a "circuit breaker:" prefixed error. Returns the new failure count and whether the circuit broke.
func (*DB) IncrementRecoveryFailures ¶ added in v0.14.0
IncrementRecoveryFailures atomically increments the recovery_failures counter for a bead. If the counter reaches maxRecoveryFailures or the first failure was more than 30 minutes ago, sets needs_human=1. Returns the new failure count and whether needs_human was set.
func (*DB) InsertFinding ¶ added in v0.20.0
InsertFinding inserts a new pr_findings row. Insertion is OR IGNORE so a duplicate finding_hash is silently skipped — dedup is keyed on finding_hash. created_at defaults to now (UTC) when f.CreatedAt is the zero time.
func (*DB) InsertPR ¶
InsertPR adds a new PR record. ci_passing is intentionally omitted so the DB default (1 = passing) always applies for new PRs, avoiding silent insertion of a failing PR due to Go's zero-value false. has_pending_reviews is explicitly set to 1 (pending) so new PRs don't appear in Ready to Merge until bellows confirms no reviews are pending. This closes the race window where GitHub assigns reviewers (e.g. Copilot) asynchronously after PR creation. bellows_managed defaults to 0 for ext-* (externally created) PRs so the reconcile loop's auto-adoption release path is not triggered on every fresh insert; users opt in via the assign_bellows IPC action which sets both flags (Forge-l125). An explicit pr.BellowsManaged=true on the struct pre-pins an ext-* row at insert time and automatically sets bellows_manually_assigned=1 so that reconcile does not clear the flag on the next cycle.
func (*DB) InsertPendingRule ¶ added in v0.10.0
InsertPendingRule adds a new pending warden rule for the given anvil.
func (*DB) InsertWicketIssue ¶ added in v0.11.0
func (db *DB) InsertWicketIssue(issue WicketIssue) error
InsertWicketIssue inserts a new wicket_issues row.
func (*DB) InsertWorker ¶
InsertWorker adds a new worker record.
func (*DB) InsertWorkerIfMissing ¶ added in v0.8.0
InsertWorkerIfMissing adds a worker record only when no row with the same id already exists. This avoids unnecessary WAL churn on repeated poll cycles (e.g. bellows upserts) where the row is stable between polls.
func (*DB) IsIssueTracked ¶ added in v0.11.0
IsIssueTracked returns true when a wicket_issues row exists for the given repo and issue number.
func (*DB) IsPRReadyToMerge ¶
IsPRReadyToMerge reports whether the given PR currently satisfies all ready-to-merge conditions: CI passing, not conflicting, no unresolved review threads, no pending review requests, and not in a needs_fix/closed/merged state. Approval is not required — repos without branch protection rules should still surface PRs as mergeable; GitHub enforces required reviews at merge time.
func (*DB) IsPendingOrphan ¶ added in v0.4.0
IsPendingOrphan reports whether the given bead is already in the pending_orphans list.
func (*DB) LastAssayRunAt ¶ added in v0.20.0
LastAssayRunAt returns the started_at timestamp of the most recent assay_runs row for the given anvil and PR number. Returns the zero time (no error) when no run exists. Used by the Bellows Assay trigger gate to debounce repeat runs.
func (*DB) LastEventTime ¶ added in v0.12.0
LastEventTime returns the timestamp of the most recent event of the given type. The second return value is false when no such event exists. Any DB error is returned to the caller.
func (*DB) LastPollAllAnvils ¶ added in v0.12.0
func (db *DB) LastPollAllAnvils() ([]AnvilPollStatus, error)
LastPollAllAnvils returns the most recent poll or poll_error event for every distinct anvil that has ever been polled. It is used by Hearth when no config-derived anvil list is available (e.g. when forge.yaml is not found in the working directory or ~/.forge/).
func (*DB) LastPollPerAnvil ¶ added in v0.5.0
func (db *DB) LastPollPerAnvil(anvilNames []string) ([]AnvilPollStatus, error)
LastPollPerAnvil returns the most recent poll or poll_error event for each of the given anvil names. Anvils with no poll history are omitted.
func (*DB) LastReviewedSHA ¶ added in v0.20.0
LastReviewedSHA returns the head_sha of the most recent assay_runs row for the given anvil and PR number. Returns "" (no error) when no run exists.
func (*DB) LastWicketScanAt ¶ added in v0.11.0
LastWicketScanAt returns the timestamp of the most recent wicket_scan_done event, or nil when no such event exists yet.
func (*DB) LastWorkerBranchForBead ¶ added in v0.4.0
LastWorkerBranchForBead returns the branch from the most recent worker record for a given bead in a given anvil. Returns an empty string (not an error) if no worker record exists yet.
func (*DB) LastWorkerLogPath ¶
LastWorkerLogPath returns the log path from the most recent worker for a bead.
func (*DB) LatestWardenRejectMessage ¶ added in v0.8.0
LatestWardenRejectMessage returns the message from the most recent warden_reject event for the given bead, or "" if none exists.
func (*DB) ListForgeSessionMessages ¶ added in v0.16.0
func (db *DB) ListForgeSessionMessages(sessionID int64) ([]ForgeSessionMessage, error)
ListForgeSessionMessages returns the messages for a session in insertion order (oldest first), suitable for direct rendering in the chat view.
func (*DB) ListForgeSessions ¶ added in v0.16.0
func (db *DB) ListForgeSessions(createdBy string, limit int) ([]ForgeSession, error)
ListForgeSessions returns sessions ordered by most-recent activity first. When createdBy is non-empty, only sessions started by that user are returned; this keeps the sidebar scoped to the signed-in user. Limit is clamped to [1, 200].
func (*DB) ListForgeSessionsMissingAnvil ¶ added in v0.16.0
func (db *DB) ListForgeSessionsMissingAnvil() ([]ForgeSession, error)
ListForgeSessionsMissingAnvil returns sessions whose anvil column is empty, ordered by id ASC for stable iteration. Used by the `forge backfill-anvils` admin command to repair legacy rows created before the anvil field became required.
func (*DB) ListForgeSessionsWithCounts ¶ added in v0.16.0
func (db *DB) ListForgeSessionsWithCounts(createdBy string, limit int) ([]ForgeSessionWithCount, error)
ListForgeSessionsWithCounts returns sessions with their message counts using a single LEFT JOIN query, eliminating the N+1 pattern of a per-row COUNT. When createdBy is non-empty, rows owned by that user plus unattributed rows (created_by="") are returned, consistent with forgeSessionVisibleTo.
func (*DB) ListPendingOrphans ¶ added in v0.4.0
func (db *DB) ListPendingOrphans() ([]PendingOrphan, error)
ListPendingOrphans returns all beads awaiting user decision, ordered by creation time.
func (*DB) ListWicketIssues ¶ added in v0.11.0
func (db *DB) ListWicketIssues(opts ListWicketIssuesOpts) ([]WicketIssue, error)
ListWicketIssues returns wicket_issues rows matching the given options, ordered by created_at descending.
func (*DB) LogEventAt ¶ added in v0.11.1
LogEventAt records an event with an explicit timestamp. It is intended for use in tests where deterministic timestamps are required.
func (*DB) MarkFindingPosted ¶ added in v0.20.0
MarkFindingPosted records that a finding was successfully posted as a GitHub review comment: it sets posted=1, stores the returned comment ID, resets the consecutive-miss counter, clears resolved_at (so a previously resolved finding reappears in OpenPostedFindings), and clears the stale gh_thread_id (the new comment lives on a fresh thread). Used by the Assay posting layer after a successful inline-comment POST.
func (*DB) MarkNeedsHuman ¶ added in v0.7.0
MarkNeedsHuman immediately sets needs_human=1 for a bead, creating the retries row if it doesn't exist. Use this when a pipeline outcome definitively requires human attention (e.g. no-diff hard reject) without waiting for the circuit breaker to trip after multiple failures.
func (*DB) MarkResolved ¶ added in v0.20.0
MarkResolved sets resolved_at to now (UTC) for the finding identified by findingHash.
func (*DB) MarkWorkerStalled ¶
MarkWorkerStalled sets a worker's status to stalled and records the time.
func (*DB) MergedPRs ¶ added in v0.8.0
MergedPRs returns all PRs with status=merged that have a non-empty, non-external bead_id (i.e. beads that Forge can attempt to close).
func (*DB) NeedsAttentionBeads ¶
func (db *DB) NeedsAttentionBeads(maxCI, maxRev, maxRebase int) ([]NeedsAttentionBead, error)
NeedsAttentionBeads returns all beads with needs_human=1, clarification_needed=1, or status=stalled, enriched with title from queue_cache or workers tables. It also includes PRs that have exhausted their CI-fix, review-fix, or rebase attempt limits. The maxCI/maxRev/maxRebase thresholds determine which PRs are considered exhausted.
func (*DB) NeedsHumanBeadIDSet ¶ added in v0.3.0
NeedsHumanBeadIDSet returns a set of "beadID\x00anvil" keys for all beads currently marked needs_human=1 in the retries table. This intentionally includes any reason that set needs_human (dispatch circuit breaker trips, crucible child failures, exhausted retries, clarification needed that escalated to human review, etc.), and excludes all rows where needs_human=0. Callers can use this set for O(1) membership checks in the dispatch loop to determine which beads are not eligible for automatic dispatch.
func (*DB) NeedsHumanBeads ¶
func (db *DB) NeedsHumanBeads() ([]RetryRecord, error)
NeedsHumanBeads returns all beads that have exhausted retries.
func (*DB) OpenPRsWithDetail ¶ added in v0.5.0
func (db *DB) OpenPRsWithDetail() ([]OpenPRDetail, error)
OpenPRsWithDetail returns all non-terminal PRs with title resolution and full status fields.
func (*DB) OpenPostedFindings ¶ added in v0.20.0
OpenPostedFindings returns the findings on the given anvil/PR that have been posted (posted=1) and not yet resolved (resolved_at IS NULL). The Assay posting layer uses these to detect findings that disappeared on a later review — accumulating consecutive misses until their threads are auto-resolved.
func (*DB) OrphanedMonitoringBellowsWorkers ¶ added in v0.16.0
func (db *DB) OrphanedMonitoringBellowsWorkers() ([]OrphanedWorkerInfo, error)
OrphanedMonitoringBellowsWorkers returns bellows monitoring workers whose underlying PR is missing or already terminal (merged/closed), using a single LEFT JOIN query rather than per-row GetPRByNumber lookups.
func (*DB) PRByNumber ¶
PRByNumber returns the PR record for a given GitHub PR number, or nil if not found.
func (*DB) PendingRetries ¶
func (db *DB) PendingRetries() ([]RetryRecord, error)
PendingRetries returns retries that are ready to be attempted (next_retry <= now).
func (*DB) PostedFindingHashes ¶ added in v0.20.0
PostedFindingHashes returns the set of finding_hash values that have already been posted (posted=1) for the given anvil and PR. Assay uses this to suppress re-posting Nit findings on a repeat review of the same PR. The returned map is never nil — an empty map means nothing has been posted yet.
func (*DB) PurgeExpiredWebSessions ¶ added in v0.16.0
PurgeExpiredWebSessions removes all sessions whose expires_at is in the past. Returns the number of rows removed.
func (*DB) QueryPendingRulesByAnvil ¶ added in v0.10.0
func (db *DB) QueryPendingRulesByAnvil() (map[string][]PendingRule, error)
QueryPendingRulesByAnvil returns all pending warden rules grouped by anvil.
func (*DB) QueueCache ¶
QueueCache returns all cached queue items, sorted by section (ready, unlabeled, in_progress), then priority, bead ID, and anvil.
func (*DB) QueueCount ¶ added in v0.2.0
QueueCount returns the number of items currently in the queue cache. It is a lightweight alternative to QueueCache() for callers that only need the count, avoiding a full row scan and allocation.
func (*DB) ReadyToMergePRs ¶
func (db *DB) ReadyToMergePRs() ([]ReadyToMergePR, error)
ReadyToMergePRs returns PRs where: CI passing, not conflicting, no unresolved review threads, no pending review requests, and not in a terminal/fix state. Title is resolved with a best-effort lookup from queue_cache or workers.
func (*DB) RecentDailyCosts ¶
func (db *DB) RecentDailyCosts(n int) ([]struct { Date string InputTokens int OutputTokens int EstimatedCost float64 }, error)
RecentDailyCosts returns daily cost records, most recent first.
func (*DB) RecentEvents ¶
RecentEvents returns the most recent n events.
func (*DB) RecentEventsExcluding ¶ added in v0.5.0
RecentEventsExcluding returns the n most recent events, excluding the given types.
func (*DB) RecentEventsMatching ¶ added in v0.21.0
func (db *DB) RecentEventsMatching(pattern string, n int, excludeTypes []EventType) ([]Event, error)
RecentEventsMatching returns the n most recent events whose timestamp, type, bead_id, or message contains the given pattern (case-insensitive substring match), excluding the given types. Unlike RecentEventsExcluding, the match is applied at the SQL level so the entire event log is searched — not just the most recent n rows. The LIMIT still caps results at n to protect callers (e.g. the TUI render) from unbounded result sets.
An empty pattern is treated as "match everything" and is equivalent to RecentEventsExcluding.
func (*DB) RecordAssayRun ¶ added in v0.20.0
RecordAssayRun inserts a new assay_runs row. started_at defaults to now (UTC) when r.StartedAt is the zero time.
func (*DB) RemovePendingOrphan ¶ added in v0.4.0
RemovePendingOrphan removes a bead from the pending_orphans list.
func (*DB) ReplaceQueueCacheForAnvils ¶
ReplaceQueueCacheForAnvils atomically replaces the cached queue rows for the specified anvils only, leaving rows for other anvils untouched. This allows failed anvil polls to retain their last-known cached data.
func (*DB) ResetConsecutiveMiss ¶ added in v0.20.0
ResetConsecutiveMiss clears the consecutive_misses counter for a finding that was re-detected on a fresh review (so an intermittent disappearance does not accumulate toward auto-resolution).
func (*DB) ResetDispatchFailures ¶
ResetDispatchFailures clears the dispatch_failures counter and any circuit-breaker-induced needs_human flag for a bead, allowing it to be dispatched again. It only resets rows that were actually tripped by the dispatch circuit breaker (dispatch_failures > 0 with a "circuit breaker:" last_error), so unrelated needs_human states are preserved.
func (*DB) ResetPRFixCounts ¶
ResetPRFixCounts resets all fix/rebase counters on a PR and sets its status back to open so Bellows re-detects and dispatches new fix cycles.
func (*DB) ResetRecoveryFailures ¶ added in v0.14.0
ResetRecoveryFailures clears the recovery failure counter and timestamp for a bead after a successful recovery. If the row was marked needs_human by a tripped recovery circuit breaker, it also clears that recovery-specific state while preserving unrelated needs_human reasons.
func (*DB) ResetRetry ¶
ResetRetry clears the needs_human and clarification_needed flags and resets the retry count to zero, allowing the bead to be dispatched again.
func (*DB) SetClarificationNeeded ¶
SetClarificationNeeded marks or clears the clarification_needed flag for a bead. When needed=true and no retry record exists, one is created with the flag set. When needed=false, only existing records are updated (no row is created).
func (*DB) SetDailyCostLimit ¶
SetDailyCostLimit sets the cost limit for a specific date.
func (*DB) SetFindingThreadID ¶ added in v0.20.0
SetFindingThreadID stores the GitHub review thread node ID matched to a finding, so a subsequent resolution can target the thread directly.
func (*DB) StalledWorkers ¶
StalledWorkers returns active non-stalled workers whose log files have not been modified within the given staleThreshold. Workers without a log path (pending workers) are still considered stalled if their start time exceeds the threshold. Already-stalled workers are excluded to avoid repeated filesystem stat calls on log files that won't change their status. All phases listed in backgroundPhases are excluded from the global check because they only produce log output when external state changes (e.g. PR events) and can be legitimately silent for long stretches. However, lifecycle workers (quench/cifix/burnish/reviewfix/rebase/assay) that were registered with a per-worker StaleTimeout are additionally checked using that shorter threshold — these phases appear in backgroundPhases and are therefore absent from the first result set, so there is no risk of duplicates.
func (*DB) TotalCostSince ¶
TotalCostSince returns aggregate cost since a given date.
func (*DB) TouchForgeSession ¶ added in v0.16.0
TouchForgeSession bumps updated_at to now without changing other fields. Called after appending a message so the sidebar reorders correctly.
func (*DB) TouchWebSession ¶ added in v0.16.0
TouchWebSession updates the last_seen timestamp and slides the expiry forward by ttl. Returns the new expiry time.
func (*DB) UpdateForgeSession ¶ added in v0.16.0
UpdateForgeSession applies a partial update to the session row. Only the fields whose pointer is non-nil are written, so callers can rename a session without touching its status (or vice versa). The updated_at column is always advanced.
func (*DB) UpdateForgeSessionAnvil ¶ added in v0.16.0
UpdateForgeSessionAnvil sets the anvil column on a session and advances updated_at. Returns ErrForgeSessionNotFound when no row matches the id so the caller can distinguish a missing row from a no-op write.
func (*DB) UpdateForgeSessionStageAndPlan ¶ added in v0.16.0
func (db *DB) UpdateForgeSessionStageAndPlan(id int64, stage, plan *string) (*ForgeSession, error)
UpdateForgeSessionStageAndPlan sets the stage and/or plan fields on a session and advances updated_at. nil pointers leave the corresponding column untouched. Returns the updated row for callers that want to echo it back to the client without a separate SELECT.
Returns ErrForgeSessionNotFound when the UPDATE matched no rows so callers can distinguish "row missing" from "DB unavailable" — without this the handler would silently 200 OK on a vanished session id.
func (*DB) UpdatePRBellowsAssignment ¶ added in v0.16.0
UpdatePRBellowsAssignment sets both the bellows_managed and bellows_manually_assigned flags for a PR atomically. The manual flag records that a user explicitly assigned (or released) bellows via IPC, so the reconcile loop can distinguish user-initiated assignments from legacy auto-adoption and avoid clobbering them.
func (*DB) UpdatePRBellowsManaged ¶ added in v0.7.0
UpdatePRBellowsManaged sets the bellows_managed flag for a PR. When true, bellows will run lifecycle workers (quench, burnish, rebase) for this PR.
func (*DB) UpdatePRLifecycle ¶
func (db *DB) UpdatePRLifecycle(id int, ciFixCount, reviewFixCount, rebaseCount int, ciPassing bool) error
UpdatePRLifecycle updates the lifecycle state of a PR.
func (*DB) UpdatePRMergeability ¶
func (db *DB) UpdatePRMergeability(id int, ciPassing, isConflicting, hasUnresolvedThreads, hasPendingReviews, hasApproval bool) error
UpdatePRMergeability persists the mergeability state for a PR. Called by Bellows on each poll to keep the ready-to-merge view current. hasApproval is stored so the last known approval state survives daemon restarts, allowing Bellows to correctly seed lastSnap from DB and detect the ready-to-merge transition even for PRs that were already ready when the daemon restarted.
func (*DB) UpdatePRStatus ¶
UpdatePRStatus updates a PR's status and last_checked time by its internal database ID.
func (*DB) UpdatePRStatusIfNeedsFix ¶
UpdatePRStatusIfNeedsFix conditionally updates a PR's status only when the current status is needs_fix. This prevents overwriting a terminal status (e.g. merged or closed) if the PR transitions while a fix worker is running.
func (*DB) UpdatePRTitle ¶ added in v0.7.0
UpdatePRTitle sets the title for a PR record.
func (*DB) UpdateWicketIssue ¶ added in v0.11.0
func (db *DB) UpdateWicketIssue(issue WicketIssue) error
UpdateWicketIssue updates the mutable fields of an existing wicket_issues row, identified by repo + issue_number.
func (*DB) UpdateWorkerLogPath ¶
UpdateWorkerLogPath updates the log path of a worker.
func (*DB) UpdateWorkerPID ¶
UpdateWorkerPID updates the PID of a running worker.
func (*DB) UpdateWorkerPhase ¶
UpdateWorkerPhase updates the active pipeline phase for a worker.
func (*DB) UpdateWorkerStatus ¶
func (db *DB) UpdateWorkerStatus(id string, status WorkerStatus) error
UpdateWorkerStatus updates a worker's status and optionally sets completed_at.
func (*DB) UpdateWorkerTitle ¶ added in v0.12.0
UpdateWorkerTitle updates the display title of a worker.
func (*DB) UpsertProviderQuota ¶
UpsertProviderQuota creates or updates a provider's quota record.
func (*DB) UpsertRetry ¶
func (db *DB) UpsertRetry(r *RetryRecord) error
UpsertRetry creates or updates a retry record.
func (*DB) WorkersByAnvil ¶
WorkersByAnvil returns all workers for a given anvil.
type Event ¶
type Event struct {
ID int
Timestamp time.Time
Type EventType
Message string
BeadID string
Anvil string
}
Event represents a logged event.
type EventType ¶
type EventType string
EventType categorizes events in the log.
const ( EventDaemonStarted EventType = "daemon_started" EventDaemonStopped EventType = "daemon_stopped" EventConfigReload EventType = "config_reload" EventOrphanCleanup EventType = "orphan_cleanup" // EventPoll is retained as a constant for historical rows already in the // events table (and so referring code does not need to be churned), but it // is no longer written by the daemon: successful polls are tracked only in // the in-memory map on Daemon (see Daemon.lastPollMap). Failed polls // continue to be persisted as EventPollError so they remain visible. EventPoll EventType = "poll" EventPollError EventType = "poll_error" EventBeadClaimed EventType = "bead_claimed" EventSmithStarted EventType = "smith_started" EventSmithDone EventType = "smith_done" EventSmithStats EventType = "smith_stats" EventSmithFailed EventType = "smith_failed" EventSmithRecheck EventType = "smith_recheck" EventWardenStarted EventType = "warden_started" EventWardenPass EventType = "warden_pass" EventWardenReject EventType = "warden_reject" EventWardenHardReject EventType = "warden_hard_reject" EventTemperStarted EventType = "temper_started" EventTemperPassed EventType = "temper_passed" EventTemperFailed EventType = "temper_failed" EventBellowsStarted EventType = "bellows_started" EventCIFailed EventType = "ci_failed" EventQuenchStarted EventType = "ci_fix_started" EventQuenchSuccess EventType = "ci_fix_success" EventQuenchFailed EventType = "ci_fix_failed" EventReviewChanges EventType = "review_changes" EventBurnishStarted EventType = "review_fix_started" EventBurnishSuccess EventType = "review_fix_success" EventBurnishFailed EventType = "review_fix_failed" EventReviewThreadResolved EventType = "review_thread_resolved" EventBurnishSmithError EventType = "review_fix_smith_error" EventBurnishTemperFailed EventType = "review_fix_temper_failed" EventPRCreated EventType = "pr_created" EventPRMerged EventType = "pr_merged" EventPRClosed EventType = "pr_closed" EventPRConflicting EventType = "pr_conflicting" EventPRNeedsFix EventType = "pr_needs_fix" EventRebaseStarted EventType = "rebase_started" EventRebaseSuccess EventType = "rebase_success" EventRebaseFailed EventType = "rebase_failed" EventLifecycleExhausted EventType = "lifecycle_exhausted" EventClarificationNeeded EventType = "clarification_needed" EventClarificationCleared EventType = "clarification_cleared" EventRetryReset EventType = "retry_reset" EventRetryCleared EventType = "retry_cleared" EventBeadDismissed EventType = "bead_dismissed" EventSchematicStarted EventType = "schematic_started" EventSchematicDone EventType = "schematic_done" EventSchematicSkipped EventType = "schematic_skipped" EventDispatchFailed EventType = "dispatch_failed" EventDispatchCircuitBreak EventType = "dispatch_circuit_break" // EventDispatchBlockedStrandedBranch fires when the pre-dispatch remote // check finds origin/forge/<bead-id> with commits not reachable from the // base ref and no PR — a prior worker pushed but never opened a PR. EventDispatchBlockedStrandedBranch EventType = "dispatch_blocked_stranded_branch" // EventDispatchRecoveredStrandedBranch fires when the pre-dispatch remote // check finds a stranded origin/forge/<bead-id> with a changelog fragment // for the bead reachable from the branch tip (a completion signal) and no // PR — the daemon auto-opens the PR rather than escalating to needs_human. EventDispatchRecoveredStrandedBranch EventType = "dispatch_recovered_stranded_branch" EventRateLimited EventType = "rate_limited" EventCostLimitHit EventType = "cost_limit_hit" EventDispatchPaused EventType = "dispatch_paused" EventDispatchResumed EventType = "dispatch_resumed" EventSchematicSubBead EventType = "schematic_sub_bead" EventWorkerStalled EventType = "worker_stalled" EventBeadTagged EventType = "bead_tagged" EventBeadClosed EventType = "bead_closed" EventPRReadyToMerge EventType = "pr_ready_to_merge" EventPRReviewNeeded EventType = "pr_review_needed" EventPRMergeRequested EventType = "pr_merge_requested" EventPRMergeFailed EventType = "pr_merge_failed" EventPRAutoMerged EventType = "pr_auto_merged" EventError EventType = "error" EventBeadRecovered EventType = "bead_recovered" EventBeadStopped EventType = "bead_stopped" EventDepcheckStarted EventType = "depcheck_started" EventDepcheckPassed EventType = "depcheck_passed" EventDepcheckFound EventType = "depcheck_found" EventDepcheckFailed EventType = "depcheck_failed" EventDepcheckBeadCreated EventType = "depcheck_bead_created" EventDepcheckDedup EventType = "depcheck_dedup" EventVulnScanStarted EventType = "vuln_scan_started" EventVulnScanDone EventType = "vuln_scan_done" EventVulnScanFailed EventType = "vuln_scan_failed" EventVulnScanCycleDone EventType = "vuln_scan_cycle_done" EventVulnBeadCreated EventType = "vuln_bead_created" EventWardenRerun EventType = "warden_rerun" EventApproveAsIs EventType = "approve_as_is" EventForceSmith EventType = "force_smith" EventAutoLearnError EventType = "auto_learn_error" EventAutoLearnSkipped EventType = "auto_learn_skipped" EventAutoLearnRules EventType = "auto_learn_rules" EventWardenRuleLearned EventType = "warden_rule_learned" EventBeadAutoClosed EventType = "bead_auto_closed" EventNoChangesNeeded EventType = "no_changes_needed" EventPRCreationFailed EventType = "pr_creation_failed" EventPRAlreadyExists EventType = "pr_already_exists" // EventPRCreateRecovered fires when the manual create-PR-from-existing-branch // recovery opens (or registers) a PR for an already-pushed forge branch // without re-running Smith, clearing the needs_human escalation. EventPRCreateRecovered EventType = "pr_create_recovered" // Crucible events — parent bead orchestration with children on feature branches. EventCrucibleStarted EventType = "crucible_started" EventCrucibleChildDispatched EventType = "crucible_child_dispatched" EventCrucibleChildPRCreated EventType = "crucible_child_pr_created" EventCrucibleChildMerged EventType = "crucible_child_merged" EventCrucibleChildFailed EventType = "crucible_child_failed" EventCrucibleFinalPR EventType = "crucible_final_pr" EventCrucibleComplete EventType = "crucible_complete" EventCruciblePaused EventType = "crucible_paused" // Questgiver/Adventurer events — E2E quest execution and bead creation. EventQuestgiverScanDone EventType = "questgiver_scan_done" EventAdventurerStarted EventType = "adventurer_started" EventAdventurerPassed EventType = "adventurer_passed" EventAdventurerFailed EventType = "adventurer_failed" EventTestBeadCreated EventType = "test_bead_created" // Smelter events — batch warden rule flushing. EventSmelterStarted EventType = "smelter_started" EventSmelterFlushed EventType = "smelter_flushed" EventSmelterFailed EventType = "smelter_failed" EventSmelterCycleDone EventType = "smelter_cycle_done" // Wicket events — GitHub issue triage monitor. EventWicketScanDone EventType = "wicket_scan_done" EventWicketIssueTriage EventType = "wicket_issue_triage" EventWicketBeadCreated EventType = "wicket_bead_created" EventWicketClarification EventType = "wicket_clarification" EventWicketRejected EventType = "wicket_rejected" EventWicketFlaggedHuman EventType = "wicket_flagged_human" EventWicketError EventType = "wicket_error" EventWicketDispatchConfirm EventType = "wicket_dispatch_confirm" EventWicketPRLinked EventType = "wicket_pr_linked" EventWicketIssueClosed EventType = "wicket_issue_closed" EventWicketIssueStaleClose EventType = "wicket_issue_stale_close" // Pipeline hook events. EventHookFailed EventType = "hook_failed" // Recovery circuit breaker — orphan bead recovery exhausted retries. EventRecoveryCircuitBreak EventType = "recovery_circuit_break" )
type ExhaustedPR ¶
type ExhaustedPR struct {
ID int
Number int
Anvil string
BeadID string
CIFixCount int
ReviewFixCount int
RebaseCount int
Reason string
}
ExhaustedPR represents a PR that has exhausted its CI-fix, review-fix, or rebase attempt limits and needs human attention.
type Finding ¶ added in v0.20.0
type Finding struct {
ID int
Anvil string
PRNumber int
HeadSHA string
FindingHash string
File string
Anchor string
Severity string
Category string
Title string
Body string
Evidence string
SourcePass string
Posted bool
GHCommentID int64
GHThreadID string
ConsecutiveMisses int
// ResolvedAt is nil while the finding is still considered open.
ResolvedAt *time.Time
CreatedAt time.Time
}
Finding is the database representation of a single code-review finding discovered by Assay on a PR. FindingHash is the dedup key — duplicate findings (same hash) are silently ignored on insert.
type ForgeSession ¶ added in v0.16.0
type ForgeSession struct {
ID int64
Title string
Status string
Anvil string
CreatedBy string
CreatedAt time.Time
UpdatedAt time.Time
// Stage is the current Beads-Forge stage (drafting | grilling | ready).
// The AI integration bead added these values; sessions created before the
// migration get the default "drafting" via the column default.
Stage string
// Plan is the latest implementation plan emitted for the session, in
// markdown. Empty in drafting until the user requests a plan; updated by
// the AI worker each time a fresh plan is generated.
Plan string
}
ForgeSession is one conversation row that backs the Beads-Forge page in Hearth 2.0. Each session pairs a list of messages (forge_session_messages) with metadata about who started it and what stage it is in.
Status values are an open-ended TEXT to avoid an enum migration when later beads add stages (plan_ready, beads_created, etc.). The foundation bead uses only "draft" and "archived".
type ForgeSessionMessage ¶ added in v0.16.0
type ForgeSessionMessage struct {
ID int64
SessionID int64
Role string
Content string
CreatedAt time.Time
// Kind is one of: text (default chat turn), plan (claude-emitted plan
// markdown), question (structured question with options), answer (user's
// answer to a question), status (system status message — e.g. stage
// transitions). The state layer treats the value as opaque text.
Kind string
// Metadata is an optional JSON payload tied to Kind. For "question" it
// holds the options + recommendation; for "answer" it pins the
// question_id and option_id. Empty string means no metadata.
Metadata string
}
ForgeSessionMessage is one entry in a forge session conversation. Roles follow the chat-style convention: "user", "assistant", "system".
Kind extends the message with a structured payload type used to render non-text turns in the chat view (plan emissions, structured questions from the grilling stage, and the user's answers). Metadata holds a JSON-encoded payload whose shape depends on Kind.
type ForgeSessionWithCount ¶ added in v0.16.0
type ForgeSessionWithCount struct {
ForgeSession
MessageCount int
}
ForgeSessionWithCount pairs a session with its pre-computed message count. Returned by ListForgeSessionsWithCounts to avoid a separate COUNT per row.
type ListWicketIssuesOpts ¶ added in v0.11.0
type ListWicketIssuesOpts struct {
// Repo filters to a specific "owner/repo" string. Empty = all repos.
Repo string
// State filters to a specific lifecycle state. Empty = all states.
State string
// Limit caps the number of rows returned. 0 = no limit.
Limit int
}
ListWicketIssuesOpts filters for ListWicketIssues.
type NeedsAttentionBead ¶
type NeedsAttentionBead struct {
BeadID string
Anvil string
Title string
Description string
Reason string
NeedsHuman bool
ClarificationNeeded bool
FailureCount int
// PRID is non-zero when this item originates from an exhausted PR rather
// than the retries table. The caller uses this to route retry/dismiss
// actions to the correct DB operation.
PRID int
PRNumber int
}
NeedsAttentionBead represents a bead requiring human attention, combining retry metadata with a best-effort title and description lookup from queue_cache or workers.
type OpenPRDetail ¶ added in v0.5.0
type OpenPRDetail struct {
ID int
Number int
Anvil string
BeadID string
Branch string
Status PRStatus
Title string
CIPassing bool
IsConflicting bool
HasUnresolvedThreads bool
HasPendingReviews bool
HasApproval bool
CIFixCount int
ReviewFixCount int
RebaseCount int
IsExternal bool // true for PRs discovered via GitHub reconciliation (ext-* bead ID)
BellowsManaged bool // true when bellows runs lifecycle workers for this PR
}
OpenPRDetail represents an open PR with full status detail for the TUI PR panel.
type OrphanedWorkerInfo ¶ added in v0.16.0
OrphanedWorkerInfo identifies a bellows monitoring worker whose underlying PR is missing or in a terminal state. PRStatus is empty when no PR row exists.
type PR ¶
type PR struct {
ID int
Number int
Anvil string
BeadID string
Branch string
BaseBranch string // Target branch for the PR (empty = repo default base branch)
Status PRStatus
CreatedAt time.Time
LastChecked *time.Time
CIFixCount int
ReviewFixCount int
RebaseCount int
CIPassing bool
IsConflicting bool
HasUnresolvedThreads bool
HasPendingReviews bool
HasApproval bool
Title string
BellowsManaged bool // true = bellows runs lifecycle workers (quench, burnish, rebase)
BellowsManuallyAssigned bool // true = a user explicitly assigned bellows via IPC; reconcile must not clobber
}
PR represents a pull request entry.
func (*PR) IsExternal ¶ added in v0.7.0
IsExternal returns true if this PR was discovered via GitHub reconciliation rather than created by a Forge pipeline.
type PendingOrphan ¶ added in v0.4.0
type PendingOrphan struct {
BeadID string
Anvil string
Title string
Branch string
CreatedAt time.Time
}
PendingOrphan represents a bead awaiting user decision during orphan recovery.
type PendingRule ¶ added in v0.10.0
PendingRule represents a warden rule awaiting human approval.
type ProviderDailyCost ¶ added in v0.2.0
type ProviderDailyCost struct {
Provider string
InputTokens int
OutputTokens int
CacheRead int
CacheWrite int
EstimatedCost float64
}
ProviderDailyCost holds per-provider cost data for a single day.
type QueueItem ¶
type QueueItem struct {
BeadID string
Anvil string
Title string
Description string
Priority int
Status string
Labels string // JSON-encoded []string
Section QueueSection // ready / unlabeled / in_progress
Assignee string
}
QueueItem represents a cached bead from the daemon's poll.
type QueueSection ¶
type QueueSection string
QueueSection categorises a bead's position in the dispatch pipeline.
const ( QueueSectionReady QueueSection = "ready" // will be auto-dispatched QueueSectionUnlabeled QueueSection = "unlabeled" // available but not tagged for dispatch QueueSectionInProgress QueueSection = "in_progress" // currently being worked on )
type ReadyToMergePR ¶
type ReadyToMergePR struct {
ID int
Number int
Anvil string
BeadID string
Branch string
Title string
}
ReadyToMergePR represents a PR that meets all conditions for merging.
type RetryRecord ¶
type RetryRecord struct {
BeadID string
Anvil string
RetryCount int
NextRetry *time.Time
NeedsHuman bool
ClarificationNeeded bool
DispatchFailures int
RecoveryFailures int
FirstRecoveryFailure *time.Time
LastError string
UpdatedAt time.Time
}
RetryRecord tracks retry state for a bead.
type WebSession ¶ added in v0.16.0
type WebSession struct {
TokenHash string
Username string
CreatedAt time.Time
ExpiresAt time.Time
LastSeen time.Time
}
WebSession represents an authenticated web UI session.
type WicketAnvilSummary ¶ added in v0.11.0
type WicketAnvilSummary struct {
// Repo is the "owner/repo" string used as the grouping key.
Repo string
OpenCount int
NeedsHumanCount int
}
WicketAnvilSummary holds per-repo issue counts for the Hearth TUI Wicket panel.
type WicketIssue ¶ added in v0.11.0
type WicketIssue struct {
ID int
Repo string
IssueNumber int
Title string
Body string
Author string
// State is the lifecycle state of this record: "pending", "bead_created",
// "ask_clarify", "needs_human", "rejected", "dispatched", "stale", "merged", "closed".
State string
TriageAction string
TriageReason string
// BeadID is the bead identifier created for this issue; empty when no
// bead has been created yet.
BeadID string
// PRNumber is the pull request number linked to this issue (0 when no PR yet).
PRNumber int
// PRUrl is the URL of the linked pull request.
PRUrl string
// CommentCount is the number of comments seen on the last check (used to detect new replies).
CommentCount int
// AuthorRepliedAt records the last time the issue author posted a comment.
// Used for stale detection to avoid false positives when non-author comments
// (e.g. bot comments, bystander replies) bump UpdatedAt.
AuthorRepliedAt *time.Time
CreatedAt time.Time
UpdatedAt time.Time
ProcessedAt *time.Time
}
WicketIssue is the database representation of a GitHub issue that has been seen (and optionally triaged) by the Wicket monitor.
type Worker ¶
type Worker struct {
ID string
BeadID string
Anvil string
Branch string
PID int
Status WorkerStatus
Phase string // active component: smith|temper|warden|bellows|idle
Title string // bead title for display in hearth
PRNumber int // PR number for bellows-triggered workers (quench/burnish/rebase)
StartedAt time.Time
CompletedAt *time.Time
LogPath string
// StaleTimeout is an optional per-worker override for stale-detection. When > 0,
// the worker is checked independently of the global stale_interval using this
// custom threshold. Lifecycle workers (quench/burnish/rebase) use this to get
// stale detection even though they are excluded from the global background-phase
// check. Stored as seconds in the DB.
StaleTimeout time.Duration
}
Worker represents a Smith worker entry.
type WorkerStatus ¶
type WorkerStatus string
WorkerStatus represents the lifecycle state of a Smith worker.
const ( WorkerPending WorkerStatus = "pending" WorkerRunning WorkerStatus = "running" WorkerReviewing WorkerStatus = "reviewing" WorkerMonitoring WorkerStatus = "monitoring" WorkerDone WorkerStatus = "done" WorkerFailed WorkerStatus = "failed" WorkerTimeout WorkerStatus = "timeout" WorkerStalled WorkerStatus = "stalled" )