Pure classifier splitManifestByOwnership(sources) → {shared, delta}, the
FS-free core that B2b's live cross-repo sweep will feed into the campaign
ledger's setSharedGlobal/setRepoTokens (B2a).
classifyOwnership maps each source string to its layer:
shared : user | managed | plugin:* | ~/.claude.json:projects (global MCP)
delta : project | local | .mcp.json | @import | unrecognized
Anything not positively global falls to delta, so a source is never silently
folded into the once-counted shared layer (a wrong fold HIDES machine-wide
cost; a wrong delta is at worst visibly attributed to a repo). Both layers
carry the canonical summarizeByLoadPattern shape so the ledger setters consume
them verbatim.
TDD: 6 unit tests first (RED → GREEN). buildManifest/CLI output unchanged →
manifest snapshot byte-identical. Suite 1195 green (1189 + 6).
[skip-docs]: internal pure export only, no user-facing surface yet. User-facing
docs (commands/campaign.md + CLAUDE.md manifest/campaign rows) land in B2b-3
when refresh-tokens + the rendered token-bill ship.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>