indexer, mesh-catalog contract: a branch named candidate or entry is refused as reserved branch name

Builds: design 47 G §704 (branch named candidate or entry)

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-11 22:46:36 +01:00
parent 0dec003660
commit b46da94f50
7 changed files with 36 additions and 23 deletions
@@ -205,7 +205,8 @@ func refused(module, reason, words string) meshcatalog.RegistrationAsked {
return meshcatalog.RegistrationAsked{Module: module, Refused: reason, Words: words}
}
// forbiddenBranch refuses the two names the catalog's keys use (§343).
// forbiddenBranch refuses the two names the catalog's keys use (§343); the refusal's reason is `reserved branch name`
// (design 47 G §704).
func forbiddenBranch(b string) string {
if b == "candidate" || b == "entry" {
return fmt.Sprintf("the branch %q cannot be watched: the catalog keeps its revisions under <branch>.<revision> beside the key forms candidate and entry", b)
@@ -218,24 +219,26 @@ func reviewRequired(s meshcatalog.StepStatuses) bool {
return s.Review == meshcatalog.StepStatusesApprovalRequired
}
// protect refuses a watched branch the forge does not protect (§5), naming it.
func (r *Registry) protect(repository string, branches []string) (string, error) {
// protect refuses a watched branch that is named as the catalog's keys are (reason `reserved branch name`, §704) or that
// the forge does not protect (reason `unprotected branch`, §5), naming it; both empty when every branch is accepted.
func (r *Registry) protect(repository string, branches []string) (reason, words string, err error) {
for _, b := range branches {
if w := forbiddenBranch(b); w != "" {
return w, nil
return meshcatalog.RegistrationAskedRefusedReservedBranchName, w, nil
}
a, err := r.Branch(repository, b)
if err != nil {
return "", fmt.Errorf("the seat git does not answer for %s %s: %w", repository, b, err)
return "", "", fmt.Errorf("the seat git does not answer for %s %s: %w", repository, b, err)
}
if a.Error != "" {
return "", fmt.Errorf("the seat git cannot read %s %s: %s", repository, b, a.Error)
return "", "", fmt.Errorf("the seat git cannot read %s %s: %s", repository, b, a.Error)
}
if !a.Protected {
return fmt.Sprintf("the branch %s of %s is not protected: no rule refuses a direct push and admits only merges", b, repository), nil
return meshcatalog.RegistrationAskedRefusedUnprotectedBranch,
fmt.Sprintf("the branch %s of %s is not protected: no rule refuses a direct push and admits only merges", b, repository), nil
}
}
return "", nil
return "", "", nil
}
// stepLines names each step status a policy moves, one line each.
@@ -467,12 +470,12 @@ func (r *Registry) Propose(ctx context.Context, verb, caller string, change json
branches = []string{branch}
}
if len(branches) > 0 {
words, err := r.protect(next.Origin, branches)
reason, words, err := r.protect(next.Origin, branches)
if err != nil {
return meshcatalog.RegistrationAsked{}, err
}
if words != "" {
return refused(module, meshcatalog.RegistrationAskedRefusedUnprotectedBranch, words), nil
if reason != "" {
return refused(module, reason, words), nil
}
}
if len(lines) == 0 {
@@ -101,15 +101,12 @@ func TestRegistrationsAreWrittenOnTheirWarrantOnly(t *testing.T) {
}{
{"review", meshcatalog.RegistrationAskedRefusedReviewRequired, reg("a", meshcatalog.StepStatuses{Review: "required"}, "main")},
{"unprotected", meshcatalog.RegistrationAskedRefusedUnprotectedBranch, reg("a", meshcatalog.StepStatuses{}, "beta")},
{"candidate", meshcatalog.RegistrationAskedRefusedUnprotectedBranch, reg("a", meshcatalog.StepStatuses{}, "candidate")},
{"candidate", meshcatalog.RegistrationAskedRefusedReservedBranchName, reg("a", meshcatalog.StepStatuses{}, "candidate")},
{"entry", meshcatalog.RegistrationAskedRefusedReservedBranchName, reg("a", meshcatalog.StepStatuses{}, "entry")},
} {
if c.name == "candidate" {
// the name is refused whatever the forge says of it
c.why = meshcatalog.RegistrationAskedRefusedUnprotectedBranch
}
got, err := g.r.Propose(ctx, "register", "session@node", c.change)
if err != nil || got.Refused == "" || got.Ask != "" || got.Words == "" {
t.Fatalf("%s: answer %+v, %v: want a refusal naming why and no ask", c.name, got, err)
if err != nil || got.Refused == "" || got.Ask != "" || got.Words == "" || got.Refused != c.why {
t.Fatalf("%s: answer %+v, %v: want a refusal for %q naming why and no ask (design 47 G §704)", c.name, got, err, c.why)
}
}
if len(g.asks) != 0 {