feat(source-control): setup writes a self-describing team convention file - #1055
Conversation
…file Closes #1046 (audit umbrella #912, finding f6 — declared folded into #913 but never delivered by PR #925). The team-tracked .claude/source-control.md lands in shared history with no signal for a teammate who does not run these plugins. The setup apply template now opens with a self-describing preamble: read by the source-control plugin (and the guardrails commit-convention gate where installed), inert without them, a drafting aid — not team-wide enforcement, which is a commit-msg hook or CI check. The apply report for a team write states the same distinction. The header is template-owned (rewritten in place on reconfiguration, never appended twice), and prose above the first H2 is inert by construction — a new regression case in resolve-convention-pattern.test.sh proves a preambled file resolves identically to a bare one. source-control 0.17.0 -> 0.17.1 with CHANGELOG entry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 7s —— View job PR Review: feat(source-control): setup writes a self-describing team convention file
SummaryThis PR delivers audit finding f6 from #912 that was claimed in #913 but never shipped in #925. The change is small, focused, and mechanically sound. Overall: approve. Parser correctness — preamble is genuinely inertThe
The claim is verified, not assumed. Test case quality
r="$(newrepo $'# source-control configuration\n\nRead by the source-control Claude Code plugin ...\n\n## subject_pattern\nConventional Commits')"
assert_eq "preamble above first H2 is inert" "$CC_ERE" "$(run "$r")"This calls the actual resolver script against a real temp file and asserts the output equals Minor observation: The test preamble is a slightly condensed version of the SKILL.md template text (shorter wording, fewer words). Since the parse contract is purely structural (H2 heading detection, not content matching), this doesn't affect correctness — any prose before the first SKILL.md additionsTwo new documentation blocks were added:
Both are well-placed and technically accurate. The template at L227–262 shows the exact preamble text the model should write. Version and CHANGELOG
No issues foundThe PR is correct, tested, well-documented, and closes a real audit gap. No security concerns, no regressions, no structural problems. |
|
Claude finished @kyle-sexton's task in 1m 44s —— View job Security Review — PR #1055
FindingsNo security issues found. AnalysisAll four changed files were reviewed:
One observation worth noting (not a vulnerability): the preamble section in the written file is prose that Claude reads when the file is loaded. A future actor with write access to |
Resolve source-control CHANGELOG conflict by composing both sides: keep #1055's self-describing team-convention-file entry as [0.17.1] (shipped to main), and move the worktree-convention SSOT entry to a new [0.17.2] heading. Re-bump plugin.json 0.17.1 -> 0.17.2 (one increment past main's current value).
…1032) (#1059) Closes #1032 ## Summary Completes #975's adoption path: `/source-control:setup` was the last mechanism-owning surface that didn't know about `pr_body_required_sections` (added in #1029), so a team using the sanctioned guided setup flow couldn't configure the required-section scaffold or see which layer supplies it — the only path was hand-editing `.claude/source-control.md` directly. - **`check` reports a `pr_body_required_sections` row** on the effective-configuration table, resolving to the plugin's portable default (`Summary`, `Test plan`) with `won by: plugin default` when no layer sets it — a named, reportable value rather than a blank row, since "unset" is itself a meaningful state for this key. - **`apply`'s interview offers setting it**, deliberately recommending only the plugin's own portable default and never proposing a `Related`/linked-issue section (or any other org-specific list) as a universal default — it asks what the repo's actual convention requires (a PR template, a CI gate like `pr-issue-linkage`, team practice) rather than inventing one, per the plugin's Two-lane convention posture. - **The written-config template** gains the matching `## pr_body_required_sections` section, at parity with every other per-key surface (`subject_pattern`, `pr_title_pattern`, `trailer_policy`, `pr_body_attribution`). - New evals 13-14 cover the check-report default and the agnostic-interview behavior. - Plugin version bumped `0.18.0` → `0.19.0` with a matching `CHANGELOG.md` entry. This branch was rebased onto `feat/975-required-body-sections` (then onto `main` after #1029 merged) so it includes #1029's `pr_body_required_sections` key and #1055's setup-template preamble together — verified both coexist correctly in the write template (preamble header, then the per-key section list ending with the new `## pr_body_required_sections` section). ## Test plan - `npx markdownlint-cli2` over the changed skill doc and CHANGELOG: 0 errors. - `node -e "JSON.parse(...)"` over the updated `evals/evals.json`: valid JSON. - `bash scripts/check-changelog-parity.sh --check-bump origin/main`: PASS. - `bash scripts/validate-plugins.sh`: every plugin manifest and the marketplace catalog validate. - Manually verified the merged write template renders correctly end to end (preamble + `pr_body_required_sections` section both present, no duplication) after rebasing onto `main`. ## Related - #975 / #1029 — the mechanism and portable-default key this PR completes the adoption path for. - #1055 — the setup-template self-describing-preamble change this branch was rebased across; both changes touch the same "Write the config" template step and coexist without conflict. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…OT (#401) (#1021) ## Summary Plugin-side deliverable for #401. The retired external prose doc `~/.claude/docs/ghq-layout-sibling-pr-worktrees.md` (dotfiles/chezmoi repo) prescribed a stale sibling-at-`ghq`-level worktree layout and carried the babysit worktree-exemption policy. This PR closes the SSOT gap named in the issue: it states, in the plugin itself, that the plugin — not any external prose doc — is the canonical, sole source for both worktree conventions. **Nothing was actually missing or migrated.** Verification (below) confirms the babysit exemption policy is already fully captured *and machine-enforced* in the plugin, and that the "Codex-home root" the issue flags is a per-user `babysit_worktree_root` config **value**, not plugin content — migrating that literal path into a user/machine/org-agnostic plugin would be wrong. This PR clarifies canonical ownership; it does not restate the retired prose (point-don't-copy). ## Fix - `plugins/source-control/skills/babysit-prs/reference/worktrees.md` — added a statement that this file is the canonical, sole source for the ephemeral babysit-worktree exemption, with the load-bearing rationale a future maintainer could otherwise undo: rooting these worktrees outside every repository's discoverable tree (plugin data dir by default) keeps ephemeral scratch out of repository enumeration such as `ghq list`; repointing `babysit_worktree_root` back under a discoverable tree reintroduces that pollution. - `plugins/source-control/skills/worktree/SKILL.md` — added one sentence stating this skill owns the parallel-session **external-root** convention going forward (the discoverable replacement), explicitly scoped to the external-root replacement, not the abandoned sibling layout. ## Verification - **Babysit exemption fully captured + machine-enforced.** `reference/worktrees.md` Policy already documents lease-scoped cleanup and "never request global open-PR cleanup" (lines 12-31), and the operational runbook step 8 in `SKILL.md` (line 433) says "Never globally prune open-PR worktrees." `scripts/prune_babysit_worktrees.py` lines 189-194 machine-enforce it: `--lease-token` requires `--pr`, and `--prune-open-clean` requires **both** `--pr` and `--lease-token` — so no global open-PR prune path exists. - **Codex-home root is a config value, not plugin content.** `babysit_worktree_root` (userConfig in `.claude-plugin/plugin.json`) defaults to the `worktrees/` subdir of the plugin data dir. The babysit skill already migrated its root off `CODEX_HOME` to `${CLAUDE_PLUGIN_DATA}` (CHANGELOG line 735). Live grep: `CODEX_HOME|\.codex|\.agents` across the whole babysit skill → **zero matches**. - **No dangling pointer to the retired doc.** Live grep for `ghq-layout|sibling-pr-worktree` and `.claude/docs` worktree pointers across the plugin → **no plugin reference** (the only `ghq list` hits are unrelated `repo-hygiene` tree-batch features). - **markdownlint-cli2** on all three edited markdown files (root `.markdownlint-cli2.jsonc`) → `0 error(s)`. - **Commit signed** (SSH, ED25519), pushed via explicit refspec. - Out of scope, intentionally deferred (epic #398, not this issue): the setup skill emitting a per-tool worktree-root map + exemptions as *output*. Not missed — deferred. Closes #401 ## Related - Part of epic #398. - **Cross-repo follow-up (out of scope here):** physical deletion of `ghq-layout-sibling-pr-worktrees.md` and removal of its pointer from the user's `CLAUDE.md` "Reference docs" list happen in the **dotfiles/chezmoi** repo — not reachable from this worktree. - **Draft hold released:** the PR was held as draft while unrelated source-control PRs landed first (#1055 → `0.17.1`, #1029 → `0.18.0`, #1032 → `0.19.0`, and later main advanced to `0.26.2`), each requiring a merge-only freshness re-merge and re-bump. PR #853 (the last open source-control PR behind the hold) has merged, so the hold is lifted. Current state bumps `0.26.2 → 0.26.3` with the CHANGELOG entry moved to `[0.26.3]`; every `plugin.json` + `CHANGELOG.md` conflict was resolved by composing both sides (kept main's entries and newer description, re-slotted the worktree-SSOT entry on top). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
The team-tracked
.claude/source-control.mdthat/source-control:setup applywrites lands in shared history with no signal for a teammate who does not run these plugins — audit finding f6 on #912, declared folded into #913 but never delivered by PR #925 (its file list contains no setup-skill change).applytemplate now opens with a self-describing preamble: read by the source-control plugin (and the guardrails commit-convention gate where installed), inert without them, and a drafting aid, not team-wide enforcement — tool-agnostic enforcement for every committer is a commit-msg hook or CI check.applyreport for a team write states the same draft-aid vs enforcement distinction.lib/resolve-convention-pattern.test.shshows a preambled file resolves identically to a bare one (the parse contract reads only the first non-empty body line under a## <key>H2).source-control
0.17.0→0.17.1with CHANGELOG entry.Test plan
bash lib/resolve-convention-pattern.test.sh— 28 cases, 0 failed (new preamble case included)bash scripts/check-changelog-parity.sh --check-bump main— passbash scripts/check-changed-skills.sh main— setup: PASS (0 errors)Related
🤖 Generated with Claude Code