From 2036b9704080fd8176dbdf241d388b0a785b650b Mon Sep 17 00:00:00 2001 From: Mads Lorentzen Date: Wed, 19 Aug 2026 19:50:11 +0200 Subject: [PATCH] fix(reset): include documents/postings/ in the documents scope /reset's preview, delete block, and scope description all skipped documents/postings/ - the drop folder for hand-pasted posting text, documented in documents/README.md and protected as personal data by security_guards.py - and then asserted "The documents/ folder is now empty." The new test derives the folder list from the git tree, so any future drop folder fails it until /reset covers it. Review finding F26 (2026-08-19). Co-Authored-By: Claude Opus 5 (1M context) --- .claude/commands/reset.md | 8 +++-- CHANGELOG.md | 7 ++++ tests/test_reset_command.py | 72 +++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 tests/test_reset_command.py diff --git a/.claude/commands/reset.md b/.claude/commands/reset.md index ef62b97..a9df6b2 100644 --- a/.claude/commands/reset.md +++ b/.claude/commands/reset.md @@ -20,7 +20,7 @@ If `$ARGUMENTS` is empty or does not contain a recognized scope keyword, ask: > > - **`profile`** — Clears candidate data from the skill files (profile, behavioral, STAR examples, profile statements). The framework structure and writing rules are preserved. Use this to re-run `/setup` from scratch. > -> - **`documents`** — Deletes all files you've placed in the `documents/` folder (CV PDFs, LinkedIn export, diplomas, references, past applications). The folder structure and `README.md` are preserved. +> - **`documents`** — Deletes all files you've placed in the `documents/` folder (CV PDFs, LinkedIn export, diplomas, references, pasted job postings, past applications). The folder structure and `README.md` are preserved. > > - **`all`** — Both of the above. > @@ -68,7 +68,7 @@ The following files are NOT touched (they contain framework rules, not candidate ### If scope includes `documents`: -Use Glob to list all files present in `documents/cv/`, `documents/linkedin/`, `documents/diplomas/`, `documents/references/`, and `documents/applications/`. Present as: +Use Glob to list all files present in `documents/cv/`, `documents/linkedin/`, `documents/diplomas/`, `documents/references/`, `documents/postings/`, and `documents/applications/`. Present as: ``` ## Documents reset will delete: @@ -85,6 +85,9 @@ documents/diplomas/ documents/references/ - [filename] or "(empty)" +documents/postings/ + - [filename] or "(empty)" + documents/applications/ - [subfolder/filename] or "(empty)" @@ -193,6 +196,7 @@ rm -f documents/cv/* rm -f documents/linkedin/* rm -f documents/diplomas/* rm -f documents/references/* +rm -f documents/postings/* rm -rf documents/applications/*/ ``` diff --git a/CHANGELOG.md b/CHANGELOG.md index c067eb1..13e077b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,13 @@ per-file diff commands. ### Fixed +- **`/reset documents` now clears `documents/postings/`** - the drop folder for + hand-pasted job posting text was absent from the preview, the delete block, and the + user-facing scope description, after which the command told the user "The `documents/` + folder is now empty" - false whenever postings were present, and they are exactly the + personal residue a reset exists to clear. A new `tests/test_reset_command.py` derives + the folder list from the git tree, so any future drop folder fails the test until + `/reset` covers it. - **`convert_salary_excel.py` no longer corrupts US/UK-formatted numbers 1000x** - the both-separators branch always assumed European locale, so a `"1,234.56"` cell was silently converted to `1.23456` and written into `salary_data.json`. The rule is now diff --git a/tests/test_reset_command.py b/tests/test_reset_command.py new file mode 100644 index 0000000..e8919ac --- /dev/null +++ b/tests/test_reset_command.py @@ -0,0 +1,72 @@ +"""Guards for /reset's documents scope. + +/reset ends its documents pass by telling the user "The `documents/` +folder is now empty." That statement is only true if every personal-data +drop folder is actually covered by both the Step 1 preview and the +Step 3 delete block. `documents/postings/` was missing from both while +being documented in documents/README.md and protected as personal data +by tools/security_guards.py (review finding F26, 2026-08-19), so a reset +silently kept the user's hand-pasted job postings. + +The folder list is derived from the repository tree, so adding a new +drop folder under documents/ fails this test until /reset covers it. +""" +import re +import subprocess +import unittest +from pathlib import Path + +REPO = Path(__file__).resolve().parent.parent +RESET = REPO / ".claude" / "commands" / "reset.md" + + +def tracked_document_subfolders(): + """Names of documents/ subfolders tracked in git (ignores local noise).""" + out = subprocess.run( + ["git", "ls-files", "documents/"], + cwd=REPO, + capture_output=True, + text=True, + check=True, + ).stdout + folders = set() + for line in out.splitlines(): + parts = line.split("/") + if len(parts) >= 3: # documents// + folders.add(parts[1]) + return folders + + +class TestResetCoversEveryDocumentsSubfolder(unittest.TestCase): + def setUp(self): + self.text = RESET.read_text(encoding="utf-8") + self.folders = tracked_document_subfolders() + # The tree must actually contain the folders this test is about, + # or the assertions below would pass vacuously. + self.assertGreaterEqual(len(self.folders), 5, self.folders) + + def test_preview_lists_every_subfolder(self): + missing = [ + f for f in sorted(self.folders) if f"documents/{f}/" not in self.text + ] + self.assertEqual( + missing, + [], + "reset.md's preview never mentions these documents/ subfolders, " + f"so the user confirms a deletion list that omits them: {missing}", + ) + + def test_delete_block_removes_every_subfolder(self): + deleted = set(re.findall(r"rm -r?f documents/(\w+)/", self.text)) + missing = sorted(self.folders - deleted) + self.assertEqual( + missing, + [], + "reset.md's delete block has no rm line for these documents/ " + 'subfolders, yet the command then claims "The `documents/` ' + f'folder is now empty.": {missing}', + ) + + +if __name__ == "__main__": + unittest.main()