shared-asset-convention
Shared Assets Live Under .claude/, Never at the Kit Root
Section titled “Shared Assets Live Under .claude/, Never at the Kit Root”A shared asset is a file two or more modules depend on and neither owns — a template, a
fixture, a shared reference table. It lives at .claude/modules/<name>/_shared/, published by the
module that maintains it. A kit-wide asset no single module maintains lives at the kit-level
fallback .claude/_shared/. A bare kit-root _shared/ is a bug — file /t1k:issue against
the owning kit rather than relocating it in a consumer.
Resolution order on a path collision is the existing 4-layer registry priority stack — module
overlay p91+ → engine kit p90 → designer p50 → core p10 — identical to how t1k-routing-*,
t1k-activation-* and t1k-config-* fragments already merge. Highest priority wins; a per-module
_shared/ shadows the kit-level fallback at the same layer. Nothing may resolve a _shared/
path by scan order or extraction order, which is what makes the loser depend on which ZIP landed
second.
Every _shared/ path is claimed by exactly one module (module.json). An unclaimed
_shared/ file does not ship, exactly like an unclaimed script.
Why not the kit root
Section titled “Why not the kit root”Every other kit-shipped asset — rules/, skills/, agents/, hooks/, scripts/ — already
lives under .claude/. A bare kit-root _shared/ would be the only kit-shipped content outside
that tree, so it complicates install-time flattening (rule-module-ownership.md § “flattening
model”) and every doctor check that walks .claude/ for kit-owned content. Per-module-with-a-
fallback is also the reversible choice: it can be narrowed or widened later without moving a
consumer’s install path.
How to apply
Section titled “How to apply”- Adding a shared asset — put it under the maintaining module’s
_shared/, claim it in thatmodule.json, and regenerate.claude/t1k-modules.jsonin the SAME commit (module-registry-sync.md). - No module maintains it — that is usually a missing module, not a licence to go kit-root.
Use
.claude/_shared/only for a genuinely kit-wide asset with no owner. - Two modules want the same path — do not ship two copies. One module owns it; the other depends on that module.
- Referencing one — address it by its
_shared/path, never by a sibling module’s directory.
Not yet enforced end-to-end
Section titled “Not yet enforced end-to-end”Collection into the release ZIP, install-time flattening, and the consumer-side resolution gate
live in theonekit-release-action, which owns the registry merge — the gate half landed at
release-action#154 and the rest is still to come. Until it does, this rule is the decision and the
placement is advisory: core’s own tree is checked by
hooks/__tests__/shared-asset-convention.test.cjs, which is not a consumer-side guarantee.
Explicitly not in scope: deletions[] is retired in favour of canonical-files.json and gate
#20 (validate-deletions-removed.cjs) forbids re-adding it.
Rejected alternatives, the collision worked example, and the release-action wiring still owed:
docs/shared-asset-convention.md.
Related
Section titled “Related”rule-module-ownership.md (the same module-owns-it model for rules) · module-registry-sync.md
(regenerating the rollup) · wired-not-just-present.md (assert the path the runtime loads, not the
repo copy) · docs/shared-asset-convention.md.