Let issue-tracker.get answer briefly, with only its last note (issue 1071)

This commit is contained in:
jochen
2026-10-17 22:45:42 +02:00
parent f076cadece
commit 01c9ab0cf5
3 changed files with 119 additions and 4 deletions
@@ -0,0 +1,50 @@
package main
import "testing"
// A get with brief: true answers only its last note, saying how many were left out (issue 1071).
func TestGetBriefAnswersOnlyTheLastNote(t *testing.T) {
h := newHarness(t)
n := h.open(t, "the counter skips a number", "hq")
for _, text := range []string{"first", "second", "third"} {
if _, err := h.Note(n, text); err != nil {
t.Fatalf("note: %v", err)
}
}
got, err := h.call(t, "get", map[string]any{"number": float64(n), "brief": true})
if err != nil {
t.Fatalf("get brief: %v", err)
}
b, ok := got.(*Brief)
if !ok {
t.Fatalf("want *Brief, got %T", got)
}
if len(b.Notes) != 1 || b.Notes[0].Text != "third" {
t.Fatalf("want only the last note (third), got %+v", b.Notes)
}
if b.NotesLeftOut != 2 {
t.Fatalf("want notes-left-out 2, got %d", b.NotesLeftOut)
}
}
// A get without brief is unchanged (issue 1071).
func TestGetWithoutBriefIsUnchanged(t *testing.T) {
h := newHarness(t)
n := h.open(t, "the counter skips a number", "hq")
if _, err := h.Note(n, "first"); err != nil {
t.Fatalf("note: %v", err)
}
got, err := h.call(t, "get", map[string]any{"number": float64(n)})
if err != nil {
t.Fatalf("get: %v", err)
}
v, ok := got.(*View)
if !ok {
t.Fatalf("want *View, got %T", got)
}
if len(v.Notes) != 1 || v.Notes[0].Text != "first" {
t.Fatalf("want every note, got %+v", v.Notes)
}
}
+21 -4
View File
@@ -387,10 +387,16 @@ func Tools(t *Tracker) []stdio.Tool {
"links; the issues it waits for, those that depend on it (needed-by), and whether it is ready; " +
"whether its fix is delivered, as the delivery last said it (merged, held and why, delivered); and, once " +
"the last of its fixes is delivered, its fix time (fix-time) from its first move to fixing, its class and " +
"fix budget, and its phases: implementing, review, checks, merge to running.",
Input: schema([]string{"number"}, map[string]any{"number": number("the issue's number")}),
"fix budget, and its phases: implementing, review, checks, merge to running. With brief: true, a short " +
"answer instead: number, title, status, severity, needs, ready, worker and since (when diagnosing or " +
"fixing), located-in, fixed-by, the delivery line, its links without who made them or when, opened, and " +
"only its last note, with notes-left-out saying how many were left out.",
Input: schema([]string{"number"}, map[string]any{
"number": number("the issue's number"),
"brief": map[string]any{"type": "boolean", "description": "a short answer with only its last note, instead of every note and the whole triage"},
}),
Run: func(m map[string]any) (any, error) {
a, err := readArgs("get", m, "number")
a, err := readArgs("get", m, "number", "brief")
if err != nil {
return nil, err
}
@@ -398,7 +404,18 @@ func Tools(t *Tracker) []stdio.Tool {
if err != nil {
return nil, err
}
return t.View(n)
brief, err := a.flag("brief")
if err != nil {
return nil, err
}
v, err := t.View(n)
if err != nil {
return nil, err
}
if brief {
return briefOf(v), nil
}
return v, nil
}},
{Name: Seat + ".list",
Description: "The issues, newest first, one short row each — number, status, owner, title — optionally only " +
@@ -12,6 +12,7 @@ import (
"regexp"
"sort"
"strings"
"time"
)
// The events of ADR 0279.
@@ -320,6 +321,53 @@ func (t *Tracker) View(n uint64) (*View, error) {
DeliveredNotResolved: deliveredNotResolved(is), Abandoned: abandoned(is, now, staleAfter)}, nil
}
// BriefLink is a link as brief answers it: its kind and target, without who made it or when (issue 1071).
type BriefLink struct {
Kind string `json:"kind"`
Target string `json:"target"`
}
// Brief is `get` with brief: true — a short answer naming only what deciding what to do with an issue needs,
// with its last note alone and how many were left out (novox/hq issue 1071).
type Brief struct {
Number uint64 `json:"number"`
Title string `json:"title"`
Status string `json:"status,omitempty"`
Severity string `json:"severity,omitempty"`
Needs string `json:"needs,omitempty"`
Ready bool `json:"ready"`
Worker string `json:"worker,omitempty"`
Since *time.Time `json:"since,omitempty"`
LocatedIn []string `json:"located-in,omitempty"`
FixedBy []string `json:"fixed-by,omitempty"`
Delivery string `json:"delivery,omitempty"`
Links []BriefLink `json:"links,omitempty"`
Opened time.Time `json:"opened"`
Notes []Note `json:"notes,omitempty"`
NotesLeftOut int `json:"notes-left-out"`
}
// briefOf is a View shaped for brief: true: only its last note, with how many were left out.
func briefOf(v *View) *Brief {
b := &Brief{
Number: v.Number, Title: v.Title, Status: v.Status, Severity: v.Severity, Needs: v.Needs,
Ready: v.Ready, LocatedIn: v.LocatedIn, FixedBy: v.FixedBy, Delivery: v.Delivery, Opened: v.Opened,
}
if v.Worker != nil {
b.Worker = v.Worker.By
since := v.Worker.Since
b.Since = &since
}
for _, l := range v.Links {
b.Links = append(b.Links, BriefLink{Kind: l.Kind, Target: l.Target})
}
if n := len(v.Notes); n > 0 {
b.Notes = []Note{v.Notes[n-1]}
b.NotesLeftOut = n - 1
}
return b
}
// letGo says, for an issue that may have just let others go (its fix delivered, or wontfix), every issue it
// made ready: a note on each, once, and the event `ready`.
func (t *Tracker) letGo(dep uint64) error {