feat(skills): sync-advisor — measure the drift before naming the fix (v1.38.0) - #56
Conversation
…(v1.37.0) Renames `rebase-advisor` to `sync-advisor` and turns a blind router into an advisor that measures. The old skill was 43 lines and measured nothing. Its step 1 was "Confirm the user wants a full team sync" -- a question back to the user about something three git commands answer. Its description then over-routed: it triggered on "align with develop" and "branches are behind", both of which are `git pull`, and sent them to /make-no-mistakes:rebase, which stashes every worktree, rebases every local branch and auto-merges PRs. Between `git pull` and that the toolkit offered nothing, and nothing read-only at all. Six read-only predicates now run before anything is named: distance, fast-forward possible, dirty tree split by stage, untracked files the ref already tracks, worktrees behind, and branches with unpushed commits. The fifth is the threshold between a plain pull and the team command. The fourth is the one nothing else reports, and it was verified on a throwaway pair of repos rather than asserted. An untracked local file at a path the ref tracks aborts the pull outright while being invisible everywhere else: `git status` shows a plain `?? newfile.txt`, distance reports a clean 0 ahead 1 behind, and `merge-base --is-ancestor` says a fast-forward is possible. The pull then exits 1 with "The following untracked working tree files would be overwritten by merge ... Please move or remove them before you merge" -- the message names the user's own file and offers deletion as the remedy, which is the one irreversible move available. The skill reports these by name and recommends copying them out of the repo, never deleting them. Both controls were run: the pipeline printed nothing before the collision existed and named the file after. It never acts. Every fix is printed for the user to run. The single write is `git fetch origin --quiet`, which touches remote-tracking refs and nothing else, and the skill says so out loud -- without it every measurement is taken against a stale origin/<base> and reports a drift that stopped being true days ago, which is the failure this skill exists to catch. Adds `syncAdvisor.governedPaths` to make-no-mistakes.config.json: the paths whose changes get reported by name, turning "you are 12 behind" into "three hooks changed, two of them fix defects you may be looking at right now". No default -- with the key unset the skill drops the consequence line rather than falling back to a built-in list, which would be wrong in every repo but the one it was copied from and would read as measured. commands/rebase.md is untouched and stays a real destination. What changed is who decides when it applies. Origin (2026-07-31, as reported): a developer filed two bug reports against a hook with clean reproductions. One was a real defect; the other described behaviour fixed days earlier against a stale checkout, and nothing in the report separated them. Suite: 337/337 hooks, 60/60 vitest. Created by Claude Code on behalf of @lapc506
There was a problem hiding this comment.
✅ Approved
Approved — no findings. Confidence: 5.00/5.00. NITs: 1 (shown).
Walkthrough
main directly. GitFlow expectations are feature → develop → main. This is informative only and does not impact the code review verdict.
Review Walkthrough
This PR renames the rebase-advisor skill to sync-advisor and upgrades it from a basic router to a comprehensive git drift advisor that measures six read-only predicates before suggesting fixes (such as a plain fast-forward pull, a single-branch rebase, or the full team-wide /rebase). It also introduces a syncAdvisor.governedPaths behavior configuration to highlight critical modified files (e.g., hooks, scripts) by name, and updates the toolkit's package/marketplace/plugin manifests to v1.37.0.
Reviewed Areas/Files
skills/sync-advisor/SKILL.md: Analyzed the newly added multi-predicate git drift analysis and command routing logic.commands/make-no-mistakes.config.example.json&README.md: Checked the configuration schema structure and documentation.package.json,.claude-plugin/plugin.json,.claude-plugin/marketplace.json: Verified version matching consistency.CHANGELOG.md: Confirmed accurate logs and fixed missing version links.
Safety Rationale
The advisor operates strictly in a read-only manner (with the safe exception of a quiet remote-tracking fetch to avoid stale measurements) and solely outputs action recommendations for the user to run manually, guaranteeing that merging these changes introduces no automated write-side impact or workspace risks.
Approved — 0 blockers.
⚪ P4 — Nitpicks
skills/sync-advisor/SKILL.md:14— [NIT] ⚪ P4 (nit) — Unnecessary backslash escapes in YAML folded scalar block. Inside a folded scalar block (>), double quotes do not require escaping unless the entire block is wrapped in outer quotes. Dropping the backslashes from\"squash theseandcommits\")makes the text cleaner.
[pass 1]
Total findings: 1 nit (1 total)
| "sync with develop", "sync with release", "post-release sync", | ||
| "rebase all branches", "team rebase", "my worktrees are behind", | ||
| "did I miss anything from develop". | ||
| Does NOT trigger on: interactive rebase ("git rebase -i", "squash these |
There was a problem hiding this comment.
[NIT] ⚪ P4 (nit) — Unnecessary backslash escapes in YAML folded scalar block. Inside a folded scalar block (>), double quotes do not require escaping unless the entire block is wrapped in outer quotes. Dropping the backslashes from \"squash these and commits\") makes the text cleaner.
[pass 1]
Measured on origin/main @ ee0ba47: the three version files all read 1.36.0, and `gh pr diff 55 | grep -E '^\+.*"version"'` shows #55 already bumps to 1.37.0 in package.json, plugin.json and marketplace.json. Taking 1.37.0 here collides with it in all three. The same probe over #52, #46, #44 and #41 returns nothing, so 1.38.0 is free. Skipping a version costs nothing; colliding does. Minor and not major, decided by reading. A skill auto-activates on its `description` rather than being invoked by name like a command, so a renamed `name:` changes no call site. The reference search returns nothing: grep -rniI "rebase.advisor" . --exclude-dir=node_modules \ --exclude-dir=.git --exclude=CHANGELOG.md -> no hits That negative is real and not a broken search -- the same grep for `spike-recommend` returns 10 files, so cross-references of this shape are found when they exist. One surface does break and is stated rather than folded in: a user who typed `/make-no-mistakes:rebase-advisor` explicitly (README:137 documents that skills can be invoked that way) now gets an unknown skill. It fails loudly, the replacement is one row away in the same table, and the installer prunes the old file rather than leaving both live. The `[1.37.0]:` CHANGELOG reference slot is deliberately left for #55 to fill. Suite: 337/337 hooks, 60/60 vitest. Created by Claude Code on behalf of @lapc506
There was a problem hiding this comment.
✅ Approved
Approved — no findings. Confidence: 5.00/5.00.
Walkthrough
main branch directly. Per repository GitFlow guidelines, the expected flow is andres/sync-advisor → develop → main. This is a non-blocking informative warning; the technical review below focuses entirely on the quality and correctness of the code.
Walkthrough
This PR renames and completely overhauls the rebase-advisor skill into sync-advisor. Rather than blindly routing users or prompting them with generic questions, it now executes six read-only git predicates to programmatically measure branch and worktree drift. It introduces syncAdvisor.governedPaths in the tool configuration to map specific commit differences into meaningful user consequences (such as naming specific changed hooks or workflows), and suggests (but never automatically runs) the most precise Git commands (git pull --ff-only, single-branch rebase, or the full /rebase protocol).
Files Reviewed
- Skill Definition:
skills/sync-advisor/SKILL.md(fully redesigned) and removal ofskills/rebase-advisor/SKILL.md. - Configuration Example:
commands/make-no-mistakes.config.example.jsonaddingsyncAdvisor.governedPaths. - Project Metadata & Versioning:
package.json,.claude-plugin/plugin.json,.claude-plugin/marketplace.json,README.md, andCHANGELOG.md(bumped to1.38.0and references updated).
Safety Rationale
The upgraded skill operates purely in a read-only manner—measuring drift and printing explicit recommendations for the user to review and run manually—ensuring zero risk of unexpected repository manipulation or data loss.
Approved — no findings.
|
Reviewed twice, both APPROVED — Two findings in those reviews are false. Recording the measurements rather than editing the file to silence them. 1. P4 nit — "Unnecessary backslash escapes in YAML folded scalar block" at And the frontmatter parses as YAML with no backslash in the decoded value: The 2. Governance Warning — "targets
Standing verdict: 0 blockers, 0 P1, 0 P2, 1 P4 refuted above. Suite on Not merging yet: #55 also bumps the same three version files (it takes Created by Claude Code on behalf of @lapc506 |
#55 merged first, which is exactly the collision the trial merge predicted: five conflicting files, not three, because README and CHANGELOG carry the version line and the release entry alongside the three manifests. Resolved by reading each block rather than by taking a side wholesale. `git checkout --ours` would have been wrong here: #55 added a ~38-line README section and a 78-line CHANGELOG entry, and taking the whole file from HEAD discards every hunk git had already auto-merged. Instead each conflict block was resolved individually, after first confirming every block contained only a version line (an awk pass over the block interiors, zero non-version lines). - package.json, plugin.json, marketplace.json (x2), README.md line 3 -> 1.38.0. 1.37.0 belongs to #55 and is now on main; 1.38.0 is next. - CHANGELOG entries -> BOTH kept, 1.38.0 above 1.37.0. - CHANGELOG reference links -> keeping both sides produced a duplicate `[Unreleased]:` line, caught by re-reading the resolved region rather than trusting the resolver. One `[Unreleased]` now, pointing at v1.38.0, with 1.38.0 / 1.37.0 / 1.36.0 below it. Verified nothing of #55's was dropped: `git diff origin/main -- hooks/` is empty, and its README section and CHANGELOG entry are both present. Every `## [1.3x.0]` heading has exactly one matching reference link, and the three manifests parse with version 1.38.0. Suite on the merged tree: 340/340 hooks (up from 337 — #55 added three), 60/60 vitest. Created by Claude Code on behalf of @lapc506
There was a problem hiding this comment.
✅ Approved
Approved — 0 blockers, 1 P3. Confidence: 4.80/5.00.
Walkthrough
main directly. GitFlow expects development features to target develop before merging to main. This is a non-blocking informative warning; the technical review verdict below is based on code quality and correctness.
Walkthrough
This PR renames the rebase-advisor skill to sync-advisor and upgrades it from a simple routing prompt to a robust advisor that dynamically measures git repository drift via six read-only predicates before proposing recommendations. It also introduces the syncAdvisor.governedPaths configuration schema to map local file modifications to actionable warnings.
Reviewed Files/Areas
I reviewed the skill specification (skills/sync-advisor/SKILL.md), the documentation updates in README.md, configuration updates in commands/make-no-mistakes.config.example.json, package manifest version bumps (package.json, .claude-plugin/plugin.json, .claude-plugin/marketplace.json), and the CHANGELOG.md records.
Safety Rationale
The implementation is completely read-only (except for a safe, quiet git fetch) and operates purely as an advisory CLI skill with no write operations to the user's working tree or index, making it completely safe to merge.
Verdict
Approved — 0 blockers, 1 P3.
🔵 P3 — Minor
skills/sync-advisor/SKILL.md:141— 🔵 P3 (minor) — Whengit ls-filesis run from a subdirectory, it outputs paths relative to that subdirectory, whereasgit ls-treealways outputs repository-relative paths. If the skill is executed from a subdirectory, the two file lists will fail to match incomm -12even if there are identical paths, leading to missed untracked file collisions. Adding the--full-nameoption togit ls-filesforces it to always output repository-relative paths, matchinggit ls-treeand making the check robust from any working directory.
[pass 1]
Total findings: 1 business context (1 total)
| `git status`, which shows the file as a plain `??` — indistinguishable from | ||
| harmless scratch. It is invisible to the distance count, and to the | ||
| fast-forward check: a clean `0 ahead, 1 behind` fast-forward aborts exactly the | ||
| same way. And the message names the user's own files, then names deletion as a |
There was a problem hiding this comment.
🔵 P3 (minor) — When git ls-files is run from a subdirectory, it outputs paths relative to that subdirectory, whereas git ls-tree always outputs repository-relative paths. If the skill is executed from a subdirectory, the two file lists will fail to match in comm -12 even if there are identical paths, leading to missed untracked file collisions. Adding the --full-name option to git ls-files forces it to always output repository-relative paths, matching git ls-tree and making the check robust from any working directory.
[pass 1]
…irectory Reviewer P3 on `ea8c46e` claimed `git ls-tree` "always outputs repository-relative paths" while `git ls-files` is prefix-relative, so the two would fail to match from a subdirectory, and proposed `--full-name` on `ls-files`. Measured: the premise is false and the proposed fix introduces the bug it claims to prevent. From `sub/`, `ls-tree -r --name-only` prints `newfile.txt`, not `sub/newfile.txt` -- it strips the prefix exactly like `ls-files` does, so the original command matched fine and found the collision. Adding `--full-name` alone then makes `ls-files` emit `sub/newfile.txt` against `ls-tree`'s `newfile.txt`, they stop matching, and `comm -12` returns empty -- a clean bill of health for a tree about to abort the pull. But it pointed at a real weakness of a different kind. From a subdirectory both commands are SCOPED to that subtree, so a collision at the repo root is not seen at all. That is scope, not format, and no combination of format flags fixes it: `--full-name` changes how a path prints, never which paths are considered. The pathspec does. Shipped: `--full-name -- :/` on ls-files, `--full-tree` on ls-tree. Four cases run, with the command extracted verbatim from SKILL.md so the test cannot drift from the doc -- negative control from the root and from `sub/` (both empty), positive from the root and from `sub/` with one collision in each location (both list `rootfile.txt` and `sub/newfile.txt`). Suite: 340/340 hooks, 60/60 vitest. Created by Claude Code on behalf of @lapc506
|
On the P3 at The premise. The finding says The proposed fix breaks it. Empty is the dangerous output here: it reads as "no collisions" for a tree that is about to abort the pull. That is the exact failure the finding set out to prevent. The real weakness it gestured at, and the fix. From a subdirectory both commands are scoped to that subtree, so a collision at the repo root is never examined. That is scope, not format, and no format flag fixes it — comm -12 \
<(git ls-files --others --exclude-standard --full-name -- :/ | sort) \
<(git ls-tree -r --full-tree --name-only "origin/$BASE" | sort)Four cases, with the command extracted verbatim from
Net: finding rejected as written, underlying issue real and fixed, and the fix is a strictly wider check than either the original or the suggestion. Suite on Created by Claude Code on behalf of @lapc506 |
There was a problem hiding this comment.
💬 Review Comments
Comments — 0 blockers, 1 P2. Confidence: 3.80/5.00. NITs: 1 (shown).
Walkthrough
main directly instead of develop, which departs from the expected GitFlow branch sequencing (feature → develop → main). This warning is non-blocking for this review but should be corrected/noted by the maintainers.
Summary Walkthrough
This PR renames the rebase-advisor skill to sync-advisor and upgrades it from a simple router to a robust "measure-first" advisor. It introduces six read-only git predicates to assess repository drift and adds a new syncAdvisor.governedPaths configuration setting to provide specific file change context rather than a generic commit count behind.
Files/Areas Reviewed
I reviewed all modified configuration and metadata files (package.json, .claude-plugin/plugin.json, .claude-plugin/marketplace.json), the documentation files (README.md, CHANGELOG.md, commands/make-no-mistakes.config.example.json), and the core skill implementation playbook (skills/sync-advisor/SKILL.md).
Safety Rationale
Since this pull request strictly modifies markdown playbooks, metadata manifests, and configuration examples without adding any executable source code or automated commands, it poses zero operational risk to the runtime system and is entirely safe to merge.
Review completed — 0 blockers, 1 P2, 1 P4.
🟡 P2 — Major
skills/sync-advisor/SKILL.md:53— 🟡 P2 (major) — If Command 1 or Command 2 is used to resolve the base ref, the resulting value stored in$BASEwill include the remote prefix (e.g.,origin/developororigin/main). When subsequently expanded asorigin/$BASEin subsequent predicates and steps, it will resolve toorigin/origin/developororigin/origin/main. These are invalid Git references, which will cause commands likegit rev-listandgit diffto fail.
We should explicitly document stripping the remote prefix (for example, using shell parameter expansion like BASE=${BASE#*/}) in Step 0 so that $BASE is guaranteed to be just the local branch name (e.g., develop or main).
[pass 1]
⚪ P4 — Nitpicks
skills/sync-advisor/SKILL.md:53— [NIT] ⚪ P4 (nit) — Passing both--abbrev-refand--symbolic-full-nametogit rev-parseis redundant. In standard Git usage,--abbrev-refon its own is sufficient to obtain the abbreviated upstream branch name.
[pass 1]
Total findings: 1 compliance, 1 nit (2 total)
| Do not assume `develop`. Resolve in this order and say which one you used: | ||
|
|
||
| ```bash | ||
| git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' # 1. what this branch tracks |
There was a problem hiding this comment.
🟡 P2 (major) — If Command 1 or Command 2 is used to resolve the base ref, the resulting value stored in $BASE will include the remote prefix (e.g., origin/develop or origin/main). When subsequently expanded as origin/$BASE in subsequent predicates and steps, it will resolve to origin/origin/develop or origin/origin/main. These are invalid Git references, which will cause commands like git rev-list and git diff to fail.
We should explicitly document stripping the remote prefix (for example, using shell parameter expansion like BASE=${BASE#*/}) in Step 0 so that $BASE is guaranteed to be just the local branch name (e.g., develop or main).
[pass 1]
| Do not assume `develop`. Resolve in this order and say which one you used: | ||
|
|
||
| ```bash | ||
| git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' # 1. what this branch tracks |
There was a problem hiding this comment.
[NIT] ⚪ P4 (nit) — Passing both --abbrev-ref and --symbolic-full-name to git rev-parse is redundant. In standard Git usage, --abbrev-ref on its own is sufficient to obtain the abbreviated upstream branch name.
[pass 1]
Reviewer P2 on `00e2c08`, and it is right. Step 0 returned values carrying the
remote prefix while every predicate interpolates `origin/$BASE`, so the run
died on `origin/origin/develop`:
$ git rev-list --left-right --count "HEAD...origin/origin/main"
fatal: ambiguous argument … unknown revision exit=128
Fixed by normalising unconditionally (`${BASE#refs/remotes/}`, `${BASE#origin/}`)
whichever branch of the resolution produced the value. Verified both branches
with a control that must fail: where `origin/HEAD` IS set it returns
`origin/main`, normalises to `main`, and `origin/main` resolves; the
un-normalised form exits 128.
The P2 understated it. Step 0's first command resolved the base from
`@{upstream}`, which on a feature branch is that branch's OWN remote copy --
here `origin/andres/sync-advisor`. That answers "am I pushed?", which is
predicate 6's question, and would report 0 behind on a branch far behind the
real base. Stripping a prefix would have left it pointing at the wrong ref, so
the command is gone from base resolution rather than patched.
Also measured while there: `git symbolic-ref --short refs/remotes/origin/HEAD`
fails outright in this repo (`not a symbolic ref`), so it is documented as a
fall-through into the develop/main/master/trunk probe rather than as a step
that is expected to succeed.
P4 (`--abbrev-ref` and `--symbolic-full-name` redundant) is also correct --
both forms return `origin/andres/sync-advisor` here -- and is moot: that
command no longer appears.
Suite: 340/340 hooks, 60/60 vitest.
Created by Claude Code on behalf of @lapc506
|
P2 accepted — it was right, and it understated the problem. Fixed in The prefix defect, reproduced. Step 0's commands returned values carrying the remote, and every predicate interpolates Now normalised unconditionally, whichever branch of the resolution produced the value: BASE=${BASE#refs/remotes/}
BASE=${BASE#origin/}Verified on both paths, each against a control that must fail — where Where the finding understated it. The suggested remedy was to strip the prefix. Stripping alone would have left Step 0 pointing at the wrong ref, not a misspelt one. Its first command resolved the base from Stripped, that becomes One more thing measured while in there, unprompted: — so it is now documented as a fall-through into the P4 also correct, and now moot. Both forms return Suite on Created by Claude Code on behalf of @lapc506 |
There was a problem hiding this comment.
✅ Approved
Approved — 0 blockers, 1 P3. Confidence: 4.80/5.00. NITs: 1 (shown).
Walkthrough
GOVERNANCE WARNING: This PR targets the main branch directly, bypassing the standard GitFlow path of feature -> develop -> main. If your release procedures require merging to develop first, consider retargeting this PR.
Walkthrough
This PR renames the rebase-advisor skill to sync-advisor, extending it from a blind router into an active, read-only advisor that measures repository drift before suggesting remedial commands. It executes six read-only git predicates to safely determine the exact synchronization state of the user's checkout. Additionally, it integrates a new syncAdvisor.governedPaths configuration key to provide targeted consequence reporting for critical directories like hooks, scripts, and workflows.
Areas Reviewed
skills/sync-advisor/SKILL.md(analyzed all git command formulations, base resolution steps, and routing logic).package.json,.claude-plugin/plugin.json,.claude-plugin/marketplace.json(version bumps to1.38.0).README.md,CHANGELOG.md(documentation, version tables, and changelog comparison updates).commands/make-no-mistakes.config.example.json(configuration keys and default explanations).src/lib/copy-assets.ts(verified installer asset-scanning and pruning logic remains robust under renames).
Safety Rationale
The advisor logic is entirely read-only (with the safe exception of running git fetch origin --quiet to update remote-tracking refs) and only makes educational/routing recommendations, ensuring no local branch rewrites, stashes, or deletions are executed automatically.
Approved — 0 blockers, 1 P3.
🔵 P3 — Minor
skills/sync-advisor/SKILL.md:67— 🔵 P3 (minor) — Suppress noisy stderr when the reference default branch cannot be symbolic. In a fresh checkout or worktree,origin/HEADis often unset, causinggit symbolic-refto print afatal: ref refs/remotes/origin/HEAD is not a symbolic referror directly to stderr. Appending2>/dev/nullkeeps the terminal output clean during the fall-through sequence.
[pass 1]
⚪ P4 — Nitpicks
package.json:3— [NIT] ⚪ P4 (nit) — The package version in package-lock.json is currently stuck at 1.23.0 and is out of sync with package.json (1.38.0). Runnpm install --package-lock-onlyto keep them aligned.
[pass 1]
Total findings: 1 business context, 1 nit (2 total)
| { | ||
| "name": "@lapc506/make-no-mistakes", | ||
| "version": "1.37.0", | ||
| "version": "1.38.0", |
There was a problem hiding this comment.
[NIT] ⚪ P4 (nit) — The package version in package-lock.json is currently stuck at 1.23.0 and is out of sync with package.json (1.38.0). Run npm install --package-lock-only to keep them aligned.
[pass 1]
| upstream is that branch's *own* remote copy — `origin/andres/sync-advisor`, not | ||
| `origin/develop`. Measuring against it answers *"am I pushed?"*, which is | ||
| predicate 6's question, and it reports `0 behind` on a branch that is fifty | ||
| commits behind the base. It is the wrong ref, not merely the wrong spelling. |
There was a problem hiding this comment.
🔵 P3 (minor) — Suppress noisy stderr when the reference default branch cannot be symbolic. In a fresh checkout or worktree, origin/HEAD is often unset, causing git symbolic-ref to print a fatal: ref refs/remotes/origin/HEAD is not a symbolic ref error directly to stderr. Appending 2>/dev/null keeps the terminal output clean during the fall-through sequence.
[pass 1]
|
P3 on The finding asks to append a stderr-discarding redirect to the (The two blocked forms are spelled out in Those tests pass in the shipped suite — The reasoning is in that PR and applies directly: a failing command with its stderr discarded is indistinguishable from a succeeding one that printed nothing. Suppressing this particular message would be an unusually bad instance of it, because the message is the signal the fall-through depends on —
Worth noting for calibration: this guard fired three times during work on this PR, including on the heredoc that was composing this very comment. It is not theoretical. Standing verdict on Created by Claude Code on behalf of @lapc506 |
What
Renames the
rebase-advisorskill tosync-advisorand extends it from a blind router into an advisor that measures first.Why
skills/rebase-advisor/SKILL.mdwas 43 lines and measured nothing. Its step 1 was "Confirm the user wants a full team sync" — a question back to the user about something three git commands answer.Its
descriptionthen over-routed: it triggered on"align with develop"and"branches are behind"— bothgit pullcases — and sent them all to/make-no-mistakes:rebase, which stashes every worktree, rebases every local branch and auto-merges PRs. Betweengit pulland that, the toolkit offered nothing, and nothing read-only at all.The shape — measures, routes, never acts
Six read-only predicates run before anything is named:
git rev-list --left-right --count HEAD...origin/$BASEgit merge-base --is-ancestor HEAD origin/$BASEpullvs. rebase/mergegit status --porcelain, split staged vs unstagedcomm -12ofls-files --othersandls-tree -rgit worktree list --porcelain+ per-branch distance/rebasegit for-each-ref%(upstream:track)Predicate 5 is the threshold: one branch behind is a
git pull, several worktrees behind is what/rebasewas built for.Every fix is printed for the user to run —
git pull,git stash,git rebase,/make-no-mistakes:rebase. The single write isgit fetch origin --quiet(remote-tracking refs only), and the skill names it out loud: without it every measurement is taken against a staleorigin/<base>.Predicate 4 was verified, not asserted
Built a throwaway upstream/clone pair and ran both controls:
commpipeline printed nothing.newfile.txt.Then confirmed what makes it worth a dedicated predicate — it is invisible to every other signal:
This is the part of the PR worth arguing about, so it is stated plainly rather than left as a UX detail.
git pullnames the user's own files and then offers deleting them as the remedy — and deletion is the only irreversible move in the whole sequence. Everything else here (a pull, a stash, a rebase) is recoverable; removing an untracked file that was never committed is not. The abort message is what pushes toward it, and it reads as the user's own mistake, which is exactly when someone reaches for the destructive option without thinking.So the skill does the opposite of what the message suggests: it reports the colliding files by name and recommends copying them somewhere outside the repo. It never recommends deleting them. That inversion is what makes this an advisor rather than a wrapper around
git pull— a wrapper would surface git's own remedy verbatim.Corroboration beyond the synthetic repro above, reported by the reviewer of this PR and not re-measured by me: this exact class of collision blocked a pull twice today in a client repo, and in a separate session an agent lost uncommitted work to an operation of the same shape.
The consequence line
New
syncAdvisor.governedPathskey inmake-no-mistakes.config.json(the existing behaviour config, already read forlanguage):{ "syncAdvisor": { "governedPaths": [".claude/hooks/", "scripts/", ".github/workflows/"] } }Turns "you are 12 commits behind" into "three hooks changed, two of them fix defects you may be looking at right now". Paths are not hardcoded — this toolkit is shared and carries no client-repo mentions (checked: no
dojo/DOJ-NNNNstring in any added line).Degraded mode is documented in both the SKILL.md and the config example: with the key unset, the skill reports distance and routing and omits the consequence line. No built-in fallback list — an invented one is wrong in every repo but the one it was copied from, and would read as measured.
Origin
2026-07-31 (as reported, not re-measured): a developer filed two bug reports against a hook, both with clean reproductions. One was a real defect; the other described behaviour fixed days earlier against a stale checkout. Nothing in the report separated them. A commit count alone would not have separated them either — naming the changed hooks would have.
References to the old name
Searched case-insensitively across the whole repo (
grep -rniI "rebase.advisor", excludingnode_modulesand.git). Two hits, both updated:skills/rebase-advisor/SKILL.md— the skill itself (git mvtoskills/sync-advisor/, frontmattername:updated)README.md:131— the Skills table rowThe only surviving occurrence is the CHANGELOG line that documents the rename itself.
Nothing is left broken. Checked the install machinery:
src/lib/copy-assets.ts:24enumerates theskillsdirectory generically (no hardcoded skill names), andcopy-assets.ts:65-72prunes stale managed files — any path in the previous install manifest that is not in the new expected set gets removed, soskills/rebase-advisor/SKILL.mdis deleted onmake-no-mistakes update.dist/is untracked, so no stale build carries the old name.Not touched, on purpose
commands/rebase.md— untouched. It stays a real destination; what changed is who decides when it applies.Found while working, reported not fixed
Two pre-existing drifts adjacent to lines I edited. Neither is caused by this PR and neither is in its scope:
### Skills (10)but there are 11 skill directories —resolve-open-questionshas no row in that table, while.claude-plugin/marketplace.jsoncorrectly says "11 auto-activating skills". The count is unchanged by this rename (a rename is 11 → 11), so I left both the heading and the missing row alone rather than widen this PR.[Unreleased]still compared fromv1.35.0and there was no[1.36.0]:line. I did fix this one, because my entry would otherwise sit above a hole I had just widened: added[1.36.0]and[1.38.0]and repointed[Unreleased]. The[1.37.0]:slot is left empty on purpose — that version belongs to feat(hooks): a refusal no longer prints its own bypass marker (v1.37.0) #55.One cosmetic residue I am not fixing here: the pruner removes files with
rm(abs)and not the parent directory, so an existing OpenCode install keeps an emptyskills/rebase-advisor/directory after updating. An empty directory with noSKILL.mddoes not register as a skill, so this is cosmetic — but fixing it means touching the installer, which does not belong in a rename PR.Version: 1.38.0 (skipping 1.37.0 on purpose)
Measured on
origin/main @ ee0ba47— all three version files read1.36.0. Probing every open PR for a bump:#55 already takes 1.37.0 in all three files, so taking it here collides in all three. The other four open PRs bump nothing. Skipping a version costs nothing; colliding does.
The
[1.37.0]:CHANGELOG reference slot is deliberately left empty for #55 to fill.Minor and not major — decided by reading, not by habit
A rename is normally breaking, but a skill auto-activates on its
description; it is not invoked by name the way a command is, so a renamedname:changes no call site.That negative is real rather than a broken search — the positive control: the same grep for a name that is cross-referenced returns 10 files, so references of this shape are found when they exist.
One surface does break, stated here rather than folded into the above: a user who typed
/make-no-mistakes:rebase-advisorexplicitly —README.md:137documents that skills can be invoked that way — now gets an unknown skill. It fails loudly, the replacement is one row away in the same table, and the installer prunes the old file rather than leaving both live. Anyone who reads that as breaking should say so on the PR; the call is recorded in the CHANGELOG so it can be disagreed with.Also relevant to house precedent: this repo has shipped 38 minor releases and no major (
grep -cE "^## \[1\." CHANGELOG.md→ 38; no0.x,2.xor3.xheading exists).Tests
Both suites run on the final state, output not discarded:
Same baseline PR #53 reported. Nothing was skipped. Not merged — this PR is left for review.
Created by Claude Code on behalf of @lapc506