From eaa73e55abbd020b36c53e49252641b62d3d9af7 Mon Sep 17 00:00:00 2001 From: Ayobami Adegoke Date: Thu, 15 Oct 2026 10:30:49 +0100 Subject: [PATCH] fix(reset): keep the framework_version frontmatter on the two files /reset profile rewrites (#532) The full-file skeletons for 01-candidate-profile.md and 02-behavioral-profile.md started at the "#" title, while the shipped files open with a "---" block carrying framework_version. Applied as written, one /reset profile left both files failing tools/check_framework_version.py ("missing 'framework_version' in frontmatter") and invisible to tools/check_upstream_updates.py, which records a configuration error for a file with no local version - the fork update path every fork is sent through. Say to keep the frontmatter block and replace only what follows, with a note under "Profile reset" that the marker is framework data, never candidate data. The version is not hardcoded into the skeleton, where it would go stale on the next bump. A derived test applies each skeleton the way the step says and asserts the shipped file's version still parses out unchanged; it fails on master for both files. --- .claude/commands/reset.md | 6 +++-- CHANGELOG.md | 14 ++++++++++ tests/test_reset_command.py | 51 +++++++++++++++++++++++++++++++++---- 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/.claude/commands/reset.md b/.claude/commands/reset.md index a0b4a23d..37646fdb 100644 --- a/.claude/commands/reset.md +++ b/.claude/commands/reset.md @@ -138,7 +138,9 @@ Wait for the user's response. ### Profile reset -**For `01-candidate-profile.md`**, replace the file content with: +Every skill file under `.claude/skills/job-application-assistant/` opens with a `---` frontmatter block carrying its `framework_version` marker. That marker is framework data, not candidate data: `tools/check_framework_version.py` fails a file without it, and `tools/check_upstream_updates.py` cannot compare the file against upstream. Never remove or rewrite it - the two full-file replacements below keep it and replace only what follows. + +**For `01-candidate-profile.md`**, keep the file's `---` frontmatter block exactly as it is and replace everything below it with: ```markdown # Candidate Profile @@ -162,7 +164,7 @@ Wait for the user's response. ## References ``` -**For `02-behavioral-profile.md`**, replace the file content with: +**For `02-behavioral-profile.md`**, keep the file's `---` frontmatter block exactly as it is and replace everything below it with: ```markdown # Behavioral Profile diff --git a/CHANGELOG.md b/CHANGELOG.md index 39d20dfe..c8204ee9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,20 @@ per-file diff commands. ### Fixed +- **`/reset profile` keeps the `framework_version` frontmatter on the two files it rewrites** + (`.claude/commands/reset.md` Step 3, `tests/test_reset_command.py`) - the full-file skeletons + for `01-candidate-profile.md` and `02-behavioral-profile.md` started at the `#` title, while + the shipped files open with a `---` block carrying `framework_version`. Applied as written, + one `/reset profile` left both files failing `tools/check_framework_version.py` ("missing + 'framework_version' in frontmatter") and invisible to `tools/check_upstream_updates.py`, + which records a configuration error for a file with no local version to compare - the fork + update path the README sends every fork through. The step now says to keep the frontmatter + block and replace only what follows, with a note under "Profile reset" that the marker is + framework data, never candidate data; the version is not hardcoded into the skeleton, where + it would go stale on the next bump. A derived test applies each skeleton the way the step + says and asserts the shipped file's version still parses out unchanged; it fails on master. + Found while re-checking #519's class of drift. + - **`/reset profile` now targets the headings the shipped skill files actually have** (`.claude/commands/reset.md` Step 3, `tests/test_reset_command.py`) - two of its Step 3 targets had drifted. The `05-cv-templates.md` step anchored on a section beginning diff --git a/tests/test_reset_command.py b/tests/test_reset_command.py index fa6e5603..78815cba 100644 --- a/tests/test_reset_command.py +++ b/tests/test_reset_command.py @@ -166,13 +166,26 @@ class TestResetCoversEveryPersonalizedSkillFile(unittest.TestCase): SKILL_DIR = REPO / ".claude" / "skills" / "job-application-assistant" +def skeleton_step(reset_text, filename): + """(instruction sentence, fenced markdown) /reset uses to rewrite `filename` in Step 3.""" + marker = f"**For `{filename}`**" + start = reset_text.index(marker) + fence_open = reset_text.index("```markdown", start) + fence_close = reset_text.index("```", fence_open + len("```markdown")) + return reset_text[start:fence_open], reset_text[fence_open + len("```markdown"):fence_close] + + def skeleton_block(reset_text, filename): """The fenced markdown /reset writes over `filename` in Step 3.""" - marker = f"**For `{filename}`**, replace the file content with:" - start = reset_text.index(marker) - fence_open = reset_text.index("```markdown", start) + len("```markdown") - fence_close = reset_text.index("```", fence_open) - return reset_text[fence_open:fence_close] + return skeleton_step(reset_text, filename)[1] + + +def frontmatter(text): + """The leading `---` block of a skill file, or '' when it has none.""" + if not text.startswith("---\n"): + return "" + end = text.index("\n---", 4) + return text[: end + len("\n---") + 1] def h2_headings(text): @@ -211,6 +224,34 @@ class TestResetTargetsTheHeadingsTheShippedFilesHave(unittest.TestCase): "## headings, in order - a reset must not rename or drop sections", ) + 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 + # reads. The skeletons started at the `#` title, so one /reset profile + # left both files failing the version gate ("missing 'framework_version' + # in frontmatter") and invisible to the fork update checker. The + # instruction must say to keep the block, and the block must not be + # hardcoded into the skeleton, where it would go stale on the next bump. + import sys + sys.path.insert(0, str(REPO / "tools")) + from check_upstream_updates import get_framework_version_from_text + + 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") + head = frontmatter(shipped) + self.assertTrue(head, f"{filename} must ship with a frontmatter block") + instruction, block = skeleton_step(self.reset, filename) + self.assertIn("frontmatter", instruction, f"the {filename} step must say to keep the frontmatter") + self.assertNotIn("framework_version", block, "the skeleton must not hardcode a version") + # What the file looks like after the reset, applied as written. + after = head + block.lstrip("\n") + self.assertEqual( + get_framework_version_from_text(after), + get_framework_version_from_text(shipped), + f"a reset must leave {filename}'s framework_version readable and unchanged", + ) + def test_partial_anchors_exist_in_the_files_they_target(self): # (reset.md anchor phrase, file, heading the replacement must re-emit) cases = (