kunchenguid--no-mistakes
26f897c1ec
release / release-please (push) Failing after 1m49s
docs / build (push) Failing after 6m34s
release / build-and-upload (arm64, linux) (push) Has been cancelled
release / build-and-upload (arm64, windows) (push) Has been cancelled
release / build-darwin (amd64, darwin) (push) Has been cancelled
release / checksums (push) Has been cancelled
release / finalize (push) Has been cancelled
release / build-darwin (arm64, darwin) (push) Has been cancelled
release / build-and-upload (amd64, linux) (push) Has been cancelled
release / build-and-upload (amd64, windows) (push) Has been cancelled
docs / deploy (push) Has been cancelled
197 行
7.2 KiB
Go
197 行
7.2 KiB
Go
package db
|
|
|
|
import "fmt"
|
|
|
|
const (
|
|
RoundSelectionSourceUser = "user"
|
|
RoundSelectionSourceAutoFix = "auto_fix"
|
|
)
|
|
|
|
// StepRound represents one execution round within a pipeline step.
|
|
type StepRound struct {
|
|
ID string
|
|
StepResultID string
|
|
Round int
|
|
Trigger string // "initial", "auto_fix"; legacy "user_fix" is treated as "auto_fix"
|
|
FindingsJSON *string // nullable - findings produced by this round
|
|
// UserFindingsJSON, when non-nil, is the merged finding list that was
|
|
// dispatched to the fix agent after the user edited per-finding
|
|
// instructions or added their own findings. It includes both the
|
|
// selected agent-produced findings (with any attached user
|
|
// instructions) and the user-authored findings.
|
|
UserFindingsJSON *string
|
|
// SelectedFindingIDs, when non-nil, is a JSON array of finding IDs that
|
|
// were chosen (by the user or auto-fix filter) to be fixed AFTER this
|
|
// round. It is populated on the round whose findings triggered the next
|
|
// round, so that later rounds' prompts can tell which findings were
|
|
// deliberately left unselected.
|
|
SelectedFindingIDs *string
|
|
SelectionSource *string
|
|
// FixSummary, when non-nil, is the agent's one-line commit summary for
|
|
// the fix attempt performed during this round. It is only set when the
|
|
// round itself was a fix round (trigger=="auto_fix").
|
|
FixSummary *string
|
|
DurationMS int64
|
|
CreatedAt int64
|
|
}
|
|
|
|
// StepRoundStats summarizes execution rounds for a step. It lets status
|
|
// surfaces show whether a running/fixing step is in an initial pass or a fix
|
|
// pass without reloading every round in callers.
|
|
type StepRoundStats struct {
|
|
TotalRounds int
|
|
FixRounds int
|
|
LatestRound int
|
|
LatestTrigger string
|
|
LatestSelection string
|
|
LatestRoundAt int64
|
|
LatestFixRound int
|
|
LatestFixRoundAt int64
|
|
SelectedForFix bool
|
|
AutoSelectedForFix bool
|
|
PendingFixSource string
|
|
}
|
|
|
|
// IsFixRound reports whether this round was a fix attempt. Legacy "user_fix"
|
|
// rounds count: they were fix rounds dispatched by an explicit user selection.
|
|
func (r *StepRound) IsFixRound() bool {
|
|
return r.Trigger == "auto_fix" || r.Trigger == "user_fix"
|
|
}
|
|
|
|
// StepFixSummaries returns one entry per fix round for a step, in round order:
|
|
// the agent's one-line fix summary, or "" when the round recorded none.
|
|
func (d *DB) StepFixSummaries(stepResultID string) ([]string, error) {
|
|
rounds, err := d.GetRoundsByStep(stepResultID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var summaries []string
|
|
for _, r := range rounds {
|
|
if !r.IsFixRound() {
|
|
continue
|
|
}
|
|
summary := ""
|
|
if r.FixSummary != nil {
|
|
summary = *r.FixSummary
|
|
}
|
|
summaries = append(summaries, summary)
|
|
}
|
|
return summaries, nil
|
|
}
|
|
|
|
// StepRoundStats returns aggregate round information for a step result.
|
|
func (d *DB) StepRoundStats(stepResultID string) (StepRoundStats, error) {
|
|
rounds, err := d.GetRoundsByStep(stepResultID)
|
|
if err != nil {
|
|
return StepRoundStats{}, err
|
|
}
|
|
var stats StepRoundStats
|
|
latestSelectedRound := 0
|
|
latestSelectedSource := ""
|
|
for _, r := range rounds {
|
|
stats.TotalRounds++
|
|
stats.LatestRound = r.Round
|
|
stats.LatestTrigger = r.Trigger
|
|
stats.LatestRoundAt = r.CreatedAt
|
|
if r.SelectionSource != nil {
|
|
stats.LatestSelection = *r.SelectionSource
|
|
}
|
|
if r.SelectedFindingIDs != nil && *r.SelectedFindingIDs != "" {
|
|
stats.SelectedForFix = true
|
|
stats.AutoSelectedForFix = r.SelectionSource != nil && *r.SelectionSource == RoundSelectionSourceAutoFix
|
|
latestSelectedRound = r.Round
|
|
latestSelectedSource = stats.LatestSelection
|
|
}
|
|
if r.IsFixRound() {
|
|
stats.FixRounds++
|
|
stats.LatestFixRound = stats.FixRounds
|
|
stats.LatestFixRoundAt = r.CreatedAt
|
|
}
|
|
}
|
|
if latestSelectedRound == stats.LatestRound {
|
|
stats.PendingFixSource = latestSelectedSource
|
|
}
|
|
return stats, nil
|
|
}
|
|
|
|
// InsertStepRound creates a new round record for a step result. fixSummary may
|
|
// be nil for non-fix rounds or when the agent produced no summary.
|
|
func (d *DB) InsertStepRound(stepResultID string, round int, trigger string, findingsJSON *string, fixSummary *string, durationMS int64) (*StepRound, error) {
|
|
r := &StepRound{
|
|
ID: newID(),
|
|
StepResultID: stepResultID,
|
|
Round: round,
|
|
Trigger: trigger,
|
|
FindingsJSON: findingsJSON,
|
|
FixSummary: fixSummary,
|
|
DurationMS: durationMS,
|
|
CreatedAt: now(),
|
|
}
|
|
_, err := d.sql.Exec(
|
|
`INSERT INTO step_rounds (id, step_result_id, round, trigger_type, findings_json, user_findings_json, selected_finding_ids, selection_source, fix_summary, duration_ms, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
r.ID, r.StepResultID, r.Round, r.Trigger, r.FindingsJSON, r.UserFindingsJSON, r.SelectedFindingIDs, r.SelectionSource, r.FixSummary, r.DurationMS, r.CreatedAt,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("insert step round: %w", err)
|
|
}
|
|
return r, nil
|
|
}
|
|
|
|
// SetStepRoundSelection records which findings were selected for fix AFTER the
|
|
// given round produced its findings, along with whether that selection came
|
|
// from the user or auto-fix filtering. Passing a nil or empty JSON array clears
|
|
// both columns.
|
|
func (d *DB) SetStepRoundSelection(id string, selectedFindingIDs *string, source string) error {
|
|
var selectionSource *string
|
|
if selectedFindingIDs != nil && *selectedFindingIDs != "" && source != "" {
|
|
selectionSource = &source
|
|
}
|
|
if _, err := d.sql.Exec(
|
|
`UPDATE step_rounds SET selected_finding_ids = ?, selection_source = ? WHERE id = ?`,
|
|
selectedFindingIDs, selectionSource, id,
|
|
); err != nil {
|
|
return fmt.Errorf("set step round selection: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetStepRoundSelectedFindingIDs preserves the old API for callers that do not
|
|
// need to distinguish how the selection was made.
|
|
func (d *DB) SetStepRoundSelectedFindingIDs(id string, selectedFindingIDs *string) error {
|
|
return d.SetStepRoundSelection(id, selectedFindingIDs, RoundSelectionSourceUser)
|
|
}
|
|
|
|
// SetStepRoundUserFindings records the merged finding list (with user
|
|
// instructions attached and user-added findings appended) that was
|
|
// dispatched to the fix agent for the round. Passing nil clears the column.
|
|
func (d *DB) SetStepRoundUserFindings(id string, userFindingsJSON *string) error {
|
|
if _, err := d.sql.Exec(
|
|
`UPDATE step_rounds SET user_findings_json = ? WHERE id = ?`,
|
|
userFindingsJSON, id,
|
|
); err != nil {
|
|
return fmt.Errorf("set step round user findings: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetRoundsByStep returns all rounds for a step result, ordered by round number.
|
|
func (d *DB) GetRoundsByStep(stepResultID string) ([]*StepRound, error) {
|
|
rows, err := d.sql.Query(
|
|
`SELECT id, step_result_id, round, trigger_type, findings_json, user_findings_json, selected_finding_ids, selection_source, fix_summary, duration_ms, created_at FROM step_rounds WHERE step_result_id = ? ORDER BY round`,
|
|
stepResultID,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get rounds by step: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
var rounds []*StepRound
|
|
for rows.Next() {
|
|
r := &StepRound{}
|
|
if err := rows.Scan(&r.ID, &r.StepResultID, &r.Round, &r.Trigger, &r.FindingsJSON, &r.UserFindingsJSON, &r.SelectedFindingIDs, &r.SelectionSource, &r.FixSummary, &r.DurationMS, &r.CreatedAt); err != nil {
|
|
return nil, fmt.Errorf("scan step round: %w", err)
|
|
}
|
|
rounds = append(rounds, r)
|
|
}
|
|
return rounds, rows.Err()
|
|
}
|