diff --git a/docs/inventory/evidence.json b/docs/inventory/evidence.json index 06f89f1b0..c774a25fc 100644 --- a/docs/inventory/evidence.json +++ b/docs/inventory/evidence.json @@ -151,13 +151,19 @@ "status": "untested", "grandfathered": true, "plan": "none", - "seed": "GEVA-2, 2026-11-02: gstack (root skill): untested (never measured)" + "seed": "GEVA-2, 2026-11-02: gstack (root skill): untested (never measured)", + "aliases": [ + "gstack (root skill)" + ] }, "gstack-upgrade": { "status": "untested", "grandfathered": true, "plan": "none", - "seed": "GEVA-2, 2026-11-02: upgrade: untested (never measured)" + "seed": "GEVA-2, 2026-11-02: upgrade: untested (never measured)", + "aliases": [ + "upgrade" + ] }, "guard": { "status": "safety", @@ -535,7 +541,10 @@ "verdict": "CUT-CANDIDATE", "ref": "org-gstack-gbrain/research/why-null-stacks-r1.md (GEVA-100); taken by Garry as the I1 ceremony-cut eval" }, - "seed": "GEVA-2, 2026-11-02: preamble (ceremony): measurement under way (I1, ceremony-cut eval)" + "seed": "GEVA-2, 2026-11-02: preamble (ceremony): measurement under way (I1, ceremony-cut eval)", + "aliases": [ + "preamble (ceremony)" + ] }, "catalog": { "status": "untested", @@ -552,13 +561,19 @@ "note": "untold skill opened in 0 of 180 gstack-arm trials; Opus gstack minus none +5 [-3, +17]" } ], - "seed": "GEVA-2, 2026-11-02: always-on skill catalog (descriptions): measurement under way (Run D)" + "seed": "GEVA-2, 2026-11-02: always-on skill catalog (descriptions): measurement under way (Run D)", + "aliases": [ + "always-on skill catalog (descriptions)" + ] }, "capy-skill": { "status": "untested", "grandfathered": true, "plan": "none", "seed": "GEVA-2, 2026-11-02: capy/skill/gstack/SKILL.md (Capy context): untested (never measured)", + "aliases": [ + "capy/skill/gstack/SKILL.md (Capy context)" + ], "note": "loads only in Capy sessions, which the claude_code preset fair test cannot reach yet; needs a Captain call on how to test it" }, "capy-factory": { @@ -566,6 +581,9 @@ "grandfathered": true, "plan": "none", "seed": "GEVA-2, 2026-11-02: capy/skill/gstack/factory.md (Capy context): untested (never measured)", + "aliases": [ + "capy/skill/gstack/factory.md (Capy context)" + ], "note": "loads only in Capy sessions, which the claude_code preset fair test cannot reach yet; needs a Captain call on how to test it" }, "ship-review-panel": { @@ -591,19 +609,28 @@ "note": "arm D vs pooled arm C, no no-gstack arm" } ], - "seed": "GEVA-2, 2026-11-02: ship: review panel (specialist, adversarial, targeted, outside voice, red team): untested (unfair test: none arm on the SDK stub; arms from different harnesses and gstack versions; cohort mined from gstack's own catches)" + "seed": "GEVA-2, 2026-11-02: ship: review panel (specialist, adversarial, targeted, outside voice, red team): untested (unfair test: none arm on the SDK stub; arms from different harnesses and gstack versions; cohort mined from gstack's own catches)", + "aliases": [ + "ship: review panel (specialist, adversarial, targeted, outside voice, red team)" + ] }, "ship-docs-audit": { "status": "untested", "grandfathered": true, "plan": "folded into GEVA-2 registry row R56-4 (ship); proposed, not yet preregistered", - "seed": "GEVA-2, 2026-11-02: ship: docs audit (Step 14.5): untested (never measured)" + "seed": "GEVA-2, 2026-11-02: ship: docs audit (Step 14.5): untested (never measured)", + "aliases": [ + "ship: docs audit (Step 14.5)" + ] }, "autoplan-phases": { "status": "untested", "grandfathered": true, "plan": "folded into GEVA-2 registry row R56-3 (autoplan); proposed, not yet preregistered", - "seed": "GEVA-2, 2026-11-02: autoplan: CEO, design, DX and eng phases, outside voice: untested (never measured)" + "seed": "GEVA-2, 2026-11-02: autoplan: CEO, design, DX and eng phases, outside voice: untested (never measured)", + "aliases": [ + "autoplan: CEO, design, DX and eng phases, outside voice" + ] } }, "unmapped": [ diff --git a/lib/inventory.ts b/lib/inventory.ts index 06829a20a..3057128c9 100644 --- a/lib/inventory.ts +++ b/lib/inventory.ts @@ -64,7 +64,7 @@ export const PRIOR_WHY: Record = { 'not-claude-code': 'ran outside Claude Code (Messages API, Codex or Capy)', 'variant-only': 'variant against variant, no no-gstack arm', }; -const FIELDS = ['status', 'grandfathered', 'plan', 'prior', 'reason', 'owner', 'measure_by', 'sources', 'models', 'prompt', 'scope', 'masked', 'r48', 'seed', 'note']; +const FIELDS = ['status', 'grandfathered', 'plan', 'prior', 'reason', 'owner', 'measure_by', 'sources', 'models', 'prompt', 'scope', 'masked', 'r48', 'seed', 'aliases', 'note']; export type LedgerCode = | 'LEDGER_PARSE' | 'LEDGER_KEY_MISSING' | 'LEDGER_KEY_EXTRA' | 'LEDGER_STATUS_INVALID' | 'LEDGER_FIELD_UNKNOWN' @@ -73,7 +73,7 @@ export type LedgerCode = | 'LEDGER_REASON_MISSING' | 'LEDGER_OWNER_MISSING' | 'LEDGER_MEASURE_BY_MISSING' | 'LEDGER_PRIOR_INVALID' | 'INVENTORY_SOURCE_MISSING' | 'INVENTORY_PART_MISSING'; export interface LedgerProblem { key: string; code: LedgerCode; message: string } -export interface LedgerEntry { status: Status; grandfathered?: boolean; plan?: string; prior?: Array<{ ref: string; why: string[]; note?: string }>; reason?: string; owner?: string; measure_by?: string; sources?: string[]; models?: string[]; prompt?: string; scope?: string; masked?: boolean; r48?: { verdict: string; ref: string }; seed?: string; note?: string } +export interface LedgerEntry { status: Status; grandfathered?: boolean; plan?: string; prior?: Array<{ ref: string; why: string[]; note?: string }>; reason?: string; owner?: string; measure_by?: string; sources?: string[]; models?: string[]; prompt?: string; scope?: string; masked?: boolean; r48?: { verdict: string; ref: string }; seed?: string; aliases?: string[]; note?: string } export interface Ledger { parts: Record; unmapped?: Array<{ row: string; why: string }>; [k: string]: unknown } const isObj = (v: unknown): v is Record => typeof v === 'object' && v !== null && !Array.isArray(v); @@ -192,6 +192,7 @@ export interface Inventory { commit: string; date: string; tokenSource: string; keys: string[]; parts: PartRow[]; tiers: Array<{ tier: string; skills: string[] }>; dispatches: CitedDispatch[]; prior: Array<{ key: string; ref: string; why: string[]; note?: string }>; + aliases: Array<{ key: string; names: string[] }>; unmapped: Array<{ row: string; why: string }>; problems: LedgerProblem[]; } @@ -245,6 +246,7 @@ export function buildInventory(root: string): Inventory { tiers: [...tiers].sort(([a], [b]) => a.localeCompare(b)).map(([tier, skills]) => ({ tier, skills })), dispatches: checked.dispatches, prior: checked.keys.flatMap(key => (parts[key]?.prior ?? []).map(p => ({ key, ...p }))), + aliases: checked.keys.filter(k => parts[k]?.aliases?.length).map(k => ({ key: k, names: parts[k]!.aliases! })), unmapped: checked.ledger?.unmapped ?? [], problems: checked.problems, }; @@ -254,7 +256,7 @@ const n = (v: number | null) => (v === null ? 'n/a' : v.toLocaleString('en-US')) const cell = (s: string) => s.replace(/\|/g, '\\|').replace(/\n/g, ' '); function renderRanking(inv: Inventory): string[] { - const out = ['## Parts by load per run', '', 'Load per run: a skill\'s SKILL.md plus the files it must read on every use (`perInvocationTokens` in lib/context-bill.ts); a shared part\'s files. Tier 2+ skills also read the shared preamble once per session (its own row). On demand: the skill\'s other sections/ and references/ files, read when a step asks for them. Always-on: frontmatter every session\'s skill scanner reads.', '', + const out = ['## Parts by load per run', '', 'Load per run: a skill\'s SKILL.md plus the files it must read on every use (`perInvocationTokens` in lib/context-bill.ts); a shared part\'s files. Tier 2+ skills also read the shared preamble once per session (its own row). On demand: the skill\'s other sections/ and references/ files, read when a step asks for them; files a shared row also counts, such as /ship\'s review sections, appear in both. Always-on: frontmatter every session\'s skill scanner reads.', '', '| Rank | Part | Kind | Load per run (tokens) | On demand (tokens) | Always-on (tokens) | Loads | Status | Source, plan or reason |', '|---|---|---|---|---|---|---|---|---|']; inv.parts.forEach((p, i) => out.push(`| ${i + 1} | ${p.key} | ${p.kind} | ${n(p.loadTokens)} | ${n(p.onDemandTokens)} | ${n(p.alwaysOnTokens)} | ${cell(p.loads)} | ${p.status} | ${cell(p.ref)} |`)); return out; @@ -291,13 +293,14 @@ function renderKeys(inv: Inventory): string[] { const skills = inv.keys.filter(k => !SHARED_KEYS.includes(k)); return ['## Key list and aliases', '', `${inv.keys.length} keys: ${skills.length} skills (the folders \`discoverTemplates()\` finds, the root skill as \`gstack\`) and ${SHARED_KEYS.length} shared parts.`, '', `- Skills: ${skills.join(', ')}.`, `- Shared: ${SHARED_KEYS.join(', ')}.`, - '- Installed names: `gstack-` is key `` (`gstack-ship` is `ship`); `gstack-upgrade` keeps its name; the root skill installs as `gstack`.']; + '- Installed names: `gstack-` is key `` (`gstack-ship` is `ship`); `gstack-upgrade` keeps its name; the root skill installs as `gstack`.', + ...(inv.aliases.length ? [`- Other names (the ledger's \`aliases\`): ${inv.aliases.map(a => `${a.names.join(', ')} → ${a.key}`).join('; ')}.`] : [])]; } function renderNotHere(inv: Inventory): string[] { return ['## Not in this inventory', '', '- Out of scope: openclaw/skills/* and browser-skills/* (not gstack skills an agent loads from this checkout), and connect-chrome (a symlink to open-gstack-browser, counted once).', - ...inv.unmapped.map(u => `- ${u.row}: ${u.why}`)]; + ...inv.unmapped.map(u => `- Evidence-table row "${u.row}": ${u.why}.`)]; } export function renderInventory(inv: Inventory): string {