C4: the build modules ask the steps state with its objects store in requires; outputs goes
mesh/delivery-group group feat/47-G-join11 checking: 7 of 7 member(s) ready
mesh/delivery closed: a newer head of the same pull request
mesh/check checking 75d98dd9083a on 117f933dacf0 | novox/mesh-catalog#675 in group feat/47-G-join11

Builds: design 47 G A-254, A-313, A-314, §58, §547

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 22:29:28 +01:00
parent f6eb5d9efa
commit 75d98dd908
9 changed files with 37 additions and 38 deletions
@@ -151,14 +151,14 @@ func TestManifestRequiresForgeAccountKeepsKeysAndTakesFilesFromTemplates(t *test
// detected-steps: the state of the verb steps (design 47 G §326, §414), asked by the template state.
// kept-builds: the builds kept for the artifact store's holder (keptbuilds.go), by the template objects (W12.5).
kept := map[string]any{"name": "kept-builds", "storage": "object-store", "template": "objects", "options": map[string]any{"max-mib": float64(1024)}}
// outputs: a run's answer over what step-ended carries inline, by the template objects (design 47 G A-254).
outputs := map[string]any{"name": "outputs", "storage": "object-store", "template": "objects", "options": map[string]any{"max-mib": float64(256)}}
// outputs: a run's answer over what step-ended carries inline, in the steps state's objects store (design 47 G A-254, A-313).
outputs := map[string]any{"name": "steps", "storage": "key-value", "template": "state", "options": map[string]any{"ttl-seconds": float64(172800), "objects-max-mib": float64(256)}}
detected := map[string]any{"name": "detected-steps", "storage": "key-value", "template": "state", "options": map[string]any{"history": float64(1)}}
// The findings are asked in requires as a part that is no foundation part asks them (design 47 G §529, A-30).
findings := map[string]any{"name": "findings", "storage": "key-value", "template": "state"}
findingEvents := map[string]any{"name": "finding-events", "storage": "stream", "template": "events", "options": map[string]any{
"events": []any{"finding-written.*", "finding-cleared.*", "findings-stated.*"}, "max-age-days": float64(7), "max-mib": float64(16)}}
if want := []any{detected, kept, outputs, "artifact-store", "forge-account", "go-toolchain", "go-package-registry", "node-tools", findings, findingEvents}; !reflect.DeepEqual(m.Requires, want) {
if want := []any{outputs, detected, kept, "artifact-store", "forge-account", "go-toolchain", "go-package-registry", "node-tools", findings, findingEvents}; !reflect.DeepEqual(m.Requires, want) {
t.Errorf("requires = %v, want %v", m.Requires, want)
}
keeps := false
@@ -113,12 +113,12 @@ func TestManifestKeepsItsStatesClaimsItsBuildSeatAndDeclaresItsHealthTool(t *tes
for _, s := range m.State {
states = append(states, s.Name)
// A step's key is per step and dropped after its age, so the bucket cannot fill; a pause has no age.
if (s.Name == "steps") != (s.TTLSeconds > 0) {
t.Errorf("state %s has ttl-seconds %d: only steps has an age", s.Name, s.TTLSeconds)
if s.TTLSeconds > 0 {
t.Errorf("state %s has ttl-seconds %d: steps, the one with an age, is asked in requires (A-314)", s.Name, s.TTLSeconds)
}
}
slices.Sort(states)
if want := []string{"keys", "pauses", "steps", "toolchains"}; !slices.Equal(states, want) {
if want := []string{"keys", "pauses", "toolchains"}; !slices.Equal(states, want) {
t.Errorf("state = %v, want %v", states, want)
}
if len(m.Claims) != 1 || m.Claims[0].Name != Seat {
+9 -12
View File
@@ -15,10 +15,6 @@
"state": [
"toolchains",
"keys",
{
"name": "steps",
"ttl-seconds": 172800
},
"pauses"
],
"claims": [
@@ -89,6 +85,15 @@
"build_go_health"
],
"requires": [
{
"name": "steps",
"storage": "key-value",
"template": "state",
"options": {
"ttl-seconds": 172800,
"objects-max-mib": 256
}
},
{
"name": "detected-steps",
"storage": "key-value",
@@ -105,14 +110,6 @@
"max-mib": 1024
}
},
{
"name": "outputs",
"storage": "object-store",
"template": "objects",
"options": {
"max-mib": 256
}
},
"artifact-store",
"forge-account",
"go-toolchain",
@@ -145,8 +145,8 @@ func TestForgeFilesRenderFromTheBoundValuesInTheModulesOwnHome(t *testing.T) {
// template in the files/ archive (novox/hq ADR 0349 §2).
func TestManifestRequiresForgeAccountKeepsKeysAndTakesFilesFromTemplates(t *testing.T) {
m := readForgeManifest(t)
// outputs: a run's answer over what step-ended carries inline, by the template objects (design 47 G A-254).
outputs := map[string]any{"name": "outputs", "storage": "object-store", "template": "objects", "options": map[string]any{"max-mib": float64(256)}}
// outputs: a run's answer over what step-ended carries inline, in the steps state's objects store (design 47 G A-254, A-313).
outputs := map[string]any{"name": "steps", "storage": "key-value", "template": "state", "options": map[string]any{"ttl-seconds": float64(172800), "objects-max-mib": float64(256)}}
if want := []any{outputs, "forge-account", "rootless-image-build", "node-tools"}; !reflect.DeepEqual(m.Requires, want) {
t.Errorf("requires = %v, want %v", m.Requires, want)
}
@@ -118,12 +118,12 @@ func TestManifestKeepsItsStatesClaimsItsBuildSeatAndDeclaresItsHealthTool(t *tes
states = append(states, s.Name)
// A step's key is per step and dropped after its age, so the bucket cannot fill, and so is a copy's wait, which a
// stopped process leaves; a pause has no age.
if (s.Name == "steps" || s.Name == MirrorWaitsState) != (s.TTLSeconds > 0) {
t.Errorf("state %s has ttl-seconds %d: only steps and %s have an age", s.Name, s.TTLSeconds, MirrorWaitsState)
if (s.Name == MirrorWaitsState) != (s.TTLSeconds > 0) {
t.Errorf("state %s has ttl-seconds %d: only %s has an age here, steps being asked in requires (A-314)", s.Name, s.TTLSeconds, MirrorWaitsState)
}
}
slices.Sort(states)
if want := []string{"keys", MirrorWaitsState, "pauses", "steps"}; !slices.Equal(states, want) {
if want := []string{"keys", MirrorWaitsState, "pauses"}; !slices.Equal(states, want) {
t.Errorf("state = %v, want %v", states, want)
}
if len(m.Claims) != 1 || m.Claims[0].Name != Seat || !slices.Equal(m.Claims[0].Serves, []string{"current", "drop", "pause", "resume", "steps", "kept-artifact", "output"}) {
@@ -213,9 +213,9 @@ func TestTheModuleRequiresNodeToolsItPullsThrough(t *testing.T) {
}
}
// Design 47 G A-254: the module keeps a run's answer over what step-ended carries inline in an object store of its own, asked for
// in `requires` by the template objects, and serves it through the verb output.
func TestManifestRequiresTheOutputsStore(t *testing.T) {
// Design 47 G A-254, A-313, A-314: the module keeps a run's answer over what step-ended carries inline in the steps state's own
// objects store, sized in `requires` by the state option objects-max-mib, and serves it through the verb output.
func TestManifestRequiresTheStepsStateWithItsObjectsStore(t *testing.T) {
raw, err := os.ReadFile(moduleDir + "/module.json")
if err != nil {
t.Fatal(err)
@@ -226,7 +226,7 @@ func TestManifestRequiresTheOutputsStore(t *testing.T) {
if err := json.Unmarshal(raw, &doc); err != nil {
t.Fatal(err)
}
want := map[string]any{"name": "outputs", "storage": "object-store", "template": "objects", "options": map[string]any{"max-mib": float64(256)}}
want := map[string]any{"name": "steps", "storage": "key-value", "template": "state", "options": map[string]any{"ttl-seconds": float64(172800), "objects-max-mib": float64(256)}}
for _, r := range doc.Requires {
if reflect.DeepEqual(r, want) {
return
+2 -2
View File
@@ -14,7 +14,7 @@
],
"state": [
"keys",
{"name": "steps", "ttl-seconds": 172800}, "pauses",
"pauses",
{"name": "mirror-waits", "ttl-seconds": 172800}
],
"claims": [
@@ -57,7 +57,7 @@
"build_image_health"
],
"requires": [
{"name": "outputs", "storage": "object-store", "template": "objects", "options": {"max-mib": 256}},
{"name": "steps", "storage": "key-value", "template": "state", "options": {"ttl-seconds": 172800, "objects-max-mib": 256}},
"forge-account",
"rootless-image-build",
"node-tools"
@@ -110,12 +110,12 @@ func TestManifestKeepsItsStatesClaimsItsBuildSeatAndDeclaresItsHealthTool(t *tes
for _, s := range m.State {
states = append(states, s.Name)
// A step's key is per step and dropped after its age, so the bucket cannot fill; a pause has no age.
if (s.Name == "steps") != (s.TTLSeconds > 0) {
t.Errorf("state %s has ttl-seconds %d: only steps has an age", s.Name, s.TTLSeconds)
if s.TTLSeconds > 0 {
t.Errorf("state %s has ttl-seconds %d: steps, the one with an age, is asked in requires (A-314)", s.Name, s.TTLSeconds)
}
}
slices.Sort(states)
if want := []string{"keys", "pauses", "steps", "toolchains"}; !slices.Equal(states, want) {
if want := []string{"keys", "pauses", "toolchains"}; !slices.Equal(states, want) {
t.Errorf("state = %v, want %v", states, want)
}
if len(m.Claims) != 1 || m.Claims[0].Name != Seat || !slices.Equal(m.Claims[0].Serves, []string{"current", "drop", "pause", "resume", "steps", "kept-artifact", "output"}) {
@@ -279,9 +279,9 @@ func TestTheModuleRequiresNodeToolsItPullsThrough(t *testing.T) {
}
}
// Design 47 G A-254: the module keeps a run's answer over what step-ended carries inline in an object store of its own, asked for
// in `requires` by the template objects, and serves it through the verb output.
func TestManifestRequiresTheOutputsStore(t *testing.T) {
// Design 47 G A-254, A-313, A-314: the module keeps a run's answer over what step-ended carries inline in the steps state's own
// objects store, sized in `requires` by the state option objects-max-mib, and serves it through the verb output.
func TestManifestRequiresTheStepsStateWithItsObjectsStore(t *testing.T) {
raw, err := os.ReadFile(moduleDir + "/module.json")
if err != nil {
t.Fatal(err)
@@ -292,7 +292,7 @@ func TestManifestRequiresTheOutputsStore(t *testing.T) {
if err := json.Unmarshal(raw, &doc); err != nil {
t.Fatal(err)
}
want := map[string]any{"name": "outputs", "storage": "object-store", "template": "objects", "options": map[string]any{"max-mib": float64(256)}}
want := map[string]any{"name": "steps", "storage": "key-value", "template": "state", "options": map[string]any{"ttl-seconds": float64(172800), "objects-max-mib": float64(256)}}
for _, r := range doc.Requires {
if reflect.DeepEqual(r, want) {
return
+2 -2
View File
@@ -15,7 +15,7 @@
"state": [
"toolchains",
"keys",
{"name": "steps", "ttl-seconds": 172800}, "pauses"
"pauses"
],
"claims": [
{
@@ -57,8 +57,8 @@
"build_node_health"
],
"requires": [
{"name": "steps", "storage": "key-value", "template": "state", "options": {"ttl-seconds": 172800, "objects-max-mib": 256}},
{"name": "detected-steps", "storage": "key-value", "template": "state", "options": {"history": 1}},
{"name": "outputs", "storage": "object-store", "template": "objects", "options": {"max-mib": 256}},
"artifact-store",
"forge-account",
"npm-package-registry",
@@ -341,6 +341,8 @@ func TestEachTemplateIsPlannedUnderTheModulesNames(t *testing.T) {
{name: "state", entry: entry("drafts", catalogue.StorageKeyValue, catalogue.TemplateState, map[string]string{"history": "5"}),
objects: "key-value mesh-knowledge_drafts {64 MiB, age 0, history 5, new, size 262144}; object-store mesh-knowledge_drafts-objects {16 MiB, age 0, history 0, new, size 0}",
binding: "bucket mesh-knowledge_drafts"},
{name: "state naming objects-max-mib and a time to live (A-314): the same bucket as the old section's, its objects store sized", entry: entry("steps", catalogue.StorageKeyValue, catalogue.TemplateState, map[string]string{"ttl-seconds": "172800", "objects-max-mib": "256"}),
objects: "key-value mesh-knowledge_steps {64 MiB, age 172800, history 1, new, size 262144}; object-store mesh-knowledge_steps-objects {256 MiB, age 0, history 0, new, size 0}", binding: "bucket mesh-knowledge_steps"},
{name: "state naming max-mib (A-283)", entry: entry("drafts", catalogue.StorageKeyValue, catalogue.TemplateState, map[string]string{"max-mib": "8"}),
objects: "key-value mesh-knowledge_drafts {8 MiB, age 0, history 1, new, size 262144}; object-store mesh-knowledge_drafts-objects {16 MiB, age 0, history 0, new, size 0}",
binding: "bucket mesh-knowledge_drafts"},