Files
mesh-catalog/modules/mesh-knowledge/cmd/mesh-knowledge/answers.go
T
jochen eadf301f9f An ask reads at a glance on the phone: a headline, a summary and its points, the rest in a note (issues 383 and 639)
The operator would not read the Telegram ask for ADR 0343: thirty plain lines, every clause on one, no bold,
no summary. A settings proposal was the same wall of text, its values masked so the approval was blind.

- messenger: an ask's message is the asker's headline and explanation alone; who asked and when, what each
  answer does, the expiry, where else it is answered and the asker's Details travel as the message's note
  (asks.Message.Note). Answered, the message is one line, "You chose Approve at 16:06 on Telegram ·
  <headline>". An explanation that does not read at a glance (asks.Glance) is shown and logged. On a kind
  that proves who answers, an ask's values are shown whole (asks.Ask.Whole), held to the secret rule alone
  (from the earlier work on issue 383, #178).
- telegram: every message in HTML, never reworded: the title bold, a key line monospace, the note apart in
  a collapsed blockquote; markup escaped. A tap is compared by its words, whitespace aside, and a message
  shown before is still compared by the digest it was kept under.
- desk-channel: the note follows the body.
- mesh-knowledge: a record that needs the operator carries a brief (headline, summary, at most three
  points), the 30th per-record rule; its ask shows the brief and never the clauses. An ask made before is
  composed again as it was asked (form), so its warrant still holds.
- claude-code: an instructions proposal's explanation is one summary line (the proposer's, or the
  module's), its text in the Details; a question's context is at most 300 characters and its recommendation
  one point. Older proposals and questions keep their form.

The SDK is pinned at mesh-sdk 9588871 (fix/639-an-ask-reads-at-a-glance), which adds Message.Note and
asks.Glance.
2026-10-13 17:16:50 +02:00

785 lines
25 KiB
Go

package main
// The answers (novox/hq ADR 0290 rule 6, to-be 55 §2 and §3): ask, get, holds, links and history, each the shape
// an agent needs. ask, get and holds stay under a bound they state, 4 KiB unless the caller asks for more, and say
// when they were cut and how to read the rest. A drafted clause is marked as such and never served as in force.
import (
"context"
"encoding/json"
"fmt"
"sort"
"strconv"
"strings"
)
// MostQuestion is the most characters a question to ask may hold.
const MostQuestion = 500
// The bound of an answer.
const (
DefaultBound = 4 << 10
MostBound = 64 << 10
LeastBound = 1 << 10
)
func boundOf(b int) int {
switch {
case b <= 0:
return DefaultBound
case b < LeastBound:
return LeastBound
case b > MostBound:
return MostBound
}
return b
}
func size(v any) int {
raw, _ := json.Marshal(v)
return len(raw)
}
// fit is the largest n (from 0 to total) whose answer stays within the bound.
func fit(total, bound int, build func(n int) any) (any, int) {
lo, hi := 0, total
for lo < hi {
mid := (lo + hi + 1) / 2
if size(build(mid)) <= bound {
lo = mid
} else {
hi = mid - 1
}
}
return build(lo), lo
}
func clip(s string, n int) string {
s = strings.Join(strings.Fields(s), " ")
if len([]rune(s)) <= n {
return s
}
return string([]rune(s)[:n-1]) + "…"
}
// held says, for a clause, whether it holds and what changed it, in the words an agent reads.
func held(r *Record, c Clause) string {
switch {
case c.Source == SourceDrafted:
s := "drafted, not confirmed: not the record's word, never in force"
if c.Unconfirmed != "" {
s += " (the second judgement did not confirm it: " + c.Unconfirmed + ")"
}
if len(c.ChangedBy) > 0 {
if c.Status == ClauseSuperseded {
s += "; superseded by " + strings.Join(c.ChangedBy, ", ")
} else {
s += "; changed by " + strings.Join(c.ChangedBy, ", ") + ": read both"
}
}
return s
case r.Status == StatusProposed || c.Status == ClauseProposed:
return "proposed: not in force"
case c.Status == ClauseChanged:
return "changed by " + strings.Join(c.ChangedBy, ", ") + ": read both"
case c.Status == ClauseSuperseded:
if len(c.ChangedBy) > 0 {
return "superseded by " + strings.Join(c.ChangedBy, ", ")
}
return "superseded"
case c.Source == SourceConfirmed:
return "in force (drafted from prose, confirmed)"
}
return "in force"
}
// Served says whether a clause may be served as holding: in force or changed, and not drafted.
func Served(r *Record, c Clause) bool {
return r.Accepted() && c.Source != SourceDrafted && (c.Status == ClauseInForce || c.Status == ClauseChanged)
}
// AnswerClause is one clause in an answer.
type AnswerClause struct {
N int `json:"n"`
Text string `json:"text"`
Holds string `json:"holds"`
Source string `json:"source,omitempty"`
}
// AnswerRecord is one record in an answer to ask.
type AnswerRecord struct {
Record string `json:"record"`
Title string `json:"title"`
Status string `json:"status"`
Clauses []AnswerClause `json:"clauses,omitempty"`
Note string `json:"note,omitempty"`
Since []string `json:"changed-since,omitempty"`
Read string `json:"read"`
}
// Asked is the answer to ask.
type Asked struct {
Question string `json:"question"`
Records []AnswerRecord `json:"records"`
Cut string `json:"cut,omitempty"`
Bound int `json:"bound"`
Says string `json:"says,omitempty"`
}
func readHow(k Key) string {
return fmt.Sprintf("get {\"kind\": %q, \"number\": %d, \"reasoning\": true}", k.Kind, k.Number)
}
// since is what changed a record since it was accepted: the accepted records that supersede or change it.
func (k *KB) since(ctx context.Context, key Key) []string {
in, err := k.Store.Inbound(ctx, key)
if err != nil {
return nil
}
var out []string
for _, x := range in {
if x.Link.Kind != LinkSupersedes && x.Link.Kind != LinkChanges {
continue
}
from, err := k.Store.Get(ctx, x.From)
if err != nil || from == nil || !from.Current.Accepted() {
continue
}
s := x.From.String()
if x.Link.From > 0 {
s += fmt.Sprintf(" §%d", x.Link.From)
}
switch {
case x.Link.Kind == LinkSupersedes:
s += " supersedes it"
case x.Link.To.Clause > 0:
s += fmt.Sprintf(" changes §%d", x.Link.To.Clause)
default:
s += " changes it in part; which clause is not judged yet"
}
out = append(out, s)
}
return out
}
// Ask is the records and clauses bearing on a question, ranked, each saying whether it holds or what changed it.
func (k *KB) Ask(question string, bound int) (*Asked, error) {
ctx := context.Background()
question = strings.TrimSpace(question)
if question == "" {
return nil, fmt.Errorf("ask needs a question, in plain words")
}
if len([]rune(question)) > MostQuestion {
return nil, fmt.Errorf("a question is at most %d characters; this one is %d: ask it in fewer words", MostQuestion, len([]rune(question)))
}
bound = boundOf(bound)
hits, err := k.Store.Search(ctx, question, 80)
if err != nil {
return nil, fmt.Errorf("the knowledge base cannot be searched (%v)", err)
}
type group struct {
key Key
clauses []int
record bool
// score is the record's own rank, its best clause's, and a little of each other clause's.
score, best, rest float64
}
var order []*group
byKey := map[Key]*group{}
for _, h := range hits {
g := byKey[h.Key]
if g == nil {
g = &group{key: h.Key}
byKey[h.Key] = g
order = append(order, g)
}
if h.Clause > 0 {
g.clauses = append(g.clauses, h.Clause)
if h.Rank > g.best {
g.rest += g.best
g.best = h.Rank
} else {
g.rest += h.Rank
}
} else {
g.record = true
g.score += h.Rank
}
}
for _, g := range order {
g.score += g.best + g.rest/4
}
sort.SliceStable(order, func(i, j int) bool { return order[i].score > order[j].score })
var records []AnswerRecord
for _, g := range order {
e, err := k.Store.Get(ctx, g.key)
if err != nil || e == nil {
continue
}
r := e.Current
ar := AnswerRecord{Record: g.key.String(), Title: r.Title, Status: r.Status, Read: readHow(g.key), Since: k.since(ctx, g.key)}
sort.Ints(g.clauses)
for _, n := range g.clauses {
if c := r.Clause(n); c != nil {
ar.Clauses = append(ar.Clauses, AnswerClause{N: n, Text: clip(c.Text, 220), Holds: held(r, *c), Source: c.Source})
}
}
if len(ar.Clauses) == 0 {
drafted := 0
for _, c := range r.Clauses {
if c.Source == SourceDrafted {
drafted++
}
}
switch {
case len(r.Clauses) == 0 && r.Kind == KindDecision:
ar.Note = "its decision is written as prose and its clauses are not drafted yet: read its Decision section"
case drafted > 0:
ar.Note = fmt.Sprintf("%d of its clauses are drafted and not confirmed: none of them is served as in force", drafted)
}
}
records = append(records, ar)
}
out := &Asked{Question: question, Bound: bound}
if len(records) == 0 {
out.Records = []AnswerRecord{}
out.Says = "nothing the knowledge base holds bears on that; ask in other words"
return out, nil
}
// Within the bound: the most records that fit; when not even one fits whole, its clauses shortened.
build := func(n int) any {
a := *out
a.Records = records[:n]
if n < len(records) {
names := []string{}
for _, r := range records[n:min(len(records), n+6)] {
names = append(names, r.Record)
}
a.Cut = fmt.Sprintf("%d more record(s) bear on it and are not shown within %d bytes (%s…): ask with a larger bound, or narrower words",
len(records)-n, bound, strings.Join(names, ", "))
}
return &a
}
v, n := fit(len(records), bound, build)
if n == 0 {
first := records[0]
for i := range first.Clauses {
first.Clauses[i].Text = clip(first.Clauses[i].Text, 80)
}
records[0] = first
v, _ = fit(len(records), bound, build)
}
return v.(*Asked), nil
}
// ---- get -----------------------------------------------------------------------------------------------------
// GetAsk is what get takes.
type GetAsk struct {
Kind string
Number int
Reasoning bool
Section string
Version int
Bound int
}
// Got is the answer to get.
type Got struct {
Record string `json:"record"`
Title string `json:"title"`
Status string `json:"status"`
Topic string `json:"topic,omitempty"`
Date string `json:"date,omitempty"`
Deciders []string `json:"deciders,omitempty"`
Version int `json:"version"`
Versions int `json:"versions"`
Clauses []AnswerClause `json:"clauses,omitempty"`
Links []string `json:"links,omitempty"`
LinkedFrom []string `json:"linked-from,omitempty"`
Since []string `json:"changed-since,omitempty"`
Sections []string `json:"sections,omitempty"`
Reasoning []Section `json:"reasoning,omitempty"`
Insights []Insight `json:"insights,omitempty"`
Pending string `json:"pending,omitempty"`
Waiting string `json:"waiting,omitempty"`
Reviews []Review `json:"reviews,omitempty"`
Drafting *Drafting `json:"drafting,omitempty"`
Review string `json:"review,omitempty"`
Source string `json:"source,omitempty"`
Reserved string `json:"reserved,omitempty"`
NeedsOperat bool `json:"needs-operator,omitempty"`
// Brief is what the operator's ask shows of it (issue 639).
Brief *Brief `json:"brief,omitempty"`
Cut string `json:"cut,omitempty"`
Bound int `json:"bound"`
Read string `json:"read,omitempty"`
// Reconstructed, Amended and Touches are the record's fields of those names; Fields, any other it holds as
// written (hq issue 487).
Reconstructed *bool `json:"reconstructed,omitempty"`
Amended []string `json:"amended,omitempty"`
Touches []string `json:"touches,omitempty"`
Fields map[string]string `json:"fields,omitempty"`
}
// Get is one record: its clauses, its links both ways, what changed it since; its reasoning only when asked.
func (k *KB) Get(a GetAsk) (*Got, error) {
ctx := context.Background()
if a.Kind == "" {
a.Kind = KindDecision
}
key := Key{a.Kind, a.Number}
e, err := k.Store.Get(ctx, key)
if err != nil {
return nil, fmt.Errorf("the knowledge base cannot be read (%v)", err)
}
if e == nil {
return nil, fmt.Errorf("the knowledge base holds no %s", key)
}
r, version := e.Current, e.CurrentVersion
if a.Version > 0 {
vs, err := k.Store.Versions(ctx, key)
if err != nil {
return nil, err
}
if a.Version > len(vs) {
return nil, fmt.Errorf("%s has %d version(s); there is no version %d", key, len(vs), a.Version)
}
r, version = vs[a.Version-1].Record, a.Version
}
bound := boundOf(a.Bound)
g := &Got{Record: key.String(), Title: r.Title, Status: r.Status, Topic: r.Topic, Date: r.Date, Deciders: r.Deciders,
Version: version, Versions: e.Versions, Insights: r.Insights, Reviews: r.Reviews, Drafting: r.Drafting, Source: r.Source,
NeedsOperat: r.NeedsOperator, Brief: r.Brief, Bound: bound, Since: k.since(ctx, key),
Reconstructed: r.Reconstructed, Amended: r.Amended, Touches: r.Touches, Fields: r.Extra}
if r.Status == StatusReserved || r.Status == StatusWithdrawn {
g.Reserved = fmt.Sprintf("reserved by %s at %s for %q", r.ReservedBy, r.ReservedAt.Format("2006-01-02 15:04 UTC"), r.ReservedFor)
}
if e.Pending != nil && a.Version == 0 {
g.Pending = fmt.Sprintf("version %d, a progressive insight, waits for its review: history shows what it changes", e.PendingVersion)
}
if r.Ask != nil {
g.Waiting = "the operator's ask: " + r.Ask.Said
}
if a.Version == 0 {
g.Review = reviewSays(e.Working())
}
for _, c := range r.Clauses {
g.Clauses = append(g.Clauses, AnswerClause{N: c.N, Text: c.Text, Holds: held(r, c), Source: c.Source})
}
for _, l := range r.Links {
g.Links = append(g.Links, l.String())
}
if in, err := k.Store.Inbound(ctx, key); err == nil {
for _, x := range in {
s := x.From.String() + " "
if x.Link.From > 0 {
s += fmt.Sprintf("§%d ", x.Link.From)
}
s += x.Link.Kind
if x.Link.To.Clause > 0 {
s += fmt.Sprintf(" §%d", x.Link.To.Clause)
}
g.LinkedFrom = append(g.LinkedFrom, s)
}
}
for _, s := range r.Sections {
g.Sections = append(g.Sections, s.Name)
if a.Reasoning || (a.Section != "" && strings.EqualFold(a.Section, s.Name)) {
g.Reasoning = append(g.Reasoning, s)
}
}
if a.Section != "" && len(g.Reasoning) == 0 {
return nil, fmt.Errorf("%s has no section %q; it has %s", key, a.Section, strings.Join(g.Sections, ", "))
}
if !a.Reasoning && a.Section == "" {
g.Read = "its reasoning: " + readHow(key) + ", or one section with section"
}
if size(g) <= bound {
return g, nil
}
// Cut: the reasoning first, section by section from the end, then the reviews but the last, then the links
// from the end, then the clauses' text, then clauses; each cut is said. Room is kept for saying it.
room := bound - 600
var cut []string
if len(g.Reasoning) > 0 {
reasoning := g.Reasoning
v, n := fit(len(reasoning), room, func(n int) any { c := *g; c.Reasoning = reasoning[:n]; return &c })
*g = *(v.(*Got))
if n < len(reasoning) {
left := []string{}
for _, s := range reasoning[n:] {
left = append(left, s.Name)
}
cut = append(cut, fmt.Sprintf("the sections %s are not shown within %d bytes: ask for one with section, or raise bound", strings.Join(left, ", "), bound))
if spare := room - size(g); n == 0 && spare > 400 {
s := reasoning[0]
s.Text = clip(s.Text, spare/2)
g.Reasoning = []Section{s}
cut[len(cut)-1] = fmt.Sprintf("the section %s is shortened, and %s not shown: ask for one with section, or raise bound",
s.Name, strings.Join(append([]string{}, left[1:]...), ", "))
}
}
}
if size(g) > room && len(g.Reviews) > 1 {
cut = append(cut, fmt.Sprintf("%d earlier review(s) are not shown: history lists every version", len(g.Reviews)-1))
g.Reviews = g.Reviews[len(g.Reviews)-1:]
}
for _, which := range []string{"linked-from", "links"} {
if size(g) <= room {
break
}
list := func(c *Got) *[]string {
if which == "links" {
return &c.Links
}
return &c.LinkedFrom
}
full := *list(g)
v, n := fit(len(full), room, func(n int) any { c := *g; *list(&c) = full[:n]; return &c })
if n < len(full) {
*g = *(v.(*Got))
cut = append(cut, fmt.Sprintf("%d of its %s are not shown: links lists them all", len(full)-n, which))
}
}
if size(g) > room {
for i := range g.Clauses {
g.Clauses[i].Text = clip(g.Clauses[i].Text, 160)
}
cut = append(cut, "the clauses are shortened: raise bound to read them whole")
}
if size(g) > room {
clauses := g.Clauses
v, n := fit(len(clauses), room, func(n int) any { c := *g; c.Clauses = clauses[:n]; return &c })
*g = *(v.(*Got))
cut = append(cut, fmt.Sprintf("%d clause(s) are not shown: raise bound", len(clauses)-n))
}
g.Cut = strings.Join(cut, "; ")
return g, nil
}
// ---- holds ---------------------------------------------------------------------------------------------------
// HeldClause is one clause in force, with the record it comes from.
type HeldClause struct {
Record string `json:"record"`
N int `json:"n"`
Text string `json:"text"`
Holds string `json:"holds"`
}
// Holding is the answer to holds.
type Holding struct {
Of string `json:"of"`
Clauses []HeldClause `json:"clauses"`
NotYet string `json:"not-served,omitempty"`
Cut string `json:"cut,omitempty"`
Bound int `json:"bound"`
}
// Holds is the clauses in force for a topic or a record: never a drafted one, and a confirmed one marked.
func (k *KB) Holds(topic string, key *Key, bound int) (*Holding, error) {
ctx := context.Background()
bound = boundOf(bound)
var records []*Record
of := ""
switch {
case key != nil:
e, err := k.Store.Get(ctx, *key)
if err != nil {
return nil, err
}
if e == nil {
return nil, fmt.Errorf("the knowledge base holds no %s", key)
}
records, of = []*Record{e.Current}, key.String()
case strings.TrimSpace(topic) != "":
all, err := k.Store.All(ctx)
if err != nil {
return nil, err
}
for _, e := range all {
if e.Current.Topic == topic {
records = append(records, e.Current)
}
}
of = "the topic " + topic
if len(records) == 0 {
return nil, fmt.Errorf("no record has the topic %q", topic)
}
default:
return nil, fmt.Errorf("holds takes a topic or a record's number")
}
out := &Holding{Of: of, Bound: bound, Clauses: []HeldClause{}}
drafted, prose := 0, 0
for _, r := range records {
if r.Kind == KindDecision && r.Accepted() && len(r.Clauses) == 0 {
prose++
}
for _, c := range r.Clauses {
if c.Source == SourceDrafted && r.Accepted() && c.Status != ClauseSuperseded {
drafted++
}
if Served(r, c) {
out.Clauses = append(out.Clauses, HeldClause{Record: r.Key().String(), N: c.N, Text: c.Text, Holds: held(r, c)})
}
}
}
var notes []string
if drafted > 0 {
notes = append(notes, fmt.Sprintf("%d drafted clause(s) are not served: they are not confirmed as the record's word", drafted))
}
if prose > 0 {
notes = append(notes, fmt.Sprintf("%d accepted decision(s) write their decision as prose and have no clauses yet", prose))
}
out.NotYet = strings.Join(notes, "; ")
clauses := out.Clauses
v, n := fit(len(clauses), bound, func(n int) any {
c := *out
c.Clauses = clauses[:n]
if n < len(clauses) {
c.Cut = fmt.Sprintf("%d more clause(s) are in force and not shown within %d bytes: raise bound, or ask holds of one record", len(clauses)-n, bound)
}
return &c
})
if n == 0 && len(clauses) > 0 {
for i := range clauses {
clauses[i].Text = clip(clauses[i].Text, 160)
}
v, _ = fit(len(clauses), bound, func(n int) any {
c := *out
c.Clauses = clauses[:n]
c.Cut = fmt.Sprintf("the clauses are shortened, and %d not shown within %d bytes: raise bound", len(clauses)-n, bound)
return &c
})
}
return v.(*Holding), nil
}
// ---- links ---------------------------------------------------------------------------------------------------
// Linked is the answer to links.
type Linked struct {
Record string `json:"record"`
Out []LinkRow `json:"out"`
In []LinkRow `json:"in"`
}
// LinkRow is one link, read from either end.
type LinkRow struct {
Kind string `json:"kind"`
From string `json:"from"`
To string `json:"to"`
Source string `json:"source,omitempty"`
Unresolved bool `json:"unresolved,omitempty"`
}
// Links is a record's links, both ways.
func (k *KB) Links(key Key) (*Linked, error) {
ctx := context.Background()
e, err := k.Store.Get(ctx, key)
if err != nil {
return nil, err
}
if e == nil {
return nil, fmt.Errorf("the knowledge base holds no %s", key)
}
out := &Linked{Record: key.String(), Out: []LinkRow{}, In: []LinkRow{}}
from := func(k Key, clause int) string {
if clause > 0 {
return k.String() + " §" + strconv.Itoa(clause)
}
return k.String()
}
for _, l := range e.Current.Links {
out.Out = append(out.Out, LinkRow{Kind: l.Kind, From: from(key, l.From), To: l.To.String(), Source: l.Source, Unresolved: l.Unresolved})
}
in, err := k.Store.Inbound(ctx, key)
if err != nil {
return nil, err
}
for _, x := range in {
out.In = append(out.In, LinkRow{Kind: x.Link.Kind, From: from(x.From, x.Link.From), To: x.Link.To.String(), Source: x.Link.Source})
}
return out, nil
}
// ---- history -------------------------------------------------------------------------------------------------
// VersionRow is one version in a history.
type VersionRow struct {
N int `json:"n"`
By string `json:"by"`
At string `json:"at"`
Why string `json:"why"`
Status string `json:"status"`
Insight bool `json:"insight,omitempty"`
Served bool `json:"served,omitempty"`
Pending bool `json:"pending,omitempty"`
}
// Historied is the answer to history.
type Historied struct {
Record string `json:"record"`
Versions []VersionRow `json:"versions"`
From int `json:"from,omitempty"`
To int `json:"to,omitempty"`
Diff []string `json:"diff,omitempty"`
Same bool `json:"same,omitempty"`
Cut string `json:"cut,omitempty"`
Bound int `json:"bound,omitempty"`
}
// History is a record's versions, and the difference between two of them, line by line.
func (k *KB) History(key Key, from, to, bound int) (*Historied, error) {
ctx := context.Background()
e, err := k.Store.Get(ctx, key)
if err != nil {
return nil, err
}
if e == nil {
return nil, fmt.Errorf("the knowledge base holds no %s", key)
}
vs, err := k.Store.Versions(ctx, key)
if err != nil {
return nil, err
}
out := &Historied{Record: key.String()}
for _, v := range vs {
out.Versions = append(out.Versions, VersionRow{N: v.N, By: v.By, At: v.At.UTC().Format("2006-01-02 15:04:05 UTC"), Why: v.Why,
Status: v.Record.Status, Insight: v.Insight, Served: v.N == e.CurrentVersion, Pending: v.N == e.PendingVersion})
}
if from == 0 && to == 0 {
out.Bound = boundOf(bound)
versions := out.Versions
v, m := fit(len(versions), out.Bound-300, func(m int) any { c := *out; c.Versions = versions[len(versions)-m:]; return &c })
*out = *(v.(*Historied))
if m < len(versions) {
out.Cut = fmt.Sprintf("the %d oldest versions are not listed within %d bytes: raise bound", len(versions)-m, out.Bound)
}
return out, nil
}
if from < 1 || to < 1 || from > len(vs) || to > len(vs) {
return nil, fmt.Errorf("%s has versions 1 to %d; from and to name two of them", key, len(vs))
}
out.From, out.To, out.Bound = from, to, boundOf(bound)
diff := Diff(Render(vs[from-1].Record), Render(vs[to-1].Record))
out.Same = len(diff) == 0
// Within the bound: the most of the difference's lines that fit, cut at a hunk's line and said; and when the
// versions alone do not fit, the oldest of them left out first.
versions := out.Versions
v, n := fit(len(diff), out.Bound-300, func(n int) any { c := *out; c.Diff = diff[:n]; return &c })
*out = *(v.(*Historied))
var cut []string
if n < len(diff) {
cut = append(cut, fmt.Sprintf("%d of the difference's %d lines are not shown within %d bytes: raise bound, or compare nearer versions",
len(diff)-n, len(diff), out.Bound))
}
if size(out) > out.Bound-300 {
v, m := fit(len(versions), out.Bound-300, func(m int) any { c := *out; c.Versions = versions[len(versions)-m:]; return &c })
*out = *(v.(*Historied))
cut = append(cut, fmt.Sprintf("the %d oldest versions are not listed", len(versions)-m))
}
out.Cut = strings.Join(cut, "; ")
return out, nil
}
// MostDiffWork bounds the work of a difference: the lines of the two texts' changed middles multiplied. Past it,
// the middle is given whole as removed and added — still exact, only less narrow.
const MostDiffWork = 4_000_000
// Diff is the difference of two texts, line by line: each hunk headed "@@ -a,n +b,m @@", its lines "-" removed
// and "+" added, by the longest common subsequence of their lines.
func Diff(a, b string) []string {
x, y := strings.Split(a, "\n"), strings.Split(b, "\n")
// The common head and tail cost nothing to find; only the middle between them is compared.
head := 0
for head < len(x) && head < len(y) && x[head] == y[head] {
head++
}
tail := 0
for tail < len(x)-head && tail < len(y)-head && x[len(x)-1-tail] == y[len(y)-1-tail] {
tail++
}
x, y = x[head:len(x)-tail], y[head:len(y)-tail]
n, m := len(x), len(y)
if n*m > MostDiffWork {
var out []string
if n+m > 0 {
out = append(out, fmt.Sprintf("@@ -%d,%d +%d,%d @@", head+1, n, head+1, m))
}
for _, l := range x {
out = append(out, "-"+l)
}
for _, l := range y {
out = append(out, "+"+l)
}
return out
}
lcs := make([][]int, n+1)
for i := range lcs {
lcs[i] = make([]int, m+1)
}
for i := n - 1; i >= 0; i-- {
for j := m - 1; j >= 0; j-- {
if x[i] == y[j] {
lcs[i][j] = lcs[i+1][j+1] + 1
} else {
lcs[i][j] = max(lcs[i+1][j], lcs[i][j+1])
}
}
}
var out []string
var hunk []string
ha, hb, ca, cb := 0, 0, 0, 0
flush := func() {
if len(hunk) > 0 {
out = append(out, fmt.Sprintf("@@ -%d,%d +%d,%d @@", ha, ca, hb, cb))
out = append(out, hunk...)
hunk = nil
}
}
i, j := 0, 0
for i < n || j < m {
switch {
case i < n && j < m && x[i] == y[j]:
flush()
i++
j++
case i < n && (j == m || lcs[i+1][j] >= lcs[i][j+1]):
if len(hunk) == 0 {
ha, hb, ca, cb = head+i+1, head+j+1, 0, 0
}
hunk = append(hunk, "-"+x[i])
ca++
i++
default:
if len(hunk) == 0 {
ha, hb, ca, cb = head+i+1, head+j+1, 0, 0
}
hunk = append(hunk, "+"+y[j])
cb++
j++
}
}
flush()
return out
}
// reviewSays is where a proposed version stands in the knowledge base's own review, in words; "" when nothing waits.
func reviewSays(r *Record) string {
j := r.Judging
if j == nil || r.Status != StatusProposed {
return ""
}
switch j.State {
case JudgingDue:
return "waiting for the knowledge base's review"
case JudgingUnproven:
return "not reviewed: " + j.Reason
}
// When it is asked again is said, so a pause is not taken for a review lost (novox/hq issue 497).
return fmt.Sprintf("not reviewed: %s (%s; no judgement that could not be had counts as an approval", j.Reason, j.owedSays())
}