Give each pending assignment its own condition, and keep a raised row until it clears
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-group group fix/assign-says-why-a-module-is-not-there delivering: 1 of 2 delivered
mesh/delivery superseded: a newer delivery to the same trunk took over its walk

Second review of #150: one key per machine and module let a newer failure
be cleared in the tick that raised it, pruning could orphan an open
condition, and a build no longer waited for read as never asked although it
may still run.
This commit is contained in:
jochen
2026-10-08 16:45:43 +02:00
parent 249d97d1c8
commit 8adb7f1a05
7 changed files with 367 additions and 35 deletions
+9 -4
View File
@@ -76,6 +76,11 @@ func (b *natsBuilds) Ask(ctx context.Context, request BuildRequest) error {
return nil
}
// ErrNotHandedOver is a Submit that failed before the build was handed to the build seat: nothing runs. Any
// other error from Submit came after: the build may still run, and its outcome may still come (novox/hq
// issue 325).
var ErrNotHandedOver = errors.New("the build was not handed over")
func (b *natsBuilds) Submit(ctx context.Context, request BuildRequest,
wait time.Duration) (BuildResult, error) {
@@ -84,23 +89,23 @@ func (b *natsBuilds) Submit(ctx context.Context, request BuildRequest,
// the same event on EVENTS, which the controller records.
outcomes, err := b.js.Conn().SubscribeSync(BuildOutcomeOf(b.role()))
if err != nil {
return BuildResult{}, fmt.Errorf("cannot listen for a build's outcome: %w", err)
return BuildResult{}, fmt.Errorf("%w: cannot listen for a build's outcome: %w", ErrNotHandedOver, err)
}
defer func() { _ = outcomes.Unsubscribe() }()
if err := b.js.Conn().Flush(); err != nil {
return BuildResult{}, err
return BuildResult{}, fmt.Errorf("%w: %w", ErrNotHandedOver, err)
}
body, err := json.Marshal(request)
if err != nil {
return BuildResult{}, err
return BuildResult{}, fmt.Errorf("%w: %w", ErrNotHandedOver, err)
}
// Into the role's work queue and awaited: work the bus never accepted must fail here rather than
// be assumed, because nothing else will ever say so.
publish, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if _, err := b.js.Context().Publish(BuildWorkOf(b.role()), body, nats.Context(publish)); err != nil {
return BuildResult{}, fmt.Errorf("cannot submit a build: %w", err)
return BuildResult{}, fmt.Errorf("%w: cannot submit a build: %w", ErrNotHandedOver, err)
}
waiting, cancelWait := context.WithTimeout(ctx, wait)