knowledge-base.progress takes since: only what changed in a window, read from the tracker's and mesh-delivery's history, so a five-minute report says what moved (the operator, 2026-10-28)

This commit is contained in:
2026-10-28 12:33:01 +01:00
parent 25f6154564
commit 4f317201c9
18 changed files with 758 additions and 8 deletions
@@ -2,6 +2,7 @@ package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
@@ -393,3 +394,134 @@ func TestProgressOfAnIssueAndARecord(t *testing.T) {
t.Error("an issue the tracker does not hold was answered")
}
}
// sinceWorld plays the tracker and the delivery seat from what they answered on 2026-10-28 (testdata/progress/since):
// a get or a show it holds no answer for is refused, as a seat that cannot answer.
func sinceWorld(t *testing.T) func(string, any) (json.RawMessage, error) {
t.Helper()
read := func(name string) json.RawMessage { return readFixture(t, filepath.Join("since", name)) }
var rows []json.RawMessage
if err := json.Unmarshal(read("rows.json"), &rows); err != nil {
t.Fatal(err)
}
byNumber := map[int]json.RawMessage{}
for _, r := range rows {
var n struct{ Number int }
_ = json.Unmarshal(r, &n)
byNumber[n.Number] = r
}
shows := map[string]string{"novox/mesh-catalog@470687ea4cce": "show-mesh-catalog-539.json", "novox/mesh-catalog@f5d4b2f86ed4": "show-mesh-catalog-537.json",
"novox/mesh-delivery@2b801f5bd49a": "show-mesh-delivery-94.json", "novox/de-spiegel@fba6dbe3259e": "show-de-spiegel-6.json"}
return func(key string, body any) (json.RawMessage, error) {
raw, _ := json.Marshal(body)
var a struct {
Numbers []int `json:"numbers"`
Number int `json:"number"`
ID string `json:"id"`
One string `json:"one"`
}
_ = json.Unmarshal(raw, &a)
switch key {
case MilestonesVerb:
return read("milestone-47-6.json"), nil
case "seat:issue-tracker.list":
var out []json.RawMessage
for _, n := range a.Numbers {
if r, ok := byNumber[n]; ok {
out = append(out, r)
}
}
return json.Marshal(map[string]any{"rows": out, "matched": len(out)})
case "seat:issue-tracker.get":
if a.Number == 1422 || a.Number == 1956 {
return read(fmt.Sprintf("get-%d.json", a.Number)), nil
}
case "seat:mesh-delivery.deliveries":
return read("deliveries.json"), nil
case "seat:mesh-delivery.groups":
return json.RawMessage(`[]`), nil
case "seat:mesh-delivery.show":
if f, ok := shows[a.ID]; ok {
return read(f), nil
}
}
return nil, os.ErrNotExist
}
}
// The operator, 2026-10-28: "can the progress tool also report the progress made since the last call, or maybe better,
// the last 60 minutes". With since, an answer lists only what changed in that window under of, read from the
// tracker's notes, links, workers and closes, its milestones' history and mesh-delivery's transitions and checks.
func TestProgressSinceAWindowSaysOnlyWhatChanged(t *testing.T) {
at := func(s string) *time.Time { v, _ := time.Parse(time.RFC3339, s); return &v }
k := &KB{Records: progressRecords(), Seats: sinceWorld(t), Now: func() time.Time { return *at("2026-10-28T11:13:30Z") }}
p, err := k.Progress(ProgressAsk{Of: "issue 1422", Since: "2h"})
if err != nil {
t.Fatal(err)
}
lines := strings.Split(p.Tree, "\n")
if lines[0] != "17 changes in the last 120 min under issue 1422 (since "+when(at("2026-10-28T09:13:30Z"))+")" || p.Changes != 17 || p.Since != "2h0m0s" {
t.Fatalf("%d changes, since %q:\n%s", p.Changes, p.Since, p.Tree)
}
for _, want := range []string{
"▶ 1422 " + clip("The catch-up reads the trunk 'main' of repositories the catalogue follows by another branch or by a commit, and the forge holds none", 80) + " — → fixing by fix-trunk-unknown@shanks " + when(at("2026-10-28T09:38:37Z")) +
"; 9 notes, the last " + when(at("2026-10-28T11:05:21Z")) + " by shanks.node-tools",
"✓ mesh-catalog#537 (1422) opened " + when(at("2026-10-28T10:20:56Z")) + " · check pass " + when(at("2026-10-28T10:30:45Z")) + " · merged " + when(at("2026-10-28T10:34:49Z")) + ", on the machines",
"✓ mesh-delivery#94 (1126, 1422) opened ",
"▶ mesh-catalog#539 (1422, 1956) opened " + when(at("2026-10-28T11:07:42Z")) + ", now ready, waits for its merge",
} {
if !strings.Contains(p.Tree, want) {
t.Errorf("no %q in\n%s", want, p.Tree)
}
}
if strings.Contains(p.Tree, "de-spiegel") || strings.Contains(p.Tree, "triage") {
t.Errorf("a change outside issue 1422 or the window is said:\n%s", p.Tree)
}
w := strings.Join(p.Warnings, "\n")
for _, want := range []string{"keeps no history of an issue's moves", "keeps no time a merge reached its machines"} {
if !strings.Contains(w, want) {
t.Errorf("no %q in the warnings: %v", want, p.Warnings)
}
}
d, err := k.Progress(ProgressAsk{Of: "issue 1422", Since: "2h", Detail: DetailDetailed})
if err != nil {
t.Fatal(err)
}
for _, want := range []string{"│ ├── noted " + when(at("2026-10-28T09:38:09Z")) + " by shanks.node-tools: lead-47@shanks 2026-10-28 09:40Z.",
" └── merged " + when(at("2026-10-28T10:34:49Z")) + ": on the trunk its modules follow: merged there"} {
if !strings.Contains(d.Tree, want) {
t.Errorf("detailed: no %q in\n%s", want, d.Tree)
}
}
// A milestone's issue the tracker's get cannot answer falls back to its row's moved time, said as such.
m, err := k.Progress(ProgressAsk{Of: "milestone 47/L2", Since: "60m"})
if err != nil {
t.Fatal(err)
}
for _, want := range []string{"6 changes in the last 60 min under milestone 47/L2", "▶ 1126 " + clip("About 49,800 controller code lines stay after the delivery model, against a first limit of 25,000", 80) + " — moved " +
when(at("2026-10-28T11:13:28Z")) + ", now fixing (lead-47@shanks)", "✓ de-spiegel#6 (1126) check pass ", "✓ mesh-delivery#94 (1126, 1422) opened "} {
if !strings.Contains(m.Tree, want) {
t.Errorf("milestone: no %q in\n%s", want, m.Tree)
}
}
if w := strings.Join(m.Warnings, "\n"); !strings.Contains(w, "issue-tracker.get 1126") {
t.Errorf("the fallback is not said: %v", m.Warnings)
}
if small, _ := k.Progress(ProgressAsk{Of: "issue 1422", Since: "2h", Detail: DetailDetailed, Bound: LeastBound}); small == nil || size(small) > LeastBound || !strings.Contains(small.Cut, "raise bound") {
t.Errorf("a window's answer is not kept within its bound: %+v", small)
}
// A milestone's move, from its history: what it was before, what it is now, who moved it and when.
k.Now = func() time.Time { return *at("2026-10-26T18:40:00Z") }
ds, err := k.Progress(ProgressAsk{Of: "design 47", Since: "60m"})
if err != nil {
t.Fatal(err)
}
if ds.Tree != "1 change in the last 60 min under design 47 (since "+when(at("2026-10-26T17:40:00Z"))+")\n└── ✓ milestone 47/6 open → closed: folded into R4g · by shanks.node-tools "+when(at("2026-10-26T18:10:10Z")) {
t.Errorf("design:\n%s", ds.Tree)
}
for _, bad := range []string{"0m", "-5m", "25h", "a day", "5"} {
if _, err := k.Progress(ProgressAsk{Of: "issue 1422", Since: bad}); err == nil || !strings.Contains(err.Error(), "since") {
t.Errorf("since %q: %v", bad, err)
}
}
}