package store import ( "context" "fmt" "github.com/jackc/pgx/v5" ext "git.warky.dev/wdevs/amcs/internal/types" ) func (db *DB) AddProjectPersona(ctx context.Context, projectID, personaID int64, isDefault bool) error { tx, err := db.pool.Begin(ctx) if err != nil { return fmt.Errorf("begin add project persona: %w", err) } defer func() { _ = tx.Rollback(ctx) }() if _, err := tx.Exec(ctx, `select pg_advisory_xact_lock($1)`, projectID); err != nil { return fmt.Errorf("lock project persona defaults: %w", err) } if isDefault { if _, err := tx.Exec(ctx, `update project_personas set is_default = false where project_id = $1`, projectID); err != nil { return fmt.Errorf("clear default project persona: %w", err) } } if _, err := tx.Exec(ctx, ` insert into project_personas (project_id, persona_id, is_default) values ($1, $2, $3) on conflict (project_id, persona_id) do update set is_default = excluded.is_default `, projectID, personaID, isDefault); err != nil { return fmt.Errorf("add project persona: %w", err) } if err := tx.Commit(ctx); err != nil { return fmt.Errorf("commit add project persona: %w", err) } return nil } func (db *DB) RemoveProjectPersona(ctx context.Context, projectID, personaID int64) error { tag, err := db.pool.Exec(ctx, `delete from project_personas where project_id = $1 and persona_id = $2`, projectID, personaID) if err != nil { return fmt.Errorf("remove project persona: %w", err) } if tag.RowsAffected() == 0 { return fmt.Errorf("project persona link not found") } return nil } func (db *DB) ListProjectPersonas(ctx context.Context, projectID int64) ([]ext.ProjectPersona, error) { rows, err := db.pool.Query(ctx, ` select p.id, p.guid, p.name, p.description, p.summary, p.detail, p.compiled_summary, p.compiled_detail, p.compiled_at, p.tags::text[], p.created_at, p.updated_at, pp.is_default from agent_personas p join project_personas pp on pp.persona_id = p.id where pp.project_id = $1 order by pp.is_default desc, p.name `, projectID) if err != nil { return nil, fmt.Errorf("list project personas: %w", err) } defer rows.Close() var result []ext.ProjectPersona for rows.Next() { var item ext.ProjectPersona var tags []string if err := rows.Scan(&item.Persona.ID, &item.Persona.GUID, &item.Persona.Name, &item.Persona.Description, &item.Persona.Summary, &item.Persona.Detail, &item.Persona.CompiledSummary, &item.Persona.CompiledDetail, &item.Persona.CompiledAt, &tags, &item.Persona.CreatedAt, &item.Persona.UpdatedAt, &item.IsDefault); err != nil { return nil, fmt.Errorf("scan project persona: %w", err) } item.Persona.Tags = nilToEmptyStrings(tags) result = append(result, item) } return result, rows.Err() } func (db *DB) GetDefaultProjectPersona(ctx context.Context, projectID int64) (*ext.Persona, error) { row := db.pool.QueryRow(ctx, ` select p.id, p.guid, p.name, p.description, p.summary, p.detail, p.compiled_summary, p.compiled_detail, p.compiled_at, p.tags::text[], p.created_at, p.updated_at from agent_personas p join project_personas pp on pp.persona_id = p.id where pp.project_id = $1 and pp.is_default `, projectID) persona, err := scanPersona(row) if err == pgx.ErrNoRows { return nil, nil } if err != nil { return nil, fmt.Errorf("get default project persona: %w", err) } return &persona, nil }