# 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 ```text 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: 1. 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. 2. 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. 3. 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. 1. Read the visible URL, page title, company, role, and any closed/expired signals. 2. 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 3. Compare the visible company and role against the matched report. 4. 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?" 5. If the posting appears closed, refuse to generate final copy unless the candidate explicitly overrides with a known reason. 6. 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. 1. 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?") 2. Check these questions against the candidate's `config/profile.yml` or `cv.md` parameters. 3. 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. 4. 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 `). 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 1. Extract company name and role title from the page 2. Search in `reports/` by company name (case-insensitive grep) 3. If there is a match → load the full report 4. If there is a Section H or `## Application Answers` → load previous answers as a base 5. 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`, or `unknown` - `required`: `yes`, `no`, or `unknown` - `limit`: exact character/word limit if visible; otherwise `unknown` - `options`: visible options for select/radio/checkbox fields - `needs_candidate_confirmation`: `yes` for legal, demographic, work authorization, visa, relocation, salary, disability, veteran, sponsorship, background-check, or self-identification questions unless the answer is explicitly present in `config/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: 1. **Report context**: Use proof points from block B, STAR stories from block F 2. **Previous Section H / Application Answers**: If a draft or final response exists, use it as a base and refine 3. **"I'm choosing you" tone**: Same auto-pipeline framework 4. **Specificity**: Reference something specific from the JD visible on screen 5. **career-ops proof point**: Include in "Additional info" if there is a field for it 6. **Recruiter-side risk map**: Use `modes/heuristics/recruiter-side.md` to identify what doubt the question is trying to resolve (motivation, stack fit, logistics, comp, work-auth, availability, seniority) and answer that doubt directly. 7. **Disclosure discipline**: Answer logistics questions truthfully when asked, but do not volunteer sensitive or HR-only details in unrelated motivation/fit answers. **Output format:** ```text ## 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:** filled` or `**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: ```bash 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: 1. Update status to Applied via the canonical CLI: `node set-status.mjs Applied` (never hand-edit the table) 2. 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-DD` with the actual submission date. It's idempotent, so re-running is safe. 3. Refresh the report's `## Application Answers` section with the final field values and `**State:** submitted` 4. Suggest next step: run the `contacto` mode (`/career-ops contacto` where 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 `+tag` alias (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 ``, `