T-R4XL-project-local-skill-extension-mechanism
Status: closed/done · Impact: high · Complexity: medium
Provide a clean way for a consuming project to add project-specific
behavior to an upstream SDLC skill (or any plugin skill) without
modifying the upstream skill’s prose. Today, friction observed during
SDLC-on-SDLC development tends to land as PRs against
plugin/skills/<name>/SKILL.md that hard-code SDLC-specific path
patterns into a skill every downstream consumer also uses (e.g.
task-work learning to scan ## Files to touch for
plugin/skills/*/SKILL.md). That contaminates the general skill with
behavior only ever exercised by the SDLC plugin’s own dev repo.
Closing this gap gives such friction a project-local home so the
upstream skills stay general.
plugin/skills/<name>/SKILL.mdis loaded by the Claude Code runtime from${CLAUDE_PLUGIN_ROOT}(i.e. the installed plugin path). The consuming project has no documented way to add behavior on top of an upstream skill — modifications mean a PR against the plugin’s repo.- The project-check skill at
.claude/skills/project-check/is the one precedent for a fully project-local skill (its own SKILL.md declares “not part of the distributed sdlc plugin”). But it’s a standalone skill, not an extension OF an upstream skill. - Claude Code already supports project-local hooks via
.claude/settings.json(PreToolUse, PostToolUse, Stop, etc.). These can fire scripts at well-known events but don’t have a documented integration with the skill flow. - Recent post-mortem-spawned tasks that exhibit the contamination
pattern (now parked pending this mechanism):
- T-FWZH-task-work-detects-self-modifying-skill —
task-work scans for
plugin/skills/*/SKILL.mdin## Files to touch. - T-P4VB-task-define-prompts-for-executable-shadow —
task-define detects
plugin/skills/<skill>/SKILL.mdtouches and prompts forplugin/skills/<skill>/tests/run_evals.py.
- T-FWZH-task-work-detects-self-modifying-skill —
task-work scans for
Proposed
Section titled “Proposed”Define a convention that lets a consuming project (today: the SDLC plugin’s own dev repo) add behavior to an upstream skill without editing the upstream skill’s prose. Evaluate two complementary design directions:
-
(A) Hook-based extension points. Each major SDLC skill declares named extension points in its SKILL.md prose (e.g. “after Step 1, any project-local hook at
.claude/hooks/task-work-step1-post.shis invoked with the task path as argument”). Consuming projects add scripts at those paths. The skill’s prose explicitly enumerates the hook contract (inputs, outputs, exit-code semantics) so the hook is a stable contract, not implicit composition. -
(B) Project-local skill extension SKILL.md. A consuming project drops
.claude/skills/<upstream-name>-extension/SKILL.md. When the upstream skill runs, it checks for this file and — if present — treats it as additional procedural prose layered on top of the base steps. The extension file declares which step(s) it augments. Closer to “inheritance” than to “hooks”; better for prose-shaped extensions that don’t fit a single shell-out hook.
Either direction (or both, used at different layers) must satisfy:
upstream skill prose remains general, project-specific friction
lives under .claude/, and the two together produce the augmented
behavior at runtime.
Approach
Section titled “Approach”- Survey the friction examples currently parked on this task
(
task-work-detects-self-modifying-skill,task-define-prompts-for-executable-shadow). For each, sketch what the project-local extension would look like under both (A) and (B). The sketches inform which mechanism fits which kind of extension. - Pick (A) vs (B) vs both. Document the convention in
plugin/conventions/project-local-skill-extension.md(per the factor-shared-prose rule inplugin/skills/CLAUDE.md). - For (A): document the hook-event taxonomy each consuming project can rely on (which skills declare hooks at which steps, what the input/output contract is). The hook contract goes in each affected SKILL.md’s Notes section, referencing the convention doc.
- For (B): document the SKILL.md extension-file shape (what frontmatter the extension file carries, how the upstream skill discovers and composes it). The discovery rule goes in each affected SKILL.md.
- Land one of the parked carve-out tasks (likely
task-work-detects-self-modifying-skillas the simpler case) using the new mechanism, as a working example that both proves the convention and serves as a reference for the other.
Files to touch
Section titled “Files to touch”plugin/conventions/project-local-skill-extension.md(new) — the convention doc covering whichever mechanism(s) get adopted.plugin/skills/CLAUDE.md— cross-reference the new convention.plugin/skills/<affected>/SKILL.md— at least one upstream skill that declares extension points / discovery, to validate the convention..claude/skills/<affected>-extension/SKILL.mdOR.claude/hooks/<...>(new, this repo) — the working-example extension that closes the parked carve-out task.
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
plugin/conventions/project-local-skill-extension.mdexists and unambiguously specifies at least one mechanism (hooks OR SKILL.md extension) including the contract for inputs, outputs, and discovery. - AC-2: At least one upstream skill’s SKILL.md references the new convention and declares its extension points / discovery rule.
- AC-3: One of the parked carve-out tasks
(
task-work-detects-self-modifying-skillis the simplest target) ships its behavior via the new mechanism — implemented under.claude/in this repo, NOT by editing the upstreamplugin/skills/task-work/SKILL.md. The behavior is observable from a real/sdlc:task-workrun.
Out of scope
Section titled “Out of scope”- Migrating existing
.claude/skills/project-check/(which is a standalone skill, not an extension of an upstream one) to the new convention. project-check’s shape doesn’t need it. - Designing a generic “skill inheritance” framework that handles arbitrary skill composition. The scope is project-local augmentation of upstream skills, not arbitrary skill mixins.
- Building a hook registry / orchestrator. The hooks fire via Claude Code’s existing settings.json mechanism (if mechanism A is adopted); no new harness is required.
Dependencies
Section titled “Dependencies”- none (depends on Claude Code’s existing project-local
.claude/infrastructure, which already supports hooks and project-local skills)
Discovery context
Section titled “Discovery context”Filed 2026-05-21 during a /sdlc:task-review walkthrough that
surfaced multiple post-mortem-spawned tasks proposing
plugin/skills/*/SKILL.md carve-outs in general skills. The user
named the pattern explicitly: “If it’s a fix that just improves
task-work for how it works against the SDLC project itself — that
seems wrong.” This task creates the home such fixes should live
in.
Post-mortem
Section titled “Post-mortem”Captured by /sdlc:task-work on 2026-05-21. PR: pending.
Acceptance criteria coverage
Section titled “Acceptance criteria coverage”- AC-1: auto —
plugin/conventions/project-local-skill-extension.mdexists and specifies the hook-based mechanism with explicit path shape, input contract (env varTASK_FILE), exit-code semantics (informational, ignored), and discovery rule ([ -x <path> ]test). Verified by reading the committed file. - AC-2: auto —
plugin/skills/task-work/SKILL.mdStep 1a declares the extension point and references the convention; a Notes-section bullet at the bottom cross-links it (per the factor-shared-prose rule inplugin/skills/CLAUDE.md). Verified by reading the committed diff. - AC-3: agent-manual — ran
.sdlc/skill-ext/task-work/step1-post.shagainst three task files: (a) the parked carve-out2026-05-20-task-work-detects-self-modifying-skill.md(hasplugin/skills/task-work/SKILL.mdin Files to touch) — emitted the runtime/worktree note as expected; (b)2026-05-20-task-work-ancestry-helper-one-liner.md(also touches a SKILL.md) — emitted the note; (c)2026-05-20-headless-bases-syntax-parser-for-eval.md(no SKILL.md reference) — emitted nothing, exit 0. The behavior observed lives entirely under.claude/; no edit to the upstream task-work SKILL.md was needed.
What worked
Section titled “What worked”- The “shell-out hook with documented contract” shape converged quickly — no design back-and-forth needed once the alternative (project-local SKILL.md layering) was dismissed for being fuzzier-contracted than a script.
- Closing the parked carve-out task
(
2026-05-20-task-work-detects-self-modifying-skill) asclosed/donein the same PR makes the working example legible: one PR demonstrates both the mechanism and the first user. - Quality checks (7/7) and skill-prose invariants both passed first try; the convention doc didn’t break any existing check.
Friction and automation gaps
Section titled “Friction and automation gaps”.claude/was gitignored except for an explicit allowlist (!.claude/hooks/,!.claude/skills/, etc.). Adding the new.sdlc/skill-ext/tree required editing.gitignoreto add another carve-out, which is easy to miss. A/sdlc:setup-style scaffold step for “register a new project-local extension tree” would automate the un-ignore step so the next consumer doesn’t silently commit nothing. → T-ZZD1-setup-generalizes-claude-subtree-unignoretask-workStep 6 says “delegate implementation to a sub-agent” but theAgenttool was not available in this run (not in allowed-tools, not surfaced as a deferred tool). The skill proceeded by doing the implementation inline, which worked for a small task but would have scaled poorly. Either soften the prose to “delegate IF the Agent tool is available, otherwise implement inline” or ensure the Agent tool is wired into the harness for skills that name it as required. → T-PZW5-task-work-step-6-inline-impl-fallback- The hook contract specifies “working directory: the project root”, but
task-work’s Step 1a prose doesn’t show the actual invocation shape that sets cwd correctly. A small reference invocation in the convention doc (e.g.(cd "$PROJECT_ROOT" && TASK_FILE=... ./hook.sh)) would make the contract executable rather than declarative. → T-ZQ3M-project-local-extension-doc-shows-invocation
Spawned follow-up tasks
Section titled “Spawned follow-up tasks”- T-ZZD1-setup-generalizes-claude-subtree-unignore — created;
generalize
/sdlc:setup’s.gitignoreseeding to cover any well-known committed.claude/<subtree>/, includingsdlc-ext/. - T-PZW5-task-work-step-6-inline-impl-fallback — linked existing; previously spawned by another post-mortem covering the same Agent-tool-availability gap.
- T-ZQ3M-project-local-extension-doc-shows-invocation — created; add a copy-pasteable shell invocation to the convention doc so the hook contract is executable, not just declarative.