mesh-knowledge: a research effort's new version may leave out its sections
Closing a long research effort (its status and became links) meant retyping its whole text through propose. A research effort's new version that gives no sections now keeps the served version's, and its title, date and deciders when none are given; the version so composed is judged and reviewed as any other. A decision, or an effort with no served version, still needs its text. Issue 1966. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -13,44 +14,91 @@ func effortAsk(n int, status string, docs ...Section) ProposeAsk {
|
||||
Links: []LinkAsk{{Kind: LinkTouches, To: "0290"}}}
|
||||
}
|
||||
|
||||
// novox/hq issue 844: an active research effort takes new versions freely, each reviewed as a proposal.
|
||||
// novox/hq issue 844: an active research effort takes new versions freely, each reviewed as a proposal. Issue 1966:
|
||||
// a new version that leaves out its sections keeps the served version's text, title, date and deciders, and takes
|
||||
// the status, links and why given; the rules still judge the version so composed.
|
||||
func TestAnActiveEffortTakesANewVersionWithANewDocument(t *testing.T) {
|
||||
w := imported(t, newMemStore())
|
||||
r, err := w.k.ReserveNumber("author@g14", KindResearch, "an effort")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
overview := Section{Name: "00-overview.md", Text: "What an inference layer would be."}
|
||||
w.accepted(t, "author@g14", effortAsk(r.Number, StatusActive, overview))
|
||||
key := Key{KindResearch, r.Number}
|
||||
reviews := len(w.judge.reviews)
|
||||
next := effortAsk(r.Number, StatusActive, overview, Section{Name: "01-findings.md", Text: "A new finding."})
|
||||
next.Why = "findings added"
|
||||
if _, err := w.k.Propose("author@g14", next); err != nil {
|
||||
t.Fatalf("an active effort refused a new version: %v", err)
|
||||
}
|
||||
e := mustGet(t, w.k.Store, key)
|
||||
if e.Pending == nil || e.Current.Section("01-findings.md") != "" {
|
||||
t.Fatalf("the new version was served before its review")
|
||||
}
|
||||
w.reviewAll(t)
|
||||
q := w.judge.reviews[reviews]
|
||||
if strings.Contains(q, "a correction of fact (a progressive insight)") {
|
||||
t.Fatalf("the review judged a new version as an insight:\n%.400s", q)
|
||||
}
|
||||
e = mustGet(t, w.k.Store, key)
|
||||
if e.Pending != nil || e.Current.Status != StatusActive || e.Current.Section("01-findings.md") != "A new finding." ||
|
||||
len(e.Current.Insights) != 0 {
|
||||
t.Fatalf("after the review: %s, pending %v, insights %v", e.Current.Status, e.Pending != nil, e.Current.Insights)
|
||||
}
|
||||
h, err := w.k.History(key, 0, 0, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, v := range h.Versions {
|
||||
if v.Insight {
|
||||
t.Errorf("version %d is marked an insight", v.N)
|
||||
}
|
||||
findings := Section{Name: "01-findings.md", Text: "A new finding."}
|
||||
became := []LinkAsk{{Kind: LinkTouches, To: "0290"}, {Kind: LinkBecame, To: "0290"}}
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
next func(n int) ProposeAsk
|
||||
refused string // the rule a refusal names; empty when the version is taken
|
||||
status string
|
||||
docs []Section
|
||||
became bool
|
||||
}{
|
||||
{"a new document", func(n int) ProposeAsk { return effortAsk(n, StatusActive, overview, findings) },
|
||||
"", StatusActive, []Section{overview, findings}, false},
|
||||
{"closed with its sections left out", func(n int) ProposeAsk {
|
||||
return ProposeAsk{Kind: KindResearch, Number: n, Status: StatusGraduated, Links: became}
|
||||
}, "", StatusGraduated, []Section{overview}, true},
|
||||
{"abandoned with its sections left out", func(n int) ProposeAsk {
|
||||
return ProposeAsk{Kind: KindResearch, Number: n, Status: StatusAbandoned, Links: effortAsk(n, "").Links}
|
||||
}, "", StatusAbandoned, []Section{overview}, false},
|
||||
{"closed with its sections left out and no became link", func(n int) ProposeAsk {
|
||||
return ProposeAsk{Kind: KindResearch, Number: n, Status: StatusGraduated, Links: effortAsk(n, "").Links}
|
||||
}, "became", "", nil, false},
|
||||
} {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
w := imported(t, newMemStore())
|
||||
r, err := w.k.ReserveNumber("author@g14", KindResearch, "an effort")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
first := effortAsk(r.Number, StatusActive, overview)
|
||||
w.accepted(t, "author@g14", first)
|
||||
key := Key{KindResearch, r.Number}
|
||||
reviews, versions := len(w.judge.reviews), mustGet(t, w.k.Store, key).Versions
|
||||
next := c.next(r.Number)
|
||||
next.Why = c.name
|
||||
_, err = w.k.Propose("author@g14", next)
|
||||
if c.refused != "" {
|
||||
if err == nil || !strings.Contains(err.Error(), c.refused) {
|
||||
t.Fatalf("want a refusal naming %s, got %v", c.refused, err)
|
||||
}
|
||||
if e := mustGet(t, w.k.Store, key); e.Pending != nil || e.Versions != versions {
|
||||
t.Fatalf("a refused version was written: pending %v, %d versions", e.Pending != nil, e.Versions)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("an active effort refused a new version: %v", err)
|
||||
}
|
||||
e := mustGet(t, w.k.Store, key)
|
||||
if e.Pending == nil || e.Current.Status != StatusActive || e.Current.Section("01-findings.md") != "" {
|
||||
t.Fatalf("the new version was served before its review")
|
||||
}
|
||||
w.reviewAll(t)
|
||||
q := w.judge.reviews[reviews]
|
||||
if strings.Contains(q, "a correction of fact (a progressive insight)") {
|
||||
t.Fatalf("the review judged a new version as an insight:\n%.400s", q)
|
||||
}
|
||||
if !strings.Contains(q, overview.Text) {
|
||||
t.Fatalf("the review did not see the kept text:\n%.400s", q)
|
||||
}
|
||||
e = mustGet(t, w.k.Store, key)
|
||||
cur := e.Current
|
||||
if e.Pending != nil || cur.Status != c.status || len(cur.Insights) != 0 || !reflect.DeepEqual(cur.Sections, c.docs) {
|
||||
t.Fatalf("after the review: %s, pending %v, insights %v, sections %+v", cur.Status, e.Pending != nil, cur.Insights, cur.Sections)
|
||||
}
|
||||
if cur.Title != first.Title || cur.Date != first.Date || !reflect.DeepEqual(cur.Deciders, first.Deciders) {
|
||||
t.Fatalf("title, date or deciders not kept: %q %s %v", cur.Title, cur.Date, cur.Deciders)
|
||||
}
|
||||
if got := hasLink(cur, Link{Kind: LinkBecame, To: Ref{Kind: KindDecision, Number: 290}}); got != c.became {
|
||||
t.Fatalf("became link %v, want %v: %+v", got, c.became, cur.Links)
|
||||
}
|
||||
h, err := w.k.History(key, 0, 0, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, v := range h.Versions {
|
||||
if v.Insight {
|
||||
t.Errorf("version %d is marked an insight", v.N)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +124,25 @@ func TestAGraduatedEffortAndADecisionStillTakeOnlyAnInsight(t *testing.T) {
|
||||
if _, err := w.k.Propose("author@g14", p); err == nil || !strings.Contains(err.Error(), "progressive insight") {
|
||||
t.Fatalf("a decision took a plain version: %v", err)
|
||||
}
|
||||
// Issue 1966: only a research effort's new version keeps the served sections when it leaves them out. A
|
||||
// decision's insight, and an effort with no served version, still need their text.
|
||||
m, err := w.k.ReserveNumber("author@g14", KindResearch, "another effort")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, c := range []struct {
|
||||
name, rule string
|
||||
ask ProposeAsk
|
||||
}{
|
||||
{"a decision's insight with no sections", "context", ProposeAsk{Kind: KindDecision, Number: n, Title: p.Title, Topic: p.Topic,
|
||||
Deciders: p.Deciders, Clauses: p.Clauses, Links: p.Links, Homes: p.Homes, Insight: "the count was 3"}},
|
||||
{"an effort with no served version and no sections", "overview", ProposeAsk{Kind: KindResearch, Number: m.Number,
|
||||
Title: "An effort", Deciders: []string{"jochen"}, Status: StatusActive}},
|
||||
} {
|
||||
if _, err := w.k.Propose("author@g14", c.ask); err == nil || !strings.Contains(err.Error(), c.rule) {
|
||||
t.Errorf("%s: want a refusal naming %s, got %v", c.name, c.rule, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An old active effort's new text is held to the rules as of the day it is proposed, not the day the effort began.
|
||||
|
||||
@@ -389,6 +389,21 @@ func (k *KB) Propose(caller string, a ProposeAsk) (*Proposed, error) {
|
||||
default:
|
||||
return nil, fmt.Errorf("%s is %s and takes no proposal; nothing was proposed", key, held.Working().Status)
|
||||
}
|
||||
// A research effort's new version that gives no sections keeps the served version's, and its title, date and
|
||||
// deciders where none are given: closing a long effort (its status and its became links) needs no retyping of
|
||||
// its text (novox/hq issue 1966). The version so composed is judged and reviewed as any other.
|
||||
if s := held.Current; a.Kind == KindResearch && len(a.Sections) == 0 && s.Status != StatusReserved {
|
||||
r.Sections = append([]Section(nil), s.Sections...)
|
||||
if r.Title == "" {
|
||||
r.Title = s.Title
|
||||
}
|
||||
if strings.TrimSpace(a.Date) == "" {
|
||||
r.Date = s.Date
|
||||
}
|
||||
if len(a.Deciders) == 0 {
|
||||
r.Deciders = append([]string(nil), s.Deciders...)
|
||||
}
|
||||
}
|
||||
}
|
||||
if current && held != nil && !held.Current.ReservedAt.IsZero() && r.Date < held.Current.ReservedAt.Format("2006-01-02") {
|
||||
// A new record is of its own day: a date before its reservation would put it under rules older records keep.
|
||||
|
||||
@@ -975,18 +975,20 @@ func Tools(k *KB, caller func(map[string]any) string) []stdio.Tool {
|
||||
"every reason at once and writing nothing, when it breaks one of the 30 rules that judge one record or its prose — a " +
|
||||
"link to nothing, a clause without a bold lead, a superseded record cited alone, an unknown topic, a retired word. A " +
|
||||
"proposal is revised by proposing it again; an accepted record (an active research effort excepted, which takes new versions) changes only by a progressive insight (insight: what " +
|
||||
"stood before). The knowledge base then reviews each version itself, in a judgement that sees only the record, the " +
|
||||
"stood before). A research effort's new version may leave out its sections, and its title, date and deciders: it then " +
|
||||
"keeps the served version's and takes the status, links and why given, as when an effort is closed (graduated or " +
|
||||
"abandoned, with its became links). The knowledge base then reviews each version itself, in a judgement that sees only the record, the " +
|
||||
"records it links and the rules. A decision that names no home and that no accepted record links is not judged: " +
|
||||
"its review asks changes for the whole-set rule cited, which its acceptance would break. On approve it is accepted (a record that needs the operator waits for the operator's " +
|
||||
"Approve), on changes it stays proposed with the findings for its author to revise, and when no judgement can be had " +
|
||||
"it stays proposed and says so. Nothing proposed is in force.",
|
||||
Input: schema([]string{"number", "title"}, map[string]any{
|
||||
"kind": kindArg(), "number": integer("the number reserve gave"), "title": text("one line"),
|
||||
Input: schema([]string{"number"}, map[string]any{
|
||||
"kind": kindArg(), "number": integer("the number reserve gave"), "title": text("one line; a research effort's new version keeps the served title when absent"),
|
||||
"topic": text("for a decision: a topic of the reading order, such as the mesh"),
|
||||
"date": text("the day, YYYY-MM-DD (today when absent)"),
|
||||
"deciders": texts("who decided"),
|
||||
"clauses": texts("a decision's clauses in order, each \"**Lead.** the statement\""),
|
||||
"sections": map[string]any{"type": "array", "description": "the reasoning, in order: each a name and its markdown",
|
||||
"sections": map[string]any{"type": "array", "description": "the reasoning, in order: each a name and its markdown; a research effort's new version keeps the served version's when absent",
|
||||
"items": map[string]any{"type": "object", "properties": map[string]any{"name": text("such as Context"), "text": text("markdown")},
|
||||
"required": []string{"name", "text"}}},
|
||||
"links": map[string]any{"type": "array", "description": "its links: kind, to (\"0276\", \"0276 §4\", \"research 045\", \"issue 365\", a design's path), from (the clause it goes out from)",
|
||||
|
||||
Reference in New Issue
Block a user