Say a build source only for the trunk's head, and hold what C, assembly and a new go.mod reach (hq ADR 0267, review)
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request

A hand build of an older trunk commit said a closure lacking what was
imported since, and the planner would have mapped the next merge onto it.
C and assembly beside Go may include files below their directory, and a
go.mod made above a package moves it out of its module: each is now held.
This commit is contained in:
jochen
2026-10-10 03:11:06 +02:00
parent 4d1b81b6cb
commit abe5f7dc17
5 changed files with 146 additions and 15 deletions
+31 -5
View File
@@ -320,10 +320,11 @@ func build(ctx context.Context, run Runner, publish Publisher,
if fingerprint == "" {
say("source", "no source fingerprint: %s", orNoTree(src.unpinned))
}
// **Only a trunk build says its build source** (novox/hq ADR 0267): the planner maps the next merge onto
// the build source of the trunk's last build, and a branch's closure is not the trunk's.
// **Only a build of the trunk's head says its build source** (novox/hq ADR 0267): the planner maps the
// next merge onto the build source of the newest build, and a branch's closure — or an older trunk
// commit's, built by hand — is not the trunk's. Nor does a build whose context was not its trunk's head.
var sources []BuildSource
if trunk != "" && onTrunk {
if trunk != "" && onTrunk && src.contextsAtHead && atTrunkHead(ctx, run, tree, commit, trunk) {
sources = src.buildSources()
for _, s := range sources {
where := "its own repository"
@@ -380,6 +381,28 @@ func trunkOf(ctx context.Context, run Runner, clone, commit string) (string, boo
return trunk, err == nil
}
// atTrunkHead is whether a clone's commit is its trunk's head as the clone holds it.
func atTrunkHead(ctx context.Context, run Runner, clone, commit, trunk string) bool {
head, err := run(ctx, clone, "git", "rev-parse", "origin/"+trunk)
if err != nil {
return false
}
at, err := run(ctx, clone, "git", "rev-parse", commit)
if err != nil {
return false
}
return strings.TrimSpace(head) != "" && strings.TrimSpace(head) == strings.TrimSpace(at)
}
// cleanEntry is a path of the module's directory as an entry: cleaned, relative, `.` for the directory.
func cleanEntry(p string) string {
c := strings.Trim(path.Clean("/"+filepath.ToSlash(p)), "/")
if c == "" {
return "."
}
return c
}
// orNoTree is why a build has no source fingerprint, for its log.
func orNoTree(why string) string {
if why == "" {
@@ -684,6 +707,9 @@ func one(ctx context.Context, run Runner, publish Publisher,
src.contexts[a.Name] = t
}
buildDir = cloned
if t, _ := trunkOf(ctx, run, cloned, "HEAD"); t == "" || !atTrunkHead(ctx, run, cloned, "HEAD", t) {
src.contextOffHead()
}
}
// docker build accepts -f outside the context it is given; the recipe stays exactly where it was
// read from and validated against, absolute so a context elsewhere does not change which file
@@ -722,7 +748,7 @@ func one(ctx context.Context, run Runner, publish Publisher,
src.readWhole(*a.Context)
}
if a.Compiles != "" || a.Context != nil {
src.ownHas(a.From)
src.ownHas(cleanEntry(a.From))
} else {
src.ownWhole()
}
@@ -844,7 +870,7 @@ func one(ctx context.Context, run Runner, publish Publisher,
return catalogue.Built{Name: a.Name, Kind: a.Kind, Reference: reference}, nil
case catalogue.ArtifactArchive:
src.ownHas(strings.Trim(path.Clean("/"+filepath.ToSlash(a.From)), "/") + "/**")
src.ownHas(cleanEntry(a.From) + "/**")
body, err := pack(filepath.Join(tree, a.From))
if err != nil {
return catalogue.Built{}, fmt.Errorf("%s: packing %s failed: %w", module, a.Name, err)