diff --git a/CHANGELOG.md b/CHANGELOG.md index d23382c3..d2765614 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -165,6 +165,13 @@ per-file diff commands. path now counts as removed when its nearest ancestor directory exists at the base but not in HEAD; a new file or subdirectory under a directory the fork kept is still worth reviewing. +- **The `/reset` skeleton test lets a fork's profile carry its own `##` sections** (#676, + `tests/test_reset_command.py`) - it compared `/reset`'s `##` headings with + `01-candidate-profile.md` and `02-behavioral-profile.md` exactly, so a fork that added a section + to its own profile failed its CI and local runs although `/reset` worked. Off upstream it now + requires every shipped heading to appear in order, so an added section passes and a renamed, + dropped or reordered one still fails; upstream keeps the exact comparison. + ## [1.7.3] - 2026-10-19 ### Added diff --git a/tests/test_reset_command.py b/tests/test_reset_command.py index bbe4cf20..58a5a7d1 100644 --- a/tests/test_reset_command.py +++ b/tests/test_reset_command.py @@ -34,6 +34,11 @@ import unittest from pathlib import Path from tempfile import TemporaryDirectory +try: + from tests._upstream import is_upstream_checkout +except ImportError: # `python -m unittest discover -s tests` imports test modules top-level + from _upstream import is_upstream_checkout + REPO = Path(__file__).resolve().parent.parent RESET = REPO / ".claude" / "commands" / "reset.md" SETUP = REPO / ".claude" / "commands" / "setup.md" @@ -205,6 +210,12 @@ def h2_headings(text): return [line.strip() for line in text.splitlines() if line.startswith("## ")] +def appear_in_order(needles, haystack): + """Whether every needle occurs in haystack, in the same relative order.""" + remaining = iter(haystack) + return all(needle in remaining for needle in needles) + + class TestResetTargetsTheHeadingsTheShippedFilesHave(unittest.TestCase): """Step 3's skeletons and anchors must name sections that exist. @@ -230,13 +241,31 @@ class TestResetTargetsTheHeadingsTheShippedFilesHave(unittest.TestCase): for filename in ("01-candidate-profile.md", "02-behavioral-profile.md"): with self.subTest(file=filename): shipped = (SKILL_DIR / filename).read_text(encoding="utf-8") + skeleton = h2_headings(skeleton_block(self.reset, filename)) + if not is_upstream_checkout(): + # On a fork this file is the user's personalized profile, which + # may carry ## sections of its own; a rename or a drop still fails. + self.assertTrue( + appear_in_order(skeleton, h2_headings(shipped)), + f"/reset's skeleton headings for {filename} must all appear, in " + f"order, in the personalized file: {skeleton}", + ) + continue self.assertEqual( - h2_headings(skeleton_block(self.reset, filename)), + skeleton, h2_headings(shipped), f"/reset's skeleton for {filename} must carry exactly the shipped " "## headings, in order - a reset must not rename or drop sections", ) + def test_fork_heading_check_rejects_a_renamed_dropped_or_reordered_heading(self): + # The fork branch above never runs in upstream CI, so pin its strictness here. + skeleton = ["## Identity", "## Education", "## Awards"] + self.assertTrue(appear_in_order(skeleton, ["## Identity", "## Education", "## Certifications", "## Awards"])) + self.assertFalse(appear_in_order(skeleton, ["## Identity", "## Schooling", "## Awards"])) + self.assertFalse(appear_in_order(skeleton, ["## Identity", "## Awards"])) + self.assertFalse(appear_in_order(skeleton, ["## Education", "## Identity", "## Awards"])) + def test_full_file_skeletons_keep_the_framework_version_frontmatter(self): # The shipped files open with a `framework_version` marker that # check_framework_version.py requires and check_upstream_updates.py