mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 00:26:26 +00:00
The quick start walked a new user into gh repo fork - forks of public repos are always public - and two steps later had /setup write personal data into tracked files, with the only complete warning in SETUP.md section 8, a section about pulling updates that a first-time user has no reason to open during onboarding. A real user hit exactly this (#345). The warning now sits adjacent to both fork commands (README step 1, SETUP.md section 2, both pointing at section 8's private-remote recipe), and /setup checks the origin's visibility BEFORE writing anything: a public-fork origin gets a confirm-first warning instead of a note after every file is on disk. A private origin, no origin, or a non-git directory continues silently. Reported by @basilevs with a complete reproduction and fix analysis; this implements his fixes (1) and (4). Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ab5d23bad4
commit
34b8b3f91f
@@ -10,7 +10,26 @@ There are three paths into setup. Step 0 picks the right one; all three converge
|
|||||||
|
|
||||||
If `$ARGUMENTS` contains `--section <name>`, skip directly to that section in Path C for an update-only flow. Do not run the path-selection prompt below.
|
If `$ARGUMENTS` contains `--section <name>`, skip directly to that section in Path C for an update-only flow. Do not run the path-selection prompt below.
|
||||||
|
|
||||||
Otherwise, before greeting the user, scan the `documents/` folder. Use Glob with `documents/**/*` and count files per subfolder (`cv/`, `linkedin/`, `diplomas/`, `references/`, `applications/`).
|
Otherwise, first check where this working copy would publish to — **before anything is
|
||||||
|
written, not after** (the Step 4 privacy note fires only once every file is already on
|
||||||
|
disk, which is too late to inform the decision). Run `git remote get-url origin`; if the
|
||||||
|
command fails (no remote, or not a git checkout), skip this check silently. If there is
|
||||||
|
a GitHub `origin`, check it with `gh repo view <owner/repo> --json visibility,isFork`
|
||||||
|
when `gh` is available. If the origin is a **public fork** of the template — or its
|
||||||
|
visibility cannot be determined — warn now and wait:
|
||||||
|
|
||||||
|
> **Heads-up before we start:** your `origin` points at `<owner/repo>`, which is a
|
||||||
|
> public GitHub fork. This setup writes your personal data (name, contact details,
|
||||||
|
> employment history, salary expectations) into **tracked** files, and anything you
|
||||||
|
> commit *and push* to that fork is visible to anyone. Two safe options: keep your
|
||||||
|
> profile commits local and never push them, or push to a **private** repository
|
||||||
|
> instead — SETUP.md section 8 has the two-minute private-remote recipe. Want to
|
||||||
|
> continue with the setup?
|
||||||
|
|
||||||
|
Wait for the user's confirmation before showing the path prompt. A private origin, no
|
||||||
|
origin, or a non-fork remote needs no warning — continue silently.
|
||||||
|
|
||||||
|
Then, before greeting the user, scan the `documents/` folder. Use Glob with `documents/**/*` and count files per subfolder (`cv/`, `linkedin/`, `diplomas/`, `references/`, `applications/`).
|
||||||
|
|
||||||
Then welcome the user with a single message that lists three paths. The wording changes based on what was found.
|
Then welcome the user with a single message that lists three paths. The wording changes based on what was found.
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,16 @@ per-file diff commands.
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- **Onboarding warns about public forks at the point of decision** (#345) - the quick
|
||||||
|
start walked a new user into `gh repo fork` (forks of public repos are always public)
|
||||||
|
and two steps later had `/setup` write personal data into tracked files, with the only
|
||||||
|
complete warning sitting in SETUP.md section 8 - a section about pulling updates that a
|
||||||
|
first-time user has no reason to open. A real user hit exactly this. The warning now
|
||||||
|
sits adjacent to both fork commands (README step 1, SETUP.md section 2), and `/setup`
|
||||||
|
checks the origin's visibility **before** writing anything: a public-fork origin gets a
|
||||||
|
confirm-first warning instead of a note after every file is already on disk. Reported
|
||||||
|
by @basilevs with a complete reproduction and fix analysis. Pinned by the new
|
||||||
|
`tests/test_onboarding_privacy.py`.
|
||||||
- **`jobindex-search detail` rewritten against jobindex's current markup** - every
|
- **`jobindex-search detail` rewritten against jobindex's current markup** - every
|
||||||
selector the old parser used is gone from live pages, so on 4 of 5 live postings it
|
selector the old parser used is gone from live pages, so on 4 of 5 live postings it
|
||||||
returned CSS-comment text as the deadline (`"K \t\t... */"`), an external ATS URL as
|
returned CSS-comment text as the deadline (`"K \t\t... */"`), an external ATS URL as
|
||||||
|
|||||||
@@ -78,6 +78,15 @@ gh repo fork MadsLorentzen/ai-job-search --clone
|
|||||||
cd ai-job-search
|
cd ai-job-search
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> **A fork of this repo is always public** — GitHub does not allow private forks of
|
||||||
|
> public repositories — and `/setup` (step 3 below) writes your personal data (name,
|
||||||
|
> contact details, employment history, salary expectations) into **tracked** files.
|
||||||
|
> If this copy is for your own job search rather than for contributing changes back,
|
||||||
|
> use a **private repository** with this repo as `upstream` instead — the two-minute
|
||||||
|
> recipe is in [SETUP.md section 8](SETUP.md#8-pulling-upstream-updates-into-your-fork),
|
||||||
|
> and every update workflow works identically. Fork only to contribute.
|
||||||
|
|
||||||
### 2. Install job search tools
|
### 2. Install job search tools
|
||||||
|
|
||||||
PowerShell:
|
PowerShell:
|
||||||
|
|||||||
@@ -160,6 +160,14 @@ cd ai-job-search
|
|||||||
|
|
||||||
Or manually: fork on GitHub, then clone your fork.
|
Or manually: fork on GitHub, then clone your fork.
|
||||||
|
|
||||||
|
> **Before you go further: forks are public.** GitHub cannot make a fork of a public
|
||||||
|
> repository private, and `/setup` (section 6) writes your personal data into **tracked**
|
||||||
|
> files — pushing those commits to a fork publishes them. If this copy is for your own
|
||||||
|
> job search rather than for contributing, prefer a **private repository** with this repo
|
||||||
|
> as `upstream`: see section 8, step 1 for the exact commands and why committing your
|
||||||
|
> personalization there is still the right move. Everything else in this guide works
|
||||||
|
> identically either way.
|
||||||
|
|
||||||
## 3. Install job search CLI dependencies
|
## 3. Install job search CLI dependencies
|
||||||
Run these from the repository root.
|
Run these from the repository root.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
"""Guards for the onboarding privacy warnings (issue #345).
|
||||||
|
|
||||||
|
The README's quick start walks a new user into creating a public fork
|
||||||
|
(forks of public repos cannot be private) and then has /setup write
|
||||||
|
personal data into tracked files, with the only complete warning sitting
|
||||||
|
in SETUP.md section 8 - a section about pulling updates, downstream of
|
||||||
|
the decision it should inform. A real user hit exactly this. These tests
|
||||||
|
pin that the warning lives at the point of decision (adjacent to both
|
||||||
|
fork commands) and that /setup checks the origin's visibility BEFORE
|
||||||
|
writing anything, not in its closing notes.
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = Path(__file__).resolve().parent.parent
|
||||||
|
README = REPO / "README.md"
|
||||||
|
SETUP_GUIDE = REPO / "SETUP.md"
|
||||||
|
SETUP_COMMAND = REPO / ".claude" / "commands" / "setup.md"
|
||||||
|
|
||||||
|
|
||||||
|
def section(text: str, heading: str) -> str:
|
||||||
|
"""Body of a markdown section up to the next heading of the same level."""
|
||||||
|
level = heading.split(" ")[0]
|
||||||
|
pattern = re.compile(
|
||||||
|
rf"^{re.escape(heading)}\n(.*?)(?=^{level} |\Z)", re.MULTILINE | re.DOTALL
|
||||||
|
)
|
||||||
|
match = pattern.search(text)
|
||||||
|
return match.group(1) if match else ""
|
||||||
|
|
||||||
|
|
||||||
|
class TestForkWarningsAtTheDecisionPoint(unittest.TestCase):
|
||||||
|
def assert_warns(self, body: str, where: str):
|
||||||
|
self.assertRegex(
|
||||||
|
body,
|
||||||
|
re.compile(r"public", re.IGNORECASE),
|
||||||
|
f"{where}'s fork section must say the fork will be public",
|
||||||
|
)
|
||||||
|
self.assertIn(
|
||||||
|
"personal data",
|
||||||
|
body,
|
||||||
|
f"{where}'s fork section must say /setup writes personal data into tracked files",
|
||||||
|
)
|
||||||
|
self.assertRegex(
|
||||||
|
body,
|
||||||
|
re.compile(r"section 8|§8|#8-pulling", re.IGNORECASE),
|
||||||
|
f"{where}'s fork section must point at SETUP.md section 8's private-remote recipe",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_readme_quick_start_warns_next_to_the_fork_command(self):
|
||||||
|
body = section(README.read_text(encoding="utf-8"), "### 1. Fork and clone")
|
||||||
|
self.assertIn("gh repo fork", body, "sanity: the fork command lives in this section")
|
||||||
|
self.assert_warns(body, "README")
|
||||||
|
|
||||||
|
def test_setup_guide_warns_next_to_the_fork_command(self):
|
||||||
|
body = section(SETUP_GUIDE.read_text(encoding="utf-8"), "## 2. Fork and clone")
|
||||||
|
self.assertIn("gh repo fork", body, "sanity: the fork command lives in this section")
|
||||||
|
self.assert_warns(body, "SETUP.md")
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetupChecksOriginBeforeWriting(unittest.TestCase):
|
||||||
|
def test_preflight_exists_and_precedes_profile_generation(self):
|
||||||
|
text = SETUP_COMMAND.read_text(encoding="utf-8")
|
||||||
|
self.assertIn(
|
||||||
|
"git remote get-url origin",
|
||||||
|
text,
|
||||||
|
"/setup must check where the working copy would publish to",
|
||||||
|
)
|
||||||
|
preflight_at = text.index("git remote get-url origin")
|
||||||
|
writes_at = text.index("## Step 3: Generate Profile Files")
|
||||||
|
self.assertLess(
|
||||||
|
preflight_at,
|
||||||
|
writes_at,
|
||||||
|
"the origin check must run before any profile file is written - the "
|
||||||
|
"existing Step 4 note fires after everything is already on disk",
|
||||||
|
)
|
||||||
|
self.assertIn(
|
||||||
|
"public",
|
||||||
|
text[max(0, preflight_at - 2000) : preflight_at + 2000].lower(),
|
||||||
|
"the preflight must be about public visibility, not just remote presence",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user