Let a proposed record be withdrawn (issue 1081)

A dropped proposal or a stale reservation stayed proposed forever: there was
no way to say the operator decided against it. knowledge-base.withdraw takes
a reserved or proposed record to withdrawn as a new version naming why, ends
a waiting operator's ask and clears a pending review in the same version,
and refuses an already accepted, superseded or withdrawn record. The number
stays spent either way.

The old Withdraw (the background job for reservations never proposed) is
renamed WithdrawStale to make room for the new, caller-driven WithdrawRecord.
This commit is contained in:
jochen
2026-10-18 00:02:48 +02:00
parent ad80048c22
commit ddf7a5ab09
8 changed files with 204 additions and 10 deletions
@@ -37,7 +37,7 @@ import (
const Seat = "knowledge-base"
// Verbs are the seat's verbs, as the manifest declares and claims them (ADR 0290 rule 6).
var Verbs = []string{"ask", "get", "list", "findings", "holds", "links", "history", "reserve", "propose", "import", "return"}
var Verbs = []string{"ask", "get", "list", "findings", "holds", "links", "history", "reserve", "propose", "import", "return", "withdraw"}
// HealthTool is the module's own tool the node-engine asks how it is.
const HealthTool = "knowledge_health"
@@ -158,7 +158,7 @@ func background(k *KB) {
logf("[mesh-knowledge] %s", s)
}
}
if n, err := k.Withdraw(); err != nil {
if n, err := k.WithdrawStale(); err != nil {
logf("[mesh-knowledge] withdrawing reservations: %v", err)
} else if n > 0 {
logf("[mesh-knowledge] withdrew %d reservation(s) never proposed", n)
@@ -1097,6 +1097,29 @@ func Tools(k *KB, caller func(map[string]any) string) []stdio.Tool {
}
return k.Return(caller(m), key, reason)
}},
{Name: Seat + ".withdraw",
Description: "Withdraw a reserved or proposed record on your decision, with why in one line: its number stays spent and is " +
"never handed out again. A pending review of it is cleared, and a waiting operator's ask on it is ended, both as part of " +
"the one new version this writes, by you, naming why. Refused: an empty why, a record held of none, and a record already " +
"accepted (a research effort accepted at any status), superseded or withdrawn — only a reserved or a proposed record " +
"takes it.",
Input: schema([]string{"number", "why"}, map[string]any{"kind": kindArg(), "number": integer("the record's number"),
"why": text("one line: why the record is withdrawn")}),
Run: func(m map[string]any) (any, error) {
a, err := readArgs("withdraw", m, "kind", "number", "why")
if err != nil {
return nil, err
}
key, err := a.key()
if err != nil {
return nil, err
}
why, err := a.str("why")
if err != nil {
return nil, err
}
return k.WithdrawRecord(caller(m), key, why)
}},
}
}