Let a composition not run say why, the assigner not running among it, and name the indexer's words for a removed module, so a check never waits for a composition nobody makes (issue 1297, B3d)

This commit is contained in:
2026-10-23 15:41:55 +02:00
parent f625d98713
commit 3e5d4c30ff
3 changed files with 22 additions and 2 deletions
+8 -2
View File
@@ -96,10 +96,16 @@ type LevelResult struct {
// The states of a Composition (ADR 0484 clause 6): the composition check is the assigner's, so until it delivers a
// verdict says CompositionNotRun, and a line never says the composition passed on that.
//
// A composition not run may say why in its Words: AssignerNotRunning when no instance of the assigner is online, so a
// check never waits for a composition nobody is making (issue 1297).
const (
CompositionNotRun = "not-run"
CompositionPass = "pass"
CompositionFail = "fail"
// AssignerNotRunning is the Words of a composition not run because no instance of the assigner is online.
AssignerNotRunning = "the assigner is not running"
)
// Composition is what the check of the machines' composition made of a verdict: its State (not-run, pass or fail), the
@@ -156,8 +162,8 @@ func (c *Composition) check() error {
}
switch c.State {
case CompositionNotRun:
if c.Words != "" || c.SDK != "" || len(c.Machines) > 0 {
return fmt.Errorf("delivery: the verdict's composition is not run and says nothing of words, the mesh-sdk commit or machines")
if c.SDK != "" || len(c.Machines) > 0 {
return fmt.Errorf("delivery: the verdict's composition is not run and names no mesh-sdk commit or machines")
}
case CompositionPass:
if c.SDK == "" {
+10
View File
@@ -262,6 +262,16 @@ func TestVerdictCarriesSkippedCompositionAndOverBound(t *testing.T) {
if _, err := EncodeVerdict(notRun); err != nil {
t.Errorf("a composition not yet run refused: %v", err)
}
// 1297: a composition not run says why when the assigner is not running, and round-trips with its words.
notRunning := passVerdict()
notRunning.Composition = &Composition{State: CompositionNotRun, Words: AssignerNotRunning}
data, err = EncodeVerdict(notRunning)
if err != nil {
t.Fatalf("a composition not run because the assigner is not running refused: %v", err)
}
if back, err := DecodeVerdict(data); err != nil || back.Composition.Words != AssignerNotRunning {
t.Errorf("its words came back as %+v, %v", back.Composition, err)
}
failed := passVerdict()
failed.State, failed.Failed = VerdictFail, FailedFirst{Machine: "ace", Words: "x"}
failed.Composition = &Composition{State: CompositionFail, Words: "path /etc/x is owned by two modules", SDK: failed.SDK, Machines: []string{"ace"}}
+4
View File
@@ -271,6 +271,10 @@ type CandidatePull struct {
Fps map[string]CandidateModule `json:"fps,omitempty"`
}
// CandidateNoneNoModule is the None2 of a candidate's module whose directory the merge preview names no manifest for:
// the change removes the module. The indexer writes it and the assigner reads it as a removal (issue 1297).
const CandidateNoneNoModule = "the merge preview names no manifest for the module's directory"
// CandidateModule is one touched module's new-form fingerprint FP2 (`src2:` and 64 hex digits), or None2, the reason
// it has none; exactly one of them. Manifest is the digest of the module's manifest as read at the preview.
type CandidateModule struct {