diff --git a/go/catalogue/delivery_seat.go b/go/catalogue/delivery_seat.go index 7df4d86..9296f5e 100644 --- a/go/catalogue/delivery_seat.go +++ b/go/catalogue/delivery_seat.go @@ -9,7 +9,26 @@ package catalogue // DeliverySeat is the seat mesh-delivery holds. const DeliverySeat = "mesh-delivery" -// deliveryVerbs are the delivery seat's tools: six that read, and the acts of a person, a reviewer and healer H2. +// RemovedDeliveryVerbs are the delivery seat's verbs release 6 removed (novox/hq ADR 0539 clause 17): with no plan the +// gate keeps the merge's states and derives nothing after the merge. Each stays in deliveryVerbs as an optional row +// with no input, so the holder of before and the holder of release 6 both hold the seat and a caller is answered by +// name with its replacement, given here. +var RemovedDeliveryVerbs = map[string]string{ + "times": "the delivery view (nox-delivery): each machine's want and have, and since when", + "what-if": "the merge check's composition, which composes every machine a change touches", + "release": "the assigner's asks (hold, restore, backup, record), answered on the phone or in the delivery view", + "stop": "a bad fingerprint is held and its canary set back by the assigner (ADR 0429 §6, ADR 0539 clause 18)", + "retire-history": "nothing to retire: the gate keeps no delivery after its merge", +} + +// deliveryRemovedInR6 is the row of a verb release 6 removed from the delivery seat. +func deliveryRemovedInR6(name string) Verb { + return Verb{Name: name, Description: "Removed in R6 (ADR 0539): refused by name, naming its replacement: " + + RemovedDeliveryVerbs[name], Input: schema(nil, nil), Optional: true} +} + +// deliveryVerbs are the delivery seat's tools: what reads the merges and checks, and the acts of a person and healer H2 +// on a merge (ADR 0539 clause 17). func deliveryVerbs() []Verb { return []Verb{ {Name: "deliveries", Description: "Every delivery not final, and those that ended in the last day, one line " + @@ -18,15 +37,7 @@ func deliveryVerbs() []Verb { Input: schema(map[string]string{"state": "one state, e.g. delivering or held", "repository": "owner/repository", "group": "a group's id (its branch name)", "all": "\"true\": the final ones of the last thirty days too"}, nil, "all")}, - // Delivery time against the delivery budgets (novox/hq ADR 0282): what probe D16 reads, optional until its - // holder serves it. - {Name: "times", Description: "Delivery time: from a merge to every machine of its walk running the build. " + - "Each class's delivery budget (a leaf module three minutes, a core module ten), the last days' median and " + - "worst, how many within and over, the newest delivery of each class, every delivery over its budget with its " + - "longest phase, and the delivered ones whose time is not known. Given a delivery's id, its time phase by phase.", - Input: schema(map[string]string{"days": "how many days back (default 7)", "id": "one delivery's id: its phases"}, nil), - // Optional until mesh-delivery serves it: its holder lives in the catalogue (design 33 §7). - Optional: true}, + deliveryRemovedInR6("times"), {Name: "show", Description: "One delivery or group whole: its delivery plan (what it builds, what each " + "machine receives, what is not an ordinary send), every transition with when and why, the machine " + "steps of its walk, its group and its order.", @@ -35,12 +46,7 @@ func deliveryVerbs() []Verb { "(declared, built by, version skew, engine before controller, by name), its composed check and its " + "state, derived from its members.", Input: schema(map[string]string{"all": "\"true\": the groups that ended too"}, nil, "all")}, - {Name: "what-if", Description: "The delivery plan a change would have, asked of the controller's planner " + - "and kept nowhere: the modules it moves and their dependents in tiers, what each machine would receive, " + - "the steps that are not an ordinary send.", - Input: schema(map[string]string{"repository": "owner/repository", - "paths": "the files it changes, comma-separated, from the repository's root", - "base": "the branch it merges into (default main)"}, []string{"repository", "paths"})}, + deliveryRemovedInR6("what-if"), {Name: "checks", Description: "What the mesh's checks said of a pull request's head or of one commit: each " + "of the commit's mesh statuses (mesh/check, the one a main requires from R4e, hq's mesh/repo-check, " + "mesh/delivery, …) with its state, " + @@ -62,40 +68,13 @@ func deliveryVerbs() []Verb { "controller is asked for its check. With why.", Input: schema(map[string]string{"id": "the delivery's id", "why": "why, kept with the transition"}, []string{"id", "why"})}, - {Name: "release", Description: "A person's word that a held delivery goes on: it starts delivering. With why.", - Input: schema(map[string]string{"id": "the delivery's id", "why": "why, kept with the transition"}, - []string{"id", "why"})}, - {Name: "stop", Description: "Stop a delivery that is not final, with why; its walk is ended through the " + - "controller, and in a group every member after it is stopped too, naming it.", - Input: schema(map[string]string{"id": "the delivery's id", "why": "why, kept with the transition"}, - []string{"id", "why"})}, + deliveryRemovedInR6("release"), + deliveryRemovedInR6("stop"), {Name: "close", Description: "Healer H2's verb: take the one transition the table names for a delivery held " + "past its bound. Refused for any other.", Input: schema(map[string]string{"id": "the delivery's id", "why": "the condition it answers"}, []string{"id", "why"})}, - {Name: "retire-history", Description: "Retire held deliveries that are history, on a person's word, with " + - "why: each made from the forge's word for a merge on the trunk heard long after it happened — replayed " + - "when the holder first held the seat — with no walk of it open. Never released, delivered or put back: " + - "retired, final. One by id, refused when the rule does not take it; or every held one, of one " + - "repository when named, answering how many it retired and naming those it left held. dry \"true\" " + - "answers what it would do and changes nothing.", - Input: schema(map[string]string{"why": "why, kept with every transition", - "id": "one delivery's id; every held one when empty", - "repository": "owner/repository: only its held deliveries", - "dry": "\"true\": what it would retire, changing nothing"}, []string{"why"}, "dry"), - // Added after the seat's holder shipped (novox/hq issue 313): optional until mesh-delivery serves it. - Optional: true}, - {Name: "mark-ready", Description: "A reviewer's word that a delivery's head is approved: the holder puts the " + - "label mesh/ready on its pull request and then writes \"ready at \", the order the forge counts, " + - "as the mesh's own forge account, naming who reviewed it and why. Refused when the delivery's commit is " + - "no longer its pull request's head. mesh-delivery then merges it once its checks pass (novox/hq ADR 0364).", - Input: schema(map[string]string{"id": "the delivery's id (owner/repository@commit): the head reviewed", - "reviewed-by": "who reviewed it, as who@node", "why": "the review's verdict in a line"}, - []string{"id", "reviewed-by", "why"}), - // Kept one more release, optional (novox/hq issue 1022): mesh-delivery no longer serves it, but the manifest - // it still has on the mesh does until its catalogue member merges, and a seat refuses a holder that serves - // a verb it does not promise. The next release removes this entry. - Optional: true}, + deliveryRemovedInR6("retire-history"), } } diff --git a/go/catalogue/delivery_seat_test.go b/go/catalogue/delivery_seat_test.go index 7d3a619..046b93d 100644 --- a/go/catalogue/delivery_seat_test.go +++ b/go/catalogue/delivery_seat_test.go @@ -58,107 +58,3 @@ func TestTheDeliverySeatPromisesChecks(t *testing.T) { t.Fatalf("a holder without %s: %v", before[0], err) } } - -// The seat retires history on a person's word (novox/hq issue 313): `retire-history`, with why, one delivery by -// id or every held one, dry to see first. Added after the holder shipped, it is optional, so the holder that -// does not serve it yet still holds the seat, and one that does is not refused for a verb the seat lacks. -func TestTheDeliverySeatPromisesRetireHistoryOptionally(t *testing.T) { - seat, _ := SeatNamed(DeliverySeat) - var retire *Verb - for i := range seat.Serves { - if seat.Serves[i].Name == "retire-history" { - retire = &seat.Serves[i] - } - } - if retire == nil || !retire.Optional { - t.Fatalf("the delivery seat promises %v, retire-history optionally", VerbNames(seat.Serves)) - } - props, _ := retire.Input["properties"].(map[string]any) - for _, arg := range []string{"why", "id", "repository", "dry"} { - if _, has := props[arg]; !has { - t.Errorf("retire-history takes no %q", arg) - } - } - if req, _ := retire.Input["required"].([]string); !reflect.DeepEqual(req, []string{"why"}) { - t.Errorf("retire-history requires %v, wanted only why", req) - } - var without []string - for _, v := range VerbNames(seat.Serves) { - if v != "retire-history" { - without = append(without, v) - } - } - m := Manifest{Module: "mesh-delivery", Claims: []Claim{{Name: DeliverySeat, Scope: ScopeMesh, Serves: without}}} - if err := CanHold(m, seat); err != nil { - t.Fatalf("a holder without retire-history yet: %v", err) - } - m.Claims[0].Serves = VerbNames(seat.Serves) - if err := CanHold(m, seat); err != nil { - t.Fatalf("a holder serving retire-history: %v", err) - } -} - -// The seat says delivery times (novox/hq ADR 0282, issue 382): `times`, over some days or for one delivery. Added -// after the holder shipped, it is optional, so the holder that does not serve it yet still holds the seat, and one -// that does is not refused for a verb the seat lacks. -func TestTheDeliverySeatPromisesTimesOptionally(t *testing.T) { - seat, _ := SeatNamed(DeliverySeat) - var times *Verb - for i := range seat.Serves { - if seat.Serves[i].Name == "times" { - times = &seat.Serves[i] - } - } - if times == nil || !times.Optional { - t.Fatalf("the delivery seat promises %v, times optionally", VerbNames(seat.Serves)) - } - props, _ := times.Input["properties"].(map[string]any) - for _, arg := range []string{"days", "id"} { - if _, has := props[arg]; !has { - t.Errorf("times takes no %q", arg) - } - } - var without []string - for _, v := range VerbNames(seat.Serves) { - if v != "times" { - without = append(without, v) - } - } - m := Manifest{Module: "mesh-delivery", Claims: []Claim{{Name: DeliverySeat, Scope: ScopeMesh, Serves: without}}} - if err := CanHold(m, seat); err != nil { - t.Fatalf("a holder without times yet: %v", err) - } - m.Claims[0].Serves = VerbNames(seat.Serves) - if err := CanHold(m, seat); err != nil { - t.Fatalf("a holder serving times: %v", err) - } -} - -// The seat still promises `mark-ready`, optionally, for one more release (issue 1022); it was added by novox/hq issue 913: `mark-ready`, with the delivery's id, who reviewed it -// and why, so a reviewer marks a pull request through one audited verb, label and comment in the order the forge -// counts, instead of two raw forge calls. Added after the holder shipped, it is optional. -func TestTheDeliverySeatPromisesMarkReadyOptionally(t *testing.T) { - seat, _ := SeatNamed(DeliverySeat) - var mark *Verb - for i := range seat.Serves { - if seat.Serves[i].Name == "mark-ready" { - mark = &seat.Serves[i] - } - } - if mark == nil || !mark.Optional { - t.Fatalf("the delivery seat promises %v, mark-ready optionally", VerbNames(seat.Serves)) - } - if req, _ := mark.Input["required"].([]string); !reflect.DeepEqual(req, []string{"id", "reviewed-by", "why"}) { - t.Errorf("mark-ready requires %v, wanted id, reviewed-by and why", req) - } - var without []string - for _, v := range VerbNames(seat.Serves) { - if v != "mark-ready" { - without = append(without, v) - } - } - m := Manifest{Module: "mesh-delivery", Claims: []Claim{{Name: DeliverySeat, Scope: ScopeMesh, Serves: without}}} - if err := CanHold(m, seat); err != nil { - t.Fatalf("a holder without mark-ready yet: %v", err) - } -} diff --git a/go/catalogue/verbs.go b/go/catalogue/verbs.go index aad0f6d..2218d95 100644 --- a/go/catalogue/verbs.go +++ b/go/catalogue/verbs.go @@ -74,6 +74,29 @@ func VerbNames(verbs []Verb) []string { return out } +// RemovedControllerVerbs are the controller seat's verbs release 6 removed (novox/hq ADR 0539, ADR 0429 §8): no +// delivery plan, batch, tier or push exists, so nothing starts, stops, retries, follows or reads one. Each stays in +// ControllerVerbs as a row with no input, so a caller is answered by name with its replacement, given here. +var RemovedControllerVerbs = map[string]string{ + "plans": "the delivery view (nox-delivery): each machine's want and have, read from the assigner's wants and the " + + "node-engine's haves", + "plan": "the node's compositions: mesh-controller_composed holds each module's digest and judgement per node, and " + + "mesh-compose prints one module's composition for one node", + "push": "a want: the assigner writes want.. by its rules, and by hand the operator's credential writes " + + "it, naming a kept composition (ADR 0539 clause 10)", + "deliver": "nothing to start: a merged change reaches each machine by its want", + "delivery-stop": "a bad fingerprint is held and its canary set back by the assigner (ADR 0429 §6, ADR 0539 clause 18)", + "delivery-backups-again": "the assigner's backup ask, answered on the phone or in the delivery view (ADR 0539 clause 16)", + "delivery-walks": "the delivery view (nox-delivery) and the wants, haves and compositions on the bus", + "delivery-order": "no order to keep: each module reaches its machines by its own want, its providers first by rule 5", +} + +// removedInR6 is the row of a verb release 6 removed from the controller's seat: no input, refused by name. +func removedInR6(name string) Verb { + return Verb{Name: name, Description: "Removed in R6 (ADR 0539): refused by name, naming its replacement: " + + RemovedControllerVerbs[name], Input: schema(nil, nil)} +} + // ControllerSeatName is the seat the control plane holds, whose tools are the mesh's own verbs. const ControllerSeatName = "mesh-controller" @@ -115,87 +138,23 @@ var ControllerVerbs = []Verb{ "log": "a build's id (as `builds` lists it): print what the build machine said, line by line", "limit": "how many builds to list (default 20); not with log", }, nil)}, - {Name: "plans", Description: "What the last merges produced and where each stands (novox/hq ADR 0162): " + - "the tiers, the tier a plan is at, what it waits for and since when; one plan whole, given its id; " + - "or, given wait, one plan followed until it moves.", - Input: schema(map[string]string{ - "id": "a plan's id (as `plans` lists them): that plan, tier by tier", - "wait": "a plan's id: wait until it moves, then answer it as data — the plan, and what ended the wait " + - "(moved, tier, done, or bound); ask again to follow it further (novox/hq issue 229)", - "until": "with wait: moved (any change, the default), tier (it reaches another tier) or done (it ends)", - "bound": "with wait: how long to wait at most, as 15s or seconds — 20s, the default, is the longest, " + - "inside what a call is answered within", - "stop": "a plan's id: stop it — what was asked still builds, nothing further is asked", - "close": "a plan's id: close a plan that will not move again, as failed by hand (novox/hq issue 254)", - "retry": "a failed plan's id: ask its failed builds again under new ids, and carry the plan on from that tier (novox/hq ADR 0219)", - "go": "a walk's id that waits for its delivery's word: start it by hand, in place of mesh-delivery — with why, " + - "recorded in the hand-act log (novox/hq ADR 0239)", - "repository": "owner/repository: the plan a merge there would produce, saving nothing (what-if); with paths or modules", - "paths": "with repository: the files the merge would change, comma-separated, from the repository's root", - "modules": "with repository: or the modules it would change, comma-separated", - "module-dirs": "with repository and paths: the directories holding a module.json at the commit, comma-separated, " + - "as the forge's announcer says them (novox/hq issue 278); without it, a directory is a module only when the paths hold its manifest", - "data": "with repository and paths: true answers the change plan as data, the document delivery-plan answers " + - "(moved, dependents, new, tiers, machines with what each receives and waits for, steps, summary, class, head, base); " + - "without it the answer is text (novox/hq ADR 0450 §2)", - "head": "with data: the commit at hand", - "base": "with data: the branch it merges into (default main)", - "removed": "with data: the files among paths the change deletes, comma-separated", - "limit": "how many plans to list (default 10); only when listing", - "why": "with stop, close or go: why it is done by hand — required, and recorded in the hand-act log (novox/hq to-be 45 §7)", - "cause": "with stop, close or go: the cause in a word, or a condition's kind (optional)", - }, nil, "data")}, + removedInR6("plans"), // The verbs the delivery's owner asks with (novox/hq ADR 0239): the planner's answer, a group's order and // composed check, and the start and end of a walk. Callable by a person too; mesh-delivery is who needs them. {Name: "delivery-plan", Description: "Removed in R4e (ADR 0501 §5 / ADR 0503 §3): refused by name, naming its replacement", Input: schema(nil, nil)}, - {Name: "delivery-order", Description: "A delivery group's order (novox/hq ADR 0419): its members in the order " + - "they are delivered, every pair and why — built by, verb before caller, version skew, engine before " + - "controller — read from the module graph and the manifests' change alone; no `after:` line is read. The " + - "cycle, with every module and edge of it, is the one refusal.", - Input: schema(map[string]string{"members": "the members, as JSON: [{id, repository, base, head, number, paths, " + - "module_dirs, module_dirs_said, removed}]"}, []string{"members"})}, + removedInR6("delivery-order"), {Name: "delivery-check", Description: "Answers that the checker (mesh-delivery-checker) judges a group (novox/hq " + "ADR 0503 §3); asks nothing. Its inputs stay, since hq's repository check still names it (ADR 0513 §4).", Input: schema(map[string]string{"group": "the group's id (none for one head checked again)", "members": "the members, as JSON, as delivery-order takes them", "ready": "\"true\" when the pull request is marked ready: its check is queued before every other check (novox/hq ADR 0406 §3)"}, []string{"members"})}, - {Name: "deliver", Description: "The delivery's word that a walk waiting for it may start (novox/hq ADR 0239): its " + - "first tier is asked at the controller's next pass. Refused for a walk that waits for nobody.", - Input: schema(map[string]string{"plan": "the walk's id", "why": "what lets it go: its turn, a person's release"}, - []string{"plan"})}, - {Name: "delivery-stop", Description: "End a walk on its delivery's word (novox/hq ADR 0239): failed, said as stopped " + - "by whom and why; what it asked still builds and registers, nothing further is asked or sent. Given merges to " + - "leave out, every other merge the walk answered is walked again — in the open batch of its kind, or a new one — " + - "without them, without each later merge of their repository, which contains one, and without each merge moving " + - "a module one moves, each left out too and named (novox/hq ADR 0299); a batch not yet cut drops the merges. The " + - "answer names the batch, the merges walked again, the merges left out, the modules the stopped walk sent that " + - "stay on their machines, and the merges elsewhere that will still deliver a stopped change.", - Input: schema(map[string]string{"plan": "the walk's id", "why": "why", "by": "who stopped the delivery", - "without": "merges to leave out, @, comma-separated"}, - []string{"plan", "why"})}, - {Name: "delivery-backups-again", Description: "Ask again the backups a walk was held for (novox/hq ADR 0404 " + - "decision 4): a walk whose backup before its send failed or did not answer is held, nothing asked or sent; this " + - "clears those failed backups and the next look at the plans asks them again, and only them. Refused for a walk " + - "with no failed backup. The delivery's release calls it. The answer names the walk and the backups asked again.", - Input: schema(map[string]string{"plan": "the held walk's id", "why": "a person's reason, as release passes it", - "by": "who released the delivery"}, []string{"plan", "why"})}, - {Name: "delivery-walks", Description: "The walks the controller keeps (novox/hq ADR 0239): every open one and the " + - "last ended ones, each whole — its tiers, each module's state, first machines and first-node gate with its readings, and its " + - "phases from its merge to every machine running it (novox/hq ADR 0282) — and whether the delivery seat has a " + - "holder on record. Given a plan, that one.", - Input: schema(map[string]string{"plan": "one walk's id", "limit": "how many ended walks beside the open ones (default 50)"}, - nil)}, - {Name: "plan", Description: "What one machine would run, and why: the declaration the mesh would send it — " + - "or, with files, the files it would be given; or, with diff, what a push would change there against what " + - "it was last sent: resources added, removed and changed, a container's changed fields (it is recreated), " + - "and any mount whose directory on the machine would move (novox/hq ADR 0217).", - Input: schema(map[string]string{ - "node": "the machine's name", - "files": "\"true\": the files this machine would be given, instead of the declaration as JSON", - "diff": "\"true\": what a push would change on this machine, against what it was last sent; not with files", - }, []string{"node"}, "files", "diff")}, + removedInR6("deliver"), + removedInR6("delivery-stop"), + removedInR6("delivery-backups-again"), + removedInR6("delivery-walks"), + removedInR6("plan"), {Name: "assign", Description: "Put a module on a machine. Refused with the mesh's own words when it cannot resolve there, " + "or when a seat its resources are applied through is held by nothing on the machine (novox/hq ADR 0207). " + "A module not registered yet is looked for (novox/hq issue 325, ADR 0261): while its build runs — a merge " + @@ -221,22 +180,7 @@ var ControllerVerbs = []Verb{ }, []string{"node", "provision", "from", "module"})}, {Name: "unpin", Description: "Take that choice back, putting the question to the mesh again.", Input: schema(map[string]string{"node": "the machine's name", "provision": "the provision"}, []string{"node", "provision"})}, - {Name: "push", Description: "Send one machine everything it should be. With no machine named it is a push of the " + - "WHOLE mesh — every machine that is behind — and the answer says so first; behind says that outright. " + - "Answers at once that it is running, with a call id: `calls` with that id says what it sent and, for a " + - "machine named, how that machine applied it — applied, refused with its reason, or each failed resource — " + - "once it reports within wait (a push can reload the bus, which then refuses any answer still to come). A push by hand is a repair, " + - "and says why: recorded in the hand-act log (novox/hq to-be 45 §7). A machine whose push would give a " + - "running module's data another directory is held and named, the others sent (novox/hq ADR 0217): read " + - "`plan` with diff, and name the machine with move to send it.", - Input: schema(map[string]string{ - "node": "the machine's name; without it, every machine that is behind", - "move": "with node: the modules whose data may move with this push, comma-separated (optional)", - "wait": "with node: how long the call waits for the machine to report how it applied what it was sent, as \"90s\" or \"2m\"; \"0\" waits for nothing; without it, the controller's setting push-wait (optional)", - "behind": "\"true\": every machine that is behind, the whole mesh — the same as naming none, said outright; not with node", - "why": "why this is pushed by hand: recorded in the hand-act log", - "cause": "the cause in a word, or a condition's kind — the word a second push for the same reason uses (optional)", - }, []string{"why"}, "behind")}, + removedInR6("push"), {Name: "rotate", Description: "Replace a credential. A pair credential, by provision (and a consuming machine and module, " + "else every holder): both ends are re-sent together. Or a module's own secret, by machine, module and " + "name: made anew and the machine sent, so the module starts again on it — for a secret its definition " + @@ -457,26 +401,22 @@ var ControllerVerbs = []Verb{ "probe": "one probe's id (D14): its verdict alone, with every finding — the verdict shows at most twenty a probe", }, nil, "run", "probes", "signals")}, // How a module's new builds reach its machines, and the bus's planned step (novox/hq ADR 0236). - {Name: "upgrade", Description: "How each module's new builds reach its machines (novox/hq ADR 0236): rolled " + - "out one machine first and judged there at the gate, then the rest — or recorded, waiting for a person's " + - "push — with where that comes from (a person, the module, the bus, its irreplaceable data, the default) and " + - "why. With module, that one; with policy, a person's choice for it — roll-out, record (with why) or default " + - "to take the choice back. The bus is never rolled out.", + {Name: "upgrade", Description: "Whether each module's new builds wait for a person (novox/hq ADR 0236, ADR 0539 clause 14): " + + "for a module of policy record, the assigner writes no want naming a composition judged to recreate a container or move " + + "data until the operator answers its ask; with where the policy comes from (a person, the module, the bus, its " + + "irreplaceable data, the default) and why. With module, that one; with policy, a person's choice for it — roll-out, " + + "record (with why) or default to take the choice back. The bus is never moved by the assigner (ADR 0539 clause 15).", Input: schema(map[string]string{ - "module": "one module", - "policy": "with module: roll-out, record or default", - "together": "\"true\": with roll-out, every machine at once instead of one machine first", - "why": "with policy or release-backlog: why — required for record and for a release, kept", - "backlog": "\"true\": every build waiting for a gate, per machine — what a release plan would send", - "release-backlog": "\"true\": release what waits now, one machine at a time, each judged — after a release " + - "plan failed, the mesh waits for this; with why", - "cause": "with release-backlog: the cause in a word (optional)", - }, nil, "together", "backlog", "release-backlog")}, - {Name: "bus", Description: "The bus as a planned step (novox/hq to-be 45 §8, ADR 0236): what a bus upgrade " + - "would do — the bus's build on each machine against the one the mesh holds — and how the last step went. " + - "With upgrade, start one: a person's act with why, after the streams are snapshotted (snapshot-taken says " + - "where, while the mesh takes none itself), saying first whether it can be reverted; bus-maintenance is open " + - "while it runs and every stream, consumer and a round trip are checked after.", + "module": "one module", + "policy": "with module: roll-out, record or default", + "why": "with policy: why — required for record, kept", + }, nil)}, + {Name: "bus", Description: "The bus as a person's step (novox/hq to-be 45 §8, ADR 0236, ADR 0539 clause 15): what a bus " + + "upgrade would do — the bus's build on each machine against the one the mesh holds — and how the last step went. " + + "With upgrade, check one: a person's act with why, after the streams are snapshotted (snapshot-taken says where, " + + "while the mesh takes none itself), saying first whether it can be reverted; then it answers the want the operator " + + "writes with the operator's credential, want.., recorded — nothing is sent, and the assigner never " + + "moves the bus.", Input: schema(map[string]string{ "upgrade": "\"true\": start the bus's upgrade", "why": "with upgrade: why — required, recorded in the hand-act log", diff --git a/go/catalogue/verbs_r6_test.go b/go/catalogue/verbs_r6_test.go new file mode 100644 index 0000000..8e595db --- /dev/null +++ b/go/catalogue/verbs_r6_test.go @@ -0,0 +1,103 @@ +package catalogue + +import ( + "strings" + "testing" +) + +// 1943/AC4: the controller's seat no longer offers a verb that starts, stops, retries, follows or reads a delivery +// plan, nor a push: each is a row with no input whose description refuses it by name and names its replacement, and +// each is in the removed list the holder answers from. upgrade keeps its policy and drops the plan's backlog. +func TestTheControllerSeatHasNoPlanVerbs(t *testing.T) { + rows := map[string]Verb{} + for _, v := range ControllerVerbs { + rows[v.Name] = v + } + gone := []string{"plans", "plan", "push", "deliver", "delivery-stop", "delivery-backups-again", "delivery-walks", "delivery-order"} + for _, name := range gone { + v, ok := rows[name] + if !ok { + t.Errorf("%s is not in the table, so a caller is not answered by name", name) + continue + } + replacement, removed := RemovedControllerVerbs[name] + if !removed || replacement == "" { + t.Errorf("%s is not in the removed list with its replacement", name) + } + if !strings.HasPrefix(v.Description, "Removed in R6 (ADR 0539): refused by name") || !strings.Contains(v.Description, replacement) { + t.Errorf("%s = %q, want refused by name, naming %q", name, v.Description, replacement) + } + if props, _ := v.Input["properties"].(map[string]any); len(props) > 0 { + t.Errorf("%s still takes %v", name, props) + } + } + props, _ := rows["upgrade"].Input["properties"].(map[string]any) + for _, arg := range []string{"together", "backlog", "release-backlog", "cause"} { + if _, has := props[arg]; has { + t.Errorf("upgrade still takes %s, a plan's", arg) + } + } + for _, arg := range []string{"module", "policy", "why"} { + if _, has := props[arg]; !has { + t.Errorf("upgrade no longer takes %s", arg) + } + } + for _, word := range []string{"rolled", "gate", "backlog", "push"} { + if strings.Contains(rows["upgrade"].Description, word) { + t.Errorf("upgrade's description still speaks of %q: %q", word, rows["upgrade"].Description) + } + } + if !strings.Contains(rows["bus"].Description, "want") { + t.Errorf("bus does not say the operator writes the bus's want: %q", rows["bus"].Description) + } + for name := range RemovedControllerVerbs { + if v := rows[name]; !strings.HasPrefix(v.Description, "Removed in R6") { + t.Errorf("%s is in the removed list and its row says %q", name, v.Description) + } + } +} + +// 1943/AC4: the delivery seat keeps the merge's verbs and no longer offers times, what-if, release, stop or +// retire-history: each is a row with no input, optional so the holder of before and the holder of release 6 both hold +// the seat, refusing it by name; mark-ready, served by nobody since issue 1022, is gone. +func TestTheDeliverySeatHasNoPlanVerbs(t *testing.T) { + seat, ok := SeatNamed(DeliverySeat) + if !ok { + t.Fatal("the delivery seat is not in the set") + } + rows := map[string]Verb{} + for _, v := range seat.Serves { + rows[v.Name] = v + } + for _, name := range []string{"times", "what-if", "release", "stop", "retire-history"} { + v, ok := rows[name] + replacement, removed := RemovedDeliveryVerbs[name] + if !ok || !removed || replacement == "" { + t.Errorf("%s: in the table %v, in the removed list %v", name, ok, removed) + continue + } + if !strings.HasPrefix(v.Description, "Removed in R6 (ADR 0539): refused by name") || !strings.Contains(v.Description, replacement) || !v.Optional { + t.Errorf("%s = %q (optional %v), want an optional row refusing it by name", name, v.Description, v.Optional) + } + if props, _ := v.Input["properties"].(map[string]any); len(props) > 0 { + t.Errorf("%s still takes %v", name, props) + } + } + if _, ok := rows["mark-ready"]; ok { + t.Errorf("mark-ready is still promised") + } + for _, kept := range []string{"deliveries", "show", "groups", "checks", "table", "recheck", "close", "stalled"} { + if v, ok := rows[kept]; !ok || strings.HasPrefix(v.Description, "Removed") { + t.Errorf("%s is not kept (ADR 0539 clause 17)", kept) + } + } + gate := Manifest{Module: "mesh-delivery", Claims: []Claim{{Name: DeliverySeat, Scope: ScopeMesh, + Serves: []string{"deliveries", "show", "groups", "checks", "table", "stalled", "recheck", "close"}}}} + if err := CanHold(gate, seat); err != nil { + t.Errorf("the gate of release 6 cannot hold the seat: %v", err) + } + gate.Claims[0].Serves = append(gate.Claims[0].Serves, "times", "what-if", "release", "stop", "retire-history") + if err := CanHold(gate, seat); err != nil { + t.Errorf("the gate of release 5 cannot hold the seat: %v", err) + } +}