DLV-I17, I19 rulings: a seat has one trunk definition, and a pull request is read again with its head's manifest

A second repository's trunk defining a seat another trunk holds is refused and kept nowhere, with the finding
seat-defined-twice naming both repositories; indexer_seat_definitions_again for a pull request reads the head's own
generated-from, so a pull request changing that field is read right by hand too (the lead's rulings on A2i 3 and 4).

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-04 14:31:39 +01:00
parent f9e0e6544c
commit 1d31ef1e85
3 changed files with 127 additions and 18 deletions
@@ -129,9 +129,10 @@ func Tools(ask asker, now func() time.Time, read stdio.KeptState, counts func()
}},
{Name: index.SeatDefinitionsAgainTool,
Description: "Read a contracts module's seat definitions again now (design 26): its trunk's, at the commit the indexer " +
"holds the module at, or with pull and head a pull request's head, and keep them in the seat-definitions state as " +
"a merge or a pull request's push would (seat-definition-changed said where a digest moved; the trunk's " +
"seat-definition-unparsed finding written or cleared). For a read the forge did not answer. A hand act, " +
"holds the module at, or with pull and head a pull request's head (read with the head's own manifest, so its " +
"generated-from as the head has it), and keep them in the seat-definitions state as a merge or a pull request's " +
"push would (seat-definition-changed said where a digest moved; the trunk's seat-definition-unparsed and " +
"seat-defined-twice findings written or cleared). For a read the forge did not answer. A hand act, " +
"recorded through mesh-hand-acts before it is asked. indexer_seat_definition reads the result.",
Input: map[string]any{"type": "object", "required": []string{"module", "why"}, "properties": map[string]any{
"module": map[string]any{"type": "string", "description": "the contracts module, such as mesh-contracts"},
@@ -182,6 +182,42 @@ func TestTheControlRecords(t *testing.T) {
if n := len(said); n != 4 || said[2] != (SeatDefinitionChanged{Seat: "gone", Source: SourceTrunk, Repository: repo}) || said[3].Digest == "" {
t.Errorf("said %+v, want gone's removal and m3's git", said)
}
// Ruling (lead, on A2i's finding 4): another repository's trunk defining git is refused, the trunk's entry stands,
// nothing is said, and its finding names both repositories; its next merge without git clears the finding.
const other = "novox/other-contracts"
files["o1"] = map[string]string{"seats/index.json": `{"format":1,"seats":["git"]}`, "seats/git/seat.json": strings.Replace(git, "ADR 0111", "ADR 0300", 1)}
files["o2"] = map[string]string{"seats/index.json": `{"format":1,"seats":[]}`}
read := ix.ReadFile
ix.ReadFile = func(ctx context.Context, r, sha, p string) ([]byte, error) {
if r == other {
r = repo
}
return read(ctx, r, sha, p)
}
otherManifest := strings.Replace(manifest, `"mesh-contracts"`, `"other-contracts"`, 1)
mergedOther := func(merge string) {
t.Helper()
if err := ix.ApplySeatDefinitions(ctx, delivery.Forge{Kind: delivery.ForgeMerged, Repository: other, Number: 2, Merge: merge,
Manifests: map[string]string{delivery.RootPath: otherManifest}}); err != nil {
t.Fatal(err)
}
}
twice := health.FindingKey(health.ScopeModule, findingID("seat-defined-twice", "other-contracts"), SeatDefinedTwiceKind)
before := len(said)
mergedOther("o1")
if g, _ := entry("git.trunk"); g.Repository != repo || g.Commit != m3 || len(said) != before {
t.Errorf("a second repository's git replaced the trunk's entry or was said: %+v, said %+v", g, said[before:])
}
if v, ok := findings.vals[twice]; !ok || !strings.Contains(string(v), other) || !strings.Contains(string(v), repo) {
t.Errorf("finding %s = %s, want it naming both repositories", twice, v)
}
mergedOther("o2")
if _, ok := findings.vals[twice]; ok {
t.Error("the seat-defined-twice finding stands once the second repository defines git no more")
}
if g, ok := entry("git.trunk"); !ok || g.Repository != repo {
t.Errorf("the second repository's merge without git removed the first's entry: %+v", g)
}
// A pull request's head: its own entry, the trunk's answered unless it is named.
if err := ix.ApplySeatDefinitions(ctx, delivery.Forge{Kind: delivery.ForgePull, Action: delivery.PullUpdated, Repository: repo, Number: 9, Head: head,
Dirs: []string{delivery.RootPath}, Manifests: map[string]string{delivery.RootPath: manifest}}); err != nil {
@@ -225,6 +261,25 @@ func TestTheControlRecords(t *testing.T) {
if len(findings.vals) != 0 {
t.Errorf("findings %v stand after a clean build end", findings.vals)
}
// Ruling (lead, on A2i's finding 3): indexer_seat_definitions_again for a pull request reads the head's own manifest's
// generated-from: a trunk that names none, a head that names it, is read as the contracts module the head is.
ix.Modules, ix.Manifests = newMem(), newMem()
plain := `{"module":"mesh-contracts","version":"1"}`
kept, _ := delivery.EncodeManifest(delivery.Manifest{Module: "mesh-contracts", JSON: plain})
mkey, _ := delivery.ManifestsKey(delivery.ManifestDigest([]byte(plain)))
ix.Manifests.(*memStore).vals[mkey] = kept
mod, _ := delivery.EncodeModule(delivery.Module{FP2: "src2:" + strings.Repeat("a", 64), Manifest: delivery.ManifestDigest([]byte(plain)), Repository: repo, Commit: m3, Path: delivery.RootPath})
ix.Modules.(*memStore).vals["mesh-contracts"] = mod
files["h2"] = map[string]string{"module.json": manifest, "seats/index.json": `{"format":1,"seats":["git"]}`, "seats/git/seat.json": strings.Replace(git, "ADR 0111", "ADR 0400", 1)}
if r := ix.SeatDefinitionsAgain(ctx, "mesh-contracts", 9, "h2"); !r.Stored {
t.Errorf("again for the pull request's head = %+v, want it read with the head's generated-from", r)
}
if e, ok := entry(pk); !ok || e.Commit != "h2" {
t.Errorf("the pull request's entry after again = %+v (%v)", e, ok)
}
if r := ix.SeatDefinitionsAgain(ctx, "mesh-contracts", 0, ""); r.Stored || !strings.Contains(r.Answer, "trunk's manifest") {
t.Errorf("again for the trunk, whose manifest names no generated-from = %+v", r)
}
// The pull request closes: its entries and its out-of-step files go, said.
if err := ix.ApplySeatDefinitions(ctx, delivery.Forge{Kind: delivery.ForgePull, Action: delivery.PullClosed, Repository: repo, Number: 9, Head: head}); err != nil {
t.Fatal(err)
@@ -6,8 +6,10 @@ package index
// it with the format's one reader (mesh-sdk seatdef), and keeps it in its own state `seat-definitions`, keyed by seat
// and source, as its only writer. Only the trunk's entry is the seat's contract; a pull request's entries go when it
// closes or merges. A trunk definition that does not parse keeps the trunk's entry and writes the finding
// seat-definition-unparsed; generated types out of step on a trunk, said by the builder's build end, write the finding
// generated-types-out-of-step, cleared only by a clean build end of the same contracts module.
// seat-definition-unparsed; a trunk defining a seat another repository's trunk already defines is refused with the
// finding seat-defined-twice naming both (a seat has one definition); generated types out of step on a trunk, said by
// the builder's build end, write the finding generated-types-out-of-step, cleared only by a clean build end of the same
// contracts module.
import (
"context"
@@ -37,9 +39,11 @@ const (
// definition's digest changed (ADR 0608 §3).
SeatDefinitionChangedEvent = "seat-definition-changed"
SeatDefinitionsStream = "indexer-seat-definitions"
// The two findings (ADR 0608 §4, ADR 0564: the indexer writes findings, the condition keeper raises them).
// The findings (ADR 0608 §4, ADR 0564: the indexer writes findings, the condition keeper raises them).
SeatDefinitionUnparsedKind = "seat-definition-unparsed"
OutOfStepKind = "generated-types-out-of-step"
// SeatDefinedTwiceKind: a contracts module's trunk defines a seat another repository's trunk already defines.
SeatDefinedTwiceKind = "seat-defined-twice"
// The three tools (design 26).
SeatDefinitionTool = "indexer_seat_definition"
SeatDefinitionsAgainTool = "indexer_seat_definitions_again"
@@ -284,7 +288,8 @@ func NewSeatDefinitionsAgain(module string, pull int, head, why, by string, at t
const seatDefinitionsPrefix = "seat-definitions."
// SeatDefinitionsAgain reads a contracts module's seat definitions again now (indexer_seat_definitions_again): its
// trunk's at the commit the modules state holds it at, or a pull request's at the head given, with the trunk's manifest.
// trunk's at the commit the modules state holds it at, or a pull request's at the head given, with the head's own manifest
// (its generated-from as the head has it).
func (ix *Indexer) SeatDefinitionsAgain(ctx context.Context, module string, pull int, head string) TakenIn {
out := TakenIn{Module: module}
if ix.SeatDefinitions == nil || ix.ReadFile == nil || ix.Modules == nil {
@@ -315,9 +320,27 @@ func (ix *Indexer) SeatDefinitionsAgain(ctx context.Context, module string, pull
if dir == "" {
dir = delivery.RootPath
}
whose := "its trunk's"
if pull > 0 {
// A pull request is read with its head's own manifest, so a change of generated-from is read as the head has it.
file := "module.json"
if dir != delivery.RootPath {
file = path.Join(dir, file)
}
raw, err := ix.ReadFile(ctx, m.Repository, head, file)
switch {
case err != nil:
out.Answer = fmt.Sprintf("%s at %.12s cannot be read: %v", file, head, err)
return out
case raw == nil:
out.Answer = fmt.Sprintf("%s#%d's head %.12s holds no %s", m.Repository, pull, head, file)
return out
}
manifest, whose = string(raw), fmt.Sprintf("%s#%d's head's", m.Repository, pull)
}
cs := contractsModules(map[string]string{dir: manifest})
if len(cs) == 0 {
out.Answer = module + " is no contracts module: no package artifact of its trunk's manifest names generated-from"
out.Answer = module + " is no contracts module: no package artifact of " + whose + " manifest names generated-from"
return out
}
out.Repository, out.Commit, out.Path = m.Repository, m.Commit, m.Path
@@ -348,10 +371,16 @@ func (ix *Indexer) keepTrunk(ctx context.Context, repository, commit string, c c
}
ix.unparsedFinding(ctx, c.module, unparsed)
var errs []error
twice := map[string]string{} // seat -> the repository and module whose trunk holds it
for seat, d := range defs {
errs = append(errs, ix.putDefinition(ctx, SeatDefinitionEntry{Seat: seat, Source: SourceTrunk, Repository: repository,
Module: c.module, Directory: c.dir, Commit: commit}, d))
holder, err := ix.putDefinition(ctx, SeatDefinitionEntry{Seat: seat, Source: SourceTrunk, Repository: repository,
Module: c.module, Directory: c.dir, Commit: commit}, d)
if holder != "" {
twice[seat] = holder
}
errs = append(errs, err)
}
ix.twiceFinding(ctx, repository, c.module, twice)
// A seat the module's trunk no longer defines leaves the state.
keys, err := ix.SeatDefinitions.Keys(ctx)
if err != nil {
@@ -391,7 +420,8 @@ func (ix *Indexer) keepPull(ctx context.Context, repository string, pull int, he
for seat, d := range defs {
e := base
e.Seat = seat
errs = append(errs, ix.putDefinition(ctx, e, d))
_, err := ix.putDefinition(ctx, e, d)
errs = append(errs, err)
}
for seat, why := range unparsed {
e := base
@@ -410,18 +440,25 @@ func (ix *Indexer) keepPull(ctx context.Context, repository string, pull int, he
return errors.Join(errs...)
}
// putDefinition writes one parsed definition at its key, said when its digest moved.
func (ix *Indexer) putDefinition(ctx context.Context, e SeatDefinitionEntry, d seatdef.Definition) error {
// putDefinition writes one parsed definition at its key, said when its digest moved. A trunk's definition of a seat
// another repository's (or module's) trunk already defines is refused, written nowhere: a seat has one definition. The
// refused one answers who holds it ("<repository> (<module>)").
func (ix *Indexer) putDefinition(ctx context.Context, e SeatDefinitionEntry, d seatdef.Definition) (string, error) {
raw, digest, err := digestOf(d)
if err != nil {
return err
return "", err
}
e.Definition, e.Digest, e.At = raw, digest, ix.now().UTC()
moved := false
moved, holder := false, ""
err = write(ctx, ix.SeatDefinitions, SeatDefinitionKey(e.Seat, e.Source, e.Repository, e.Pull), func(value []byte, found bool) ([]byte, bool, error) {
var cur SeatDefinitionEntry
if found && json.Unmarshal(value, &cur) == nil && cur.Digest == digest && cur.Unparsed == "" {
moved = false
known := found && json.Unmarshal(value, &cur) == nil
moved, holder = false, ""
if known && e.Source == SourceTrunk && (cur.Repository != e.Repository || cur.Module != e.Module) {
holder = cur.Repository + " (" + cur.Module + ")"
return nil, false, nil
}
if known && cur.Digest == digest && cur.Unparsed == "" {
return nil, false, nil
}
moved = true
@@ -431,7 +468,23 @@ func (ix *Indexer) putDefinition(ctx context.Context, e SeatDefinitionEntry, d s
if err == nil && moved {
ix.sayChanged(ctx, SeatDefinitionChanged{Seat: e.Seat, Source: e.Source, Repository: e.Repository, Pull: e.Pull, Digest: digest})
}
return err
return holder, err
}
// twiceFinding writes the contracts module's seat-defined-twice finding for the seats its trunk defines that another
// repository's trunk already holds, naming both, or clears it when there are none.
func (ix *Indexer) twiceFinding(ctx context.Context, repository, module string, twice map[string]string) {
var lines []string
for seat, holder := range twice {
lines = append(lines, fmt.Sprintf("%s: defined by %s (%s) and already by %s", seat, repository, module, holder))
}
slices.Sort(lines)
ix.finding(ctx, findingID("seat-defined-twice", module), SeatDefinedTwiceKind, len(lines) > 0,
fmt.Sprintf("%s's trunk defines seats another trunk already defines, refused: %s", module, strings.Join(lines, "; ")),
"A seat on "+module+"'s trunk is already defined by another repository",
"A seat has one definition: the one that stood first stays the seat's contract and "+module+"'s is kept nowhere. "+
"Remove the seat from one of the two repositories; the next merge of "+module+" clears this.",
"No seat on "+module+"'s trunk is defined by another repository.")
}
// dropPull removes a pull request's entries and its out-of-step files.