## Lesson — Engine-boot helpers must inherit structured errors

**Tags:** review-guidance, error-handling, pack-substrate, adr-097, bootstrap

**Applies-to:** boundary

**Tags:** review-guidance, error-handling, pack-substrate, adr-097, bootstrap
**Scope:** packages/cli/src/utils/bootstrap-engine.ts

### Context

ADR-097 § 5 Q5 mandates synchronous fail-loud at engine boot. The `bootstrapEngine` CLI helper at `packages/cli/src/utils/bootstrap-engine.ts` is a thin wrapper around `loadInstalledPacks()` that fires immediately after `loadConfig()` in `lint`, `shield`, `compile`, and `test-rules`.

### The decline

GCA flagged `bootstrapEngine` for not wrapping `loadInstalledPacks()` in a `try/catch` that rethrows as `new TotemError('BOOTSTRAP_FAILED', ...)`. The styleguide §3 general guidance ("use `TotemError` for clear errors") seemed to apply on the surface.

### Why the wrap is wrong

`loadInstalledPacks()` already throws structured, actionable errors at every failure path:

- Malformed manifest JSON → "installed-packs manifest at `<path>` is not valid JSON. Re-run `totem sync` to regenerate." with `Error.cause` chain to the JSON parse failure
- Schema validation failure → field-level diagnostic naming the offending key
- Missing pack file → "Pack `<name>` is registered in installed-packs.json at `<path>` but the path does not exist. Re-install the pack or re-run `totem sync`."
- Pack `require()` throw → "Pack `<name>` at `<path>` could not be loaded." with `Error.cause`
- Peer-dep mismatch → structured ADR-097 § 5 Q6 error naming declared range + actual engine version
- Pack `register()` callback throw → "Pack `<name>` registration callback threw. The pack at `<path>` must be fixed or removed." with `Error.cause`
- Pack `register()` returning a Promise → "Pack `<name>` registration callback returned a Promise — registration must be synchronous per ADR-097 § 5 Q5."

Wrapping these in a generic `TotemError('BOOTSTRAP_FAILED', 'Failed to bootstrap engine: ' + err.message, ...)` collapses six distinct, pack-named, recovery-actionable errors into one opaque "BOOTSTRAP_FAILED" code with a flattened message string — losing the pack-name and the cause chain that makes the failure actionable.

### The architectural pattern

Engine-boot helpers that **delegate to a function which already produces structured errors** must inherit those errors verbatim. The fail-loud-at-boundary policy is the helper letting the underlying error propagate, not catching and re-shaping it.

This is distinct from §3's general guidance, which applies to CLI-layer flag-conflict validation and config-invalid surfaces (e.g., `--refresh-manifest` + `--force` conflict in `compile.ts:486` → `TotemConfigError` with explicit code). Those are surfaces where the CLI authors the error from scratch.

### Test of correctness

If the helper's `try/catch` is removed, the user-facing error is *more* informative (pack name + path + cause chain) rather than less. That's the signal that the wrap was lossy.

### Reference

- ADR-097 § 5 Q5 (synchronous fail-loud at engine boot)
- `packages/core/src/pack-discovery.ts` (the structured-error author)
- `.gemini/styleguide.md` § 6 entry (added during this PR's bot-review pass)
- mmnto-ai/totem PR #1795 (the substrate-wiring PR where this came up)

**Source:** mcp (added at 2026-05-02T04:19:54.446Z)
