[agent/codex:gpt-5.3-codex-spark] Update the code against the AX (Agent Experience) design pri... #3

Merged
Virgil merged 1 commit from agent/update-the-code-against-the-ax--agent-ex into dev 2026-03-30 06:44:50 +00:00
4 changed files with 232 additions and 51 deletions

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"os"
"sort"
"strings"
"forge.lthn.ai/core/cli/pkg/cli"
@ -93,17 +94,42 @@ func addLintCommands(root *cli.Command) {
lintpkg.WriteText(os.Stdout, allFindings)
}
if len(allFindings) > 0 {
if checkFormat == "text" && len(allFindings) > 0 {
summary := lintpkg.Summarise(allFindings)
fmt.Fprintf(os.Stderr, "\n%d finding(s)", summary.Total)
fmt.Fprintf(os.Stdout, "\n%d finding(s)", summary.Total)
orderedSeverities := []string{"critical", "high", "medium", "low", "info"}
seen := map[string]bool{}
var parts []string
for sev, count := range summary.BySeverity {
for _, sev := range orderedSeverities {
count := summary.BySeverity[sev]
if count == 0 {
continue
}
seen[sev] = true
parts = append(parts, fmt.Sprintf("%d %s", count, sev))
}
if len(parts) > 0 {
fmt.Fprintf(os.Stderr, " (%s)", strings.Join(parts, ", "))
var extraSeverities []string
for severity := range summary.BySeverity {
if seen[severity] {
continue
}
extraSeverities = append(extraSeverities, severity)
}
fmt.Fprintln(os.Stderr)
sort.Strings(extraSeverities)
for _, severity := range extraSeverities {
count := summary.BySeverity[severity]
if count == 0 {
continue
}
parts = append(parts, fmt.Sprintf("%d %s", count, severity))
}
if len(parts) > 0 {
fmt.Fprintf(os.Stdout, " (%s)", strings.Join(parts, ", "))
}
fmt.Fprintln(os.Stdout)
}
return nil

View file

@ -24,6 +24,7 @@ import (
var (
healthProblems bool
healthRegistry string
healthJSON bool
)
// HealthWorkflowRun represents a GitHub Actions workflow run
@ -38,11 +39,29 @@ type HealthWorkflowRun struct {
// RepoHealth represents the CI health of a single repo
type RepoHealth struct {
Name string
Status string // "passing", "failing", "pending", "no_ci", "disabled"
Message string
URL string
FailingSince string
Name string `json:"name"`
Status string `json:"status"` // "passing", "failing", "pending", "no_ci", "disabled"
Message string `json:"message"`
URL string `json:"url"`
FailingSince string `json:"failing_since"`
}
type HealthSummary struct {
TotalRepos int `json:"total_repos"`
FilteredRepos int `json:"filtered_repos"`
Passing int `json:"passing"`
Failing int `json:"failing"`
Pending int `json:"pending"`
Disabled int `json:"disabled"`
NotConfigured int `json:"not_configured"`
PassingRate int `json:"passing_rate"`
ProblemsOnly bool `json:"problems_only"`
ByStatus map[string]int `json:"by_status"`
}
type HealthOutput struct {
Summary HealthSummary `json:"summary"`
Repos []RepoHealth `json:"repos"`
}
// addHealthCommand adds the 'health' subcommand to qa.
@ -58,6 +77,7 @@ func addHealthCommand(parent *cli.Command) {
healthCmd.Flags().BoolVarP(&healthProblems, "problems", "p", false, i18n.T("cmd.qa.health.flag.problems"))
healthCmd.Flags().StringVar(&healthRegistry, "registry", "", i18n.T("common.flag.registry"))
healthCmd.Flags().BoolVar(&healthJSON, "json", false, i18n.T("common.flag.json"))
parent.AddCommand(healthCmd)
}
@ -90,18 +110,22 @@ func runHealth() error {
repoList := reg.List()
for i, repo := range repoList {
cli.Print("\033[2K\r%s %d/%d %s",
dimStyle.Render(i18n.T("cmd.qa.issues.fetching")),
i+1, len(repoList), repo.Name)
if !healthJSON {
cli.Print("%s %d/%d %s\n",
dimStyle.Render(i18n.T("cmd.qa.issues.fetching")),
i+1, len(repoList), repo.Name)
}
health := fetchRepoHealth(reg.Org, repo.Name)
healthResults = append(healthResults, health)
}
cli.Print("\033[2K\r") // Clear progress
// Sort: problems first, then passing
slices.SortFunc(healthResults, func(a, b RepoHealth) int {
return cmp.Compare(healthPriority(a.Status), healthPriority(b.Status))
return cmp.Or(
cmp.Compare(healthPriority(a.Status), healthPriority(b.Status)),
cmp.Compare(a.Name, b.Name),
)
})
// Filter if --problems flag
@ -115,19 +139,16 @@ func runHealth() error {
healthResults = problems
}
// Calculate summary
passing := 0
for _, h := range healthResults {
if h.Status == "passing" {
passing++
}
}
total := len(repoList)
percentage := 0
if total > 0 {
percentage = (passing * 100) / total
summary := summariseHealthResults(len(repoList), healthResults, healthProblems)
if healthJSON {
return printHealthJSON(summary, healthResults)
}
// Calculate human summary
passing := summary.Passing
total := summary.TotalRepos
percentage := summary.PassingRate
// Print summary
cli.Print("%s: %d/%d repos healthy (%d%%)\n\n",
i18n.T("cmd.qa.health.summary"),
@ -258,6 +279,49 @@ func healthPriority(status string) int {
}
}
func summariseHealthResults(totalRepos int, results []RepoHealth, problemsOnly bool) HealthSummary {
summary := HealthSummary{
TotalRepos: totalRepos,
FilteredRepos: len(results),
ByStatus: make(map[string]int),
ProblemsOnly: problemsOnly,
}
for _, health := range results {
summary.ByStatus[health.Status]++
switch health.Status {
case "passing":
summary.Passing++
case "failing":
summary.Failing++
case "pending":
summary.Pending++
case "disabled":
summary.Disabled++
case "no_ci":
summary.NotConfigured++
}
}
if summary.TotalRepos > 0 {
summary.PassingRate = (summary.Passing * 100) / summary.TotalRepos
}
return summary
}
func printHealthJSON(summary HealthSummary, repos []RepoHealth) error {
data, err := json.MarshalIndent(HealthOutput{
Summary: summary,
Repos: repos,
}, "", " ")
if err != nil {
return err
}
cli.Print("%s\n", string(data))
return nil
}
func printHealthGroup(status string, repos []RepoHealth, style *cli.AnsiStyle) {
if len(repos) == 0 {
return

View file

@ -28,6 +28,7 @@ var (
issuesMine bool
issuesTriage bool
issuesBlocked bool
issuesJSON bool
issuesRegistry string
issuesLimit int
)
@ -65,10 +66,25 @@ type Issue struct {
URL string `json:"url"`
// Computed fields
RepoName string
Priority int // Lower = higher priority
Category string // "needs_response", "ready", "blocked", "triage"
ActionHint string
RepoName string `json:"repo_name"`
Priority int `json:"priority"` // Lower = higher priority
Category string `json:"category"` // "needs_response", "ready", "blocked", "triage"
ActionHint string `json:"action_hint"`
}
type IssueCategoryOutput struct {
Category string `json:"category"`
Count int `json:"count"`
Issues []Issue `json:"issues"`
}
type IssuesOutput struct {
TotalIssues int `json:"total_issues"`
FilteredIssues int `json:"filtered_issues"`
ShowingMine bool `json:"showing_mine"`
ShowingTriage bool `json:"showing_triage"`
ShowingBlocked bool `json:"showing_blocked"`
Categories []IssueCategoryOutput `json:"categories"`
}
// addIssuesCommand adds the 'issues' subcommand to qa.
@ -87,6 +103,7 @@ func addIssuesCommand(parent *cli.Command) {
issuesCmd.Flags().BoolVarP(&issuesBlocked, "blocked", "b", false, i18n.T("cmd.qa.issues.flag.blocked"))
issuesCmd.Flags().StringVar(&issuesRegistry, "registry", "", i18n.T("common.flag.registry"))
issuesCmd.Flags().IntVarP(&issuesLimit, "limit", "l", 50, i18n.T("cmd.qa.issues.flag.limit"))
issuesCmd.Flags().BoolVar(&issuesJSON, "json", false, i18n.T("common.flag.json"))
parent.AddCommand(issuesCmd)
}
@ -119,9 +136,11 @@ func runQAIssues() error {
repoList := reg.List()
for i, repo := range repoList {
cli.Print("\033[2K\r%s %d/%d %s",
dimStyle.Render(i18n.T("cmd.qa.issues.fetching")),
i+1, len(repoList), repo.Name)
if !issuesJSON {
cli.Print("%s %d/%d %s\n",
dimStyle.Render(i18n.T("cmd.qa.issues.fetching")),
i+1, len(repoList), repo.Name)
}
issues, err := fetchQAIssues(reg.Org, repo.Name, issuesLimit)
if err != nil {
@ -129,9 +148,18 @@ func runQAIssues() error {
}
allIssues = append(allIssues, issues...)
}
cli.Print("\033[2K\r") // Clear progress
totalIssues := len(allIssues)
if len(allIssues) == 0 {
emptyCategorised := map[string][]Issue{
"needs_response": {},
"ready": {},
"blocked": {},
"triage": {},
}
if issuesJSON {
return printCategorisedIssuesJSON(0, emptyCategorised)
}
cli.Text(i18n.T("cmd.qa.issues.no_issues"))
return nil
}
@ -150,6 +178,10 @@ func runQAIssues() error {
categorised = filterCategory(categorised, "blocked")
}
if issuesJSON {
return printCategorisedIssuesJSON(totalIssues, categorised)
}
// Print categorised issues
printCategorisedIssues(categorised)
@ -363,6 +395,38 @@ func printCategorisedIssues(categorised map[string][]Issue) {
}
}
func printCategorisedIssuesJSON(totalIssues int, categorised map[string][]Issue) error {
categories := []string{"needs_response", "ready", "blocked", "triage"}
filteredIssues := 0
categoryOutput := make([]IssueCategoryOutput, 0, len(categories))
for _, category := range categories {
issues := categorised[category]
filteredIssues += len(issues)
categoryOutput = append(categoryOutput, IssueCategoryOutput{
Category: category,
Count: len(issues),
Issues: issues,
})
}
output := IssuesOutput{
TotalIssues: totalIssues,
FilteredIssues: filteredIssues,
ShowingMine: issuesMine,
ShowingTriage: issuesTriage,
ShowingBlocked: issuesBlocked,
Categories: categoryOutput,
}
data, err := json.MarshalIndent(output, "", " ")
if err != nil {
return err
}
cli.Print("%s\n", string(data))
return nil
}
func printTriagedIssue(issue Issue) {
// #42 [core-bio] Fix avatar upload
num := cli.TitleStyle.Render(cli.Sprintf("#%d", issue.Number))

View file

@ -25,6 +25,7 @@ var (
reviewMine bool
reviewRequested bool
reviewRepo string
reviewJSON bool
)
// PullRequest represents a GitHub pull request
@ -81,6 +82,12 @@ type Review struct {
State string `json:"state"`
}
type ReviewOutput struct {
Repository string `json:"repository"`
MyPRs []PullRequest `json:"my_pull_requests"`
Requested []PullRequest `json:"requested_pull_requests"`
}
// addReviewCommand adds the 'review' subcommand to the qa command.
func addReviewCommand(parent *cli.Command) {
reviewCmd := &cli.Command{
@ -95,6 +102,7 @@ func addReviewCommand(parent *cli.Command) {
reviewCmd.Flags().BoolVarP(&reviewMine, "mine", "m", false, i18n.T("cmd.qa.review.flag.mine"))
reviewCmd.Flags().BoolVarP(&reviewRequested, "requested", "r", false, i18n.T("cmd.qa.review.flag.requested"))
reviewCmd.Flags().StringVar(&reviewRepo, "repo", "", i18n.T("cmd.qa.review.flag.repo"))
reviewCmd.Flags().BoolVar(&reviewJSON, "json", false, i18n.T("common.flag.json"))
parent.AddCommand(reviewCmd)
}
@ -121,9 +129,38 @@ func runReview() error {
// Default: show both mine and requested if neither flag is set
showMine := reviewMine || (!reviewMine && !reviewRequested)
showRequested := reviewRequested || (!reviewMine && !reviewRequested)
var myPRs, requestedPRs []PullRequest
if showMine {
if err := showMyPRs(ctx, repoFullName); err != nil {
var err error
myPRs, err = fetchPRs(ctx, repoFullName, "author:@me")
if err != nil {
return log.E("qa.review", "failed to fetch your PRs", err)
}
}
if showRequested {
var err error
requestedPRs, err = fetchPRs(ctx, repoFullName, "review-requested:@me")
if err != nil {
return log.E("qa.review", "failed to fetch review requests", err)
}
}
if reviewJSON {
data, err := json.MarshalIndent(ReviewOutput{
Repository: repoFullName,
MyPRs: myPRs,
Requested: requestedPRs,
}, "", " ")
if err != nil {
return err
}
cli.Print("%s\n", string(data))
return nil
}
if showMine {
if err := printMyPRs(myPRs); err != nil {
return err
}
}
@ -132,7 +169,7 @@ func runReview() error {
if showMine {
cli.Blank()
}
if err := showRequestedReviews(ctx, repoFullName); err != nil {
if err := printRequestedPRs(requestedPRs); err != nil {
return err
}
}
@ -140,13 +177,8 @@ func runReview() error {
return nil
}
// showMyPRs shows the user's open PRs with status
func showMyPRs(ctx context.Context, repo string) error {
prs, err := fetchPRs(ctx, repo, "author:@me")
if err != nil {
return log.E("qa.review", "failed to fetch your PRs", err)
}
// printMyPRs shows the user's open PRs with status
func printMyPRs(prs []PullRequest) error {
if len(prs) == 0 {
cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.qa.review.no_prs")))
return nil
@ -161,13 +193,8 @@ func showMyPRs(ctx context.Context, repo string) error {
return nil
}
// showRequestedReviews shows PRs where user's review is requested
func showRequestedReviews(ctx context.Context, repo string) error {
prs, err := fetchPRs(ctx, repo, "review-requested:@me")
if err != nil {
return log.E("qa.review", "failed to fetch review requests", err)
}
// printRequestedPRs shows PRs where user's review is requested
func printRequestedPRs(prs []PullRequest) error {
if len(prs) == 0 {
cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.qa.review.no_reviews")))
return nil