New src/core/source-id.ts consolidates the three regex sites that drifted
across the codebase (utils.ts permissive, sources-ops.ts strict,
source-resolver.ts strict). Exports:
- SOURCE_ID_RE: ^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$ (strict kebab,
1-32 chars, no underscores, alphanumeric boundaries)
- isValidSourceId(s): boolean — for silent-fallback tiers (dotfile,
brain_default config)
- assertValidSourceId(s): void, throws — for explicit-validation tiers
(explicit --source flag, GBRAIN_SOURCE env, cycleLockIdFor primitive)
Dependency-free by design (no engine imports), so both PGLite and
Postgres engines can pull it without circular-import risk. Replaces
the soon-to-be-removed local validators in utils.ts and sources-ops.ts;
preserves both call shapes (boolean + throwing) per the codex outside-voice
finding that resolver tiers need both.
19 unit tests covering valid ids, length boundary (32-char max), underscore
rejection, path-traversal shapes, edge hyphens, whitespace, non-ASCII,
non-string inputs, and TypeScript narrowing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>