Plugin: source-control · Source: design review of the tracked convention config
Problem
.claude/source-control.md is resolved as a single project-level file — skills/commit/SKILL.md:25, skills/pull-request/SKILL.md:31, skills/setup/SKILL.md:38-42,146.
docs/MIGRATION-PLAYBOOK.md:196-240 (seam 2, "Tracked rich config under ${CLAUDE_PROJECT_DIR}") mandates three layers, resolved in a fixed order:
The proven shape is a single tracked file .claude/<plugin>.md (Markdown, for model-facing guidance) or .claude/<plugin>.yaml (structured rules), each with a gitignored *.local.* personal overlay and an optional ~/.claude/<plugin>.md user-global.
Resolution + override semantics. Resolve user-global → team (project) → local overlay, additive-preferred: a later layer adds to or refines earlier layers rather than silently replacing them.
Recommended consumer .gitignore. Ship the overlay convention with the one line the consumer adds: .claude/*.local.*
None of the three are implemented. source-control is one of eight fleet surfaces that are single-layer.
Consequence: a commit convention cannot follow an operator across repos or machines, and a personal deviation from team policy has nowhere to live. Per-machine reconfiguration currently requires editing the team-tracked file.
Reference implementation to mirror: plugins/toolchain/reference/resolution-ladder.md:31-35, which conforms exactly.
Scope
- Add the
~/.claude/source-control.md user-global layer.
- Add the
.claude/source-control.local.md gitignored personal overlay.
- Implement the mandated ladder — user-global → team → local — as rung 1 of the existing three-rung convention ladder. Rungs 2 (repo
CLAUDE.md/rules/commit-msg hook) and 3 (Conventional Commits default) are unchanged.
- Merge semantics: per-key override, documented as a deliberate deviation. The seam's default is concatenation (right for the
security-guidance precedent's prose blocks, meaningless for a regex — two subject_pattern values cannot concatenate). All four keys are scalars or closed lists, so this is the seam's sanctioned "genuinely must override … per key, never by dropping the base layer wholesale" case. Record it explicitly rather than deviating silently.
- Ship the recommended
.gitignore line to the consumer. Do not write to the consumer's root .gitignore — no setup skill in the fleet does, and the prohibition is stated at plugins/toolchain/skills/setup/SKILL.md:141 and docs/conventions/topic-docs/README.md:263.
- Teach
setup check / apply all three layers: check renders the effective merge and which layer won each key; apply gains user-global and local-overlay targets. The existing gitignore guard must split — a gitignored team file stays a hard STOP (skills/setup/SKILL.md:192-195), a gitignored local overlay is required.
- Fix the bundled-template defect (below).
plugin.json semver bump + plugin CHANGELOG.md entry.
Defect included in scope
Two bundled copies of the template exist and disagree:
plugins/source-control/skills/commit/.claude/source-control.md:22 — trailer_policy: none
plugins/source-control/skills/setup/.claude/source-control.md:22 — the full Co-Authored-By: Claude <model> (<context>) <noreply@anthropic.com> trailer
Same key, opposite default, in one plugin. Collapse to a single template.
Out of scope
- File-format change (
.md → .yaml or YAML frontmatter). A moved config path is a breaking change per docs/MIGRATION-PLAYBOOK.md:368-372 (semver bump plus a CHANGELOG entry in every adopting plugin). This layering work is additive and non-breaking; format is a separate decision and lands separately if at all.
- Per-subdirectory / monorepo scoping. Deferred, not excluded. Trigger: the first monorepo needing per-subproject convention. Exactly one fleet surface supports it today (
work-items, via a CWD→root climb at tools/work-item-tracker/lib/binding.sh:17-27); commit/SKILL.md:25 currently enforces the opposite for this plugin.
- Fleet-wide conformance and the governing-doc gap — see the linked issues.
Verification notes
- Built-in-first gate (
docs/PLUGIN-PHILOSOPHY.md:80-85) is cleared: userConfig provably cannot host this. Per https://code.claude.com/docs/en/plugins-reference#user-configuration, Claude Code "reads it back from user settings, the --settings flag, and managed settings only; entries in a project's .claude/settings.json or .claude/settings.local.json are ignored" — so a userConfig path or value cannot be team-shared. docs/PLUGIN-PHILOSOPHY.md:159-160 states the same rule locally.
- Security criterion 4 (
docs/MIGRATION-PLAYBOOK.md:657-659, "no reading consumer files outside ${CLAUDE_PROJECT_DIR}") is in apparent tension with seam 2's blessed ~/.claude/ user-global. The first-party security-guidance plugin reads ~/.claude/claude-security-guidance.md, so the reconciliation is that user-home config is the operator's own declared surface, not consumer repository data. Flagging for review rather than assuming it.
Related
Plugin:
source-control· Source: design review of the tracked convention configProblem
.claude/source-control.mdis resolved as a single project-level file —skills/commit/SKILL.md:25,skills/pull-request/SKILL.md:31,skills/setup/SKILL.md:38-42,146.docs/MIGRATION-PLAYBOOK.md:196-240(seam 2, "Tracked rich config under${CLAUDE_PROJECT_DIR}") mandates three layers, resolved in a fixed order:None of the three are implemented.
source-controlis one of eight fleet surfaces that are single-layer.Consequence: a commit convention cannot follow an operator across repos or machines, and a personal deviation from team policy has nowhere to live. Per-machine reconfiguration currently requires editing the team-tracked file.
Reference implementation to mirror:
plugins/toolchain/reference/resolution-ladder.md:31-35, which conforms exactly.Scope
~/.claude/source-control.mduser-global layer..claude/source-control.local.mdgitignored personal overlay.CLAUDE.md/rules/commit-msg hook) and 3 (Conventional Commits default) are unchanged.security-guidanceprecedent's prose blocks, meaningless for a regex — twosubject_patternvalues cannot concatenate). All four keys are scalars or closed lists, so this is the seam's sanctioned "genuinely must override … per key, never by dropping the base layer wholesale" case. Record it explicitly rather than deviating silently..gitignoreline to the consumer. Do not write to the consumer's root.gitignore— no setup skill in the fleet does, and the prohibition is stated atplugins/toolchain/skills/setup/SKILL.md:141anddocs/conventions/topic-docs/README.md:263.setup check/applyall three layers:checkrenders the effective merge and which layer won each key;applygains user-global and local-overlay targets. The existing gitignore guard must split — a gitignored team file stays a hard STOP (skills/setup/SKILL.md:192-195), a gitignored local overlay is required.plugin.jsonsemver bump + pluginCHANGELOG.mdentry.Defect included in scope
Two bundled copies of the template exist and disagree:
plugins/source-control/skills/commit/.claude/source-control.md:22—trailer_policy: noneplugins/source-control/skills/setup/.claude/source-control.md:22— the fullCo-Authored-By: Claude <model> (<context>) <noreply@anthropic.com>trailerSame key, opposite default, in one plugin. Collapse to a single template.
Out of scope
.md→.yamlor YAML frontmatter). A moved config path is a breaking change perdocs/MIGRATION-PLAYBOOK.md:368-372(semver bump plus a CHANGELOG entry in every adopting plugin). This layering work is additive and non-breaking; format is a separate decision and lands separately if at all.work-items, via a CWD→root climb attools/work-item-tracker/lib/binding.sh:17-27);commit/SKILL.md:25currently enforces the opposite for this plugin.Verification notes
docs/PLUGIN-PHILOSOPHY.md:80-85) is cleared:userConfigprovably cannot host this. Per https://code.claude.com/docs/en/plugins-reference#user-configuration, Claude Code "reads it back from user settings, the--settingsflag, and managed settings only; entries in a project's.claude/settings.jsonor.claude/settings.local.jsonare ignored" — so auserConfigpath or value cannot be team-shared.docs/PLUGIN-PHILOSOPHY.md:159-160states the same rule locally.docs/MIGRATION-PLAYBOOK.md:657-659, "no reading consumer files outside${CLAUDE_PROJECT_DIR}") is in apparent tension with seam 2's blessed~/.claude/user-global. The first-partysecurity-guidanceplugin reads~/.claude/claude-security-guidance.md, so the reconciliation is that user-home config is the operator's own declared surface, not consumer repository data. Flagging for review rather than assuming it.Related