N3 (forge bridge): the bridge names its own buckets heads and inputs from its credential's module

Both were constants of the module's name; they are now `<module>_heads` and `<module>_inputs` of the credential's module.

Builds: design 47 G A-296

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 20:40:14 +01:00
parent 29517d702c
commit 3ac9947dc8
5 changed files with 15 additions and 11 deletions
@@ -7,9 +7,9 @@ import (
"github.com/nats-io/nats.go/jetstream"
)
// headsBucket is the module's state bucket heads, `<module>_<name>` as the indexer's buckets are named: the head last
// written on FORGE for each pull request (manifest state entry heads, novox/hq ADR 0443 §5).
const headsBucket = forgeBridgeModule + "_heads"
// headsBucket is the module's state bucket heads, `<module>_<name>`, module its credential's (design 47 G A-296): the head
// last written on FORGE for each pull request (manifest state entry heads, novox/hq ADR 0443 §5).
func headsBucket(module string) string { return module + "_heads" }
// kvHeads is the bucket heads as the bridge's headStore.
type kvHeads struct{ kv keptStore }
@@ -279,7 +279,7 @@ func startBed(t *testing.T, openReply string, prepare func(ctx context.Context,
}
// The bridge's states, made from the manifest's state entries and its requires before the process starts; the state
// performed under the bus name the seat mesh-broker's show answers for it (boundPerformed), never an old name (§577).
for _, name := range []string{headsBucket, boundPerformed, boundFindings, inputsBucket, groupkeys.EndAsksBucket, groupkeys.OwedAsksBucket} {
for _, name := range []string{headsBucket(forgeBridgeModule), boundPerformed, boundFindings, inputsBucket(forgeBridgeModule), groupkeys.EndAsksBucket, groupkeys.OwedAsksBucket} {
if _, err := js.CreateKeyValue(ctx, jetstream.KeyValueConfig{Bucket: name, History: 1}); err != nil {
t.Fatal(err)
}
@@ -651,7 +651,7 @@ func TestBridgeReadsTheForgeAtStartAndAfterAReconnectAndHearsNoOlderEvent(t *tes
}
}
// What the bridge kept of an earlier write: the head of mesh-delivery#12.
kv, err := js.KeyValue(ctx, headsBucket)
kv, err := js.KeyValue(ctx, headsBucket(forgeBridgeModule))
if err != nil {
t.Fatal(err)
}
@@ -17,7 +17,7 @@ import (
// directory, its manifest and lock-file hashes, by the directory's git tree hash, so a merge reads the forge only for the
// directories it changed (issue 1126; internal/bridge kept.go). A tree hash names its bytes, so a value is never stale;
// a merge adds a value only for each directory it changed (a few kilobytes each).
const inputsBucket = forgeBridgeModule + "_inputs"
func inputsBucket(module string) string { return module + "_inputs" }
// keptTimeout bounds one read or write of the bucket: the bucket is a saving, never a reason to fail a merge.
const keptTimeout = 2 * time.Second
@@ -51,7 +51,7 @@ func (k *kept) Get(tree string) (bridge.Inputs, bool) {
return bridge.Inputs{}, false // not kept (or not readable): read from the forge
}
if err := json.Unmarshal(e.Value(), &in); err != nil || in.Manifest == "" {
k.logger.Printf("%s.%s cannot be read, so its directory is read from the forge: %v", inputsBucket, tree, err)
k.logger.Printf("inputs.%s cannot be read, so its directory is read from the forge: %v", tree, err)
return bridge.Inputs{}, false
}
k.mu.Lock()
@@ -74,6 +74,6 @@ func (k *kept) Put(tree string, in bridge.Inputs) {
ctx, cancel := context.WithTimeout(context.Background(), keptTimeout)
defer cancel()
if _, err := k.kv.Put(ctx, tree, data); err != nil {
k.logger.Printf("%s.%s is not kept, so the next merge reads its directory from the forge again: %v", inputsBucket, tree, err)
k.logger.Printf("inputs.%s is not kept, so the next merge reads its directory from the forge again: %v", tree, err)
}
}
@@ -131,7 +131,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
call := callerOn(conn, cred.User)
// No forge event is heard while the open pull requests are read and said (ADR 0443 §5): events and syncs take turns.
var turn sync.Mutex
kv, err := process.EssentialBucket(ctx, js, "bridge", headsBucket, "keep the heads it wrote on the forge stream")
kv, err := process.EssentialBucket(ctx, js, "bridge", headsBucket(cred.Module), "keep the heads it wrote on the forge stream")
if err != nil {
return err
}
@@ -216,7 +216,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
}
reader.Start(ctx, js, cred, nil)
// What a merge read of each module directory, by its tree (issue 1126): a merge reads only the directories it changed.
inputs := newKept(process.OptionalBucket(ctx, js, inputsBucket, "a merge's module directories are kept in memory only, so the first merge after a start reads every one", logger.Printf), logger)
inputs := newKept(process.OptionalBucket(ctx, js, inputsBucket(cred.Module), "a merge's module directories are kept in memory only, so the first merge after a start reads every one", logger.Printf), logger)
mv = &mover{call: call, pub: rec, heads: heads, watched: watched, inputs: inputs, groups: groups, findings: findingsWriter, turn: &turn, logger: logger}
// The hub's handler of the forge's events, for every event its consumes filter lets through. Essential: the forge's
// events are what the bridge carries. The forge's events are the seat git's (novox/hq design 47 *The seat `git`*),
@@ -44,7 +44,11 @@ func theNamesAreTheManifests(t *testing.T) {
own = append(own, forgeBridgeModule+"_"+kept.Name)
}
}
for _, b := range []string{headsBucket, inputsBucket} {
// Design 47 G A-296: its own buckets are named from its credential's module, never a constant.
if headsBucket("n1-bridge") != "n1-bridge_heads" || inputsBucket("n1-bridge") != "n1-bridge_inputs" {
t.Errorf("the own buckets of n1-bridge are %s and %s", headsBucket("n1-bridge"), inputsBucket("n1-bridge"))
}
for _, b := range []string{headsBucket(forgeBridgeModule), inputsBucket(forgeBridgeModule)} {
if !slices.Contains(own, b) {
t.Errorf("the bridge keeps %s, its manifest's states are %v", b, own)
}