A build keeps what it was told
The builder announces a build with the resolved manifest, the path inside the repository, and every artifact it stood on. The control plane received all of it and kept none of it. That was survivable while the catalogue heard the same announcement directly. It stops being survivable the moment the catalogue was not there to hear it — which on a fresh mesh is always, and always for the same modules: the shared base, the store the catalogue runs on, and the catalogue itself are each necessarily built BEFORE the catalogue exists to hear about them. The graph's foundation is the part the graph never sees. Replaying those builds needs what they said, not a summary. Without the manifest there are no requires/provides edges; without `against` there are no build edges, which are the ones that answer "a base moved, what must be rebuilt". A replay carrying neither would restore the module list and leave the question the catalogue exists for still wrong, while looking fixed. Kept null rather than empty where a build predates this, so a replay can say it is holding nothing instead of inventing an empty declaration for a module that certainly had one. And `built_against`, not `built_on`: that column exists and means the machine, which is a different fact about a different subject. Toward novox/hq 04-ISSUES/050. Claude-Session: https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -23,6 +23,18 @@ type Build struct {
|
||||
Commit string
|
||||
// On is the machine that did it.
|
||||
On string
|
||||
// Path is where inside the repository the module lives (novox/hq ADR 0069).
|
||||
Path string
|
||||
// Manifest is the declaration the builder resolved, as it announced it.
|
||||
//
|
||||
// **Kept because the catalogue may not have been listening.** The announcement carries this
|
||||
// and the catalogue turns it into the module's requires/provides edges. On a fresh mesh the
|
||||
// modules built before the catalogue exists are exactly the ones it most needs, so the mesh
|
||||
// has to be able to say afterwards what they declared (novox/hq 04-ISSUES/050).
|
||||
Manifest []byte
|
||||
// Against is every artifact this build stood on, as references rather than module names —
|
||||
// what makes a build edge derived rather than declared (ADR 0009).
|
||||
Against []string
|
||||
// Failed is the builder's own words, empty when it worked.
|
||||
Failed string
|
||||
Made []Artifact
|
||||
@@ -49,15 +61,21 @@ func (i *Inventory) RecordBuild(ctx context.Context, b Build) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
against, err := json.Marshal(b.Against)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var module *string
|
||||
if b.Module != "" {
|
||||
module = &b.Module
|
||||
}
|
||||
_, err = i.store.Pool().Exec(ctx,
|
||||
`insert into build (id, repository, ref, module, commit_hash, built_on, failed, made)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
`insert into build (id, repository, ref, module, commit_hash, built_on, failed, made,
|
||||
source_path, manifest, built_against)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
on conflict (id) do nothing`,
|
||||
b.ID, b.Repository, b.Ref, module, b.Commit, b.On, b.Failed, made)
|
||||
b.ID, b.Repository, b.Ref, module, b.Commit, b.On, b.Failed, made,
|
||||
b.Path, manifestOrNil(b.Manifest), against)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -144,3 +162,15 @@ func (i *Inventory) Held(ctx context.Context) (map[string]string, error) {
|
||||
}
|
||||
return held, rows.Err()
|
||||
}
|
||||
|
||||
// manifestOrNil keeps the difference between "declared nothing" and "predates this being kept".
|
||||
//
|
||||
// A build recorded before the mesh kept manifests has no manifest, and that is not the same as one
|
||||
// whose manifest was empty. A replay can then say which it is holding instead of inventing an
|
||||
// empty declaration for a module that certainly had one.
|
||||
func manifestOrNil(raw []byte) any {
|
||||
if len(raw) == 0 {
|
||||
return nil
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
-- What a build result carried and the mesh threw away.
|
||||
--
|
||||
-- novox/hq 04-ISSUES/050. The builder announces a build with the resolved manifest, the path
|
||||
-- inside the repository, and every artifact it was built against. The control plane receives all
|
||||
-- of it and kept none of it: `build` held the repository, the ref, the commit and what was made.
|
||||
--
|
||||
-- That was survivable while the catalogue heard the same announcement directly. It stops being
|
||||
-- survivable the moment the catalogue was not there to hear it — which on a fresh mesh is always,
|
||||
-- and always for the same modules. The shared base, the store the catalogue itself runs on, and
|
||||
-- the catalogue: each is necessarily built BEFORE the catalogue exists to hear about it, so the
|
||||
-- graph's foundation is the part the graph never sees.
|
||||
--
|
||||
-- Replaying those builds needs what they said, not a summary of it. Without the manifest there
|
||||
-- are no requires/provides edges; without `against` there are no build edges, which are the ones
|
||||
-- that answer "a base moved, what must be rebuilt". A replay carrying neither would restore the
|
||||
-- module list and leave the question the catalogue exists for still wrong, while looking fixed.
|
||||
--
|
||||
-- Empty and null-free, so every build recorded before this keeps exactly the meaning it had: a
|
||||
-- row with no manifest is one that predates this, and a replay says so rather than inventing an
|
||||
-- empty declaration.
|
||||
--
|
||||
-- `built_against` rather than `built_on`: that column already exists and means the MACHINE that
|
||||
-- did the build, which is a different fact about a different subject.
|
||||
alter table build add column source_path text not null default '';
|
||||
alter table build add column manifest jsonb;
|
||||
alter table build add column built_against jsonb;
|
||||
Reference in New Issue
Block a user