# DOX — packages/blackhole-plugin/src/shared

Files in this directory. One row per source file. See change: add-blackhole-plugin.

| File | Purpose |
|------|---------|
| `blackhole-config.ts` | Re-declared `BlackholeConfig` + `ModelRef`, `FIELD_DESCRIPTORS` (kind/enum/bounds — the managed-key allowlist), `KNOWN_KEYS`, `DEFAULTS`, `THINKING_LEVELS`, `WORKER_CHAINS`, and `validateBlackholeConfig(body)` — the security boundary for `PUT` (rejects unknown keys, enum/type/bound violations; rejection is atomic). Bounds mirror blackhole's `positiveInt` (>0), `nonNegativeInt` (>=0, `cooldownHours`) and `dropperPressureThreshold` `(0,1]`. `null` explicitly UNSETS a model/chain key. `SOURCE-VERSION PIN: pi-blackhole@0.4.5`. Change: `roleSlotRef`, `validateBlackholeConfig(body,{allowRoleSlots})`; `ModelRef.role/roleStatus` are client UI state only. See change: add-role-aware-model-refs. |
| `chain-model.ts` | Pure chain algebra: `readChain`/`writeChain` (index 0 = `<worker>Model`, rest = `<worker>FallbackModels`), `moveEntry`, `removeEntry`, `canRemove` (a chain of one is never emptied), `appendEntry` (appends model ref), `recommendedDefaults` (pure rank-heuristic for flash/haiku/mini candidates with distinct provider preference), `normalizeModel` (trims strings, drops cleared `contextWindow`/`cooldownHours` as ABSENT not `0`, preserves `_`-annotation keys). Exports `ModelCandidate`. See change: add-blackhole-plugin, blackhole-model-picker-chains. Change: `toWire(entry)` (role-bound entry → `{role,…}`), `normalizeModel` keeps UI-only `role`/`roleStatus`. See change: add-role-aware-model-refs. |
| `example-config.snapshot.json` | Vendored snapshot of `pi-blackhole@0.4.5` `example-config.json`. Drift guard input only — refreshed by hand when the SOURCE-VERSION PIN is bumped. Detects OUR descriptors drifting from the pin (key-set only), never upstream drift; no network fetch in CI. |
| `__tests__/blackhole-config.test.ts` | L1. Validator IS the security boundary: numeric bounds mirroring blackhole's coercers (test-plan E1-E10), enum/type violations (E11-E12), unknown-key rejection incl. the deliberately-unmanaged real keys (E13), atomic rejection (E14), raw-PUT bypass (X10), model-entry rules. Plus the descriptor drift guard vs `example-config.snapshot.json` — documents honestly that it catches key-SET drift from the PIN only, never upstream or type/bound drift. |
| `__tests__/chain-model.test.ts` | L1. Pure chain algebra: read/write chain order (E18), promotion via `moveEntry` (E19), boundary no-ops, `canRemove`/`removeEntry` never emptying a chain (E21), `normalizeModel` writing a cleared `contextWindow` as ABSENT not `0` (E20) while keeping `cooldownHours: 0` (disabled) and `_`-annotations; `appendEntry` (E1, E2); `recommendedDefaults` rank buckets, registry order, cap/dedupe, distinct-provider preference, empty/no-candidate handling (E3-E7), micro-perf (P1). See change: add-blackhole-plugin, blackhole-model-picker-chains. |
