5.4 KiB
title, date, category, module, problem_type, component, severity, applies_when, tags
| title | date | category | module | problem_type | component | severity | applies_when | tags | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Building a shared cached primitive across self-contained skills | 2026-06-29 | docs/solutions/skill-design/ | repo-grounding-cache | architecture_pattern | tooling | medium |
|
|
Building a shared cached primitive across self-contained skills
Context
Repo-grounding skills (ce-pov, ce-plan, ce-optimize, ce-ideate, ce-brainstorm, ce-code-review, ce-compound, ce-debug) each independently re-derived the same question-agnostic "project profile" (stack, deps, conventions, structure) on every run — an expensive grounding pass repeated per skill and per invocation. We wanted one shared, cached primitive. But the plugin's hard constraint (AGENTS.md "File References in Skills") forbids cross-skill imports: the converter copies each skill directory as an isolated unit, so a skill may only reference files inside its own tree. There is no shared-module mechanism.
Guidance
Build the "shared" primitive as byte-duplicated assets plus a parity test, not a shared import:
- Duplicate the assets into every consuming skill. Here that was three files per skill: a protocol/schema reference (
references/repo-profile-cache.md), a bundled helper script (scripts/repo-profile-cache.py), and a derivation persona (references/agents/repo-profiler.md). Each consumer carries identical copies. - Guard file drift with a
tests/byte-identity test — declare the asset filenames x the consumer-skill list and assert each copy equals the first (mirrortests/compound-support-files.test.ts). Adding a consumer = drop in the copies + add its name to the test'sCONSUMER_SKILLS. - Invoke the bundled script via the
SKILL_DIRanchor, never${CLAUDE_SKILL_DIR}(seedocs/solutions/skill-design/bundled-script-path-resolution-across-harnesses.md). - Put deterministic work in the script, judgment in the persona. The Python helper does git keying + validity + atomic read/write (unit-testable); the LLM persona derives the profile only on a miss.
- Share state through a single OS-temp location, keyed by content/identity, so any skill reads what another wrote (
/tmp/compound-engineering/repo-profile/<root-sha>/<head-sha>.json).
Why This Matters
The parity test guards file drift but not integration drift: a consumer can carry byte-identical assets yet wire the grounding phase wrong (skip the cache, or — worse — skip the still-required question-specific grounding). Two layers are needed:
- Parity test (
bun test) — proves the duplicated files are identical across skills. - Per-consumer
skill-creatorgrounding-phase eval — proves each skill actually uses the primitive correctly: takes the agnostic slice from the cache AND keeps its question-specific work fresh. This is the only check that catches integration drift, and it is manual (not run by CI).
One more unguarded seam: per-skill field reads (e.g. a SKILL.md that reads conventions.testing from the profile JSON) are not byte-duplicated, so renaming a schema field passes the parity test and the version bump yet silently breaks consumers — document a "grep the consumers for the field" step in the schema-change checklist.
When to Apply
- Several skills re-derive the same stable, question-agnostic data and you want to compute it once.
- The shared thing is genuinely reusable across skills (not one skill's private concern).
- You can express the shared contract as a small, self-contained asset set (reference + script + persona) rather than a code dependency.
Do not reach for this when only one skill needs the data, or when the "shared" data is actually question-specific per skill (then there is nothing stable to cache).
Examples
The validation layering that made the difference:
tests/repo-profile-cache-parity.test.ts # FILE drift: 3 assets x 8 skills byte-identical
skill-creator evals (per consumer) # INTEGRATION drift: agnostic-from-cache AND
# question-specific-still-fresh, per skill
The grounding-phase eval pattern is cheap and high-signal precisely because the cache logic lives at the front of each skill — you can drive a fresh subagent through just the grounding phase (cache HIT/MISS/NO-CACHE) and observe its decisions without running the whole skill. That pattern caught real wiring issues on every batch it ran.
Related
docs/solutions/skill-design/bundled-script-path-resolution-across-harnesses.md— theSKILL_DIRanchor used to invoke the shared scriptdocs/solutions/skill-design/script-first-skill-architecture.md— deterministic-script / model-presents splitdocs/solutions/best-practices/cache-invalidation-input-set-completeness.md— the cardinal rule for the cache this pattern shippeddocs/solutions/skill-design/paired-old-vs-new-injection-skill-evals.md— generalizes the field-rename parity gap noted here into a rule: prove the anti-drift test fails on one-sided drift before trusting it.- AGENTS.md "Shared Repo-Grounding Profile Cache" and "File References in Skills"