diff --git a/bridged/src/main/java/dev/ltms/bridged/Bridged.java b/bridged/src/main/java/dev/ltms/bridged/Bridged.java
index fcbbc78..34e828a 100644
--- a/bridged/src/main/java/dev/ltms/bridged/Bridged.java
+++ b/bridged/src/main/java/dev/ltms/bridged/Bridged.java
@@ -203,17 +203,17 @@ public final class Bridged {
leads = () -> leadTerminals;
}
- // CB-548: config-declared architect slots. Slots live in config (name → strong-model
- // profile); the terminal → slot binding is the live half, sourced from the slots' declared
- // terminals today and swapped for a live binding by the later spawn lifecycle. The registry
- // is what CallerResolver resolves against and what that lifecycle will read profiles from;
+ // CB-548: config-declared architect slots. Config supplies only the stable name → profile
+ // map; the terminal → slot binding is owned by the registry and is empty at startup, so no
+ // pane resolves to an architect until the later spawn lifecycle binds one. The registry is
+ // what CallerResolver resolves against and what that lifecycle will read profiles from;
// nothing here spawns a slot.
ArchitectRegistry architects = new ArchitectRegistry(
- cfg.architects() == null ? Map.of() : cfg.architects(),
- () -> cfg.architectTerminals());
+ cfg.architects() == null ? Map.of() : cfg.architects());
if (!architects.slots().isEmpty()) {
- log.info("architect slots: {} configured {}, terminals {}", architects.slots().size(),
- architects.slots().keySet(), cfg.architectTerminals().keySet());
+ log.info("architect slots: {} configured {} — none bound yet (a slot is idle until the "
+ + "spawn lifecycle binds a live terminal to it)",
+ architects.slots().size(), architects.slots().keySet());
}
// Status-gated injector (CB-103): the single writer into workers, fed by a poller.
@@ -326,12 +326,12 @@ public final class Bridged {
+ " is unset or empty — export it before starting bridged");
}
callers = CallerResolver.withLeadsAndArchitects(identity, true, token, leads,
- architects::terminalBindings);
+ architects::snapshot);
log.info("auth: token mode (bearer required for non-worker callers, env {})",
cfg.auth().tokenEnv());
} else {
callers = CallerResolver.withLeadsAndArchitects(identity, false, null, leads,
- architects::terminalBindings);
+ architects::snapshot);
log.info("auth: loopback-trust (any loopback non-worker caller is the primary)");
}
diff --git a/bridged/src/main/java/dev/ltms/bridged/auth/ArchitectRegistry.java b/bridged/src/main/java/dev/ltms/bridged/auth/ArchitectRegistry.java
index c5548b3..1e7c673 100644
--- a/bridged/src/main/java/dev/ltms/bridged/auth/ArchitectRegistry.java
+++ b/bridged/src/main/java/dev/ltms/bridged/auth/ArchitectRegistry.java
@@ -2,37 +2,38 @@ package dev.ltms.bridged.auth;
import dev.ltms.bridged.config.BridgedConfig;
+import java.util.HashMap;
import java.util.Map;
-import java.util.function.Supplier;
/**
* The architect-slot registry (CB-548): every gateway-local architect name and the strong-model
- * profile it points at, plus the live binding from a live architect's herdr terminal to its slot.
+ * profile it points at, plus the live bindings from a live architect's herdr terminal to
+ * its slot.
*
*
Two halves, split by who owns each:
*
* - slots — configured once, keyed by the gateway-local unique name; each carries the
- * {@code profile} reference the future spawn lifecycle will read when it stands the
- * slot up. A read-only snapshot taken at construction.
- * - terminal bindings — a {@link Supplier} consulted on every read, so a binding
- * injected after startup (an operator pin, or the later lifecycle once it spawns a
- * session) takes effect without a restart. {@link CallerResolver} reads this to turn a pane
- * into an {@link Role#ARCHITECT}.
+ * {@code profile} reference the spawn lifecycle reads when it stands the slot up. A read-only
+ * snapshot taken at construction.
+ * - terminal bindings — owned by this registry and initially empty. Config
+ * declares no architect terminal, so at startup every slot is idle and nothing resolves to an
+ * architect; a session only becomes one when the spawn lifecycle {@linkplain #bind(String,
+ * String) binds} its terminal to a slot. {@link CallerResolver} reads this through
+ * {@link #snapshot()} to turn a pane into an {@link Role#ARCHITECT}.
*
*
- * Spawning/lifecycle is deliberately a separate unit: this class only exposes the map the
- * resolver resolves against and the profile lookup that lifecycle will call. Nothing here
- * creates or manages an architect session.
+ *
Spawning/lifecycle is deliberately a separate unit: this class only owns the bindings and
+ * exposes the map the resolver resolves against plus the profile lookup lifecycle will call.
+ * Nothing here creates or manages an architect session.
*/
public final class ArchitectRegistry {
private final Map slots;
- private final Supplier