diff --git a/.claude/commands/reset.md b/.claude/commands/reset.md index 6ca4bbb5..a0b4a23d 100644 --- a/.claude/commands/reset.md +++ b/.claude/commands/reset.md @@ -171,11 +171,13 @@ Wait for the user's response. ## Overview -## Strongest Behavioral Traits +## Core Behavioral Drives -## How I Work Best +## Strongest Behaviors -## Growth Areas +## How You Work Best + +## Growth Areas (frame positively in applications) ## Mapping to Job Posting Language @@ -205,12 +207,12 @@ Also remove any `## Calibration from Past Applications` section, which `/setup` Leave the rest of `04-job-evaluation.md` intact: the five scoring dimensions and their score bands, the weighting, the Language Gate, the red-flag guidance, the Company Research Checklist and cache schema, and the salary benchmark section. If `/setup` Step 3.4 ever personalizes a value not in the table above, add it here too. -**For `05-cv-templates.md`**, locate the section that begins with `**Profile statement templates` and extends through the role-specific template blocks. Replace only that section with: +**For `05-cv-templates.md`**, locate the section that begins with the line `**Create 2-3 profile statement templates for your main role types:**` and extends through the role-specific template blocks, including any statements labeled *[Used for: _]* that `/setup` Path A extracted from archived applications. Stop before the paragraph that begins "Statements labeled" - that is framework guidance, not candidate data. Replace only that section with: ```markdown -**Profile statement templates:** +**Create 2-3 profile statement templates for your main role types:** - + ``` Then restore the contact block inside the file's LaTeX template to its placeholder tokens: `\name{[FIRST_NAME]}{[LAST_NAME]}`, `\address{[YOUR_ADDRESS]}{}{}`, `\phone[mobile]{[YOUR_PHONE]}`, `\email{[YOUR_EMAIL]}`, the `\extrainfo{...}` line's `[YOUR_LINKEDIN_URL]` and `[YOUR_GITHUB_URL]`, and `[YOUR_NAME]` in the `pdftitle`. Leave all other content in `05-cv-templates.md` intact. diff --git a/CHANGELOG.md b/CHANGELOG.md index 832f077b..39d20dfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,23 @@ per-file diff commands. ### Fixed +- **`/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 + `**Profile statement templates`, a line the guide has never contained (its heading is + `**Create 2-3 profile statement templates for your main role types:**`), so the wipe found + nothing: the user's role-tailored profile statements, and any `*[Used for: ...]*` + statements `/setup` Path A extracted from archived applications, survived a reset that + reported success. The `02-behavioral-profile.md` skeleton used headings the shipped file + does not have (`Strongest Behavioral Traits`, `How I Work Best`, a truncated `Growth Areas`) + and dropped `## Core Behavioral Drives` - the assessment table the shipped file carries - so + one reset left the file's headings permanently different + from every other fork's, with nothing to recreate the table. The anchor now names the real + heading and the replacement re-emits it; the skeleton carries the shipped headings in order. + `01-candidate-profile.md` and `07-interview-prep.md` were already correct. Two new tests + derive both checks from the shipped files, so a renamed heading fails until `/reset` + follows it; both fail on master. + - **The `/apply` host-check test derives its installed-portal hosts from the skills tree instead of a hand-copied set** (#497, `tests/test_apply_host_check.py`) - `SHIPPED_PORTAL_HOSTS` was six names typed into the test and nothing tied it to anything: a portal added by diff --git a/tests/test_reset_command.py b/tests/test_reset_command.py index 1ff40eca..fa6e5603 100644 --- a/tests/test_reset_command.py +++ b/tests/test_reset_command.py @@ -163,6 +163,83 @@ class TestResetCoversEveryPersonalizedSkillFile(unittest.TestCase): f"{mislabeled}", ) +SKILL_DIR = REPO / ".claude" / "skills" / "job-application-assistant" + + +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] + + +def h2_headings(text): + return [line.strip() for line in text.splitlines() if line.startswith("## ")] + + +class TestResetTargetsTheHeadingsTheShippedFilesHave(unittest.TestCase): + """Step 3's skeletons and anchors must name sections that exist. + + Two of them drifted from the files they rewrite. The behavioral-profile + skeleton used "Strongest Behavioral Traits" / "How I Work Best" / "Growth + Areas" where the shipped file has "Strongest Behaviors" / "How You Work + Best" / "Growth Areas (frame positively in applications)", and dropped + "Core Behavioral Drives" - the structured assessment table /setup collects + and /interview reads - so one reset left the file's headings permanently + different from every other fork's. The CV-guide step anchored on a line + beginning "**Profile statement templates", which the guide has never + contained (its heading is "**Create 2-3 profile statement templates for + your main role types:**"), so the wipe found nothing and the user's + role-tailored statements survived a reset that reported success. Both are + derived here from the shipped files, so a renamed heading fails this test + until /reset follows it. + """ + + def setUp(self): + self.reset = RESET.read_text(encoding="utf-8") + + def test_full_file_skeletons_keep_the_shipped_headings(self): + 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") + self.assertEqual( + h2_headings(skeleton_block(self.reset, filename)), + 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_partial_anchors_exist_in_the_files_they_target(self): + # (reset.md anchor phrase, file, heading the replacement must re-emit) + cases = ( + ( + r"locate the section that begins with the line `([^`]+)`", + "05-cv-templates.md", + ), + ( + r"The entire `([^`]+)` section", + "07-interview-prep.md", + ), + ) + for pattern, filename in cases: + with self.subTest(file=filename): + match = re.search(pattern, self.reset) + self.assertIsNotNone(match, f"reset.md no longer states its anchor for {filename}") + anchor = match.group(1) + shipped = (SKILL_DIR / filename).read_text(encoding="utf-8") + self.assertIn( + anchor, + shipped, + f"/reset anchors {filename} on {anchor!r}, which the shipped file does not contain - " + "the wipe would silently find nothing", + ) + # The replacement block re-emits the anchor line so the heading survives the wipe. + step = self.reset[match.start():] + block_start = step.index("```markdown") + len("```markdown") + block = step[block_start: step.index("```", block_start)] + self.assertIn(anchor, block, f"the replacement for {filename} must keep the heading {anchor!r}") if __name__ == "__main__": unittest.main()