Files
2026-07-13 12:20:01 +08:00

4.8 KiB

Shared Repo-Grounding Profile Cache

Read this when a repo-grounding skill needs the question-agnostic project profile (stack, deps, conventions, structure). 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.

This file is byte-duplicated into every consuming skill (the plugin has no cross-skill import mechanism). All copies must stay identical; tests/repo-profile-cache-parity.test.ts enforces it. The deterministic cache I/O lives in the co-located scripts/repo-profile-cache.py; 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:

  • Stack & versions — languages, major frameworks + versions, build/test tooling.
  • Dependency surface — manifest + lockfile paths, top-level dependencies, project license + dependency licenses.
  • Topology — monorepo/workspace map, deployment model, API styles, data stores, module layout.
  • Conventions & instruction files — paths + digests of the root AGENTS.md/CLAUDE.md/GEMINI.md/ARCHITECTURE.md/README.md/CONTRIBUTING.md/STRATEGY.md.
  • VocabularyCONCEPTS.md canonical terms.

What is NOT cached (always re-globbed fresh)

Never read from the cache — recompute every run:

  • 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 call-sites/footprint, prior-decision matches, feature patterns, git history of touched files, tracker/PR activity, external research.

Cache location & key

/tmp/compound-engineering/repo-profile/<root-sha>/<head-sha>.json
  • <root-sha> = lexicographically-first git rev-list --max-parents=0 HEAD — the repo identity (stable, shared across worktrees and clones).
  • <head-sha> = git rev-parse HEAD — the working state.

Two checkouts at the same commit share the same entry. Lookup is git metadata only; on a hit, only this one file is read.

Protocol — how a skill uses it

Invoke the helper via the SKILL_DIR anchor (set SKILL_DIR to the absolute path of the directory containing the SKILL.md you just read; the Bash tool's cwd is the user's project, not the skill dir):

SKILL_DIR="<absolute path of this skill's directory>";
python3 "$SKILL_DIR/scripts/repo-profile-cache.py" get

get prints exactly one of:

  • HIT then the profile JSON on the following lines → load it as the agnostic profile; skip derivation.
  • MISS then a write-path on the next line → dispatch the repo-profiler persona to derive the profile, write its JSON output to a file, then persist it. This put runs after the profiler, so it is a separate Bash-tool call from the get above — shell variables do not persist between calls, so re-set SKILL_DIR in the same command:
    SKILL_DIR="<absolute path of this skill's directory>";
    python3 "$SKILL_DIR/scripts/repo-profile-cache.py" put <profile-json-file>
    
  • NO-CACHE → no git repo or no writable cache. Derive the profile fresh for this run and skip put (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), license, root instruction/doc files, CONCEPTS.md/STRATEGY.md, topology sources (Dockerfile, .github/workflows/, .cursor/rules). A dirty source file, docs/plans/*, 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 SKILL_DIR 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.