Files
santifer--career-ops/modes/titles.md
T
wehub-resource-sync d083df1fdb
CodeQL Analysis / Analyze (javascript-typescript) (push) Failing after 2s
Web CI / web typecheck + build (push) Failing after 1s
Release Please / release-please (push) Failing after 1s
CodeQL Analysis / Analyze (go) (push) Failing after 16s
chore: import upstream snapshot with attribution
2026-07-13 12:02:43 +08:00

6.7 KiB
Raw Blame History

Mode: titles — Adjacent Job-Title Suggestions

Purpose

The scanner only surfaces what portals.yml title_filter.positive matches — and that list is written from the titles the user already knows to search for. The same job ships under many names (Solutions Architect / Forward Deployed Engineer / Customer Engineer), so the search is silently narrower than the CV justifies. This mode reads the CV and proposes adjacent titles the user isn't searching for yet — then, only after explicit confirmation, writes the accepted keywords into title_filter.positive so the very next scan casts the wider net.

patterns Step 1b makes the same kind of retargeting recommendation ("consider adding archetype X and reweighting portals.yml title_filter.positive"), but only after ≥5 progressed applications and only from interview-session signal. This mode is the day-zero, CV-driven complement. It is also the inverse of upskill: upskill finds skills missing for current targets; this finds new targets reachable from current skills.

Inputs

  • cv.md — the only source of evidence for suggestions (required)
  • config/profile.ymlarchetypes (name / level / fit) for what's already targeted
  • modes/_profile.md — target roles, framing, and any deal-breakers the user has recorded
  • portals.yml — the current title_filter.positive (and negative) keywords
  • Optional: if data/applications.md has ≥5 entries progressed beyond Evaluated, note which suggestions the outcome data supports (e.g. an axis that is already converting) — cross-reference patterns rather than duplicating its analysis.

The Three Axes

Classify every suggestion on exactly one axis, and say which:

  • Lateral — same work, different label. The core recall win: the user already does this job; the market just posts it under a name they don't search for.
  • Stretch — one level up or larger scope than the CV's strongest evidence. Plausible, but a hiring manager would probe the gap.
  • Pivot — an adjacent function reachable from existing CV evidence (e.g. heavy client-facing delivery work → pre-sales engineering).

Output Contract (per suggestion)

For each suggested title, show exactly:

  • Title — the market title as actually posted, not an invented hybrid
  • Axis — Lateral / Stretch / Pivot
  • CV evidence — 12 lines from cv.md quoted verbatim. If you cannot quote it, do not suggest it.
  • Honest gap note — what a hiring manager would question; "none" is allowed for Lateral suggestions but must be earned
  • Market-reality note — how common the title is, where it tends to be posted, seniority skew, or noise level

Aim for 510 suggestions, Lateral first. Fewer good suggestions beat a padded list — this system optimizes for quality, not quantity.

Filters (apply BEFORE showing suggestions)

  1. Dedup against existing coverage. Mirror the matcher semantics in scan.mjs (buildTitleFilter / compileKeyword): the scanner lowercases both sides and keeps a job when any positive keyword is a case-insensitive substring of the title (23 letter keywords match on word boundaries instead). So drop any candidate title that an existing positive keyword already substring-matches — it is already covered, and suggesting it adds zero new recall.
  2. Deal-breaker filter. Never suggest titles that violate the deal-breakers recorded in modes/_profile.md (e.g. "no people management" rules out Engineering Manager; "no on-site" rules out field roles). Titles matching title_filter.negative keywords are also off the table — the user already excluded them.
  3. Never invent experience. Every suggestion must be traceable to quoted cv.md lines — the source-of-truth boundary applies to suggestions exactly as it does to CV content. Keywords get reformulated, never fabricated. If the evidence isn't in cv.md, ask the user; don't stretch a quote to fit.

Confirm Gate — Writing Accepted Titles (HARD RULE)

When the user accepts one or more suggestions:

  1. Derive keywords, not raw titles. The filter matches substrings, so the keyword should be the shortest phrase that still identifies the role family ("Forward Deployed" covers Forward Deployed Engineer/Architect/Lead).
  2. Attach a breadth warning to any substring-dangerous keyword: because matching is substring-based, a short or generic keyword floods the scan. Propose "Solutions Architect", never bare "Architect" — bare "Architect" would also match Data Architect, Enterprise Architect, Security Architect. If the user insists on a broad keyword, warn once and comply.
  3. Skip keywords that duplicate existing coverage (same dedup rule as above); preserve the casing style already used in the user's portals.yml.
  4. Show the exact YAML diff against portals.yml title_filter.positive before touching anything.
  5. Never write to portals.yml without explicit user confirmation. "Show me the diff" is not a yes. Silence is not a yes.
  6. portals.yml (user layer) is the only file this mode writes by default. This mode proposes no negative keywords — precision guards for noisy keywords are deferred to #1353's seniority-tier helper.
  7. Separately-confirmed exception: accepted titles can additionally become fit: adjacent archetypes in config/profile.yml (an existing schema field — see config/profile.example.yml). Mention that this is possible, but do it only if the user asks — never write archetypes by default. When the user does ask, that write gets its own YAML diff and its own separate confirmation; never bundle the portals.yml and config/profile.yml writes into one confirmation.

After the Write

  • Suggest /career-ops scan — the wider filter only pays off on the next scan.
  • Suggest upskill scoped to a Stretch title the user liked, to see the gap map between the CV and that next-level target.

Error Handling

  • cv.md missing → stop and point at onboarding (node doctor.mjs --json). There is no evidence base to suggest from, and inventing one is forbidden.
  • portals.yml missing, or title_filter.positive empty → offer to create it from templates/portals.example.yml first, then re-run this mode. (An empty positive list means the scanner matches everything — nothing to broaden.)
  • config/profile.yml or modes/_profile.md missing → hard stop: do not generate suggestions. Point at onboarding (node doctor.mjs --json) and stop, then re-run this mode once both files exist — the same fix-first-then-re-run behavior as a missing portals.yml above. Deal-breakers live in modes/_profile.md — suggestions generated without them can propose exactly what the user excluded.