G §421, §683: the node-engine reads the entry its want names by fingerprint and keeps its entries and wants

The node's own composer reads the catalog half through meshcatalogclient's Entries cache over its own calls of
mesh-catalog's verb entry, and no longer watches or reads the indexer's modules, manifests and verdicts or the
builder's builds: a module's manifest is its entry's, each artifact's reference is made from the digest its want's
part names (artifact-store://<module>/<artifact>/blobs/<digest>, A-50). A want whose entry is asked or not found is
held, its have the phase held with a detail opening entry-asked: or entry-missing: naming the fingerprint. The
entries and wants it composes from are kept in composed-<node> (entry.<digits> and want.<module>), written before
the composition that uses them, read at start before anything is asked, a kept want standing until the wants'
live read answers, and dropped when nothing names them. The shadow keeps its reads (G §628).

The node grant gains the verb entry and the running stream's consumer calls; OverNATS hears the running stream
as the link's session does.

Builds: design 47 G §421, §683, §614, §628, A-47, A-50
This commit is contained in:
2026-11-11 23:55:50 +01:00
parent 2b916ac043
commit cf378f9a25
13 changed files with 849 additions and 16 deletions
+10 -1
View File
@@ -508,6 +508,9 @@ const NotComposedDetail = "the node cannot compose it, and keeps running what it
// composerMark says h is a mark NotComposed wrote: failed, its detail opening with NotComposedDetail.
func composerMark(h Have) bool {
if h.Phase == string(nodeengineclient.PhaseHeld) && heldForEntry(h.Detail) {
return true
}
return h.Phase == string(nodeengineclient.PhaseFailed) && strings.HasPrefix(h.Detail, NotComposedDetail)
}
@@ -651,8 +654,14 @@ func (a *Agent) NotComposedEach(ctx context.Context, why string, held map[string
a.mu.Lock()
a.notComposed[module] = mark
a.mu.Unlock()
phase, detail := string(nodeengineclient.PhaseFailed), NotComposedDetail+why
if heldForEntry(why) {
// The entry its want names is not held: the phase held, its detail opening entry-asked: or entry-missing: and
// naming the fingerprint (design 47 G section 421).
phase, detail = string(nodeengineclient.PhaseHeld), why
}
a.writeHave(ctx, Have{Module: module, FP: mark.prior.FP, Composed: mark.prior.Composed, Sealed: mark.prior.Sealed,
Phase: string(nodeengineclient.PhaseFailed), Detail: NotComposedDetail + why})
Phase: phase, Detail: detail})
}
}
+6
View File
@@ -1772,6 +1772,12 @@ func TestTheNodesOwnCompositionIsAppliedWhenItDiffersAndARecordMoveWaitsForAYes(
if h := b.haves.last("web"); h.Phase != string(nodeengineclient.PhaseRunning) || h.Composed != dig {
t.Errorf("web, which composes, is marked beside idle: %+v", h)
}
// Design 47 G §421: a module whose want's entry the catalog half holds none of is the phase held, its detail opening
// entry-missing: and naming the fingerprint; composed again, its have is what it runs.
b.ag.NotComposedEach(context.Background(), "", map[string]string{"idle": EntryMissing + " the catalog half holds no entry of the build src2:x"})
if h := b.haves.last("idle"); h.Phase != string(nodeengineclient.PhaseHeld) || h.Composed != idleDig || h.Detail != EntryMissing+" the catalog half holds no entry of the build src2:x" {
t.Errorf("idle's have while its entry is missing: %+v", h)
}
b.ag.NotComposedEach(context.Background(), "", nil)
if h := b.haves.last("idle"); h.Phase != string(nodeengineclient.PhaseRunning) || h.Composed != idleDig {
t.Errorf("idle's have once it composes again: %+v", h)
+13
View File
@@ -13,6 +13,19 @@ import (
// disagree (design 47 G section 524).
const PartsDiffer = "parts-differ:"
// EntryAsked and EntryMissing open the detail of a have held for the entry the want names (design 47 G section 421 and
// section 614): the entry is asked of the catalog half and not yet answered, or the half answered that it holds none. Both
// are the phase held, and each names the fingerprint.
const (
EntryAsked = "entry-asked:"
EntryMissing = "entry-missing:"
)
// heldForEntry says whether why is a hold for the entry a want names.
func heldForEntry(why string) bool {
return strings.HasPrefix(why, EntryAsked) || strings.HasPrefix(why, EntryMissing)
}
// partsDifference checks a want's `parts` against the digests of the archive, process and container resources of the
// composition, which are the digests the apply fetches and checks (design 47 G section 524). A want with no `parts` (an
// assigner of before G, or a node whose record does not state `want-parts`) is applied as today: it returns "". With