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

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
Multiple skills independently re-derive the same expensive, question-agnostic data
You want one shared cache/helper/persona reused across skills that cannot import each other
Adding a cross-skill optimization where the plugin's self-contained-skill rule forbids shared modules
cross-skill
cache
skill-design
duplication
parity-test
skill-creator-eval

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:

  1. 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.
  2. 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 (mirror tests/compound-support-files.test.ts). Adding a consumer = drop in the copies + add its name to the test's CONSUMER_SKILLS.
  3. Invoke the bundled script via the SKILL_DIR anchor, never ${CLAUDE_SKILL_DIR} (see docs/solutions/skill-design/bundled-script-path-resolution-across-harnesses.md).
  4. 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.
  5. 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-creator grounding-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.

  • docs/solutions/skill-design/bundled-script-path-resolution-across-harnesses.md — the SKILL_DIR anchor used to invoke the shared script
  • docs/solutions/skill-design/script-first-skill-architecture.md — deterministic-script / model-presents split
  • docs/solutions/best-practices/cache-invalidation-input-set-completeness.md — the cardinal rule for the cache this pattern shipped
  • docs/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"