Shared Repo-Grounding Profile Cache — Protocol¶
Read this when a repo-grounding skill (initial consumer: vt-c-pov) needs the
question-agnostic project profile (stack, deps, topology, conventions,
vocabulary). The profile is derived once and reused within a session and across
sessions and skills at an unchanged commit — only the question-specific
grounding for the current run is ever re-derived.
Single shared script (not copied) — SPEC-145 Decision 2 / AD-2¶
Unlike the upstream compound-engineering plugin (which byte-duplicates the cache into every skill because its skills ship separately), V025 is a single monorepo. There is exactly one script:
Consumers resolve it via TOOLKIT_ROOT — never a hardcoded absolute path and
never a per-skill copy. There is therefore no cross-copy parity test; a
reference-integrity test (tests/test-repo-profile-cache.sh) asserts the single
shared path is present, executable, and referenced via TOOLKIT_ROOT.
The deterministic cache I/O lives in that script; the derivation-on-miss is done
by the co-located references/agents/repo-profiler.md
persona.
What is cached (the agnostic profile)¶
A single JSON object, versioned by profile_schema_version, carrying five
top-level keys (all required — a profile missing any key is rejected, not
served):
- stack — languages, major frameworks + versions, build/test tooling.
- dependencies — manifest + lockfile paths, top-level deps, licenses.
- topology — monorepo/workspace map, deployment model, module layout.
- conventions — paths + digests of the root instruction/doc files
(
AGENTS.md,CLAUDE.md,README.md,PRODUCT-VISION.md,ARCHITECTURE.md,CONTRIBUTING.md,STRATEGY.md). - vocabulary — canonical terms (e.g.
CONCEPTS.md,docs/concepts/).
What is NOT cached (always re-globbed fresh)¶
- The
docs/solutions/enumeration (a new learning, even uncommitted, must be visible — re-globbing it is ~free and the match reads files fresh anyway). - Subdirectory-scoped instruction files (area-scoped
CLAUDE.md/AGENTS.md). - All question-specific grounding: a candidate's precedent matches, prior decisions, feature patterns, git history of touched files, external research.
Cache location & key¶
<root-sha>= lexicographically-firstgit rev-list --max-parents=0 HEAD— the repo identity (stable, shared across worktrees and clones).<head-sha>=git rev-parse HEAD— the working state.
Keying on <root-sha>/<head-sha> means the several parallel worktrees this
project runs key separately and cannot serve a sibling worktree's profile.
Protocol — how a skill uses it¶
Resolve TOOLKIT_ROOT first (the Bash tool's cwd is the user's project, not the
plugin), then call the shared helper. TOOLKIT_ROOT is the directory containing
plugins/; from a skill's base directory plugins/core-standards/skills/<name>/
it is ../../../...
TOOLKIT_ROOT="<absolute path of the toolkit root (the dir containing plugins/)>"
python3 "$TOOLKIT_ROOT/plugins/core-standards/scripts/repo-profile-cache.py" get
get prints exactly one of:
HITthen the profile JSON on the following lines → load it as the agnostic profile; skip derivation.MISSthen a write-path on the next line → dispatch therepo-profilerpersona to derive the profile, write its JSON output to a file, then persist it. Thisputruns after the profiler, so it is a separate Bash-tool call from thegetabove — shell variables do not persist between calls, so re-setTOOLKIT_ROOTin the same command:NO-CACHE→ no git repo or no writable cache. Derive the profile fresh for this run and skipput(nothing to persist).
In all three cases, after the agnostic profile is in hand, run this skill's question-specific grounding fresh on top of it.
Freshness (delta-aware)¶
A cached entry is a HIT only when, at the current HEAD, its
profile_schema_version matches and no profile-input path is dirty or
newly-added. Freshness is checked with git status --porcelain
--untracked-files=all, so untracked (??) new inputs invalidate too. The
profile-input set is a conservative superset of every file the schema
derives from:
- dependency manifests + lockfiles (any depth, all ecosystems),
- license, root instruction/doc files, version selectors, topology sources
(
Dockerfile,.github/workflows/, …), - V025-specific registry/design-state files (SPEC-145 US-2):
.design-state.yaml,intake/projects.yaml,plugins/*/plugin.json,.claude-plugin/skill-symlinks.manifest.
A dirty source file or other non-input path does not invalidate. Completeness of this set is the cardinal-rule safety requirement: over-invalidating costs a re-derive; under-invalidating would serve a stale profile.
Degradation¶
The cache is an optimization, never a correctness dependency. Outside a git
repo, with no writable /tmp, or on an unreadable/malformed entry, the helper
returns NO-CACHE/MISS (exit 0) and the skill derives fresh. It never blocks
and never serves a profile it cannot prove fresh. And if the helper invocation
itself fails — a non-zero exit, empty output, or an unresolved TOOLKIT_ROOT
so the script isn't found — treat it exactly like NO-CACHE: derive the profile
fresh this run and proceed. Never stall waiting on the cache.