mesh-knowledge: review every proposal in a judgement of its own, and serve no review verb (hq issue 425, ADR 0290 as updated)
mesh/merge-gate pass: builds mesh-knowledge → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds mesh-knowledge → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
The mesh proves which machine called, not which agent on it (hq issue 365), so a review verb could not tell a reviewer from the author. Each version proposed is now judged by claude-code.claude_code_judge in a separate call that sees only the record, the records it links and the rules: approve accepts (or asks the operator for a needs-operator record), changes keeps it proposed with the findings for its author, and a judgement that cannot be had or read leaves it proposed, says so, and never counts as approval.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
|
||||
// The knowledge base's writes (novox/hq ADR 0290 rules 4 and 5, to-be 55 §4, §5 and §6): a number reserved from
|
||||
// the issue tracker, a record proposed and judged by the 29 rules, reviewed by someone who did not write it, and
|
||||
// accepted — at once, or on the operator's proven warrant when it needs the operator — with every clause it
|
||||
// changes or supersedes moved accordingly and the whole record set judged after.
|
||||
// the issue tracker, a record proposed and judged by the 29 rules, reviewed by the knowledge base's own judgement
|
||||
// (review.go), and accepted — at once, or on the operator's proven warrant when it needs the operator — with every
|
||||
// clause it changes or supersedes moved accordingly and the whole record set judged after.
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -83,6 +83,10 @@ type KB struct {
|
||||
// mu serialises the writes: one instance holds the seat, and a write reads, judges and writes as one.
|
||||
mu sync.Mutex
|
||||
|
||||
// kick wakes the background work when a proposal waits for its review; nil where nothing runs in the
|
||||
// background (the tests take each step themselves).
|
||||
kick chan struct{}
|
||||
|
||||
refMu sync.Mutex
|
||||
ref *Reference
|
||||
refAt time.Time
|
||||
@@ -246,9 +250,10 @@ var numberedClause = regexp.MustCompile(`^(\d+)\.\s+`)
|
||||
// MostRecordBytes is the most one record may hold.
|
||||
const MostRecordBytes = 512 << 10
|
||||
|
||||
// Propose writes a record whole as proposed, or refuses it naming every rule it breaks. A proposal is revised
|
||||
// as new versions until it is reviewed; an accepted record is changed only by a progressive insight (Insight),
|
||||
// which waits for its own review while the accepted version stays the one served.
|
||||
// Propose writes a record whole as proposed, or refuses it naming every rule it breaks. Every version proposed
|
||||
// waits for the knowledge base's own review (review.go), which the background work takes at once. A proposal is
|
||||
// revised as new versions by its author until it is accepted; an accepted record is changed only by a progressive
|
||||
// insight (Insight), which is reviewed like a proposal while the accepted version stays the one served.
|
||||
func (k *KB) Propose(caller string, a ProposeAsk) (*Proposed, error) {
|
||||
ctx := context.Background()
|
||||
k.mu.Lock()
|
||||
@@ -345,6 +350,9 @@ func (k *KB) Propose(caller string, a ProposeAsk) (*Proposed, error) {
|
||||
if !contains(r.Writers, caller) {
|
||||
r.Writers = append(r.Writers, caller)
|
||||
}
|
||||
// Every version proposed is reviewed by the knowledge base itself, once; its earlier reviews stay with the
|
||||
// versions they reviewed.
|
||||
r.Judging = judgingOf(r)
|
||||
if !current && a.Kind == KindResearch && r.Effort == "" {
|
||||
r.Effort = held.Current.Status
|
||||
}
|
||||
@@ -365,106 +373,25 @@ func (k *KB) Propose(caller string, a ProposeAsk) (*Proposed, error) {
|
||||
}
|
||||
n := held.Versions + 1
|
||||
k.emit(EventProposed, r, caller, why, n)
|
||||
says := fmt.Sprintf("%s is proposed as version %d. Nothing of it is in force until a review by someone who did not write it approves it", key, n)
|
||||
k.wake()
|
||||
if r.Judging.State == JudgingUnproven {
|
||||
return &Proposed{Record: key.String(), Version: n, Status: StatusProposed,
|
||||
Says: fmt.Sprintf("%s is proposed as version %d, and %s. Nothing of it is in force.", key, n, r.Judging.Reason)}, nil
|
||||
}
|
||||
says := fmt.Sprintf("%s is proposed as version %d. The knowledge base reviews it now, in a judgement of its own; nothing of it "+
|
||||
"is in force until that judgement approves it", key, n)
|
||||
if r.NeedsOperator {
|
||||
says += ", and then the operator's Approve"
|
||||
}
|
||||
if !current {
|
||||
says = fmt.Sprintf("A progressive insight to %s is proposed as version %d; the accepted version is served until a review approves it", key, n)
|
||||
says = fmt.Sprintf("A progressive insight to %s is proposed as version %d. The knowledge base reviews it now; the accepted "+
|
||||
"version is served until that review approves it", key, n)
|
||||
}
|
||||
says += ". get shows the review's verdict and findings once it is made; on changes, you revise it by proposing it again"
|
||||
return &Proposed{Record: key.String(), Version: n, Status: StatusProposed, Says: says + "."}, nil
|
||||
}
|
||||
|
||||
// ---- review and acceptance ---------------------------------------------------------------------------------
|
||||
|
||||
// ReviewAsk is one review.
|
||||
type ReviewAsk struct {
|
||||
Kind string
|
||||
Number int
|
||||
Verdict string
|
||||
Findings string
|
||||
}
|
||||
|
||||
// Reviewed is the answer to review.
|
||||
type Reviewed struct {
|
||||
Record string `json:"record"`
|
||||
Verdict string `json:"verdict"`
|
||||
Status string `json:"status"`
|
||||
Says string `json:"says"`
|
||||
}
|
||||
|
||||
// Review takes a review of the version proposed, by someone who did not write it. On approve the record is
|
||||
// accepted, or, when it needs the operator, asked of the operator; on changes it stays proposed, the findings
|
||||
// kept with it.
|
||||
func (k *KB) Review(caller string, a ReviewAsk) (*Reviewed, error) {
|
||||
ctx := context.Background()
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
key := Key{a.Kind, a.Number}
|
||||
if a.Verdict != VerdictApprove && a.Verdict != VerdictChanges {
|
||||
return nil, fmt.Errorf("verdict is approve or changes, not %q; nothing was reviewed", a.Verdict)
|
||||
}
|
||||
if a.Verdict == VerdictChanges && strings.TrimSpace(a.Findings) == "" {
|
||||
return nil, errors.New("a review that asks for changes says them in findings; nothing was reviewed")
|
||||
}
|
||||
held, err := k.Store.Get(ctx, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the knowledge base cannot be read (%v); nothing was reviewed", err)
|
||||
}
|
||||
if held == nil || held.Working().Status != StatusProposed {
|
||||
status := "not held"
|
||||
if held != nil {
|
||||
status = held.Working().Status
|
||||
}
|
||||
return nil, fmt.Errorf("%s is %s: only a proposed record is reviewed", key, status)
|
||||
}
|
||||
if !proven(caller) {
|
||||
return nil, fmt.Errorf("the runtime does not prove who calls (hq issue 365), so a review cannot be told from one by %s's "+
|
||||
"author, and none is taken until it does. Nothing was reviewed", key)
|
||||
}
|
||||
w := held.Working().Clone()
|
||||
// The record's writers; an imported record's are hq's, reviewed there, and none of them is named here.
|
||||
var writers []string
|
||||
for _, by := range append([]string{w.Author}, w.Writers...) {
|
||||
if by != "" {
|
||||
writers = append(writers, by)
|
||||
}
|
||||
}
|
||||
for _, by := range writers {
|
||||
if !proven(by) {
|
||||
return nil, fmt.Errorf("%s has a version written while the runtime proved no caller, so who wrote it cannot be told from "+
|
||||
"who reviews it: its author is to propose it again under a new number. Nothing was reviewed", key)
|
||||
}
|
||||
}
|
||||
if contains(writers, caller) {
|
||||
return nil, fmt.Errorf("%s has a version %s wrote, who called this review: a record is reviewed by someone who wrote none "+
|
||||
"of it. Nothing was reviewed", key, caller)
|
||||
}
|
||||
reviewed := held.CurrentVersion
|
||||
if held.Pending != nil {
|
||||
reviewed = held.PendingVersion
|
||||
}
|
||||
w.Reviews = append(w.Reviews, Review{By: caller, At: k.now(), Verdict: a.Verdict, Findings: strings.TrimSpace(a.Findings), Version: reviewed})
|
||||
if a.Verdict == VerdictChanges {
|
||||
if err := k.Store.Save(ctx, Version{By: caller, At: k.now(), Why: "reviewed: changes asked", Record: w, Insight: held.Pending != nil},
|
||||
held.Pending == nil, false, held.Revision); err != nil {
|
||||
return nil, fmt.Errorf("the review could not be kept (%v)", err)
|
||||
}
|
||||
k.emit(EventReviewed, w, caller, "changes asked", held.Versions+1)
|
||||
return &Reviewed{Record: key.String(), Verdict: a.Verdict, Status: StatusProposed,
|
||||
Says: fmt.Sprintf("%s stays proposed; the findings are kept with version %d for its author to answer with a revision.", key, reviewed)}, nil
|
||||
}
|
||||
if w.NeedsOperator {
|
||||
return k.askOperator(ctx, held, w, caller)
|
||||
}
|
||||
k.emit(EventReviewed, w, caller, "approved", held.Versions+1)
|
||||
r, err := k.accept(ctx, held, w, caller, "accepted after a review by "+caller, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Reviewed{Record: key.String(), Verdict: a.Verdict, Status: r.Status,
|
||||
Says: fmt.Sprintf("%s is %s and its clauses are in force.", key, r.Status)}, nil
|
||||
}
|
||||
// ---- acceptance ------------------------------------------------------------------------------------------
|
||||
|
||||
// accept makes a reviewed version the one served: accepted, its clauses in force, every clause and record it
|
||||
// changes or supersedes moved; then the whole record set is judged. by is who acted; warrant, the operator's word
|
||||
|
||||
Reference in New Issue
Block a user