Kubernetes support — implementation plan
- **Feature:** kubernetes-support - **Design:** [design.md](design.md) - **Tasks:** [tasks.md](tasks.md) - **Status:** implementation-plan - **Branch:** `k8s_support`
Overview
Kubernetes support — implementation plan
- Feature: kubernetes-support
- Design: design.md
- Tasks: tasks.md
- Status: implementation-plan
- Branch:
k8s_support
This document is a step-by-step guide for implementing the design. Each step is paired with an explicit verification. Tasks in tasks.md reference the corresponding steps here.
Conventions
- All file paths in this document are relative to the repository root unless noted otherwise.
- Each step's verify clause specifies how the implementer confirms the step succeeded. Steps without a verification are not considered complete.
- Commits are atomic per task in tasks.md; a task may span multiple steps here, but the steps collectively map to one self-contained commit.
- The order of steps within a phase is significant; dependencies are stated explicitly where a later step requires the output of an earlier one.
Prerequisites
Before starting P0:
- The three ADRs (0001, 0002, 0003) have been authored in
docs/adr/. docs/wip/kubernetes-support/exists with this file,design.md, andtasks.md.- The branch
k8s_supportis checked out (confirm withgit branch --show-current). - The plugin-structure test currently passes on the branch (confirm with
bash test/test-plugin-structure.sh— captures the pre-P0 baseline).
Phase 0 — Profile-first refactor
Goal. Introduce klaude-plugin/profiles/ as a top-level directory; migrate programming-language checklist content from klaude-plugin/skills/review-code/reference/<lang>/ to klaude-plugin/profiles/<lang>/review-code/; author the shared detection procedure and the six consumer symlinks; restructure the review-code workflow to be index-driven; update the plugin-structure test, CLAUDE.md, and README.md. Behavior must remain equivalent to pre-P0 when review-code is invoked on diffs affecting the existing programming-language profiles.
Step 0.1 — Create the profiles/ top level and migrate programming-language checklists
For each language in (go, python, java, js_ts, kotlin):
- Create directory
klaude-plugin/profiles/<lang>/review-code/. - Move the existing files from
klaude-plugin/skills/review-code/reference/<lang>/toklaude-plugin/profiles/<lang>/review-code/usinggit mv(preserves history). - Author
klaude-plugin/profiles/<lang>/DETECTION.mdusing the mandatory three-section schema (see design.md §Detection mechanics):## Path signals(empty for programming-language profiles; file-extension detection does not involve path heuristics),## Filename signals(empty — language detection is extension-based, not filename-based),## Content signals(the file-extension rule: "any file whose extension matches.go/.py/ etc."). The three sections must be present even when empty, so the shared procedure iterates predictably. - Author
klaude-plugin/profiles/<lang>/overview.md— a one-page summary: what the profile covers (the programming language), when it activates, and "Looking up dependencies" targets (context7, language-specific references). - Author
klaude-plugin/profiles/<lang>/review-code/index.md— lists the four migrated files (security-checklist.md,solid-checklist.md,code-quality-checklist.md,removal-plan.md) in the "Always load" section with one-line descriptions. No conditional entries for programming-language profiles. - Remove the now-empty
klaude-plugin/skills/review-code/reference/<lang>/directory.
After iterating through all five languages, remove klaude-plugin/skills/review-code/reference/ itself (now empty).
Audit downstream migration impact. Check .github/scripts/template-sync.sh for a run_plugin_migration function with a dirs_to_remove array (or equivalent mechanism). Determine whether the removal of klaude-plugin/skills/review-code/reference/ requires an entry there to clean up downstream projects that have migrated from an older template version. Document the decision (add an entry OR document that no entry is needed — CLAUDE.md's "don't touch historical entries" directive covers pre-v0.5.0 historical paths, not new migrations introduced by this feature). If an entry is added, follow the existing entry format and date the addition.
Verify.
ls klaude-plugin/profiles/shows exactlygo,java,js_ts,kotlin,python(plus, after P1,k8s).ls klaude-plugin/skills/review-code/reference/returns "No such file or directory".- For each language:
test -f klaude-plugin/profiles/<lang>/{DETECTION.md,overview.md,review-code/index.md}succeeds. - For each language: the profile's
DETECTION.mdcontains the three required sections (## Path signals,## Filename signals,## Content signals) — any may be empty but all three headings must be present. git log --follow klaude-plugin/profiles/go/review-code/security-checklist.mdshows history continuous with the old path.- Template-sync audit decision documented in the PR description or as a comment in
template-sync.sh.
Step 0.2 — Author the shared profile-detection procedure
Create klaude-plugin/skills/_shared/profile-detection.md with the following mandatory sections:
- Purpose. Single source of truth for "compute the set of active profiles for the current context." Used by six consuming skills; the shared file exists to prevent interpretation drift.
- Per-consumer input model. Enumerate what each consuming skill passes as detection input:
review-code→ git diff (staged or explicit range, scoped to the diff's touched files).review-spec→ git diff when invoked standalone; the feature directory's full file list when invoked byimplement.test→ git diff (mid-feature) or full feature-directory file list (post-implementation).implement→ the current sub-task's target file list, optionally augmented by the diff accumulated so far.design→ user-declared signal OR keyword inference from the idea prose. Detection in the design phase is not file-based. The file must spell out the interaction: "check idea prose against the high-precision auto-trigger set (Kubernetes,K8s,Helm chart,kubectl,kustomize,manifest.yaml,Deployment resource,StatefulSet,DaemonSet,CronJob); if any match, ask the user to confirm activation. If no auto-trigger matches but the idea is ambiguous — names infrastructure, deployment, runtime, or platform concerns without naming a specific technology, or includes tokens likecluster/namespace/podthat collide with non-K8s meanings — ask explicitly." Rationale: the narrow auto-trigger set avoids noisy false positives from overloaded tokens.document→ the feature directory's current file list; diff is optional.
- Detection algorithm. Iterate
klaude-plugin/profiles/*/DETECTION.md. For each profile, evaluate signals in cost order (path → filename → content). Apply authority rule: filename or content signal matches activate the profile; path signal alone does not activate. Bounded content inspection: ~16 KB per file; multi-document YAML inspected per----separated block. Every profile'sDETECTION.mduses the three-section schema (## Path signals/## Filename signals/## Content signals); the shared procedure applies the same algorithm against each profile's declared values. - Unset-variable check. Before returning results, verify
${CLAUDE_PLUGIN_ROOT}is set. If unset: emit a loud error message naming the variable and pointing at CLAUDE.md's Profile Conventions section; return the empty-set result so consumers fall back to generic guidance rather than panicking. (Per ADR 0003 §Decision, the brace form is mandatory —$CLAUDE_PLUGIN_ROOTwithout braces is NOT substituted by the Claude Code harness and will never be used here.)
Authoring caveat for the shared file itself. klaude-plugin/skills/_shared/profile-detection.md lives INSIDE the plugin tree and is therefore subject to ${CLAUDE_PLUGIN_ROOT} substitution when an agent reads it. When the file needs to describe the variable BY NAME (e.g., "check that $CLAUDE_PLUGIN_ROOT is set"), use the bare form $CLAUDE_PLUGIN_ROOT (without braces) — the bare form is not substituted (verified 2026-04-18, ADR 0003 §Verification). When the file uses the variable as a PATH that must resolve at runtime (e.g., ${CLAUDE_PLUGIN_ROOT}/profiles/*/DETECTION.md), use the brace form — that is the intended substitution. Mixing the two conventions in the same file is normal and correct.
5. Output shape. A list of records, one per matched profile: {profile: <name>, triggered_by: [<signal descriptions>], files: [<paths>]}. triggered_by names the signal type that fired (e.g., "filename: Chart.yaml", "content: apiVersion+kind in block 2").
Verify. test -f klaude-plugin/skills/_shared/profile-detection.md. The file contains all five sections by heading. File size is ~120–200 lines (a few pages; not a brief summary). Content is readable by a skilled contributor unfamiliar with the plugin — pass it to a colleague for a sanity read.
Step 0.3 — Create the six consumer symlinks
Create the following symlinks (each is shared-profile-detection.md pointing at ../_shared/profile-detection.md):
klaude-plugin/skills/review-code/shared-profile-detection.mdklaude-plugin/skills/review-spec/shared-profile-detection.mdklaude-plugin/skills/design/shared-profile-detection.mdklaude-plugin/skills/implement/shared-profile-detection.mdklaude-plugin/skills/test/shared-profile-detection.mdklaude-plugin/skills/document/shared-profile-detection.md
Verify.
- For each symlink path P:
test -L Psucceeds;readlink Preturns../_shared/profile-detection.md;realpath Presolves to the shared file.
Step 0.4 — Restructure the review-code workflow
Update the following files to consume shared-profile-detection.md and the index-driven loading pattern. Each file needs specific literal-string replacements, not just behavioral intent — the old prose hardcodes reference/<lang>/ paths in multiple places, and the grep verification below catches missed replacements.
klaude-plugin/skills/review-code/SKILL.md— prose gains a sentence linking[shared-profile-detection.md](shared-profile-detection.md)(per CLAUDE.md convention: consumers reference the per-skill symlink, not the shared source). No change to the description frontmatter.klaude-plugin/skills/review-code/review-process.md:- "Step 2: Detect primary language" renames to "Step 2: Detect active profiles" and delegates to the shared procedure.
- Former Steps 3–6 (SOLID / Removal / Security / Quality) collapse into:
- "Step 3: Load profile review indexes." For each active profile, resolve
${CLAUDE_PLUGIN_ROOT}/profiles/<name>/review-code/index.md; collect always-load entries and conditional entries whose stated triggers match the diff. - "Step 4: Apply checklists." Iterate the resolved checklists; each checklist's findings are emitted with
(profile, checklist)as the grouping key.
- "Step 3: Load profile review indexes." For each active profile, resolve
- Subsequent steps (self-check, indexing, output formatting) are renumbered but otherwise unchanged.
- Replace every literal occurrence of
reference/<lang>/andreference/{lang}/(any variant) with the${CLAUDE_PLUGIN_ROOT}/profiles/...equivalent or a description of the index-driven loading step.
klaude-plugin/skills/review-code/review-isolated.md— the same restructure pattern, adapted for the isolated sub-agent variant.- Literal path string to replace: the sub-agent prompt template in Step 2 currently injects
klaude-plugin/skills/review-code/reference/{language_key}/into the spawned agent's prompt. This literal string must be replaced with the list of resolved checklists (produced in Step 1 when preparing the scope block). The sub-agent receives the list, not a path. - Replace every other literal occurrence of
reference/<lang>/in the file.
- Literal path string to replace: the sub-agent prompt template in Step 2 currently injects
klaude-plugin/agents/code-reviewer.md— the prompt updates to iterate the(profile, checklist)list it is given, rather than iterating fixed category names.- Literal path string to replace: the agent's current Step 2 says "Load the corresponding reference checklists from
klaude-plugin/skills/review-code/reference/{lang}/" with the full extension table duplicated. Rewrite to: "Apply the checklists provided in the input payload; for each(profile, checklist)record, read the checklist content from${CLAUDE_PLUGIN_ROOT}/profiles/<profile>/review-code/<checklist>and apply it to the diff." Remove the extension table (detection is no longer the agent's responsibility; the calling skill has already produced the list).
- Literal path string to replace: the agent's current Step 2 says "Load the corresponding reference checklists from
Verify.
- Grep check (expanded scope to include
agents/):grep -rn 'reference/' klaude-plugin/skills/review-code/ klaude-plugin/agents/code-reviewer.mdreturns no lines. If any remain, replacement was incomplete. - Grep check:
grep -rn '${CLAUDE_PLUGIN_ROOT}/profiles/' klaude-plugin/skills/review-code/returns matches at the relevant points (Step 3 of review-process.md, equivalent in review-isolated.md). - Manual dry-run: invoke
/kk:review-codeon a Go-only diff (e.g., a recent commit touching only.gofiles). The output identifies thegoprofile as active and loads the four checklists now atprofiles/go/review-code/. Findings coverage and categories match pre-P0 output qualitatively. klaude-plugin/agents/code-reviewer.mdparses cleanly (front-matter valid, instructions coherent on a manual read).
Step 0.5 — Update the plugin-structure test
Modify test/test-plugin-structure.sh:
- Add
EXPECTED_PROFILES=("go" "java" "js_ts" "kotlin" "python")(k8swill be added in alphabetical position in Step 1.3 of P1, after k8s content files exist; the array is alphabetised and the structure test treats it as a set). - Per-profile assertions (presence-conditional). Assertions are predicated on what the profile declares; they do NOT require every profile to populate every phase subdirectory:
- Directory
klaude-plugin/profiles/<name>/exists (required for every profile inEXPECTED_PROFILES). - File
klaude-plugin/profiles/<name>/DETECTION.mdexists (required). DETECTION.mdcontains the three required section headings (## Path signals,## Filename signals,## Content signals). Any may be empty; all must be present. Usegrep -c '^## Path signals' ...and similar to verify each header appears exactly once.- File
klaude-plugin/profiles/<name>/overview.mdexists (required). - For each phase subdirectory name in (
review-code,design,test,implement,document,review-spec): IF `klaude-plugin/profiles/<
- Directory