Show and change the build queue through the controller, and have plans follow it (hq ADR 0219)

Nothing showed what waited for a build machine, and an ask could not be
dropped without leaving the plan that made it waiting for ever. New verbs:
queue, cancel, clear, rebuild, replay, kill, pause, resume, and plans retry.
Every ask a person drops is recorded failed through the same take-in as a
failed build; a plan keeps the id it asked each module under and matches
its outcome by it. replay is a dry run unless registered, and registering
an older commit than one registered since needs --older (hq issue 207).
A plan waiting on a seat paused on every holder says so and is not late;
a failed plan can be retried, and a rebuild joins the plan holding the
module instead of running beside it.
This commit is contained in:
jochen
2026-10-05 19:17:56 +02:00
parent e610f2d92c
commit 106507b1d3
13 changed files with 1772 additions and 58 deletions
+30
View File
@@ -3,8 +3,11 @@ package inventory
import (
"context"
"encoding/json"
"errors"
"sort"
"time"
"github.com/jackc/pgx/v5"
)
// What has been built.
@@ -161,6 +164,33 @@ func (i *Inventory) Builds(ctx context.Context, module string, limit int) ([]Bui
return out, rows.Err()
}
// BuildByID is one build's record, by the id its request carried — what a plan matches its outcome
// by when the outcome names no module (novox/hq ADR 0219), and what `rebuild` and `replay` read the
// repository, path, ref and commit from. False when no outcome with that id was recorded.
func (i *Inventory) BuildByID(ctx context.Context, id string) (Build, bool, error) {
var b Build
var made []byte
var asked *time.Time
err := i.store.Pool().QueryRow(ctx,
`select id, repository, ref, coalesce(module,''), commit_hash, built_on, failed, made,
coalesce(source_path,''), asked, at
from build where id = $1`, id).Scan(&b.ID, &b.Repository, &b.Ref, &b.Module, &b.Commit,
&b.On, &b.Failed, &made, &b.Path, &asked, &b.At)
if errors.Is(err, pgx.ErrNoRows) {
return Build{}, false, nil
}
if err != nil {
return Build{}, false, err
}
if asked != nil {
b.Asked = *asked
}
if err := json.Unmarshal(made, &b.Made); err != nil {
return Build{}, false, err
}
return b, true, nil
}
// Held is every artifact this mesh has built, keyed "<module>/<artifact>".
//
// **The successful build of each module asked last wins**, which is the same rule the rest of the
+5
View File
@@ -49,6 +49,11 @@ type PlanModule struct {
FirstAt *time.Time `json:"first_at,omitempty"`
Commit string `json:"commit,omitempty"`
Why string `json:"why,omitempty"`
// Build is the id of the build the plan asked for this module (novox/hq ADR 0219), so the plan
// matches its outcome by id — the one thing every outcome echoes, a failed one that never learnt
// its module's name included. Empty in a plan from before it was kept, which is matched by
// module, or by repository and path, as before.
Build string `json:"build,omitempty"`
}
// The states a plan passes through.