diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/configheld.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/configheld.go index 8b457a63e..698811892 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/configheld.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/configheld.go @@ -87,9 +87,7 @@ func (h *Holder) askAgainForConfig(id, why string) { h.mu.Unlock() switch { case refused == nil: - if _, err := h.Recheck(id, why, byOwner); err != nil { - h.Logf("[mesh-delivery] %s: its check could not be asked again for a config value: %v", id, err) - } + h.Logf("[mesh-delivery] %s: its check could not be asked again for a config value: %v", id, checkerRefusal(id)) case proposed: h.askProposedAgain(id, why) } diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go index c369104d7..9ad6342c5 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/holder_test.go @@ -329,15 +329,12 @@ func (f *fakeLinks) Linked(repository string, number int) ([]int, error) { return out, nil } -// Stalled: a delivery past its state's bound is listed with what H2 may do; close takes only that. Only a proposed +// Stalled: a delivery past its state's bound is listed with what H2 may do. Only a proposed // delivery has a bound now: its check was asked and not answered. -func TestStalledAndCloseWorkFromTheTable(t *testing.T) { +func TestStalledWorksFromTheTable(t *testing.T) { w := newWorld(t) id := IDOf("novox/app", head) w.h.PullUpdated(pr("novox/app", 7, head, "feat/x", "src/a.go")) - if _, err := w.h.Close(id, "too early"); err == nil { - t.Fatal("closed within its bound") - } w.later(3 * time.Hour) stalled := w.h.Stalled() if len(stalled) != 1 || stalled[0].ID != id || stalled[0].State != Proposed { @@ -352,16 +349,10 @@ func TestStalledAndCloseWorkFromTheTable(t *testing.T) { t.Fatalf("a merged delivery is stalled: %+v", s) } } - if _, err := w.h.Close(id, "x"); err == nil { - t.Fatal("H2 closed a merged delivery") - } // A merge without a passing check is held: a person's, with no transition for H2. w.h.PullUpdated(pr("novox/app", 8, "ffffffffffff0000", "feat/q")) w.h.PullMerged(merged("novox/app", 8, "ffffffffffff0000", "eeeeeeeeeeee0000")) w.later(48 * time.Hour) - if _, err := w.h.Close(IDOf("novox/app", "ffffffffffff0000"), "x"); err == nil { - t.Fatal("H2 closed a held delivery") - } } // novox/hq issue 867, ADR 0382 §1 (contract A): each member of a group in `groups` says its pull request number, full diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/main.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/main.go index 5b73dbf71..694da81e7 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/main.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/main.go @@ -298,9 +298,6 @@ func boolArg(a map[string]any, k string) bool { return false } -// aPerson is who an act through the seat is by: the seat does not name its caller. -const aPerson = "a person, through the mesh-delivery seat" - func need(a map[string]any, keys ...string) error { for _, k := range keys { if strArg(a, k) == "" { @@ -367,36 +364,10 @@ func tools(h *Holder, l *listening) []stdio.Tool { } return h.Checks(strArg(a, "repository"), number, strArg(a, "commit")) }}, - {Name: seat + "table", - Description: "The state table every delivery runs by: each transition with its guard, each state's bound " + - "and what healer H2 may do once it has passed; ", - Run: func(map[string]any) (any, error) { return TableText(), nil }}, {Name: seat + "stalled", Description: "Every delivery held past its state's bound, with the transition the table lets healer H2 take. " + "Also a merge refused or not made past its bound, a group half merged, and a head with no merge check.", Run: func(a map[string]any) (any, error) { return h.Stalled(), nil }}, - {Name: seat + "recheck", - Description: "Refused for every delivery: the checker checks a head again on every move of the head, its trunk or its " + - "group, and nothing is asked of anyone here (novox/hq ADR 0447, issue 1318). Kept so a caller is told where to read.", - Input: map[string]any{"type": "object", "properties": map[string]any{"id": str("the delivery's id"), - "why": str("why, kept with the transition")}, "required": []string{"id", "why"}}, - Run: func(a map[string]any) (any, error) { - if err := need(a, "id", "why"); err != nil { - return nil, err - } - return h.Recheck(strArg(a, "id"), strArg(a, "why"), aPerson) - }}, - {Name: seat + "close", - Description: "Healer H2's verb: for a proposed delivery past its bound, take the transition the table " + - "names for that state. Refused for any other.", - Input: map[string]any{"type": "object", "properties": map[string]any{"id": str("the delivery's id"), - "why": str("the condition it answers")}, "required": []string{"id", "why"}}, - Run: func(a map[string]any) (any, error) { - if err := need(a, "id", "why"); err != nil { - return nil, err - } - return h.Close(strArg(a, "id"), strArg(a, "why")) - }}, {Name: "delivery_merge_again", Description: "On the operator's word, ask again at once the merge of an approved delivery the forge refused, or of an approved " + "group held or half merged, at the heads the Approve named (novox/hq issue 1275): every member is read first and " + diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/manifest_test.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/manifest_test.go index 2a340989e..6a6f502a7 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/manifest_test.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/manifest_test.go @@ -77,7 +77,7 @@ func TestItClaimsTheDeliverySeatAndFollowsWhatItHandles(t *testing.T) { } // The verbs the seat keeps: times, what-if, release, stop and retire-history went with the plans (ADR 0539 §17), and // settle with the merge states (design 47 G §431). - if want := []string{"deliveries", "show", "groups", "source", "checks", "table", "stalled", "recheck", "close"}; !reflect.DeepEqual(m.Claims[0].Serves, want) { + if want := []string{"deliveries", "show", "groups", "source", "checks", "stalled"}; !reflect.DeepEqual(m.Claims[0].Serves, want) { t.Fatalf("serves %v, wanted %v", m.Claims[0].Serves, want) } // The forge messages' layout is the bridge's published package forgemsg, taken by its series (ADR 0634 §3, design 32). @@ -293,7 +293,7 @@ func TestTheGateHoldsTheSeatAsTheSDKsTableOfRelease6HasIt(t *testing.T) { t.Errorf("the manifest serves %s, which G removed with the merge states", removed) } } - for _, verb := range []string{"deliveries", "show", "groups", "checks", "table", "stalled", "recheck", "close"} { + for _, verb := range []string{"deliveries", "show", "groups", "checks", "stalled"} { if !served[verb] { t.Errorf("the gate does not serve %s, which the seat keeps", verb) } diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/onestatus_test.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/onestatus_test.go index f1d40f348..9d4378eea 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/onestatus_test.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/onestatus_test.go @@ -177,21 +177,6 @@ func TestAFailingMeshCheckRejects(t *testing.T) { } } -// 1302 (lead's (2)): the recheck verb refuses a delivery on a mesh/check base, saying the checker checks again on every -// move and where to read it. -func TestRecheckRefusesAMeshCheckDelivery(t *testing.T) { - w := newWorld(t) - p := pr(oneRepo, 1, oneHead, "feat/one") - requiresCheck(w, p) - w.h.PullUpdated(p) - w.h.CommitStatus(checkStatus(oneRepo, oneHead, "failure", oneTrunk)) - w.settleAll() - _, err := w.h.Recheck(IDOf(oneRepo, oneHead), "try again", "tester") - if err == nil || !strings.Contains(err.Error(), "checker_pull") || !strings.Contains(err.Error(), "checker") { - t.Fatalf("recheck answered %v, want a refusal naming the checker and checker_pull", err) - } -} - // 1318 (the gate's part): a pull request closed unmerged asks the controller to forget nothing: forget-checks is not called. func TestAClosedPullRequestAsksNoForgetChecks(t *testing.T) { w := newWorld(t) diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record.go index 1d5123b51..27e998d4e 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record.go @@ -55,13 +55,12 @@ const ( OpFree = "free" // remove from pinned, by revision OpEnd = "end" // set-fields: ended OpAct = "act" // set-entry into acts, by id: an act owed the forge with its outcome - OpRemove = "remove" // remove the instance, at its retention OpHold = "hold" // a month's list: set-entry into records, by instance OpDrop = "drop" // a month's list: remove from records, by instance ) // RecordOperations are a record's operations, in the order the manifest's type allows them. -var RecordOperations = []string{OpOpen, OpGroup, OpTrace, OpEntry, OpOutcome, OpAway, OpBack, OpPin, OpFree, OpAct, OpEnd, OpRemove} +var RecordOperations = []string{OpOpen, OpGroup, OpTrace, OpEntry, OpOutcome, OpAway, OpBack, OpPin, OpFree, OpAct, OpEnd} // HeldOperations are the month's list's operations. var HeldOperations = []string{OpHold, OpDrop} diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record_test.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record_test.go index d1a7f09e4..066cb1d55 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record_test.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/record_test.go @@ -146,8 +146,10 @@ func (f *ledgerFake) Apply(instance, operation string, body any) (uint64, error) } func (f *ledgerFake) Remove(instance string) error { - _, err := f.Apply(instance, OpRemove, map[string]any{}) - return err + f.seq[instance]++ + f.states[instance] = json.RawMessage("null") + f.history = append(f.history, instance+" remove") + return nil } func (f *ledgerFake) Get(instance string) (json.RawMessage, uint64, error) { diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/table.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/table.go index e79102a52..dadf6b98c 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/table.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/table.go @@ -381,33 +381,3 @@ func pastBound(d *Delivery, now time.Time) bool { b, bounded := boundOf(d) return bounded && !d.State.Final() && (b.For == 0 || now.Sub(boundSince(d)) > b.For) } - -// TableText is the table as the `table` verb answers it. -func TableText() map[string]any { - var rows []map[string]any - for _, r := range Table { - from := make([]string, 0, len(r.From)) - for _, s := range r.From { - if s == None { - from = append(from, "(none)") - continue - } - from = append(from, string(s)) - } - kind := "observed" - if r.Act { - kind = "asked" - } - rows = append(rows, map[string]any{"from": strings.Join(from, ", "), "event": r.Event, "to": r.To, - "guard": r.Guard, "taken": kind}) - } - bounds := map[string]any{} - for s, b := range Bounds { - var h2 []string - for _, e := range b.H2 { - h2 = append(h2, string(e)) - } - bounds[string(s)] = map[string]any{"bound": b.For.String(), "h2": h2, "says": b.Says} - } - return map[string]any{"transitions": rows, "bounds": bounds} -} diff --git a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/verbs.go b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/verbs.go index 7c0ea4fe6..ccc8d919e 100644 --- a/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/verbs.go +++ b/modules/mesh-delivery-gate/cmd/mesh-delivery-gate/verbs.go @@ -252,51 +252,6 @@ func (h *Holder) Stalled() []StalledLine { return out } -// Recheck is the `recheck` verb. The checker judges every head and checks it again on every move of its trunk (ADR 0447 §4), and -// the controller judges none (ADR 0524 §5), so there is no ask to make: a delivery that exists is refused, naming the checker -// (the lead's decision for issue 1302), and an unknown id is refused as before. -func (h *Holder) Recheck(id, why, by string) (string, error) { - h.mu.Lock() - defer h.mu.Unlock() - if h.deliveries[id] == nil { - return "", fmt.Errorf("no delivery %q", id) - } - return "", checkerRefusal(id) -} - -// Close is healer H2's verb: for a proposed delivery past its bound, its check is asked once more; only a transition -// the table names for that state counts, any other answer is refused. -func (h *Holder) Close(id, why string) (string, error) { - h.mu.Lock() - d := h.deliveries[id] - if d == nil { - h.mu.Unlock() - return "", fmt.Errorf("no delivery %q", id) - } - b, bounded := boundOf(d) - if !bounded || len(b.H2) == 0 { - h.mu.Unlock() - return "", fmt.Errorf("%s is %s: H2 has no transition there — the state is the operator's", id, d.State) - } - if !pastBound(d, h.Now()) { - h.mu.Unlock() - return "", fmt.Errorf("%s has been %s for %s, within its bound of %s: nothing to close", id, d.State, - h.Now().Sub(boundSince(d)).Round(time.Second), b.For) - } - if d.State == Proposed { - return h.reask(d, why) // unlocks - } - h.mu.Unlock() - return "", fmt.Errorf("%s is %s: H2 has no transition there", id, d.State) -} - -// reask is H2's close of a proposed delivery past its bound. A head's check is the checker's, which asks nothing of anyone -// and checks again on its own (ADR 0447 §4), so there is no check to ask once more. Called under the lock; it unlocks. -func (h *Holder) reask(d *Delivery, why string) (string, error) { - h.mu.Unlock() - return "", checkerRefusal(d.ID) -} - // Status is the module's own `delivery_status`. func (h *Holder) Status(listening string) map[string]any { h.mu.Lock() diff --git a/modules/mesh-delivery-gate/module.json b/modules/mesh-delivery-gate/module.json index 514b72be9..b015d3760 100644 --- a/modules/mesh-delivery-gate/module.json +++ b/modules/mesh-delivery-gate/module.json @@ -12,10 +12,7 @@ "groups", "source", "checks", - "table", - "stalled", - "recheck", - "close" + "stalled" ] } ], @@ -248,10 +245,6 @@ "ended" ], "sequence": true - }, - { - "name": "remove", - "effect": "remove" } ] }, @@ -343,7 +336,7 @@ "config": { "refusal-stall-floor-minutes": { "type": "number", - "description": "minutes, at least, before a merge refused because its trunk moved, whose check the gate asked again, is said as stalled; twice the repository's last check time when that is longer (ADR 0505 §4).", + "description": "minutes, at least, before a merge refused because its trunk moved, whose check the gate asked again, is said as stalled; twice the repository's last check time when that is longer (ADR 0505 \u00a74).", "minimum": 1, "maximum": 1440, "trusted": false, @@ -351,7 +344,7 @@ }, "author-new-head-minutes": { "type": "number", - "description": "minutes a delivery that needs its author's new head (a conflict, a failed check, a rejected group) waits for one before the operator hears of it (ADR 0505 §5).", + "description": "minutes a delivery that needs its author's new head (a conflict, a failed check, a rejected group) waits for one before the operator hears of it (ADR 0505 \u00a75).", "minimum": 1, "maximum": 10080, "trusted": false,