feat(source-control): layer convention config across three layers#660
Conversation
|
Claude finished @kyle-sexton's task in 3m 36s —— View job PR Review: feat(source-control): layer convention config across three layers
SummaryThis PR is well-designed. The three-layer resolution, the per-key override deviation from seam 2's concatenating default, and the gitignore-guard split are all architecturally sound and correctly motivated. One genuine inconsistency needs resolution before merge; the rest are minor. Finding 1 — Inconsistency:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dbae64effb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Claude finished @kyle-sexton's task in 3m 38s —— View job PR Review: feat(source-control): layer convention config across three layers
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9eebb8b4f6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The `source-control.md` convention config was resolved as a single project-level file, so a commit convention could not follow an operator across repos or machines and a personal deviation from team policy had nowhere to live — per-machine reconfiguration meant editing the team-tracked file. Seam 2 of the migration playbook mandates three layers and fixes their order; none were implemented. It now resolves `~/.claude/source-control.md` (user-global) → `.claude/source-control.md` (team, tracked) → `.claude/source-control.local.md` (gitignored personal overlay). The layering rules live in one new bundled reference that `/commit`, `/pull-request`, and `/setup` all read, rather than each restating them. Merge is per-key override, recorded as a deliberate deviation from the seam's concatenating default: concatenation is right for the first-party security-guidance precedent, whose layers are prose blocks that accumulate, but every key here is a scalar or closed list — two `subject_pattern` regexes cannot concatenate into a third valid regex, and a concatenated `trailer_policy` would emit two trailers. `/setup apply` gains a `layer=user|team|local` target (default `team`), and `check` renders the effective merge as a row per key with the layer that supplied it instead of reporting the team file's values as the whole convention. The gitignore guard splits, since one verdict was being applied to layers that need opposite ones: a gitignored team file stays a hard STOP, a gitignored personal overlay is the success condition and is never staged, and the user-global file gets no git command at all — it sits outside the worktree, where `git check-ignore` and `git status` return a meaningless verdict, or a confidently wrong one when the home directory is itself a repository. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three review findings on the layering change. Rung 2 of the subject-convention ladder was gated on "no config file exists". A user-global layer contributing only `trailer_policy` leaves `subject_pattern` unresolved while a file plainly exists, so the ladder never fell through and `/commit` proceeded with no convention resolved. Fall-through is now per key: a key absent from every layer is unresolved regardless of which files are present. The local-overlay verification used a bare `git check-ignore`, which consults the index and reports nothing for an already-tracked file — gitignore rules do not apply to tracked files. "No rule exists" and "a rule exists but the overlay was committed anyway" were indistinguishable from its output, and they need opposite remediations. Verified against git directly: for a tracked file matching a rule, bare `check-ignore` exits 1 with no output while `--no-index` reports the matching rule. The skill now runs `check-ignore --no-index` for rule existence and `ls-files` for index membership, and reports `git rm --cached` rather than a `.gitignore` line when the overlay is already tracked. Also: the team-layer STOP message goes to stderr, and the setup eval cases are back in id order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9eebb8b to
44ebf12
Compare
|
Claude finished @kyle-sexton's task in 2m 54s —— View job PR Review: feat(source-control): layer convention config across three layers
The 44ebf12 fix commit addressed all three technical findings raised by the prior review rounds. Here is the full picture of what landed and what remains. What 44ebf12 fixedRung 2 per-key fall-through (second Claude review Correctness 1; Codex P2 on commit/SKILL.md)
The old gate ("no config file exists") was wrong: a user-global file that only contributes
Both the authoritative reference and the consuming skill now agree. ✓ Two-probe local overlay verification (second Claude review Correctness 2; Codex P2 on setup/SKILL.md)
IGNORE_MATCH="$(git -C "$REPO_ROOT" check-ignore --no-index -v -- "$OVERLAY")" && HAS_RULE=1 || HAS_RULE=0
TRACKED="$(git -C "$REPO_ROOT" ls-files -- "$OVERLAY")"
if [ "$HAS_RULE" -eq 1 ] && [ -z "$TRACKED" ]; then echo "OK: ..."
elif [ -n "$TRACKED" ]; then echo "FAIL: ... git rm --cached ..."
else echo "FAIL: no ignore rule ..."
fi
STOP message to stderr + eval ID ordering (second Claude review minors)
Remaining item worth noting before mergeNon-interactive
The interactive path at line 139 explicitly says "carry only the keys that genuinely differ from the merge below it." The non-interactive path skips that check. A user running This is the Codex P2 finding that was not addressed in 44ebf12. It is not a blocker — the convention resolves correctly in the present, and the overlay is recoverable — but it is an inconsistency between the two apply paths that the "carry only differing keys" rationale applies to both. Fix this → Security criterion 4 — needs a human ruling before mergeThe PR author correctly flags If the playbook's prohibition is meant to have a standing exception for Correctness checks (all pass in current state)
SummaryAll technical findings from the prior review rounds are closed. One Codex P2 item (non-interactive |
…ively `apply layer=local subject_pattern=X` wrote both `subject_pattern` and `pr_title_pattern` unconditionally. Against a team file already declaring `X`, that pins the value in the personal overlay, so a later team convention change is silently ignored on that machine — the failure per-key override exists to prevent. The interview path already resolved the layers below and carried only genuine deviations; the non-interactive path now does the same, and reports that the value already holds instead of materializing an overlay that restates the team layer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 3m 28s —— View job PR Review: feat(source-control): layer convention config across three layers
Three fix commits landed since the initial reviews. This pass verifies what changed and whether anything remains actionable. What the fix commits addressed
This commit fixes three findings from the prior reviews in one shot:
Spelling fix (
The Codex P2 finding (review at line 129): the non-interactive
The interview path already did this correctly; the non-interactive path now matches. Eval case 10 ( What remains openSecurity criterion 4 — still needs a decision, not a note. The PR description says "Confirming that reading is a review decision, and it is the one thing here worth a second opinion." The The first-party Eval coverage — no user-global-only scenario. No case tests the path where State of the PRAll five actionable findings from prior reviews are resolved. The two remaining items are a documentation ruling (non-blocking, PR author already flagged it) and a coverage note (minor, follow-on). The design is sound — the per-key override deviation is recorded, the gitignore guard split correctly inverts between team and local layers, the two-probe Ready to merge once the security criterion 4 ruling is tracked somewhere durable. |
|
Review findings addressed — three commits, plus a note on the security question. Correctness 1 (rung 2 fallback) — Correctness 2 (tracked overlay) — Codex thread 3 (non-interactive overlay pinning) — Nits — all three: Security criterion 4 — why this PR does not rule on itYou are right that a PR description is the wrong home for a deviation ruling, and I have not moved it into one. The reason is a constraint rather than a disagreement:
So the honest state is: the tension is real, it is recorded in #647 and here, and the next plugin author does still have the open question — but closing it is a maintainer decision, tracked at #649. If you would rather this PR wait on that ruling, say so and I will hold it; the layering work itself is independent of which way the ruling goes. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0c20814912
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…a rewrite Two review findings, both the same class as the rung-2 bug: a decision gated on file presence rather than on the resolved value. A non-interactive `apply layer=local subject_pattern=X` composed the target file from the invocation alone, so an overlay already carrying `trailer_policy: none` lost it — a one-key update silently changed commit behavior the user never asked about. The target layer is now read first and every key the invocation did not name is carried through; a key is removed only when the user explicitly clears it. The omit-if-equal rule still applies, but to the requested keys only. The `apply` interview skipped hook/rule/history inference whenever a config file existed. A layer contributing only `trailer_policy` leaves `subject_pattern` unresolved, so the interview would recommend the bundled Conventional Commits default over a `commit-msg` hook demanding ticket-prefixed subjects. Inference is now gated on the effective merged `subject_pattern` being unresolved. The two remaining file-presence phrasings in `/commit` and `/pull-request` were reconciled to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 57s —— View job PR Review: feat(source-control): layer convention config across three layers
What
|
| Finding | Source | Status |
|---|---|---|
config-resolution.md "warn" vs "FAIL" |
Review 1, Finding 1 | ✅ Fixed in 44ebf12 |
| Rung 2 fallback gated on file presence | Review 2, Correctness 1; Codex P2 | ✅ Fixed in 44ebf12 |
Tracked overlay check using bare check-ignore |
Review 2, Correctness 2; Codex P2 | ✅ Fixed in 44ebf12 |
| STOP to stderr; eval ID ordering | Review 2, nits | ✅ Fixed in 44ebf12 |
| Non-interactive apply omits equal keys | Review 3; Codex P2 | ✅ Fixed in 0c20814, eval 10 |
| Overlay key preservation during non-interactive update | Codex P2 (after 0c20814) |
✅ Fixed in b350eb5, eval 11 |
| Inference gated on file presence in apply interview | Codex P2 (after 0c20814) |
✅ Fixed in b350eb5 |
| Security criterion 4 ruling | All reviews; Codex P1 | ⏳ Deferred to #649 (needs-human) — not resolvable here |
| User-global-only eval scenario | Reviews 1–3 | 📋 Minor follow-on |
Correctness checks (all pass)
- The two new behaviors in
b350eb5compose correctly: the "update, not rewrite" rule and the "omit-if-equal for requested keys only" rule are applied to disjoint key sets when they interact, with no conflict. - Inference path in all three skills now consistently targets the effective resolved
subject_pattern, not file presence. - Eval case 11 fixtures reference the correct files (
.claude/source-control.mdand.claude/source-control.local.md), matching the scenario where an overlay already exists. config-resolution.mdis still the authoritative reference document and remains consistent with all three consuming skills after this commit.
Summary
All seven technical findings are resolved. The security criterion 4 ruling is correctly deferred to #649 — this PR cannot and should not close it, and the code change is sound on the existing precedent. The user-global-only eval gap is a follow-on. Ready to merge.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b350eb55bb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Control tower hold (do-not-merge), 2026-07-20 ~09:50Z. This PR's layering scheme (including the recorded per-key-merge deviation from the seam-2 concatenation default) is exactly the subject of #649 — the governance decision on the three consumer-config layering deviations — which is still status: needs-decision with an operator brief pending (T5, 08:51Z). Conventions are retained Class C: the operator decides #649, then this PR either rides the ruling or adjusts to it. Hold is content-neutral — CI/review proceed; only the merge waits. Cross-noted on #649. [Tower decide-and-log; reversible] |
…ailure The layer=local post-write guard printed FAIL but did not exit, so the apply path continued to step 7 and reported an effective merge over a personal overlay left shareable — visible to `git status` when no ignore rule matches, or already in team history when the overlay is tracked. The team-layer guard already exits non-zero on its STOP; the local branches now match it, and the closing prose documents the halt for both guards with each failure's own remediation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 33s —— View job PR Review: feat(source-control): layer convention config across three layers
What
|
| Finding | Source | Status |
|---|---|---|
config-resolution.md "warn" vs "FAIL" |
Review 1, Finding 1 | ✅ Fixed in 44ebf12 |
| Rung 2 fallback gated on file presence | Review 2, Correctness 1; Codex P2 | ✅ Fixed in 44ebf12 |
Tracked overlay check using bare check-ignore |
Review 2, Correctness 2; Codex P2 | ✅ Fixed in 44ebf12 |
| STOP to stderr; eval ID ordering | Review 2, nits | ✅ Fixed in 44ebf12 |
| Non-interactive apply omits equal keys | Review 3; Codex P2 | ✅ Fixed in 0c20814, eval 10 |
| Overlay key preservation during non-interactive update | Codex P2 (after 0c20814) |
✅ Fixed in b350eb5, eval 11 |
| Inference gated on file presence in apply interview | Codex P2 (after 0c20814) |
✅ Fixed in b350eb5 |
| Local-overlay validation failure doesn't halt apply | Codex P2 (after b350eb5) |
✅ Fixed in e8c4fed |
| Security criterion 4 ruling | All reviews; Codex P1 | ⏳ Deferred to #649 (needs-human) — correct |
| User-global-only eval scenario | Reviews 1–3 | 📋 Minor — follow-on |
Correctness checks (all pass in current state)
- Both FAIL branches exit non-zero. The no-rule and already-tracked cases both halt before step 7. No path through the local overlay guard can proceed to an effective-merge report while the overlay is shareable. ✓
- FAIL messages go to stderr. Consistent with the team-layer STOP and standard shell convention. ✓
- Remediation split. The tracked branch takes precedence (add the ignore line to a tracked file → no effect;
git rm --cachedis the correct first step). ✓ - Inline comment explains the invariant. The comment at line 299 names the hazard — "proceeding here would report an effective merge over a personal file that is still shareable" — so a future editor knows why these branches exit rather than warn and continue. ✓
- Eval cases 10 and 11 are internally consistent with their fixtures and cover the non-interactive update contracts: omit-if-equal to lower-layer merge (eval 10), and preserve unrequested overlay keys (eval 11). ✓
Security criterion 4 — deferred correctly
The Codex P1 comment on config-resolution.md:33 was left unresolved by Kyle deliberately: the ruling belongs to #649 (needs-human), and recording it here would resolve #649's substance under a different file name. That reasoning is correct. The thread is appropriately unresolved.
Summary
All eight technical findings from all review rounds are resolved. The security criterion 4 ruling is correctly deferred to #649. The user-global-only eval gap remains a minor follow-on. The code itself is ready; the merge gate is the #649 ruling and the control tower hold.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e8c4feda7e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The carry-through rule from the previous commit was too broad. `type_list` and `pr_title_pattern` are functions of `subject_pattern`, so preserving them across a `subject_pattern` change leaves a self-contradicting config: `apply layer=team subject_pattern=^[A-Z]+-\d+: .+` against a Conventional-Commits file kept the bundled 11-type vocabulary beside a ticket-prefix regex, and `/commit` would pre-check against types the pattern does not use. Derived keys are now recomputed — dropped when the new pattern has no type vocabulary, re-added when it does — while independent keys like `trailer_policy` are still carried through. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6976d42645
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The write path already recomputed derived keys; the resolution contract still merged `type_list` per key on its own. A user-global (or team) layer supplying Conventional Commits plus its type list, overridden by a later layer that changes only `subject_pattern` to a ticket-prefix regex, left the inherited vocabulary in the effective config beside a pattern that has no types — so `/commit` would pre-check against types the effective pattern does not use. `type_list` is now bound to the effective `subject_pattern`: dropped when the merged pattern is custom, defaulted to the bundled 11-type list when it is Conventional-Commits-shaped. New commit eval case 9 pins the drop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # plugins/source-control/.claude-plugin/plugin.json # plugins/source-control/CHANGELOG.md
|
Claude finished @kyle-sexton's task in 3m 3s —— View job PR Review: feat(source-control): layer convention config across three layers
What
|
| Finding | Source | Status |
|---|---|---|
config-resolution.md "warn" vs "FAIL" |
Review 1, Finding 1 | ✅ Fixed in 44ebf12 |
| Rung 2 fallback gated on file presence | Review 2, Correctness 1; Codex P2 | ✅ Fixed in 44ebf12 |
Tracked overlay check using bare check-ignore |
Review 2, Correctness 2; Codex P2 | ✅ Fixed in 44ebf12 |
| STOP to stderr; eval ID ordering | Review 2, nits | ✅ Fixed in 44ebf12 |
| Non-interactive apply omits equal keys | Review 3; Codex P2 | ✅ Fixed in 0c20814, eval 10 |
| Overlay key preservation during non-interactive update | Codex P2 (after 0c20814) |
✅ Fixed in b350eb5, eval 11 |
| Inference gated on file presence in apply interview | Codex P2 (after 0c20814) |
✅ Fixed in b350eb5 |
| Local-overlay validation failure doesn't halt apply | Codex P2 (after b350eb5) |
✅ Fixed in e8c4fed |
type_list retained at merge for custom patterns |
Codex P2 (after 6976d42) |
✅ Fixed in 9072758, commit eval 9 |
| Security criterion 4 ruling | All reviews; Codex P1 | ✅ Maintainer ruling received; wording fix deferred to #648 |
| User-global-only eval scenario | Reviews 1–3 | 📋 Minor — follow-on |
Correctness checks (all pass in current state)
type_listbinding: The fix inconfig-resolution.mdis symmetric — dropped for custom patterns, defaulted to the bundled 11-type list for Conventional-Commits-shaped patterns with no explicittype_listin any layer. Both directions correct. ✓- Write-path and resolution-path consistency:
6976d42fixed the write path (recompute derived keys);9072758fixed the resolution path (bindtype_listto the effectivesubject_pattern). Setup eval 12 pins the write path; commit eval 9 pins the resolution path. The two evals use scenarios that exercise the same invariant from different angles. ✓ - Fixture consistency for commit evals 8 and 9: Both use the same fixture files. Case 8 focuses on
subject_pattern/trailer_policyper-key override; case 9 focuses ontype_listdropping. No conflict — the expected outputs test different aspects of the same merge. ✓ pr_title_patterndeferred expansion:config-resolution.md:69–70correctly states expansion against the effectivesubject_pattern. The overlay fixture declarespr_title_pattern: Same as subject_pattern.explicitly, which is redundant but not incorrect — it resolves to the ticket-prefix pattern either way. ✓- Setup eval 12 fixture consistency: Team file has
subject_pattern: Conventional Commits+type_list. Applyinglayer=team subject_pattern=^[A-Z]+-\d+: .+should rewrite with ticket-prefix pattern, droptype_list(derived key), carrytrailer_policy: Co-Authored-By: Claude …(independent key). Correctly covered by the expectations in eval 12. ✓
Summary
All nine technical findings from every review round are resolved. The security criterion 4 ruling is in. The user-global-only eval gap remains a minor follow-on. The code is complete and correct; the merge gate is the #649 governance decision tracked by the control tower hold.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2a96410d18
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…692) Closes #648 ## Summary The tracked-rich-config seam in `docs/MIGRATION-PLAYBOOK.md` fixes the resolution order for every consumer-config surface in the fleet. `docs/PLUGIN-PHILOSOPHY.md` states that fleet audits check conformance **per registry row** — and this rule had no row, so nothing audited it. Adds `docs/conventions/consumer-config-layering/` (README + CHANGELOG, following the `hook-telemetry` shape), registers it in the Convention registry, and points seam 2 at it rather than restating the rules in two places. **The contract owns the axis, never the keys.** Layer set, precedence, override semantics, overlay naming, and the resolution algorithm. Which keys a surface has and how they validate stay with that concern's own owner doc. That boundary is what keeps this from colliding with "One owner doc per shared concern" — layering is genuinely cross-cutting; keys are not. Deviations are recorded **as observed, not ratified**. Ruling on them is a separate human-gated decision (#649) and nothing here rules on anything. ## Correction to the issue's evidence table The issue cites **15 surfaces**. A direct re-census found **12**, and two of the issue's verdicts do not survive reading the files: | Issue's claim | What the files say | |---|---| | `songwriting` — undeclared deviation, "first-match-wins, freezes the template" | `songwriting` has no consumer-config surface at all. Its only `.claude/` read is the consuming project's `CLAUDE.md` / `.claude/rules` (`skills/setup/SKILL.md:58`) — that is seam 3 steering, not seam 2 tracked config. Not a row. | | `code-tidying` — undeclared deviation ("overrides this file entirely") | The phrase is real (`skills/tidy/lanes/docs-prose.md:14`), but the thing being overridden is the plugin's own **bundled** lane file, not a consumer layer. That is a bundled-default rung, structurally the same as `toolchain`'s rung 4. Recorded as single-layer over a bundled default. | | `work-items` (×2), `standards.yaml`, `out-of-scope` counted as separate single-layer surfaces | `out-of-scope` is not a plugin. `standards.yaml` is the root-relocation knob **of** the `standards` surface, not a second surface. `work-items` is one surface — and it is the one the census nearly missed, because its config is `.work-item-tracker.json` at the repo root rather than under `.claude/`. It is in the table. | Net: fewer surfaces, and one genuinely new finding — **`ai-briefing` advertises an overlay layer it does not implement.** Its setup recommends a `.claude/ai-briefing/**/*.local.*` gitignore line, but no read path resolves a `.local.*` file and nothing defines what one would do. That is the only surface telling consumers to gitignore a layer that has no effect. Every file:line in the table was re-read directly rather than taken from a summary. ## Also: security criterion 4 Narrowed per the maintainer ruling on #660. Its "no absolute paths, no reading consumer files outside `${CLAUDE_PROJECT_DIR}`" wording read as forbidding the `~/.claude/<plugin>.md` user-global that **seam 2 mandates** and that **criterion 3 already uses** for consumer credentials — a self-contradiction a reviewer hit in practice on #660. It now says consumer *repository* files and carves out the operator's own Claude Code home explicitly, so the next plugin author does not re-litigate it. ## Test plan - `markdownlint-cli2` across `docs/**/*.md` with the repo config — 0 errors. - `typos --config _typos.toml docs/` — clean. - Every relative link in the four changed/added files resolved against the working tree by hand. `link-check` is a weekly `fail: false` cron, so nothing gates this at PR time. - Census citations spot-checked by reading the cited line directly: `toolchain/reference/resolution-ladder.md:32`, `autonomy/skills/setup/SKILL.md:355`, `docs/conventions/standards/README.md:35`, `disk-hygiene/skills/clean/SKILL.md:77`, `code-tidying/skills/tidy/SKILL.md:58`, `docs/conventions/topic-docs/README.md:224`, `ai-briefing/skills/generate/SKILL.md:63`, `codebase-health/skills/audit/SKILL.md:70`, `repo-fleet-hygiene/skills/setup/SKILL.md:20`. Docs-only; no plugin behavior changes. ## Related - #647 / #660 — the first per-plugin conformance migration (`source-control`); independent of this PR, and currently held under a `do-not-merge` label - #649 — the deviation rulings this PR deliberately does not make --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The config contract permitted "an any-of list of anchored regexes" but never defined how such a list serializes or matches, and `/commit` only says a non-`Conventional Commits` value is used as a regex. A repo that persisted the documented list form would have the whole list handed to a matcher, rejecting valid subjects or building an invalid regex. Rather than invent a grammar for a form nothing parses, the affordance is removed: `subject_pattern` is exactly one value. A convention with several accepted shapes is expressed as alternation inside that one regex, which every consumer already evaluates correctly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 18s —— View job PR Review: feat(source-control): layer convention config across three layers
What
|
| File | Change |
|---|---|
config-resolution.md:14–20 |
Authoritative definition narrowed to exactly one value; alternation cited as the canonical form |
commit/SKILL.md:22 |
Rung 1 drops "or any-of list" — "anything else is already a single anchored regex" |
setup/SKILL.md:202–205 |
Interview guidance replaces "ask for a short any-of list" with "express alternatives as alternation inside one regex" |
setup/SKILL.md:225–228 |
Inline skeleton comment updated — "exactly one anchored regex — never a list" |
No remaining "any-of list" references appear in any source-control plugin file. ✓
Correctness check: fixture consistency for commit eval 9
Eval case 9 (inherited-type-list-drops-for-an-effective-custom-pattern) exercises the interaction between the any-of-list fix and the type_list binding rule:
- Team file (
commit/.claude/source-control.md):subject_pattern: Conventional Commits,type_list: build, chore, ci, …,trailer_policy: none - Personal overlay (
commit/.claude/source-control.local.md):subject_pattern: ^[A-Z]+-\d+: .+,pr_title_pattern: Same as subject_pattern. - Expected effective merge: ticket-prefix pattern (overlay wins
subject_pattern) +type_listabsent (effective pattern is custom, not CC-shaped) +trailer_policy: none(team wins)
Both the eval expectations and the fixtures are consistent with the type_list binding rule in config-resolution.md:65–72. ✓
No new issues introduced
The change is narrowly scoped to removing a single documented but unimplementable feature. All downstream consumers (/commit rung 1, /setup interview and skeleton, config-resolution.md) are updated consistently, and the authoritative reference doc's treatment of the other keys (type_list, pr_title_pattern, trailer_policy) is untouched.
Full state of all findings across all review rounds
| Finding | Source | Status |
|---|---|---|
config-resolution.md "warn" vs "FAIL" |
Review 1, Finding 1 | ✅ Fixed in 44ebf12 |
| Rung 2 fallback gated on file presence | Review 2, Correctness 1; Codex P2 | ✅ Fixed in 44ebf12 |
Tracked overlay check using bare check-ignore |
Review 2, Correctness 2; Codex P2 | ✅ Fixed in 44ebf12 |
| STOP to stderr; eval ID ordering | Review 2, nits | ✅ Fixed in 44ebf12 |
| Non-interactive apply omits equal keys | Review 3; Codex P2 | ✅ Fixed in 0c20814, eval 10 |
| Overlay key preservation during non-interactive update | Codex P2 (after 0c20814) |
✅ Fixed in b350eb5, eval 11 |
| Inference gated on file presence in apply interview | Codex P2 (after 0c20814) |
✅ Fixed in b350eb5 |
| Local-overlay validation failure doesn't halt apply | Codex P2 (after b350eb5) |
✅ Fixed in e8c4fed |
type_list retained at merge for custom patterns (write path) |
Codex P2 (after e8c4fed) |
✅ Fixed in 6976d42, setup eval 12 |
type_list retained at merge for custom patterns (resolution path) |
Codex P2 (after 6976d42) |
✅ Fixed in 9072758, commit eval 9 |
subject_pattern "any-of list" affordance undefined |
Codex P2 (after 9072758) |
✅ Fixed in d0c29cfe |
| Security criterion 4 ruling | All reviews; Codex P1 | ✅ Maintainer ruling received; wording fix deferred to #648 |
| User-global-only eval scenario | Reviews 1–3 | 📋 Minor — follow-on |
All 11 technical findings from all review rounds are resolved. The security criterion 4 ruling is in. The user-global-only eval gap remains a minor follow-on. The code is complete and correct; the merge gate is the #649 governance decision tracked by the control tower hold.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d0c29cfe24
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The interview told the skill to record "same as `subject_pattern`" while the resolution contract recognizes only the literal `Same as `subject_pattern`.` — different casing, no trailing period. A config written from the interview would therefore not be recognized as a deferral and would be pre-checked as a regex in its own right. Both sides now state the exact spelling and say the match is literal. The bundled fixtures already used the canonical form. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 56s —— View job PR Review: feat(source-control): layer convention config across three layers
What
|
| Location | Value | Status |
|---|---|---|
config-resolution.md:24 — definition |
Same as `subject_pattern`. |
✅ Canonical |
config-resolution.md:76 — expansion clause |
Same as \`subject_pattern\`. |
✅ Canonical |
setup/SKILL.md:212 — interview writer instruction |
Same as `subject_pattern`. |
✅ Canonical (fixed in this commit) |
setup/SKILL.md:241 — skeleton placeholder |
"Same as \subject_pattern`."` |
✅ Canonical |
setup/.claude/source-control.md (fixture) |
Same as \subject_pattern`.` |
✅ Already canonical |
commit/.claude/source-control.md (fixture) |
Same as \subject_pattern`.` |
✅ Already canonical |
commit/.claude/source-control.local.md (fixture) |
Same as \subject_pattern`.` |
✅ Already canonical |
The table at setup/SKILL.md:53 displays Same as subject_pattern (no backticks, no period) — this is in an illustrative display rendering of check output where Markdown markup is stripped for a text table, not a stored value. Not an issue.
No new eval case was added for this specific fix — Kyle's note explains why: the three bundled fixtures already used the canonical form; the fix was purely the writer instruction drifting from the contract.
Full state of all findings across all review rounds
| Finding | Source | Status |
|---|---|---|
config-resolution.md "warn" vs "FAIL" |
Review 1, Finding 1 | ✅ Fixed in 44ebf12 |
| Rung 2 fallback gated on file presence | Review 2, Correctness 1; Codex P2 | ✅ Fixed in 44ebf12 |
Tracked overlay check using bare check-ignore |
Review 2, Correctness 2; Codex P2 | ✅ Fixed in 44ebf12 |
| STOP to stderr; eval ID ordering | Review 2, nits | ✅ Fixed in 44ebf12 |
| Non-interactive apply omits equal keys | Review 3; Codex P2 | ✅ Fixed in 0c20814, eval 10 |
| Overlay key preservation during non-interactive update | Codex P2 (after 0c20814) |
✅ Fixed in b350eb5, eval 11 |
| Inference gated on file presence in apply interview | Codex P2 (after 0c20814) |
✅ Fixed in b350eb5 |
| Local-overlay validation failure doesn't halt apply | Codex P2 (after b350eb5) |
✅ Fixed in e8c4fed |
type_list retained at merge for custom patterns (write path) |
Codex P2 (after e8c4fed) |
✅ Fixed in 6976d42, setup eval 12 |
type_list retained at merge for custom patterns (resolution path) |
Codex P2 (after 6976d42) |
✅ Fixed in 9072758, commit eval 9 |
subject_pattern "any-of list" affordance undefined |
Codex P2 (after 9072758) |
✅ Fixed in d0c29cfe |
pr_title_pattern deferral marker exact spelling |
Codex P2 (after d0c29cfe) |
✅ Fixed in 7d5206b |
| Security criterion 4 ruling | All reviews; Codex P1 | ✅ Maintainer ruling received; wording fix deferred to #648 |
| User-global-only eval scenario | Reviews 1–3 | 📋 Minor — follow-on |
Summary
All 12 technical findings from every review round are resolved. The 7d5206b commit is narrow and correct — the deferral marker definition and the writer instruction now agree on the exact literal, and all three fixture files were already canonical. The code is complete and correct; the merge gate remains the #649 governance decision tracked by the control tower hold.
…ion exception class (#649) (#705) ## What Ratifies the `standards` precedence inversion as a **named, sanctioned exception class** in the consumer-config layering contract — the disposition [#649](#649) recommended for that surface ("reads as defensible" / policy-floor). - **README** (`docs/conventions/consumer-config-layering/README.md`): defines the **policy-floor precedence-inversion** class under Merge semantics (team layer wins a direct conflict; personal layers stay add/tighten-only; provenance reported — with a three-part qualification test and external prior art). Moves `standards` from an observed deviation to the ratified exemplar; reconciles the Deviations intro and the Implementers conformance cell. - **CHANGELOG**: 1.1 entry. ## Why a minor bump Default precedence (user → team → local, additive-preferred) is unchanged for every surface; this only carves out a conditional inversion for one surface *class*. That is additive relaxation → **minor** per the contract's own SemVer note. The contract version lives solely in `CHANGELOG.md`; the Convention registry row in `PLUGIN-PHILOSOPHY.md` names-and-points without a version, so no lockstep drift. ## Scope Deliberately narrow: the policy-floor class + `standards` only. The other two deviations named in #649 are dispositioned via follow-up issues, each owning its own contract-row update: - **#701** `code-tidying` docs-prose lane → **CORRECT** (decompose to per-key override), `status: ready`. - **#703** `songwriting` template overrides → **decomposability decision gates correct-vs-ratify**, `status: needs-decision` (human-gated; not yet actionable). Applied under operator momentum delegation; **veto window open** — conventions remain operator-owned and any veto reverts mechanically. Closes #649 ## Related - #701 — `code-tidying` docs-prose conformance correction (this PR does not close it). - #703 — `songwriting` decomposability decision (human-gated; this PR does not close it). - #647 / #660 — `source-control` per-key deviation; confirmed by the ratified per-key path, already merged, unaffected. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ecify the approve mechanic and review-context precondition (#675) (#706) Closes #675 **Gate-off flip precondition** for the autopilot merge tier (#476). All changes are prose/contract + test-pinning: **no behavioral change** to the merge gate or any script, and the tier still ships **DISABLED**. Implements the operator-approved RECOMMENDED options recorded on #675: - **§3 wiring — fork (b), reference-file restructure.** Autopilot's step 3 in `SKILL.md` no longer inlines a base-only merge command that would ignore the tier flags when the tier is enabled. It now points at `reference/safety.md`, the single home for both the base and the enabled-tier merge paths, so an ENABLED config can no longer merge via the flagless base path. Chosen over raising the skill-quality line cap (`SKILL.md` sits at 499/500). - **Second-account approve mechanic (specified).** The concrete out-of-band approval the gate's distinct-bot criterion requires: `gh pr review … --approve` under a distinct `<approver-bot-logins>` identity (`GH_TOKEN` or `gh auth switch`, never the PR author or a lane identity), only after a genuine clean review pass, on the live head so `--expected-head` holds. - **Review-context requiredness — fork (a), required-context precondition.** Enabling the tier now carries a documented operator precondition: the base branch ruleset must make the review workflow a **required** status context, so `mergeStateStatus == CLEAN` actually proves the review ran. Chosen over a merge-gate review-context config (rejected fork b) to keep the gate deterministic with nothing new to wire — and deliberately kept distinct from the existing snapshot-side `babysit_review_gate_context` (a review-trigger timing signal, not a merge predicate). The skill-contract tests are split/extended to pin all three contracts against drift (the worker-tier push paragraph, which has no merge tier, keeps its inline-command assertion). ## Related - Stacked on #665 (`feat/476-autopilot-merge-tier`) and **must land after it** — this PR is based on that branch, not `main`; retarget to `main` once #665 merges. - **Version reconciliation at rebase:** bumped to `0.14.1`, continuous with the current base (`feat/476` @ `0.14.0`). #665's plugin version is being re-composed to `0.15.0` (collision with #660's `0.14.0`); when #665 lands, rebase this and re-bump to one increment past whatever `main` then carries (expected `0.15.1`). - Precedent for the flip-precondition designation: #642, #476. ### Verification - `engine.test.sh`: 312 tests pass, ruff clean, guarded-wrapper behavior checks pass. - `check-skill.sh babysit-prs`: PASS (SKILL.md 499/500, internal refs resolve, markdownlint clean). - markdownlint on `safety.md` + `CHANGELOG.md`: 0 errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ent seam Seams-first foundation for hard-gating commit subjects and PR titles (#914, #919): a shared, zero-dependency resolver that reads the effective enforcement pattern from the consumer's tracked convention and hands hooks a POSIX-ERE regex. - lib/resolve-convention-pattern.sh + 19 tests: team-tracked-layer-only reads (policy-floor by construction), ERE dialect normalization with non-ERE rejection, CC-keyword expansion owned in one place, unresolved -> no enforcement (never the bundled Conventional Commits default). - scripts/sync-resolve-convention-pattern.sh + CI job: the sync scaffold the guardrails consumers (#914, #919) register into. - docs/conventions/commit-convention/: concern owner-doc (enforcement vs drafting), registered in the PLUGIN-PHILOSOPHY convention registry. - config-resolution.md: drafting-vs-enforcement reconciliation section. - consumer-config-layering: correct the stale source-control row (all-three layering landed in #660; drop the in-flight #647 note). - MIGRATION-PLAYBOOK: codify swim-lane execution. Refs #913 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ent seam Seams-first foundation for hard-gating commit subjects and PR titles (#914, #919): a shared, zero-dependency resolver that reads the effective enforcement pattern from the consumer's tracked convention and hands hooks a POSIX-ERE regex. - lib/resolve-convention-pattern.sh + 19 tests: team-tracked-layer-only reads (policy-floor by construction), ERE dialect normalization with non-ERE rejection, CC-keyword expansion owned in one place, unresolved -> no enforcement (never the bundled Conventional Commits default). - scripts/sync-resolve-convention-pattern.sh + CI job: the sync scaffold the guardrails consumers (#914, #919) register into. - docs/conventions/commit-convention/: concern owner-doc (enforcement vs drafting), registered in the PLUGIN-PHILOSOPHY convention registry. - config-resolution.md: drafting-vs-enforcement reconciliation section. - consumer-config-layering: correct the stale source-control row (all-three layering landed in #660; drop the in-flight #647 note). - MIGRATION-PLAYBOOK: codify swim-lane execution. Refs #913 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ent seam Seams-first foundation for hard-gating commit subjects and PR titles (#914, #919): a shared, zero-dependency resolver that reads the effective enforcement pattern from the consumer's tracked convention and hands hooks a POSIX-ERE regex. - lib/resolve-convention-pattern.sh + 19 tests: team-tracked-layer-only reads (policy-floor by construction), ERE dialect normalization with non-ERE rejection, CC-keyword expansion owned in one place, unresolved -> no enforcement (never the bundled Conventional Commits default). - scripts/sync-resolve-convention-pattern.sh + CI job: the sync scaffold the guardrails consumers (#914, #919) register into. - docs/conventions/commit-convention/: concern owner-doc (enforcement vs drafting), registered in the PLUGIN-PHILOSOPHY convention registry. - config-resolution.md: drafting-vs-enforcement reconciliation section. - consumer-config-layering: correct the stale source-control row (all-three layering landed in #660; drop the in-flight #647 note). - MIGRATION-PLAYBOOK: codify swim-lane execution. Refs #913 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ent seam Seams-first foundation for hard-gating commit subjects and PR titles (#914, #919): a shared, zero-dependency resolver that reads the effective enforcement pattern from the consumer's tracked convention and hands hooks a POSIX-ERE regex. - lib/resolve-convention-pattern.sh + 19 tests: team-tracked-layer-only reads (policy-floor by construction), ERE dialect normalization with non-ERE rejection, CC-keyword expansion owned in one place, unresolved -> no enforcement (never the bundled Conventional Commits default). - scripts/sync-resolve-convention-pattern.sh + CI job: the sync scaffold the guardrails consumers (#914, #919) register into. - docs/conventions/commit-convention/: concern owner-doc (enforcement vs drafting), registered in the PLUGIN-PHILOSOPHY convention registry. - config-resolution.md: drafting-vs-enforcement reconciliation section. - consumer-config-layering: correct the stale source-control row (all-three layering landed in #660; drop the in-flight #647 note). - MIGRATION-PLAYBOOK: codify swim-lane execution. Refs #913 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ent seam Seams-first foundation for hard-gating commit subjects and PR titles (#914, #919): a shared, zero-dependency resolver that reads the effective enforcement pattern from the consumer's tracked convention and hands hooks a POSIX-ERE regex. - lib/resolve-convention-pattern.sh + 19 tests: team-tracked-layer-only reads (policy-floor by construction), ERE dialect normalization with non-ERE rejection, CC-keyword expansion owned in one place, unresolved -> no enforcement (never the bundled Conventional Commits default). - scripts/sync-resolve-convention-pattern.sh + CI job: the sync scaffold the guardrails consumers (#914, #919) register into. - docs/conventions/commit-convention/: concern owner-doc (enforcement vs drafting), registered in the PLUGIN-PHILOSOPHY convention registry. - config-resolution.md: drafting-vs-enforcement reconciliation section. - consumer-config-layering: correct the stale source-control row (all-three layering landed in #660; drop the in-flight #647 note). - MIGRATION-PLAYBOOK: codify swim-lane execution. Refs #913 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ent seam Seams-first foundation for hard-gating commit subjects and PR titles (#914, #919): a shared, zero-dependency resolver that reads the effective enforcement pattern from the consumer's tracked convention and hands hooks a POSIX-ERE regex. - lib/resolve-convention-pattern.sh + 19 tests: team-tracked-layer-only reads (policy-floor by construction), ERE dialect normalization with non-ERE rejection, CC-keyword expansion owned in one place, unresolved -> no enforcement (never the bundled Conventional Commits default). - scripts/sync-resolve-convention-pattern.sh + CI job: the sync scaffold the guardrails consumers (#914, #919) register into. - docs/conventions/commit-convention/: concern owner-doc (enforcement vs drafting), registered in the PLUGIN-PHILOSOPHY convention registry. - config-resolution.md: drafting-vs-enforcement reconciliation section. - consumer-config-layering: correct the stale source-control row (all-three layering landed in #660; drop the in-flight #647 note). - MIGRATION-PLAYBOOK: codify swim-lane execution. Refs #913 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ent seam Seams-first foundation for hard-gating commit subjects and PR titles (#914, #919): a shared, zero-dependency resolver that reads the effective enforcement pattern from the consumer's tracked convention and hands hooks a POSIX-ERE regex. - lib/resolve-convention-pattern.sh + 19 tests: team-tracked-layer-only reads (policy-floor by construction), ERE dialect normalization with non-ERE rejection, CC-keyword expansion owned in one place, unresolved -> no enforcement (never the bundled Conventional Commits default). - scripts/sync-resolve-convention-pattern.sh + CI job: the sync scaffold the guardrails consumers (#914, #919) register into. - docs/conventions/commit-convention/: concern owner-doc (enforcement vs drafting), registered in the PLUGIN-PHILOSOPHY convention registry. - config-resolution.md: drafting-vs-enforcement reconciliation section. - consumer-config-layering: correct the stale source-control row (all-three layering landed in #660; drop the in-flight #647 note). - MIGRATION-PLAYBOOK: codify swim-lane execution. Refs #913 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ent seam Seams-first foundation for hard-gating commit subjects and PR titles (#914, #919): a shared, zero-dependency resolver that reads the effective enforcement pattern from the consumer's tracked convention and hands hooks a POSIX-ERE regex. - lib/resolve-convention-pattern.sh + 19 tests: team-tracked-layer-only reads (policy-floor by construction), ERE dialect normalization with non-ERE rejection, CC-keyword expansion owned in one place, unresolved -> no enforcement (never the bundled Conventional Commits default). - scripts/sync-resolve-convention-pattern.sh + CI job: the sync scaffold the guardrails consumers (#914, #919) register into. - docs/conventions/commit-convention/: concern owner-doc (enforcement vs drafting), registered in the PLUGIN-PHILOSOPHY convention registry. - config-resolution.md: drafting-vs-enforcement reconciliation section. - consumer-config-layering: correct the stale source-control row (all-three layering landed in #660; drop the in-flight #647 note). - MIGRATION-PLAYBOOK: codify swim-lane execution. Refs #913 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ent seam (#925) Seams-first foundation (#912) for hard-gating commit subjects and PR titles. Gives a zero-dependency guardrails hook a machine-readable path to the effective enforcement pattern **without changing the consumer-facing markdown surface** (which just landed in #660). ## What lands - **`lib/resolve-convention-pattern.sh` + 19 tests** — the single artifact every enforcement hook sources. Reads the effective `subject_pattern`/`pr_title_pattern` and emits a POSIX-ERE regex. - **`scripts/sync-resolve-convention-pattern.sh` + CI job** — the sync scaffold the guardrails consumers (#914, #919) register into (copies list intentionally empty until then). - **`docs/conventions/commit-convention/`** — concern owner-doc (enforcement vs drafting), registered in the PLUGIN-PHILOSOPHY convention registry. - **`config-resolution.md`** — drafting-vs-enforcement reconciliation section. - **`consumer-config-layering`** — correct the stale `source-control` row (all-three layering landed in #660; drop the in-flight #647 note). - **`MIGRATION-PLAYBOOK`** — codify swim-lane execution. ## Locked contract (binds #914/#919) 1. **Dialect** = POSIX ERE; normalizes `(?:`->`(`, `\d`->`[0-9]`, `\w`/`\s`; rejects un-expressible PCRE (lookaround/backref/`\b`) -> no enforcement + visible note. 2. **Unresolved -> no enforcement** (never the bundled CC default -- CC is not lane-1-eligible). 3. **Policy-floor via team-only reads** -- enforcement reads the tracked `.claude/source-control.md` only; gitignored/user-global overlays are drafting-only and can never weaken a gate. ## Verification - `bash lib/resolve-convention-pattern.test.sh` -> 19/19 pass - `shellcheck --rcfile=.shellcheckrc` clean on all three scripts - `scripts/sync-resolve-convention-pattern.sh --check` passes ## Related - #912 -- umbrella tracking issue (not closed here) - Unblocks #914 (CC-layer content gate) and #919 (opt-in commit-msg hook), which vendor this resolver Closes #913 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Closes #647
Summary
source-control.mdwas resolved as a single project-level file. It now resolves three layers in the order seam 2 ofdocs/MIGRATION-PLAYBOOK.mdmandates —~/.claude/source-control.md(user-global) →.claude/source-control.md(team, tracked) →.claude/source-control.local.md(gitignored personal overlay) — merged per key.The layering rules live in one new bundled reference,
plugins/source-control/reference/config-resolution.md, which/commit,/pull-request, and/setupall read instead of each restating them. Shape mirrorsplugins/toolchain/reference/resolution-ladder.md.Per-key override is a recorded deviation, not a silent one. The seam's default is concatenation, which is right for the first-party
security-guidanceprecedent whose layers are prose blocks that genuinely accumulate. Every key here is a scalar or a closed list: twosubject_patternregexes cannot concatenate into a third valid regex, and a concatenatedtrailer_policywould emit two trailers. This is the seam's sanctioned "genuinely must override … per key, never by dropping the base layer wholesale" case./setup applygainslayer=user|team|local(defaultteam);checkrenders the effective merge as a row per key with the layer that supplied it, rather than reporting the team file's values as if they were the whole convention.The gitignore guard splits, because one verdict was being applied to layers that need opposite ones:
The user-global case is not an omission:
git check-ignoreandgit statuson a path outside the repository return a meaningless verdict, or a confidently wrong one when the home directory is itself a git repository.Deviations from the issue's scope list
Scope item 7 ("Fix the bundled-template defect" / "Collapse to a single template") is closed as investigated, premise false — nothing was collapsed. The two files the issue names are not bundled templates:
filesfield of the cases that consume them —skills/commit/evals/evals.jsoncase 7 andskills/setup/evals/evals.jsoncases 3 and 6.trailer-policy-none-honored-in-pathspec-form; it requirestrailer_policy: noneto test that--traileris omitted. Collapsing toward setup's full-trailer value would break that case outright, and a single shared file can only hold one value./setupwrites the config from a skeleton embedded inline in its ownSKILL.md; the plugin ships no template file.The real default is self-consistent and was verified rather than assumed:
commit/SKILL.mddefaults to emitting the trailer, and the inline skeleton omitstrailer_policyunless the repo has a trailer convention, so the absent case falls through to that default.This PR instead adds a
.claude/source-control.local.mdfixture to each of those two skills and three new eval cases covering the layering contract (per-key override, effective-merge rendering, layer routing, and the no-git-commands user-global write) — the fixtures item 7 would have deleted are what make those cases assert anything.Security criterion 4 flagged for review, not decided here.
docs/MIGRATION-PLAYBOOK.md:657-659("no reading consumer files outside${CLAUDE_PROJECT_DIR}") sits in apparent tension with seam 2's blessed~/.claude/user-global. The reconciliation this PR assumes is that user-home config is the operator's own declared surface rather than consumer repository data — the first-partysecurity-guidanceplugin reads~/.claude/claude-security-guidance.mdon the same basis. Confirming that reading is a review decision, and it is the one thing here worth a second opinion.Test plan
scripts/validate-plugins.sh— passes; root README catalog regenerated vianode scripts/generate-catalog.mjsafter the manifest description changed.scripts/run-plugin-tests.sh— all plugin tests passed or were skipped (exit 0).markdownlint-cli2againstplugins/source-control/**/*.mdwith the repo config — 0 errors.link-checkis a weeklyfail: falsecron, so it gates nothing at PR time.git ls-files --eolon the three new files —i/lf w/lf, matching the* text=auto eol=lfattribute.evals.jsonfiles.Not run: the model-graded eval cases themselves. No first-party runner executes them today (
docs/MIGRATION-PLAYBOOK.md§"Evals"), so the new cases are verified as schema-valid fixtures with existingfiles, not as passing runs.Related
needs-human, deliberately untouched