# OpenSpec Change: barrel-explicit-exports - **Change ID**: barrel-explicit-exports - **Scope**: cross-cutting — every package under `packages/*/` - **Status**: PROPOSED - **Date**: 2026-05-03 - **Reference**: `openspec/AGENTS.md` §6a (Barrel exports — explicit named, not `export *`) ## Summary Replace `export * from "./.js"` with explicit named re-exports in every per-feature `/index.ts` barrel across the monorepo. The package-root `src/index.ts` files are out of scope for this change — those keep `export *` for now and may be migrated to `export * as from` namespace exports in a future change (also permitted under §6a). Generated selector indexes (`*.select/index.ts`) are also out of scope, since they are the one place §6a explicitly allows transparent forwarding. The motivation is direct from §6a: the public API of a feature module must be the contract the barrel declares, not whatever the source file happens to export today. `export *` silently leaks every new symbol added to the source, which (a) defeats reviewability, (b) makes accidental private-helper exposure trivial, and (c) blurs the documented surface area against the actual one. Tree-shaking is unaffected — `"sideEffects": false` rollup/esbuild treat both forms identically. ## Scope **In:** 536 distinct `/index.ts` files across 70 packages, totalling ~875 `export *` lines (the remaining ~37 of the 912 total live in package roots / generated selectors / impl files). **Out:** - 21 package-root `src/index.ts` files (deferred to a follow-up change). - 14 `*.select/index.ts` generated selector forwarders (allowed by §6a). - 11 non-index impl files using `export *` (e.g. `aes-256-gcm.ts` forwarding to its `.select/` directory) — these are forwarder shims for the selector mechanism, not feature barrels. - 1 `.d.ts` third-party type stub. ## Why one cross-cutting change instead of one per package This is a single mechanical rule application, not a design decision per package. The rule is already canonicalised in `openspec/AGENTS.md` §6a; there is nothing to negotiate per package. Splitting into 70 micro-proposals would inflate process overhead without changing what gets shipped or how it is reviewed. The per-package verification gate (§13: `lint typecheck test` + 100% coverage, plus dependent sweep on foundation packages) is preserved — the change is implemented one package at a time, gates run per package, and only after all 70 packages are green is the change archived. ## Behavior change None. Every symbol that was reachable through `export *` remains reachable, with the same name and same type. Consumers continue to import via `import { X } from "@suaveplan///"` exactly as before. The diff is barrel-internal: the barrel now lists the symbols by name instead of forwarding the whole module. The one observable difference is what does NOT happen any more — adding a new export to `.ts` will not auto-leak through the barrel. This is the intended invariant. ## Verification per package For every package touched: 1. `bunx turbo lint typecheck test --filter=@suaveplan/` — green. 2. `bunx turbo test:coverage --filter=@suaveplan/` — 100/100/100/100. 3. Foundation packages (`error`, `events`, `logger`, `validation`, plus `crypto-utils` since several primitive packages depend on it): also `bunx turbo test --filter=...@suaveplan/` to catch dependent regressions. 4. Banned-pattern scan and missing-`.md` scan re-run to confirm no incidental regressions. ## Impact ### Affected dependents Cross-cutting; every package in `packages/*/` is affected. Foundation packages enumerate their dependent sets via `bunx turbo ls --filter=...@suaveplan/` per the §13 step 10 / §14a contract — those sweeps run per foundation package as it ships. For non-foundation packages, the per-package gate in step 1 above is sufficient (no dependents on a leaf package exist by definition). ### Migration None for consumers. Internal-only refactor. ## Spec delta `openspec/specs/monorepo-structure/spec.md` gains a `Barrel export form` requirement that codifies §6a as an enforceable spec scenario (so future packages cannot regress).