Accept a modules value with the new fingerprint alone, and say mesh/check is the required status (issue 1851, S7 and S16, ADR 0453 §6, ADR 0503 §3 §5)

FP and None stay readable for build-agent's path until R4g; the gate's status
comment and the checks verb now name mesh/check as what a main requires.
This commit is contained in:
2026-10-22 17:46:56 +02:00
parent 3b3cabd2b0
commit f7cd91e49d
4 changed files with 42 additions and 6 deletions
+2 -1
View File
@@ -42,7 +42,8 @@ func deliveryVerbs() []Verb {
"paths": "the files it changes, comma-separated, from the repository's root",
"base": "the branch it merges into (default main)"}, []string{"repository", "paths"})},
{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/merge-gate, mesh/repo-check, mesh/delivery, …) with its state, " +
"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, " +
"description and when it was set; the merge check's full verdict as the controller said it — each " +
"layer's summary, the machine that ran it, when, its build id and its report; and whether the branch's " +
"protection would let it merge, every required status being success.",
+3 -1
View File
@@ -29,7 +29,9 @@ const (
)
// The contexts of the statuses the mesh owes the forge: the gate's, the delivery's view of a head and of a group
// (design 47), and the checker's, which is not required until R4f (ADR 0484 clause 2, ADR 0453 clause 4).
// (design 47), and the checker's. From R4e the checker's mesh/check is the one status a repository's main requires,
// in place of mesh/merge-gate and mesh/repo-check (ADR 0503 clauses 3 and 5); a main that requires only
// mesh/repo-check (hq) keeps it, and the controller writes it for a repository the checker writes no status for.
const (
StatusContextMergeGate = "mesh/merge-gate"
StatusContextDelivery = "mesh/delivery"
+32
View File
@@ -0,0 +1,32 @@
package delivery
import (
"strings"
"testing"
)
// From R4e a modules value needs no old form (novox/hq ADR 0453 clause 6; issue 1851): a value with only the new
// fingerprint, or only the reason it has none, is written and read; an old value with fp or none still reads; a value
// with no fingerprint of either form and no reason is still refused.
func TestAModulesValueNeedsNoOldForm(t *testing.T) {
fp2 := SourcePrefix2 + strings.Repeat("a", 64)
for _, m := range []Module{
{FP2: fp2, Repository: "a/b", Commit: "c"},
{None2: "no lock", Repository: "a/b", Commit: "c"},
} {
data, err := EncodeModule(m)
if err != nil {
t.Fatalf("%+v was refused: %v", m, err)
}
back, err := DecodeModule(data)
if err != nil || back.FP2 != m.FP2 || back.None2 != m.None2 || back.FP != "" || back.None != "" {
t.Fatalf("decoded %+v, %v", back, err)
}
}
if _, err := DecodeModule([]byte(`{"v":1,"fp":"src1:x","repository":"a/b","commit":"c"}`)); err != nil {
t.Fatalf("an old value no longer reads: %v", err)
}
if _, err := EncodeModule(Module{Repository: "a/b", Commit: "c"}); err == nil {
t.Fatal("a value with no fingerprint and no reason was accepted")
}
}
+5 -4
View File
@@ -370,12 +370,13 @@ func (c Candidate) check() error {
// Module is the value of a modules key: the module's source fingerprint, or none and why (a build that resolves
// packages when built gets none, said here), and where it was computed: Repository and Commit are the trunk merge,
// Src the source revision then (0 before the gate writes source.main). Exactly one of FP and None is set.
// Src the source revision then (0 before the gate writes source.main). At most one of FP and None is set.
//
// From R4c the indexer writes the new form beside it (novox/hq ADR 0453 clause 6): FP2, the fingerprint of the new
// form (`src2:` and 64 hex digits), or None2, why it has none; one of them whenever either is, never both; and
// Manifest, the digest of the module's manifest, the key of the manifests bucket. A value written before has none of
// the three, and a reader of today's shape ignores them.
// the three, and a reader of today's shape ignores them. From R4e a value may carry the new form alone (ADR 0453 clause
// 6; issue 1851): one of FP, None, FP2 and None2 is set, and the old form stays readable.
type Module struct {
V int `json:"v"`
FP string `json:"fp,omitempty"`
@@ -443,8 +444,8 @@ func (m Module) check() error {
switch {
case m.FP != "" && m.None != "":
return fmt.Errorf("delivery: a module has both a fingerprint and a reason for none")
case m.FP == "" && m.None == "":
return fmt.Errorf("delivery: a module has neither a fingerprint nor a reason for none")
case m.FP == "" && m.None == "" && m.FP2 == "" && m.None2 == "":
return fmt.Errorf("delivery: a module has neither a fingerprint nor a reason for none, of either form")
case m.FP != "" && !strings.HasPrefix(m.FP, SourcePrefix):
return fmt.Errorf("delivery: the module's fingerprint %q does not start with %s", m.FP, SourcePrefix)
case m.FP2 != "" && m.None2 != "":