feat(manifest): ownership split for machine-wide token roll-up (v5.9 B2b-1) [skip-docs]
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>
This commit is contained in:
parent
cefa751990
commit
872b8ac281
2 changed files with 126 additions and 1 deletions
|
|
@ -168,6 +168,56 @@ export function summarizeByLoadPattern(sources) {
|
|||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source strings (the `source` field buildManifest stamps) that belong to the
|
||||
* SHARED GLOBAL layer — config paid once per machine and identical in every
|
||||
* repo: the global ~/.claude CLAUDE.md, managed enterprise policy, and the
|
||||
* global MCP slice of ~/.claude.json. Installed plugins are also shared but are
|
||||
* matched by the `plugin:` prefix below, not by this set.
|
||||
*/
|
||||
const SHARED_GLOBAL_SOURCES = Object.freeze(new Set(['user', 'managed', '~/.claude.json:projects']));
|
||||
|
||||
/**
|
||||
* Classify one manifest source as part of the once-counted shared global layer
|
||||
* or a per-repo delta (v5.9 B2b). Anything not positively identified as global
|
||||
* (project / local / .mcp.json / @import / unrecognized) falls to `delta`, so a
|
||||
* source is never silently folded into the shared layer — a wrong fold would
|
||||
* HIDE machine-wide cost, whereas a wrong delta is at worst attributed visibly
|
||||
* to a repo.
|
||||
* @param {string} source
|
||||
* @returns {'shared'|'delta'}
|
||||
*/
|
||||
export function classifyOwnership(source) {
|
||||
if (typeof source === 'string') {
|
||||
if (source.startsWith('plugin:')) return 'shared'; // installed plugins are machine-global
|
||||
if (SHARED_GLOBAL_SOURCES.has(source)) return 'shared';
|
||||
}
|
||||
return 'delta';
|
||||
}
|
||||
|
||||
/**
|
||||
* Partition manifest sources by ownership for the machine-wide token roll-up,
|
||||
* returning two load-pattern summaries in the exact shape `summarizeByLoadPattern`
|
||||
* emits ({always,onDemand,external,unknown:{tokens,count}}), so the campaign
|
||||
* ledger setters (`setSharedGlobal` / `setRepoTokens`) consume them verbatim.
|
||||
*
|
||||
* - `shared`: the global layer, identical across repos — set ONCE on the ledger
|
||||
* root so the roll-up counts it exactly once (the structural double-count guard).
|
||||
* - `delta`: this repo's own project/local contribution beyond the shared layer.
|
||||
*
|
||||
* The split is total: every source lands in exactly one layer.
|
||||
* @param {Array<{source:string, loadPattern:string, estimated_tokens:number}>} sources
|
||||
* @returns {{shared:object, delta:object}}
|
||||
*/
|
||||
export function splitManifestByOwnership(sources) {
|
||||
const shared = [];
|
||||
const delta = [];
|
||||
for (const s of sources || []) {
|
||||
(classifyOwnership(s.source) === 'shared' ? shared : delta).push(s);
|
||||
}
|
||||
return { shared: summarizeByLoadPattern(shared), delta: summarizeByLoadPattern(delta) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Distribute the cascade-level estimated tokens across the individual files
|
||||
* proportional to their byte size. claudeMd.estimatedTokens is computed for
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue