17 KiB
Mode: apply — Live Application Assistant
Apply
voice-dna.md(if present) to free-text answers and cover-letter fields — full guardrail, conversational voice included (Tier 1 + Tier 2). See_shared.md→ Voice DNA.
Interactive mode for when the candidate is filling out an application form in Chrome. It reads what is on the screen, loads the previous context of the job, and generates personalized responses for each form question.
Requirements
- Best with Playwright in visible mode: In visible mode, the candidate sees the browser and the agent can interact with the page.
- Without Playwright: the candidate shares a screenshot or pastes the questions manually.
Workflow
1. DETECT → Read active Chrome tab (screenshot/URL/title)
2. IDENTIFY → Extract company + role from the page
3. SEARCH → Match against existing reports in reports/
4. LOAD → Read full report + Section H / Application Answers (if they exist)
5. PREFLIGHT → Confirm posting liveness + company/role match before drafting
5b. PRE-SCAN → Scan page for knock-out questions (degree, experience, work authorization/visa, sponsorship, salary floors)
6. ANALYZE → Identify ALL visible form questions
7. GENERATE → For each question, generate a personalized response
8. PRESENT → Show formatted responses for copy-paste
9. PERSIST → Save the final filled/submitted answers into the report
Step 5 — Preflight gate
Before generating any application answers, verify that the form still points to the intended active job. This gate runs after the page has been detected, the company/role has been identified, and the matching report has been loaded.
Blacklist check (#1742): before any form filling starts, if data/blacklist.md exists, check the visible company against it (case- and punctuation-insensitive). The file is the candidate's own do-not-apply list — on a hit, STOP and surface their own recorded decision: "{Company} is on your blacklist (since {Since}): {Reason}. Do you still want to apply?" Require an explicit yes before generating or filling anything — never silently refuse, never silently proceed; the candidate's call always wins. Absent file = skip this check.
Cross-channel check (#1596): before drafting — and ALWAYS before the user authorizes an agency to submit on their behalf — check data/applications.md for an existing row with the same company+role under a different Via (agency vs direct, or two agencies). A double submission burns the candidate with both the agency and the employer. If found, stop and ask the user which channel owns the candidacy. If the end employer is still unknown (Company ?), the check still runs in degraded form — it is never silently skipped:
-
Ask the user (or the recruiter, via the user) for the client company name first — the reveal is the cheapest fix and unlocks the full check.
-
If the name is not available, check the tracker for
?rows with the same Via + a similar role (the same agency re-blasting one listing) and for similar-role rows at plausible-match companies; surface anything close. -
Then STOP and require explicit user acknowledgment before the agency is authorized: "The end employer is unknown, so I cannot verify you haven't already applied to this company directly. Authorize anyway?" Never proceed on silence — the reveal-time check only catches damage after the fact.
-
Read the visible URL, page title, company, role, and any closed/expired signals.
-
If a URL is available, verify liveness with Playwright:
- active posting evidence: title/role + job description or form fields + submit/apply path
- closed posting evidence: expired/closed/no longer accepting applications, missing JD with only nav/footer, hard redirect to generic careers/search, or 404/410
-
Compare the visible company and role against the matched report.
-
If company or title changed materially, stop before drafting and ask: "The form appears to be for [visible company] — [visible role], but the matched report is [report company] — [report role]. Do you want me to re-evaluate, adapt with this mismatch, or stop?"
-
If the posting appears closed, refuse to generate final copy unless the candidate explicitly overrides with a known reason.
-
If liveness cannot be verified because the candidate only pasted questions or a screenshot, state that limitation and ask the candidate to confirm the company, role, and active posting before drafting.
Do not continue to Step 6 until this preflight is resolved.
Step 5b — Pre-scan for knock-out questions
Read the entire page/form to scan for knock-out questions BEFORE generating full responses. These are questions designed to automatically disqualify candidates who do not meet critical criteria.
- Common knock-out question areas to target:
- Minimum years of experience (e.g., "Do you have at least 5 years of professional software engineering experience?")
- Degree requirements (e.g., "Do you have a Bachelor's degree in Computer Science or a related field?")
- Work authorization/Visa sponsorship (e.g., "Will you now or in the future require visa sponsorship to work in the United States?")
- Salary floors/expectations (e.g., "What is your target salary / expectation?")
- Check these questions against the candidate's
config/profile.ymlorcv.mdparameters. - If a knock-out question is detected where the candidate's profile represents a potential mismatch (e.g., candidate needs sponsorship and the form automatically filters out sponsorship-needy applicants, or candidate's salary expectations mismatch the visible JD/form floors):
- Highlight the specific knock-out question to the candidate immediately.
- Present a clear warning block:
⚠️ KNOCK-OUT WARNING: The form asks "[question text]". Based on your profile/CV, answering "[profile answer]" may trigger immediate automatic rejection by the ATS. How would you like to answer this, or do you want to skip applying? - Stop and wait for the candidate's confirmation before drafting any further answers.
- If no knock-out questions are found, or the candidate resolves the warning, proceed to Step 6.
Applying to several roles in one sitting? This preflight verifies the single form in front of you. Before a multi-role session — especially against scanner entries marked **Verification:** unconfirmed (batch mode) — run the pipeline mode Liveness sweep first (node check-liveness.mjs --file <urls>). It drops the dead postings from data/pipeline.md in one batch so you never open a tab on an expired role.
Step 1 — Detect the job
With Playwright: Take a snapshot of the active page. Read title, URL, and visible content.
Without Playwright: Ask the candidate to:
- Share a screenshot of the form (Read tool can read images)
- Or paste the form questions as text
- Or say company + role so we can search for it
Step 2 — Identify and search for context
- Extract company name and role title from the page
- Search in
reports/by company name (case-insensitive grep) - If there is a match → load the full report
- If there is a Section H or
## Application Answers→ load previous answers as a base - If there is NO match → notify and offer to run a quick auto-pipeline
Step 3 — Detect changes in the role
If the role on screen differs from the one evaluated:
- Notify the candidate: "The role has changed from [X] to [Y]. Do you want me to re-evaluate or adapt the responses to the new title?"
- If adapt: Adjust responses to the new role without re-evaluating, only after the candidate explicitly accepts the mismatch
- If re-evaluate: Execute full A-F evaluation, update report, regenerate Section H
- Update tracker: Change role title in applications.md if applicable
Step 6 — Analyze form questions
Identify ALL visible questions:
- Free text fields (cover letter, why this role, etc.)
- Dropdowns (how did you hear, work authorization, etc.)
- Yes/No (relocation, visa, etc.)
- Salary fields (range, expectation)
- Upload fields (resume, cover letter PDF)
Classify each question:
- Already answered in Section H or
## Application Answers→ adapt the existing response - New question → generate response from the report + cv.md
For each field, preserve the application form contract:
field_type:text,textarea,select,radio,checkbox,number,file, orunknownrequired:yes,no, orunknownlimit: exact character/word limit if visible; otherwiseunknownoptions: visible options for select/radio/checkbox fieldsneeds_candidate_confirmation:yesfor legal, demographic, work authorization, visa, relocation, salary, disability, veteran, sponsorship, background-check, or self-identification questions unless the answer is explicitly present inconfig/profile.yml
Never invent answers for legal, demographic, work-authorization, visa/sponsorship, salary, disability, veteran, background-check, relocation, or self-identification fields. If the answer is not present in config/profile.yml or visible context, mark it as needing candidate confirmation and provide the safest question to ask the candidate.
Step 7 — Generate responses
For each question, generate the response following:
- Report context: Use proof points from block B, STAR stories from block F
- Previous Section H / Application Answers: If a draft or final response exists, use it as a base and refine
- "I'm choosing you" tone: Same auto-pipeline framework
- Specificity: Reference something specific from the JD visible on screen
- career-ops proof point: Include in "Additional info" if there is a field for it
- Recruiter-side risk map: Use
modes/heuristics/recruiter-side.mdto identify what doubt the question is trying to resolve (motivation, stack fit, logistics, comp, work-auth, availability, seniority) and answer that doubt directly. - Disclosure discipline: Answer logistics questions truthfully when asked, but do not volunteer sensitive or HR-only details in unrelated motivation/fit answers.
Output format:
## Responses for [Company] — [Role]
Based on: Report #NNN | Score: X.X/5 | Archetype: [type]
---
### 1. [Exact form question]
> [Response ready for copy-paste, or "Ask candidate: ..." if the field needs confirmation]
### 2. [Next question]
> [Response]
...
---
Notes:
- [Any observations about the role, changes, etc.]
- [Personalization suggestions the candidate should review]
Step 8 — Persist application snapshot
After the final answers are filled into the form or handed to the candidate for copy-paste, update the matched report with an additive ## Application Answers section. If the candidate later confirms submission, update that same section from filled to submitted.
The section must include:
**Date:** YYYY-MM-DD**State:** filledor**State:** submitted- Free-text answers exactly as submitted
- Dropdown/radio/checkbox selections made
- Number or short-answer fields such as compensation, availability, start date, and work authorization
- Files used, including CV, cover letter, portfolio, or other uploads with version/path when known
Write the section at the end of the report, or replace only the existing ## Application Answers section if it already exists. Do not rename, reorder, or edit the existing A-H report blocks or ## Keywords extracted.
Use application-answers.mjs when possible to format/upsert the section:
node application-answers.mjs --report reports/NNN-company-role-date.md --input answers.json --state filled
Step 9 — Post-apply (optional)
If the candidate confirms that they submitted the application:
- Update status to Applied via the canonical CLI:
node set-status.mjs <report#> Applied(never hand-edit the table) - Seed the follow-up schedule: run
node followup-seed.mjs {num} --json(where{num}is the tracker row number). If the candidate applied on a different day than today, pass--date YYYY-MM-DDwith the actual submission date. It's idempotent, so re-running is safe. - Refresh the report's
## Application Answerssection with the final field values and**State:** submitted - Suggest next step: run the
contactomode (/career-ops contactowhere available) for LinkedIn outreach
Scroll handling
If the form has more questions than the visible ones:
- Ask the candidate to scroll and share another screenshot
- Or paste the remaining questions
- Process in iterations until the entire form is covered
Known ATS Quirks
Field-tested across ~12 Playwright-driven applications (Ashby, Greenhouse, Lever, Workable). These quirks silently break an apply run if not accounted for.
Ashby — email-based candidate dedup
- Symptom: Submitting a second application at the same company silently fails or merges into the existing candidate record. Ashby deduplicates by email per company.
- Agent: Before filling the email field, check whether an earlier report for the same company already exists in
reports/. If it does, warn the candidate and pre-fill a+tagalias (e.g.,user+teamname@domain.com) as the suggested value. - Candidate: Confirms or changes the email before the form is submitted.
Lever — hCaptcha intercepts checkbox/radio clicks
- Symptom: Programmatic
click()on checkboxes or radio buttons triggers an hCaptcha challenge mid-form, blocking the rest of the fill. - Agent: Fill
<input type="text">,<textarea>, and<select>fields only. Skip all checkboxes, radio buttons, and the captcha widget. List the skipped fields with their recommended values so the candidate can tick them. - Candidate: Completes the checkboxes, solves the captcha, and clicks Submit.
Workable — SPA re-renders break form refs
- Symptom: Workable's SPA re-renders form components between fills, invalidating element references. Sequential
fill()calls hit stale-element errors. - Agent: Copy each answer to the clipboard and present a numbered paste list. If Playwright is active, dispatch
Ctrl+Vper field with a fresh element query before each paste — do not cache refs across fields. - Candidate: Pastes remaining answers manually if clipboard dispatch fails, then submits.
React-select autocomplete widgets
- Symptom:
react-select(common in Greenhouse, Ashby, Lever for location/department fields) destroys and recreates its internal DOM on every keystroke. Cached refs go stale instantly. - Agent: Type character-by-character with short delays (~100 ms). Re-snapshot after every selection to pick up the new DOM state. Never cache element references across interactions.
- Candidate: Verifies each selected value is correct before moving on; corrects any mis-selection inline.
Huge native <select> elements (1 000+ options)
- Symptom: Country, university, or field-of-study dropdowns contain thousands of
<option>entries. Snapshotting them floods context and stalls the agent. - Agent: Use
select_optiondirectly by value or visible label. Never snapshot the full option list. If the exact label is unknown, ask the candidate for the value instead of dumping options into context. - Candidate: Provides the correct label when the agent cannot infer it from
config/profile.yml.
Job-board host ≠ application host — re-check the URL after "Apply"
- Symptom: The posting is discovered on one ATS, but clicking Apply hands off to a different ATS for the actual form. Enterprise career sites (commonly Phenom-, iCIMS-, or Radancy-hosted) frequently redirect into a Workday, Greenhouse, or SmartRecruiters application flow. Choosing fill tactics from the board URL applies the wrong quirks.
- Agent: After the Step 5 preflight, follow the Apply button/redirect and read the URL of the page that actually renders the form fields. Match your fill tactics to that host — not the board the job was discovered on. A
myworkdayjobs.comhandoff in particular means the Workday quirk below applies. - Candidate: Confirms the destination page looks like the right company/role before the agent starts filling.
Workday — set-value doesn't register on React fields
- Symptom: Setting a Workday text field's value programmatically (without real keystrokes) leaves it visually filled but empty to Workday's validation — the React
onChangenever fires, so Save throws "required" on a visibly-filled field. Yes/No dropdowns also vary their option order per question, so a positional click can select the wrong answer (e.g. "No" on are you authorized to work?). - Agent: For required text fields, type real keystrokes (focus → select-all → type), or verify each value registered before Save. Survey the whole step top-to-bottom first (the address block is often below the fold) and fill from the candidate's saved profile (
config/profile.yml/cv.md) proactively, rather than discovering fields via validation errors. For dropdowns, use type-ahead (open → type the option text → confirm the highlight) instead of positional clicks, and verify each selection. - Candidate: Reviews the filled step — especially work-authorization/sponsorship dropdowns and any EEO/legal attestations — before Save/Submit.