diff --git a/modules/mesh-knowledge/README.md b/modules/mesh-knowledge/README.md index 7bd8c06c..ea173edf 100644 --- a/modules/mesh-knowledge/README.md +++ b/modules/mesh-knowledge/README.md @@ -73,14 +73,15 @@ record it goes out from. progressive insight in the dated, marked form. A rule holds a record from the day hq's checks began to hold it (`RuleFrom` in `rules.go`): - - retired words from 2026-10-07; - - daemons' lines and deferrals from 2026-10-09; + - retired words from 2026-10-07 and daemons' lines from 2026-10-09 for a research effort; for a decision record + both from 2026-10-11, since hq's words check never read a decision record; + - deferrals from 2026-10-09; - the clauses from 2026-10-11; - the decision record's sections and the live-citation rule, which no hq check held on a decision record, from 2026-10-11, the day the knowledge base takes the write. A progressive insight keeps its record's date, so a correction to an older record is not refused by a rule - that never held it. A new record is dated no earlier than the day its number was reserved. + that never held it, and it keeps the links the accepted record holds. A new record is dated no earlier than the day its number was reserved. 3. Revise it by proposing again; every version is kept. 4. A `review` by someone else either approves it or asks for changes. On approve the record and its clauses are accepted and in force. Every clause it changes moves to `changed`, and every record it supersedes moves to diff --git a/modules/mesh-knowledge/cmd/mesh-knowledge/dated_test.go b/modules/mesh-knowledge/cmd/mesh-knowledge/dated_test.go index 45b077aa..20bbbdec 100644 --- a/modules/mesh-knowledge/cmd/mesh-knowledge/dated_test.go +++ b/modules/mesh-knowledge/cmd/mesh-knowledge/dated_test.go @@ -56,11 +56,16 @@ func TestEachDatedRuleHoldsFromItsDay(t *testing.T) { {"clause-lead", "2026-10-10", "2026-10-11"}, {"checked", "2026-10-10", "2026-10-11"}, } { - if Holds(c.rule, c.before) || !Holds(c.rule, c.from) { - t.Errorf("%s holds a record of %s: %v, of %s: %v", c.rule, c.before, Holds(c.rule, c.before), c.from, Holds(c.rule, c.from)) + if Holds(c.rule, KindResearch, c.before) || !Holds(c.rule, KindResearch, c.from) { + t.Errorf("%s holds research of %s: %v, of %s: %v", c.rule, c.before, Holds(c.rule, KindResearch, c.before), c.from, Holds(c.rule, KindResearch, c.from)) } } - if !Holds("title", "2026-01-01") { + for _, rule := range []string{"retired-word", "daemon-line"} { + if Holds(rule, KindDecision, "2026-10-10") || !Holds(rule, KindDecision, "2026-10-11") { + t.Errorf("%s holds a decision from another day than 2026-10-11", rule) + } + } + if !Holds("title", KindDecision, "2026-01-01") { t.Error("an undated rule does not hold an old record") } } @@ -77,3 +82,17 @@ func TestANewRecordIsNotDatedBeforeItsReservation(t *testing.T) { t.Fatalf("a new record dated before its reservation: %v", err) } } + +// hq's words check never read a decision record: a decision keeps the words it was written with. A correction to +// ADR 0276 (2026-10-10), which writes words and lines those checks held only research and issues to, is taken. +func TestAnInsightToADecisionOfOctoberKeepsItsWords(t *testing.T) { + w := newWorld(t, newMemStore(), hqMain(t)) + if _, err := w.k.Import("import@test", ImportAsk{}, hqMain(t)); err != nil { + t.Fatal(err) + } + for _, n := range []int{276, 240, 244, 272} { + if _, err := w.k.Propose("corrector@ace", insightTo(t, w, Key{KindDecision, n})); err != nil { + t.Errorf("an insight to ADR %04d was refused: %v", n, err) + } + } +} diff --git a/modules/mesh-knowledge/cmd/mesh-knowledge/rules.go b/modules/mesh-knowledge/cmd/mesh-knowledge/rules.go index f789ef5d..e3b62980 100644 --- a/modules/mesh-knowledge/cmd/mesh-knowledge/rules.go +++ b/modules/mesh-knowledge/cmd/mesh-knowledge/rules.go @@ -121,7 +121,7 @@ func allText(r *Record) string { func JudgeRecord(r *Record, held *Entry, lookup Lookup, ref *Reference) []Refusal { var out []Refusal refuse := func(rule, format string, a ...any) { - if Holds(rule, r.Date) { + if Holds(rule, r.Kind, r.Date) { out = append(out, Refusal{Rule: rule, Says: fmt.Sprintf(format, a...)}) } } @@ -215,16 +215,20 @@ func JudgeRecord(r *Record, held *Entry, lookup Lookup, ref *Reference) []Refusa continue } } - switch l.Kind { - case LinkBecame: + kept := held != nil && held.Current.Accepted() && hasLink(held.Current, l) + switch { + case kept: + // An insight keeps what the accepted record linked, as hq wrote it: a change hq named by record waits + // for its clause to be judged. + case l.Kind == LinkBecame: if r.Kind != KindResearch || !(l.To.Kind == KindDecision || l.To.Kind == TargetDesign) || l.To.Clause > 0 { refuse("link-fits", "became goes from a research effort to a decision record or a design: not %s", l) } - case LinkChanges: + case l.Kind == LinkChanges: if !decision || l.To.Kind != KindDecision || l.To.Clause == 0 { refuse("link-fits", "changes goes from a decision (or a clause of it) to a clause of another decision, named as 0276 ยง4: not %s", l) } - case LinkSupersedes, LinkExtends: + case l.Kind == LinkSupersedes || l.Kind == LinkExtends: if r.Kind != l.To.Kind || !isRecord || l.To.Clause > 0 { refuse("link-fits", "%s goes from a record to a record of its kind: not %s", l.Kind, l) } @@ -555,8 +559,19 @@ var RuleFrom = map[string]string{ "live-citation": "2026-10-11", } -// Holds says whether a rule holds a record of this date. A record with no day is held to every rule. -func Holds(rule, date string) bool { +// DecisionFrom overrides RuleFrom for a decision record: hq's words check never read 02-DECISIONS ("a decision +// record keeps the words it was written with"), so its retired words and daemons' lines hold a decision only from +// the day the knowledge base takes the write. +var DecisionFrom = map[string]string{ + "retired-word": "2026-10-11", + "daemon-line": "2026-10-11", +} + +// Holds says whether a rule holds a record of this kind and date. A record with no day is held to every rule. +func Holds(rule, kind, date string) bool { from, dated := RuleFrom[rule] + if d, ok := DecisionFrom[rule]; ok && kind == KindDecision { + from = d + } return !dated || !isDay.MatchString(date) || date >= from }