Let a walk wait for its delivery's word, and serve the delivery's owner (hq ADR 0239)
mesh/merge-gate pass: every machine composes with the change as it did without (0 of 4 compose)
mesh/delivery delivered
mesh/delivery-group group feat/mesh-delivery delivered: every member is delivered

While the mesh-delivery seat has a holder on record, a merge that moves no
core module opens its walk and asks nothing until mesh-delivery or a person
says go; nothing of it is registered before its turn, so no other send
carries it. The controller keeps the planner, the gate, sending and the
walk, and gains the verbs the owner asks with: delivery-plan, -order,
-check (a group composed as one future state), deliver, delivery-stop,
delivery-walks; every walk kept is said as plan-moved.
This commit is contained in:
jochen
2026-10-07 00:01:42 +02:00
parent ba26ba2772
commit 487aa040de
29 changed files with 1562 additions and 39 deletions
+62 -12
View File
@@ -61,12 +61,43 @@ type mergeCheckInput struct {
repository, tree string
// changed are the paths the change touches, for the width of its rebuild.
changed []string
// group are the other heads of a delivery group composed with this one (novox/hq ADR 0239), each laid
// over the mesh after the one before: the group judged as one future state.
group []groupChange
// admin is a PostgreSQL the gate may create and drop databases in.
admin string
// say is where progress goes; the verdict is returned.
say io.Writer
}
// groupChange is one other head of a delivery group: its repository, its checkout and what it changes.
type groupChange struct {
Repository string `json:"repository"`
Tree string `json:"tree"`
Changed []string `json:"changed,omitempty"`
}
// readGroup reads the other heads of a group's composed check from the file the build seat wrote.
func readGroup(path string) ([]groupChange, error) {
if path == "" {
return nil, nil
}
raw, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var g []groupChange
if err := json.Unmarshal(raw, &g); err != nil {
return nil, fmt.Errorf("the group's heads are not readable: %w", err)
}
for _, c := range g {
if c.Repository == "" || c.Tree == "" {
return nil, errors.New("a head of the group names no repository or no checkout")
}
}
return g, nil
}
// mergeVerdict is what the gate found.
type mergeVerdict struct {
Verdict string `json:"verdict"` // pass, warning or fail
@@ -129,6 +160,8 @@ func mergeGateCommand(ctx context.Context, args []string) error {
repository := set.String("repository", "", "owner/repository of the change")
tree := set.String("tree", "", "the change's checkout: every module.json in it replaces the mesh's")
changed := set.String("changed", "", "the paths the change touches, comma-separated, for its rebuild's width")
group := set.String("group", "", "a delivery group's other heads, as JSON [{repository, tree, changed}], composed "+
"with this one as one future state (novox/hq ADR 0239)")
asJSON := set.Bool("json", false, "print the verdict as JSON")
if _, err := parseAround(set, args); err != nil {
return err
@@ -144,12 +177,16 @@ func mergeGateCommand(ctx context.Context, args []string) error {
if err != nil {
return err
}
others, err := readGroup(*group)
if err != nil {
return err
}
out := io.Writer(os.Stdout)
if *asJSON {
out = os.Stderr
}
v, err := judgeChange(ctx, mergeCheckInput{facts: f, repository: *repository, tree: *tree, changed: splitList(*changed),
admin: *admin, say: out})
group: others, admin: *admin, say: out})
if err != nil {
return err
}
@@ -212,24 +249,37 @@ func judgeChange(ctx context.Context, in mergeCheckInput) (mergeVerdict, error)
// which may differ from what the mesh holds for reasons that are not this change's (a module pinned
// at an older commit, a main the mesh has not built yet).
var reach *mergeReach
if in.repository != "" && len(in.changed) > 0 {
r, err := reachOfChange(in.facts, in.repository, in.changed, in.tree)
if err != nil {
v.Notes = append(v.Notes, "what a merge of the change would rebuild could not be worked out, so every "+
"definition in its tree is judged: "+err.Error())
} else {
reach = &r
// The change, then each other head of its delivery group laid over it in turn (novox/hq ADR 0239): one
// future state of the mesh, judged as the group would leave it.
heads := append([]groupChange{{Repository: in.repository, Tree: in.tree, Changed: in.changed}}, in.group...)
for i, h := range heads {
var r *mergeReach
if h.Repository != "" && len(h.Changed) > 0 {
got, err := reachOfChange(in.facts, h.Repository, h.Changed, h.Tree)
if err != nil {
v.Notes = append(v.Notes, "what a merge of "+h.Repository+" would rebuild could not be worked out, so "+
"every definition in its tree is judged: "+err.Error())
} else {
r = &got
}
}
if i == 0 {
reach = r
}
if h.Tree == "" {
continue
}
}
if in.tree != "" {
var failures []string
change, failures, err = shelfWithChange(base, sources, in.facts, in.repository, in.tree, v.Modules,
scopeOfReach(reach, in.repository))
change, failures, err = shelfWithChange(change, sources, in.facts, h.Repository, h.Tree, v.Modules,
scopeOfReach(r, h.Repository))
if err != nil {
return v, err
}
v.Failures = append(v.Failures, failures...)
}
if len(in.group) > 0 {
say("a delivery group: %d head(s) composed together", len(heads))
}
say("judging %d module(s) changed against %d machine(s), as the snapshot of %s says they run",
len(v.Modules), len(in.facts.Machines), in.facts.Taken.Format(time.RFC3339))