@teamscala/service-boot 0.23.492 → 0.23.494

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "name": "@teamscala/service-boot",
8
8
  "type": "module",
9
- "version": "0.23.492",
9
+ "version": "0.23.494",
10
10
  "sideEffects": false,
11
11
  "imports": {
12
12
  "#service-boot/*": "./src/*",
@@ -65,6 +65,10 @@ export async function mountServerPackages(
65
65
  const mountRegistry = createMountRegistry();
66
66
  const mountCtx = createMountContext(serviceSlug, config, app, mountRegistry, getSharedDb);
67
67
  const serverPackages = (config.server_packages ?? []) as string[];
68
+ /** Packages that loaded but whose mount threw — unavailable, not fatal.
69
+ * Named in the summary so a degraded boot is legible without grepping for
70
+ * the per-package error line. */
71
+ const degraded: string[] = [];
68
72
 
69
73
  if (serverPackages.length === 0) {
70
74
  log.info(`[scala-install:${serviceSlug}] No server_packages declared`);
@@ -125,11 +129,34 @@ export async function mountServerPackages(
125
129
  }
126
130
  } catch (err) {
127
131
  if (err instanceof BootFatalError) throw err;
128
- throw new BootFatalError(
129
- "mount-server-packages",
130
- serviceSlug,
131
- `server_package "${pkg}" mount failed: ${err instanceof Error ? err.message : err}`,
132
- err,
132
+ // A PLUGIN THAT FAILS TO MOUNT IS UNAVAILABLE, NOT FATAL. `crash-isolation`
133
+ // states the law directly — "a failure in one package does not crash the
134
+ // host" — and names the plugin failure domain: "a broken plugin degrades
135
+ // to 'that plugin unavailable'". This arm used to wrap ANY mount error in
136
+ // a BootFatalError, so one plugin's missing config killed a whole service.
137
+ //
138
+ // MEASURED 2026-09-07: scala-agents-ui, the operator's fleet monitoring UI,
139
+ // booted through db, auth, routes and every other server_package, then died
140
+ // at @teamscala/opencode because OPENCODE_SERVER_URL was absent. systemd
141
+ // retried five times and gave up, agents.scala.business served 502, and the
142
+ // operator lost sight of his fleet — over one optional integration missing
143
+ // one variable, on a page that needs no opencode connection to render.
144
+ //
145
+ // THE ASYMMETRY DECIDES IT. A service that boots without one plugin still
146
+ // serves every route that plugin did not own, and the gap is loud in this
147
+ // log line and counted in the summary below. A service that refuses to boot
148
+ // serves NOTHING — including the surface that would have explained why.
149
+ //
150
+ // NOT-FOUND STAYS FATAL, deliberately: that arm is above and untouched. A
151
+ // package that LOADS and then throws is a broken plugin, which is this
152
+ // case. A package that cannot be resolved at all means the declared
153
+ // dependency set was never installed — an install-integrity failure, where
154
+ // booting a half-installed service would serve wrong results while looking
155
+ // healthy.
156
+ degraded.push(pkg);
157
+ log.error(
158
+ `[scala-install:${serviceSlug}] server_package "${pkg}" UNAVAILABLE — mount failed; ` +
159
+ `the service continues without it: ${err instanceof Error ? err.message : err}`,
133
160
  );
134
161
  }
135
162
  }
@@ -137,7 +164,12 @@ export async function mountServerPackages(
137
164
  log.info(
138
165
  `[scala-install:${serviceSlug}] Registered ${mountRegistry.handlers.size} handler(s), ` +
139
166
  `${mountRegistry.wsRoutes.size} ws route(s), ` +
140
- `${mountRegistry.bootHooks.size} boot hook(s)`,
167
+ `${mountRegistry.bootHooks.size} boot hook(s)` +
168
+ // NAMED, NEVER COUNTED. A bare "1 degraded" sends the reader hunting for
169
+ // which one; the name is the whole value of the line.
170
+ (degraded.length > 0
171
+ ? `, ${degraded.length} server_package(s) UNAVAILABLE: ${degraded.join(", ")}`
172
+ : ""),
141
173
  );
142
174
 
143
175
  return mountRegistry;