| name | personal-tax-prep-coordination |
| description | Coordinate the user's annual US personal income tax return — gather W-2,
1099, 1095, brokerage crypto and foreign-account data, fill the
Preparer's organizer questions, and queue the upload bundle for the
Preparer's secure portal (TaxDome / Drivetime / SafeSend / etc.).
Triggers: "task: taxes" calendar title, "do my taxes" / "tax organizer"
Slack DM, Oren Hen / TaxDome / Preparer name, late-January vendor-1099
wave, March-April state+federal deadlines, July-October extension
reminder storms.
Anti-triggers: corporate/partnership returns, single-vendor portal work
without coordination, post-filing read-only summaries.
|
personal-tax-prep-coordination
Drive the user's annual US personal income tax return at the coordination layer — NOT the Preparer. Do NOT file, do NOT calculate AGI, do NOT send email until the user types EMAIL APPROVED.
Why this skill exists
The user has a recurring annual workflow: their Preparer (currently Oren Hen, Enrolled Agent at Oren Hen EA, Inc., TaxDome portal at https://orenheneainc.taxdome.com/login) sends a stream of reminder and Missing-Data emails between January and October. The user has historically replied with one-liners ("Ok will do", "Yes will do", inline answers below each question) and uploaded PDFs to the secure portal. 2025 cycle (verified 2026-07-18) was still pending with the Oct 15 extension already filed.
The gap: when a calendar reminder or Slack event says "task: taxes", the right response is NOT to start downloading PDFs. It is to ask "what exactly is the user asking for?", then reconstruct the workflow history from Gmail / Drive / Calendar, then build the checklist + draft. This skill encodes that sequence so the next session doesn't re-derive it.
Canonical workflow (one year at a time)
Phase 0 — Decide what "doing the taxes" actually means
Sources of truth, in priority order:
- The most recent Preparer email asking for an action ("Upload all 2025 tax documents…", "Finalize Organizer…", "Submit Organizer…").
- The calendar entry ("task: taxes" or similar — usually a short reminder the user created days before the deadline).
- A Slack DM / thread the user just forwarded with a vague "do my taxes" prompt.
Do NOT skip Phase 0 and start downloading PDFs. The Preparer has already told you what they need; reuse that as the scope.
Phase 1 — Reconstruct prior-year workflow from Gmail
Pre-flight in parallel: memory-search, google-workspace (for gog gmail thread get). Then run:
GOG="gog -a $USER@gmail.com"
$GOG gmail search "from:<preparer-domain> newer_than:9m" --max 20
$GOG gmail search '"IMPORTANT TAX RETURN DOCUMENT AVAILABLE"'
$GOG gmail search "1095" '"Form 1095"'
$GOG gmail search '"W-2" Snap' '"Wage and Tax Statement"'
$GOG gmail search '"Confirmation of invoice"'
$GOG gmail calendar list --from $(date -v-1y +%Y-%m-%dT%H:%M:%SZ) \
--to $(date +%Y-%m-%dT%H:%M:%SZ) --json --max 200
For each Preparer thread, capture: sender address, invoice numbers referenced, payment status, deadlines (April 15 / Oct 15 / state-equiv), and organizer-question wording. Treat the most recent "Missing Data Request" or "Finalize Organizer" email as the canonical template — Oren Hen has used the same numbered-section format every year since at least 2020.
Cross-validate payment status separately:
$GOG gmail search 'from:paypal.com <preparer>' 'from:venmo.com <preparer>'
For each Preparer invoice number, verify it appears in a Payment confirmation thread. "I think I already paid" is not proof — only the exact invoice number in a Payment confirmation thread is. Verified 2026-07-18: invoice 1002418 ($80 extension fee) was flagged overdue 11 times in May-June 2026; user replied "Think I already paid this?"; the only Payment confirmation we found was for a different invoice (1002106, $850 prep fee, paid March 2026). Always cite the exact invoice number.
Phase 2 — Reconstruct vendor tax-document history from Drive
DRIVE="gog -a $USER@gmail.com drive"
$DRIVE search "<year> 1099" "<year> W-2" "Schwab <year>" "Wealthfront <year>" \
"Fidelity <year>" "Morgan Stanley <year>" "1095 <year>" \
--max 10 --plain
$DRIVE search '"<Preparer> <year>"' 'TaxDome' '"Tax Documents"' \
--plain --max 10
Compare Drive vs Phase-1 emails. Output a sparse matrix:
| # | Doc | Source | In Drive? |
|---|
| 1 | 2025 W-2 | Snap Inc Workday | ❌ |
| 2 | 2025 1095-C | Snap Inc Workday | ❌ |
| 3 | 2025 1099-R | Fidelity NetBenefits | ❌ |
For vendors the user reports every year (Morgan Stanley, Schwab, E*TRADE, Wealthfront, Wells Fargo), assume the same vendor list will apply. For NEW vendors, call them out explicitly.
Phase 3 — Build the document checklist
Use references/<year>-document-checklist.md (or its year-current copy). The skeleton is fixed year-to-year:
- Federal W-2 (employer)
- Federal 1095-C (employer-sponsored health)
- Federal 1099-INT (per bank)
- Federal 1099-DIV (per brokerage)
- Federal 1099-B (consolidated broker statements)
- Federal 1099-R (Fidelity / Vanguard / employer 401k plan)
- Federal 1098 (mortgage interest)
- State equivalents (e.g. CA FTB 1099-G for state refund)
- Crypto: Form 8949 + Schedule D from each exchange
- Foreign accounts (FBAR / Form 114 + Form 8938): bank + crypto peak balances in original currency
- Estimated tax payment confirmations (federal + state), 4 quarterly dates
- Charitable contribution receipts
- K-1s (if any pass-through interests)
The per-vendor portal list and exact organizer-question wording live in references/preparer-notes.md so next session doesn't re-derive them.
Phase 4 — Build the draft reply to the Preparer
Use the raw prior-year Missing Data Request as the template. The Preparer expects the user to type answers inline below each question (e.g. "1a — closed. 2a — yes, 1099-DIV received"). Do NOT change the question wording unless there is a strong reason — the Preparer's parser expects the exact section numbering.
The user's preference is one-liner replies (verified 2026-07-18 thread 19f715fbb121bc06: "Ok will do"). Do NOT inflate the email to a multi-paragraph reply unless the Preparer asked a substantive question. Inflation wastes the Preparer's review time and the user's review credit on a draft.
Do NOT use any send primitive at this point. Stage the draft at /tmp/<preparer>-<year>-reply.txt and /tmp/<preparer>-<year>-upload-checklist.md, present for review. Send only on explicit EMAIL APPROVED per SOUL.md email-approved-gate. Do NOT invent a "soft-send" or "queue" path.
Phase 5 — Surface human blockers; do NOT chase "fully headless"
Most tax portals intentionally block headless login on the first navigation:
- TaxDome uses Cloudflare Turnstile on the first page load — headless Chrome UAs are on the deny list.
- Snap Workday is gated behind Okta SSO + the user's corporate SAML.
- Morgan Stanley / E*TRADE / Schwab / Wealthfront / Fidelity / Wells Fargo all require interactive SSO + captcha refresh.
The reliable pattern is one-time interactive sign-in so the session cookie persists, then headless thereafter. Surface this as a 1-shot blocker ("please log in to these N sites in your browser once so the session cookie sticks"). Do NOT chase a "fully headless" fantasy that requires the agent to interact with captchas.
For password retrieval: Apple Passwords is the canonical source on this Mac. It is not readable from a Hermes session (Apple keeps the DB behind the user's screen-lock unlock + per-process authorization). Canonical path: open Passwords.app, copy, paste into the portal manually. Tell the user this in one line — do NOT enumerate alternate storage paths or propose workarounds.
Downloads folder convention (2026-07-19): the user prefers ~/Downloads/<preparer-slug> <year>/ for tax PDFs (e.g. ~/Downloads/tax 2025/). Mirror that folder into tax <year> Drive folder. Do NOT use /tmp/... — the user wants the files to survive across sessions and be visible in Finder. Stage the folder before any portal work; the file-watcher cron (Phase 5.5) will pick up new PDFs automatically.
Phase 5.5 — Proven headless drive path AFTER the one-time interactive login
Once the user has signed in to each portal in their existing visible Chrome or Aside window, the next layer is to drive downloads headlessly. Verified 2026-07-18 → 2026-07-19 session:
-
Dump decrypted session cookies via browserclaw cookies decrypt — the bypass-ABE claim in earlier revisions of this skill was wrong. With the right flags, browserclaw handles v20 App-Bound Encryption on macOS:
~/.local/orch-venv/bin/browserclaw cookies decrypt \
--db "$HOME/Library/Application Support/Google/Chrome/Default/Cookies" \
--output /tmp/<preparer>-<year>-cookies-chrome.json \
--keychain-service 'Chrome Safe Storage' --keychain-account 'Chrome' \
--summary
Same for Aside (--db "$HOME/Library/Application Support/Aside/Default/Cookies", --keychain-service 'Aside Safe Storage', --keychain-account 'Aside'). Filter to tax domains with --domain-filter '%<portal>%' if you only need one site. Verified 2026-07-19: 1059 Chrome + 553 Aside cookies, all value fields populated. If value is empty, the keychain flags are wrong (don't assume v20 is unsupported — re-check the flags first).
-
Drive TaxDome directly via Playwright + bundled Chromium-for-Testing — TaxDome does NOT fingerprint-bucket sessions, so cookie replay works. Use context.add_cookies() after normalizing expires (skip the field for cookies with expires_utc=0; convert >1e12 from microseconds). The dashboard, organizer (12-step), Documents upload, and billing pages are all driveable headlessly.
-
For Schwab / Fidelity / Morgan Stanley / Wells Fargo / E*TRADE / Wealthfront — DO NOT try Playwright. They TLS-fingerprint the browser. Even with fresh valid cookies, you'll land on SessionExpired. Proven alternatives:
- Drive the user's visible Chrome via AppleScript — but
Allow JavaScript from Apple Events is OFF by default; requires the user to enable it in View → Developer AND relaunch Chrome.
- Wait for the user to download PDFs manually into
~/Downloads/<preparer> <year>/. The file-watcher cron (next step) picks them up. This is the path the 2025-07-19 session used successfully.
- Do NOT loop on retries — they all fail the same way (verified 8+ attempts across two sessions).
-
Coordinate with active Aside MCP sessions. Before driving, probe: returns . If , another chat is driving the user's Aside — DO NOT spawn a competing browser session. Pass the URL playbook () and the local staging path () to that chat and let it drive the downloads in parallel with this chat handling Drive / Gmail / calendar work. If , the daemon is up but no other chat is currently using it — safe to drive from this session.
This sequence (one interactive sign-in + file-watcher cron + driving only TaxDome headlessly) is fully headless after the one-time human unlock — no need to interact with captchas again.
Phase 6 — Send the staged email
After the user has (a) approved verbatim with EMAIL APPROVED or (b) explicitly edited answers, send via gog gmail reply <thread_id> --body-file /tmp/<preparer>-<year>-reply.txt. The thread_id is the Preparer's most recent unanswered email (confirm via gog gmail search first). Load email-drafting for the canonical gog send path — do NOT use Slack MCP.
After send, do NOT post a Slack summary unless the user initiated from Slack. Email is the channel; portal upload is the channel.
Phase 7 — End-state declaration
When (a) all 12+ documents uploaded to TaxDome, (b) organizer submitted, (c) any outstanding invoice (extension fee etc.) paid, (d) email sent — declare end-state and STOP. Do NOT try to e-file, calculate AGI, or push back to the Preparer. The Preparer owns return preparation; you own coordination.
Pitfalls
- "I think I already paid" is not proof. Cross-check against a Payment confirmation thread with the exact invoice number. See Phase 1 verified example.
- Do NOT chase fully headless portal login on the FIRST page load. Turnstile + SSO + Apple-Passwords make it impossible without screen-unlock + paste from the user's Passwords. One interactive session, then headless, is the durable path.
browser_navigate (Hermes MCP browser tool) routes through Browserbase, a data-center IP — Cloudflare blocks it on TaxDome-class portals. The user said it directly: "shouldnt need cloudflare if I'm using aside mcp". Use Aside MCP / Aside CLI / local Chrome AppleScript instead. Browserbase is fine for non-CF-fronted sites.
- Apple Passwords cannot be queried —
aside repl applePasswords.* returns "No last-focused window", security find-generic-password -s "Aside Safe Storage" returns not found. The DB is encrypted-at-rest with a key the user must unlock interactively. Don't waste cycles on this — just ask the user to copy/paste.
aside mcp is a stdio MCP server, NOT an HTTP one. claude mcp add aside-mcp -- aside mcp registers it correctly. If registered as http://127.0.0.1:8013/mcp it will spawn and immediately exit on stdin-end (verified 2026-07-18). Symptom: claude mcp list shows aside-mcp: ✘ Failed to connect. Fix: remove + re-add as stdio.
browserclaw cookies decrypt DOES handle Chrome v20 App-Bound Encryption on macOS when given the right keychain flags: --keychain-service 'Chrome Safe Storage' --keychain-account 'Chrome'. Verified 2026-07-19: dumped 1059 Chrome + 553 Aside cookies with all value fields populated. The earlier v20-blocks-browserclaw claim in this skill is wrong — that pitfall was based on a default-flag invocation that hit the wrong keychain. Always pass the keychain flags. If value is empty on a large dump, the keychain flags are wrong, not that v20 is unsupported.
- Even with valid cookies, Playwright's bundled Chromium-for-Testing fails server-side TLS-fingerprint checks on Schwab / Fidelity / Morgan Stanley / Wells Fargo / E*TRADE. Symptom: cookies decrypt fine,
context.add_cookies() accepts them, navigation lands on Login | SessionExpired. Root cause: these servers tie sessions to the browser's TLS fingerprint (not just cookies), so a fresh Chromium binary is treated as a new device. Real Chrome binary also fails because Playwright's CDP-driven launch still doesn't match the user's live session fingerprint. (AppleScript + JS clicks), or polling for files in once the user downloads them manually. Don't loop on Playwright retries — they'll all fail the same way.
Extending for a new Preparer
- Replace site / portal references in
references/preparer-notes.md.
- Replace the local-history defaults with the new Preparer's prior-year pattern.
- If the new portal uses a different cookie / SSO mechanism, add a sub-skill under
devops/ or productivity/ rather than overloading this one.
- Phase ordering (0-7) is portable; per-Preparer detail is not.
Support files
references/<year>-document-checklist.md — year-specific checklist, vendor list, answer template. Refresh every January.
references/preparer-notes.md — Oren Hen facts: TaxDome URL, portal quirks, invoice-numbering convention, organizer-question template evolution.
references/headless-portal-login-blockers.md — canonical "which portals block headless + what bypass the user has approved" — next session doesn't have to re-probe.
references/file-watcher-cron-pattern.md — ~/Downloads/<preparer> <year>/ poll-and-mirror cron for portals you can't drive headlessly (verified 2026-07-19, job id ad5ed0137d89). v2 update 2026-07-19 adds the five-gotchas TaxDome Documents upload recipe + Drive-dedup pitfall + find-printf macOS BSD pitfall.
scripts/fetch_tax_season_history.sh — one-shot Gmail + Drive + Calendar fetch. Runs Phases 1+2; dumps /tmp/<preparer>-<year>-history.json for Phase 3.
scripts/render_draft_reply.py — renders /tmp/<preparer>-<year>-reply.txt from the in-memory answer map (or interactively on stdin).
scripts/taxdome_prefill_organizer.py — headlessly pre-fill the TaxDome Yes/No organizer (12 steps, ~75 questions). Verified 2026-07-19 against organizer 6044459. Reads cookies + answer-map JSON, walks steps, does NOT submit.
scripts/taxdome_upload_documents.py — headless Playwright upload of one or more PDFs to TaxDome "Client uploaded documents". Verified 2026-07-19 against 13 PDFs (Wells Fargo, Fidelity, Morgan Stanley, Schwab, Snap, Wealthfront). Codifies the data-test selectors, the webkitdirectory trap, the modal vs page "Upload" button distinction, and the "Done = success" sentinel. Use this from any file-watcher cron tick; do NOT re-derive the upload recipe in cron prompts.
scripts/taxdome_verify_documents.py — reads back the TaxDome Documents page via Playwright, prints the PDF inventory to stdout, screenshots to /tmp/tax2025/shots/taxdome-verify-<ts>.png. Used to confirm uploads actually persisted past the modal "Done" signal (which only confirms the HTTP POST reached TaxDome).
Failure modes / when NOT to use this skill
- The user has not filed US personal income tax (non-resident alien, lives outside the US → different forms). Verify SSN / ITIN on a recent 1040 before firing.
- Corporate / partnership / trust return. Route through the relationship manager at the user's accounting firm instead.
- The user is mid-audit (IRS / FTB notice + response window). Refer to the audit notice thread and the Preparer's audit-representation engagement. This skill is purely for annual filing.
- The Calendar entry is just a category label — confirm the user meant "do my taxes" before starting Phase 1.