feat(source-control): pr_body_required_sections accepts none (no required sections) - #1142
Conversation
…uired sections (0.20.0) The key could express a list or absence (absence yields the portable default), so a repo whose convention is no PR-body sections could not state that in config. The literal keyword `none` now resolves to zero required sections, parallel to trailer_policy / pr_body_attribution: the create draft emits no section scaffold and the pre-create gate has nothing to require; the closing-keyword check is independent and unchanged, and ad hoc Related content from real refs is never dropped. `none` participates in per-key layering as a resolved value, not an absence — a layer declaring it overrides a lower layer's list, while a key unset everywhere still falls through to the portable default. Documented in reference/config-resolution.md and the pr-body-convention seam README; setup check renders a resolved `none` distinctly and the apply interview offers it. New pull-request evals 19-20 and setup eval 16 cover resolution, layering, and reporting. Closes #1138 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude encountered an error after 1s —— View job I'll analyze this and get back to you. |
|
Claude encountered an error after 0s —— View job I'll analyze this and get back to you. |
|
Warning Automated review did not complete — this is an infrastructure failure, not a review verdict. Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."
Re-running the job, or pushing a new commit, will retry the review. |
|
Warning Automated security review did not complete — this is an infrastructure failure, not a review verdict. Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."
Re-running the job, or pushing a new commit, will retry the review. |
|
Pre-merge CI classification
Merging per the drive-to-merged directive on record for this issue chain (#1138 → #1141). |
… consensus window (#1144) Closes #1139 ## Summary `/source-control:setup`'s convention-inference history signal previously sampled a fixed `git log -50` tail — too small to detect a convention shift or an informal variant family (live-run evidence in the issue: a 2,122-subject year-scale analysis found a rising ticket-prefix pattern at 78.8% recent vs 71.9% older with Conventional Commits at 0%, invisible at n=50). Inference now runs one `git log --since="<window>" --no-merges --date=short --format='%ad|%s'` pass, excludes auto-subjects (`Revert `/`fixup!`/`squash!`; merges via `--no-merges`), bucket-classifies in-context, and reports volume-weighted percentages with a recent-vs-older recency split — the user picks from the evidence table; no bucket is silently promoted into config. Every knob is plugin `userConfig`, never a constant (per the item's author-stated design principle): `setup_inference_window` (git-approxidate string, default `1 year`), `setup_inference_recency_days` (default `90`), `setup_inference_min_commits` (default `50`). A surviving literal `${user_config.…}` placeholder applies the manifest default. Keys declared in `plugin.json`, documented in the README config table. Generic caveats handled and stated in the report when they apply: shallow clones (`git rev-parse --is-shallow-repository`) report the actual covered span; young repos widen to full history, then degrade to low-confidence below the min-commits threshold; squash-merge-only repos are flagged as one signal (subjects ARE the PR titles), not two independently corroborating ones. source-control 0.20.0 → 0.21.0 + CHANGELOG. ## Test plan - `jq -e` validates `plugin.json` and `evals.json`. - New userConfig entries follow the current plugins-reference manifest schema (type/title/description/default/min; fetched 2026-07-23). - New setup eval 17 (`inference-uses-consensus-window-not-fixed-sample`) covers: configurable window with manifest-default fallback, auto-subject exclusion, bucketed recency-split percentages, evidence-table pick (no silent promotion). Conforms to the skill-quality evals schema. - `typos` clean over the touched tree. ## Related - Issue chain: #1138 (merged, PR #1142) → #1139 (this) → #1140 → #1141 (source-control convention SSOT series). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ly-convention spoke split (#1146) Closes #1140 ## Summary Skill-quality pass over `source-control:setup` per the F4 audit finding. The audit's MD041/MD013 lint findings were **verified against the repo's actual markdownlint config first** (per the issue's explicit instruction): `.markdownlint-cli2.jsonc` disables both MD013 and MD041, so they do not apply under the repo's own gate — no lint edits made for them; markdownlint reports clean. Added a `## Gotchas` surface recording real first-contact failure patterns from the live audits: omission-never-resets per-key fallthrough, `none` vs absence as distinct states, resolved-value (not file-presence) inference gating, nested-directory cwd-relative read degradation, linked-worktree hooks-dir resolution, `--since` committer-date vs `%ad` author-date recency skew (review-caught during #1139), and same-session stale `userConfig` reads. **Hub-split decision: DONE, not deferred.** #1139's consensus-window inference had pushed the hub to 512 lines — over the skill-quality gate's 500-line hard cap — so the split fell out naturally: the `apply` convention write path (layer selection, non-interactive update semantics, the 7-step interview, the written-file template, per-layer verification scripts, failure remediations) moved verbatim to a progressive-disclosure spoke `skills/setup/reference/apply-convention.md` (332 lines, one relative link re-depthed), with a normative pointer + summary in the hub. Hub now 211 lines. source-control 0.21.0 → 0.22.0 + CHANGELOG. ## Test plan - `skill-quality:check` (bundled `check-skill.sh`, twenty-check static gate) on `setup`: **PASS — 0 errors**; all 8 base-ref trigger phrases preserved; markdownlint clean; no Gotchas WARN; hub 211/500 lines (only remaining note: advisory soft target 200). - Spoke content moved verbatim from the hub (diff-inspected); the one internal link (`config-resolution.md`) re-depthed for the new location. - `jq -e` validates `plugin.json`. ## Related - Issue chain: #1138 (PR #1142, merged) → #1139 (PR #1144, merged) → #1140 (this) → #1141 (source-control convention SSOT series). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ntion_source (#1149) Closes #1141 ## Summary Author-directed reopen of #913's recorded "no YAML / no path rename" decision — the four reopen grounds (author directive, a real consuming machine hand-syncing three byte-identical regex copies, the ecosystem's prose-only AGENTS.md pointer standard with no machine format, and the plugin-audit recurring-concerns memory contradicting the decline) are recorded in #1141; a comment recording the outcome goes on #913 at merge. The team-tracked `.claude/source-control.md` may now declare `## convention_source`: a **repo-relative flat-scalar YAML file** (`subject_pattern`, `pr_title_pattern`, optional `pr_body_required_sections` list or `none`, optional `dialect:` defaulting `posix-ere`) that enforcement (commit-msg hooks, CI) and drafting (any agent) consume as one source. Key contract points, all recorded in the commit-convention seam README: - **Back-compat absolute:** no pointer → today's markdown-H2 behavior, zero action for existing consumers. - **Everything repo-declared:** the path is the consuming repo's call (no hardcoded doc root; no well-known search list in V1 — recorded decision with rationale). The `Conventional Commits` keyword and the pr-title deferral marker work identically on both surfaces. - **Per-key precedence:** the neutral file is authoritative for keys it carries; omitted keys fall back to the markdown H2; plugin-only keys (`trailer_policy`, `pr_body_attribution`) stay `.claude/`-side; user/local overlay layers unchanged. - **Enforcement contract unchanged:** POSIX ERE only, unresolved = no enforcement, team-only policy floor — including the pointer itself (an overlay cannot redirect the gate). A declared-but-broken pointer (absolute/backslash/`..` path, missing file) or a non-`posix-ere` dialect **fails closed** with a diagnostic, never a silent markdown fallback that could enforce a migration-retired pattern. - **Incumbent steelman documented:** markdown-H2's doubles-as-documentation purposes tested against the move (YAML `#` comments + prose pointer doc); frontmatter-hybrid re-examined and declined for V1 with rationale. - **`setup apply`** gains the offer-and-migrate path: repo-chosen path, YAML write, pointer declaration, and retiring duplicated markdown keys so both surfaces are never authoritative for the same key. Monorepo per-directory scoping: out of scope V1, recorded. `lib/resolve-convention-pattern.sh` extended; the guardrails vendored copy synced byte-identical via `scripts/sync-resolve-convention-pattern.sh` (gate: resolve-convention-pattern-sync), with guardrails 0.12.3 → 0.13.0 + CHANGELOG per the bump gate. source-control 0.22.0 → 0.23.0 + CHANGELOG. ## Test plan - `bash lib/resolve-convention-pattern.test.sh`: **44 cases, 0 failed** — 14 new neutral-SSOT cases: quoted/double-quoted/unquoted YAML values, CC keyword and deferral marker in YAML, explicit `posix-ere` dialect, non-ERE dialect → fail closed, missing-file pointer → fail closed with no stale markdown fallback, absolute/traversal pointer rejection, omitted-key markdown fallback, YAML-wins-over-unretired-duplicate, comment/blank inertness, overlay-pointer ignored (team-only floor). - `scripts/sync-resolve-convention-pattern.sh --check`: guardrails copy byte-identical. - `skill-quality:check` on `setup`: PASS, 0 errors. - `jq -e` validates both manifests and the evals file; new setup eval 18 covers the apply migration path. ## Related - #913 — the reopened decision (outcome comment lands there on merge). - Issue chain: #1138 (PR #1142) → #1139 (PR #1144) → #1140 (PR #1146) → #1141 (this), all merged in order. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Closes #1138
Summary
pr_body_required_sectionsnow accepts the literal keywordnone— "this repo requires no PR-body sections" as a stated convention, previously unstatable (absence yields the portable default, the opposite of what a deliberately empty-body repo wants). Parallel to the sibling keystrailer_policyandpr_body_attribution: a resolvednonemeans/pull-request createdrafts no section scaffold and the §2.4.2.2 pre-create gate requires nothing. The §2.4.2.1 closing-keyword check is independent and unchanged; ad hoc## Relatedcontent from real refs is still never dropped.noneparticipates in per-key layering as a resolved value, not an absence — a layer declaring it overrides a lower layer's list wholesale, while a key unset in every layer still falls through to the portable default (Summary,Test plan).Surfaces updated:
reference/config-resolution.md(value grammar + layering semantics),docs/conventions/pr-body-convention/README.md(owner doc — new "none: no required sections" section with the consumer-evidence rationale),pull-request/reference/create.md(§2.4.1 resolve step, template build, §2.4.2.2 vacuous pass),setup/SKILL.md(check-report renderingnone (no required sections), interview offer, config template). source-control 0.19.0 → 0.20.0 + CHANGELOG.Test plan
jq -evalidates both editedevals.jsonfiles andplugin.json.evals.schema.json(requiredid+prompt, kebab-casename, no extra fields).none→ empty scaffold, gate passes vacuously, closing-keyword check still enforced; fixturesource-control-required-none.md), eval 20 (nonewins the per-key override across all three layers; absence vsnonedistinguished), setup eval 16 (check report renders resolvednonedistinctly from unset).typosclean over the touched trees.Related
noneprecedents:trailer_policy,pr_body_attribution(config-resolution.md).🤖 Generated with Claude Code