fix(reset): clear the writing-style patterns /setup infers from past cover letters (#825)

/setup Path A (Step A5) adds a "## Patterns Observed in Past Applications" section to 03-writing-style.md from the user's past cover letters. /reset profile never removed it and its preview listed 03 as framework-only, so inferred candidate data survived a confirmed RESET. Step 1 now reads 03, the preview names the section, Step 3 removes it and leaves the writing rules intact, and the NOT-touched list names 08 and 09 instead.

Adds TestResetCoversPathAInferredContent, derived from the Step A5 inference bullets, so the next Path A write-target cannot be missed silently.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
DivyaandClaude Fable 5.1 authored and GitHub committed 2026-11-08 19:34:38 +01:00
1 parent 4db9ed7308
commit 2b3f613fda
2 files changed
+82 -2

No files matched your search

+10 -2
View File
@@ -40,13 +40,14 @@ Read the current state of these files and report whether each has content or is
- `.claude/skills/job-application-assistant/01-candidate-profile.md` - `.claude/skills/job-application-assistant/01-candidate-profile.md`
- `.claude/skills/job-application-assistant/02-behavioral-profile.md` - `.claude/skills/job-application-assistant/02-behavioral-profile.md`
- `.claude/skills/job-application-assistant/03-writing-style.md` *(any "Patterns Observed in Past Applications" section only — the writing rules are preserved)*
- `.claude/skills/job-application-assistant/04-job-evaluation.md` *(personalized match areas, career goals, and life-situation constraints only — the scoring framework is preserved)* - `.claude/skills/job-application-assistant/04-job-evaluation.md` *(personalized match areas, career goals, and life-situation constraints only — the scoring framework is preserved)*
- `.claude/skills/job-application-assistant/05-cv-templates.md` *(profile statements section and the contact block inside the LaTeX template only — framework structure is preserved)* - `.claude/skills/job-application-assistant/05-cv-templates.md` *(profile statements section and the contact block inside the LaTeX template only — framework structure is preserved)*
- `.claude/skills/job-application-assistant/06-cover-letter-templates.md` *(contact line and signature inside the LaTeX template only — framework structure is preserved)* - `.claude/skills/job-application-assistant/06-cover-letter-templates.md` *(contact line and signature inside the LaTeX template only — framework structure is preserved)*
- `.claude/skills/job-application-assistant/07-interview-prep.md` *(STAR examples and STAR candidates sections only — framework structure is preserved)* - `.claude/skills/job-application-assistant/07-interview-prep.md` *(STAR examples and STAR candidates sections only — framework structure is preserved)*
- `.claude/skills/job-scraper/search-queries.md` *(role titles, domain keywords, and location terms only — query structure is preserved)* - `.claude/skills/job-scraper/search-queries.md` *(role titles, domain keywords, and location terms only — query structure is preserved)*
This list must stay in step with what `/setup` Step 3 populates: every skill file it writes candidate data into is cleared here. This list must stay in step with what `/setup` writes in Step 3 and infers in Path A's Step A5: every skill file that receives candidate data from either path is cleared here. Known gap: the opening patterns Path A adds to `06-cover-letter-templates.md` are not labelled, so they cannot yet be told apart from the shipped templates and are kept.
Present as: Present as:
@@ -59,6 +60,10 @@ Present as:
- 02-behavioral-profile.md — [has content / already empty] - 02-behavioral-profile.md — [has content / already empty]
Full file will be replaced with a blank template. Full file will be replaced with a blank template.
- 03-writing-style.md — [has observations from past applications / nothing to clear]
Any "Patterns Observed in Past Applications" section that /setup drew from your
own cover letters will be removed. The writing rules are preserved.
- 04-job-evaluation.md — [has personalized criteria / already blank] - 04-job-evaluation.md — [has personalized criteria / already blank]
Your match areas, career goals, energizing/draining tasks, and life-situation Your match areas, career goals, energizing/draining tasks, and life-situation
constraints will be restored to placeholders. The scoring framework (dimensions, constraints will be restored to placeholders. The scoring framework (dimensions,
@@ -78,7 +83,8 @@ Present as:
restored to placeholders. The query structure and filter sections are preserved. restored to placeholders. The query structure and filter sections are preserved.
The following files are NOT touched (they contain framework rules, not candidate data): The following files are NOT touched (they contain framework rules, not candidate data):
- 03-writing-style.md - 08-application-forms.md
- 09-web-research.md
Outside the profile scope, still holding your personal data: CLAUDE.md and Outside the profile scope, still holding your personal data: CLAUDE.md and
cv/main_example.tex. This scope covers skill files only. cv/main_example.tex. This scope covers skill files only.
@@ -197,6 +203,8 @@ Every skill file under `.claude/skills/job-application-assistant/` opens with a
## Using This in Applications ## Using This in Applications
``` ```
**For `03-writing-style.md`**, remove any `## Patterns Observed in Past Applications` section, which `/setup` Path A writes from the user's own cover letters. The shipped file has no such section, so leave every other section of `03-writing-style.md` intact: its rules are framework, not candidate data.
**For `04-job-evaluation.md`**, restore the values `/setup` Step 3.4 personalized back to their placeholder tokens, leaving every surrounding line untouched: **For `04-job-evaluation.md`**, restore the values `/setup` Step 3.4 personalized back to their placeholder tokens, leaving every surrounding line untouched:
| Line to restore | Token | | Line to restore | Token |
+72
View File
@@ -131,6 +131,78 @@ def setup_step3_skill_files():
return files return files
def setup_path_a_inference_rules():
"""(skill file, section headings) pairs /setup Path A's Step A5 writes into.
Step A5's inference rules are bullets of the form
'- **`<file>`** ...: ... under "## <Heading>" ...'. Path A writes from the
user's own documents and past applications, outside Step 3's headings, so
deriving coverage from Step 3 alone missed these targets.
"""
a5 = section(SETUP.read_text(encoding="utf-8"), "### Step A5:", "### Step A6:")
rules = []
for line in a5.splitlines():
match = re.match(r"^- \*\*`([^`]+\.md)`", line)
if match:
rules.append((match.group(1), re.findall(r'"(## [^"]+)"', line)))
return rules
class TestResetCoversPathAInferredContent(unittest.TestCase):
"""/reset profile must clear what /setup Path A infers from past applications.
The regression: Path A adds '## Patterns Observed in Past Applications' to
03-writing-style.md from the user's own cover letters, while reset.md listed
03 as 'NOT touched (they contain framework rules, not candidate data)' - so
the user's past phrasing survived a reset that said the file held none.
"""
def setUp(self):
self.text = RESET.read_text(encoding="utf-8")
self.rules = setup_path_a_inference_rules()
self.files = {name for name, _ in self.rules}
# Guard against a vacuous pass if Step A5's bullet format changes.
self.assertGreaterEqual(len(self.files), 6, self.rules)
self.assertIn("03-writing-style.md", self.files)
def test_step1_reads_every_path_a_file(self):
"""The preview's has-content/nothing-to-clear verdicts come from Step 1's read list."""
inventory = section(self.text, "Read the current state of these files", "Present as:")
missing = sorted(f for f in self.files if f not in inventory)
self.assertEqual(missing, [], f"Step 1 never reads these Path A targets, so their preview line is unchecked: {missing}")
def test_preview_lists_every_path_a_file(self):
preview = section(
self.text, "### If scope includes `profile`:", "### If scope includes `documents`:"
)
listed = section(preview, "## Profile reset will clear:", "The following files are NOT touched")
missing = sorted(f for f in self.files if f not in listed)
self.assertEqual(missing, [], f"Path A writes into these, but the preview never offers to clear them: {missing}")
def test_preserved_list_claims_no_path_a_file_is_framework_only(self):
preserved = section(self.text, "The following files are NOT touched", "```")
mislabeled = sorted(f for f in self.files if f in preserved)
self.assertEqual(mislabeled, [], f"listed as framework-only, but Path A writes candidate data into: {mislabeled}")
def test_execution_removes_every_section_path_a_adds(self):
"""Each heading must be named in the Step 3 instruction for its own file.
An instruction runs from its '**For `<file>`**' line to the next one, so
a heading attached to another file's instruction does not count.
"""
execution = section(self.text, "### Profile reset", "### Documents reset")
blocks = {}
for match in re.finditer(r"^\*\*For `(?:[^`]*/)?([^`/]+)`\*\*(.*?)(?=^\*\*For `|\Z)", execution, re.S | re.M):
blocks[match.group(1)] = blocks.get(match.group(1), "") + match.group(2)
missing = sorted(
f"{name}: {heading}"
for name, headings in self.rules
for heading in headings
if heading not in blocks.get(name, "")
)
self.assertEqual(missing, [], f"Path A adds these sections, but no Step 3 instruction for that file removes them: {missing}")
class TestResetCoversEveryPersonalizedSkillFile(unittest.TestCase): class TestResetCoversEveryPersonalizedSkillFile(unittest.TestCase):
def setUp(self): def setUp(self):
self.text = RESET.read_text(encoding="utf-8") self.text = RESET.read_text(encoding="utf-8")