fix(bus,webui,cli): CB5 review fixes, binding summary on the inbox, raise and trail reads, copy-service-unbindable status, Console allowlist (#1873)

- broker: the hire.raise answer, the inbox console preview and the trail read carry a
  binding as {instance, change} only; the raise event keeps the full proposal the resolve reads.
- copy-launch status(): a copy whose role needs a service a copy cannot bind reads
  copy-service-unbindable (new fixed status text) instead of a gitea binding step.
- cli instances: an unbound copy's line names its missing services.
- webui serve: the instances verb rebuilds each copy binding from the fixed status table.
- bus.js reads the binding change field only.
- browser tests: system Chromium starts with --disable-extensions; the A3 booted-bus test passes it.
This commit is contained in:
jason.woltje committed 2026-11-12 19:19:22 -06:00
1 parent fdbc34413b
commit 56aeefb370
12 files changed
+106 -20

No files matched your search

+4 -1
View File
@@ -12,6 +12,7 @@
import { readFileSync } from "node:fs";
import { CliError } from "./errors.mjs";
import { copyBindingStatus } from "../../organization/src/copy-binding-status.mjs";
import { SERVICES } from "../../organization/src/vocabulary.mjs";
export const INSTANCES_USAGE = "mosaic business instances BUSINESS [--json] [--tester] | " +
"business instance add BUSINESS INSTANCE --definition DEF [--vars FILE] [--launch] [--bot BOT --bot-id N] --revision REV [--tester] | " +
@@ -226,7 +227,9 @@ export function formatInstances(view) {
for (const [name, copy] of Object.entries(view.copies ?? {})) {
if (!copy?.binding) continue;
const { status, next } = copyBindingStatus(copy.binding.status);
lines.push(` ${name} (copy): ${status}. ${next}`);
// The services the copy lacks, as registered service labels only; nothing else the bus sent.
const missing = copy.state === "unbound" && Array.isArray(copy.missing) ? SERVICES.filter((s) => copy.missing.includes(s)) : [];
lines.push(` ${name} (copy): ${status}${missing.length ? ` (missing ${missing.join(", ")})` : ""}. ${next}`);
}
const definitions = Object.entries(view.definitions);
const accepted = definitions.filter(([, d]) => d.accepted).map(([name]) => name);