From thinking-frameworks-skills
Scans taxable brokerage lots for unrealized losses above threshold, checks wash-sale risks across household accounts including spousal, proposes harvest pairs with similar replacements.
npx claudepluginhub lyndonkl/claude --plugin thinking-frameworks-skillsThis skill uses the workspace's default tool permissions.
- [Overview](#overview)
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Tax-loss harvesting realizes losses in a taxable brokerage to offset gains (and up to $3,000 of ordinary income per year) without changing the household's economic position — the proceeds buy a similar-but-not-identical security, keeping market exposure intact. The hardest part is the wash-sale rule: a sale at a loss is disallowed if a substantially identical security is bought within 30 days in any household account, including a spouse's IRA.
This skill scans the taxable brokerage for harvest candidates and validates each against the wash-sale window across all household accounts.
The caller provides:
taxable_holdings — lots in the taxable brokerage, ideally per-lot: {symbol, shares, cost_basis_cents, value_cents, acquisition_date}. If only aggregate position-level data is available, mark lot_resolution: aggregate and document the limitation.all_account_transactions_60d — every buy/sell/dividend reinvest across every household account (taxable, 401k, IRA, spouse's IRA, HSA) in the last 60 days. Used for wash-sale detection.planned_buys_30d (optional) — dividend reinvest schedules, 401k contributions, automatic deposits — anything that will buy in the next 30 days.loss_threshold_cents — minimum unrealized loss to consider; default 50000 ($500).today — ISO date.Per IRS, a wash sale is triggered when a security sold at a loss is replaced by a "substantially identical" security purchased within the 61-day window centered on the sale date (30 days before, 30 days after).
For this skill:
TLH Scan Progress:
- [ ] Step 1: Filter taxable lots with unrealized loss >= loss_threshold
- [ ] Step 2: For each candidate, check 30-day backward wash-sale window
- [ ] Step 3: For each candidate, check 30-day forward planned-buys window
- [ ] Step 4: Identify substitute security (similar exposure, not identical)
- [ ] Step 5: Compute realized loss and tax savings estimate
- [ ] Step 6: Emit harvest proposals with explicit wash-sale guard
- [ ] Step 7: Surface candidates blocked by wash sale with the unblock date
unrealized_loss_cents = value_cents − cost_basis_cents (negative). Keep lots where |unrealized_loss_cents| ≥ loss_threshold_cents AND unrealized_loss_cents < 0.
If lot-level resolution is unavailable, fall back to position-level using weighted_avg_cost_basis. Document lot_resolution: aggregate and warn that some lots may have gains while others have losses; only the aggregate is loss-positive.
For each candidate symbol, look back 30 days in all_account_transactions_60d for any buy of the same or substantially-identical security. If found, the sale would be partially or fully washed — disallow until 30 days after the most recent buy. Surface as blocked_until: YYYY-MM-DD.
Look at planned_buys_30d for the same or substantially-identical security. Common cases:
Resolutions:
Pick a substitute that maintains market exposure. The skill carries a default mapping (see Substitute pairs). The substitute must:
tax_savings_cents ≈ |realized_loss_cents| × marginal_rate, where marginal rate is provided or defaulted to 24% for offsetting ordinary income up to $3,000 and 15% for offsetting long-term gains. Surface both.
Each proposal includes:
For losses that are blocked, emit a blocked[] list with unblock_date so the user knows when the candidate becomes harvestable.
Reasonable starting pairs (skill emits these as proposed, not authoritative):
| Sell | Buy substitute | Rationale |
|---|---|---|
| VTI (Total US Market) | ITOT (Total US Market, iShares) | Different family, similar exposure |
| VOO (S&P 500) | VTI (Total US Market) | Different index |
| VXUS (Total Intl) | IXUS (Total Intl, iShares) | Different family |
| BND (US Aggregate Bond) | AGG (US Aggregate Bond, iShares) | Different family, same exposure — borderline; prefer a slightly different index like SCHZ |
| IEFA (Developed Intl) | VEA (Developed Intl) | Different family |
| FXAIX (Fidelity S&P 500) | FSKAX (Fidelity Total Market) | Different index |
Always document the substitute as a suggestion — the user verifies appropriateness for their plan.
{
"as_of": "2026-04-25",
"candidates_total": 5,
"harvest_proposals": [
{
"id": "tlh_20260425_001",
"sell": {
"account_id": "acc_inv_fid_001",
"symbol": "VTI",
"lots": [
{ "acquisition_date": "2024-09-12", "shares": 35, "cost_basis_cents": 985250, "value_cents": 935500 }
],
"realized_loss_cents": -49750
},
"buy": {
"account_id": "acc_inv_fid_001",
"symbol": "ITOT",
"shares": 38,
"value_cents": 935500,
"rationale": "Substantially similar exposure to VTI, different index family — not substantially identical per common interpretation"
},
"wash_sale_check": {
"backward_30d_clear": true,
"forward_30d_clear": false,
"blockers": [
{ "type": "401k_payroll_buy", "symbol_substantially_identical": "FSKAX", "buy_date": "2026-05-02" }
],
"remediation": "Change 401k allocation away from FSKAX for the 2026-05-02 cycle, or harvest a different security"
},
"tax_savings_estimate_cents": 11940,
"drip_disable_required": true,
"status": "remediation_required"
}
],
"blocked": [
{
"symbol": "VXUS",
"reason": "spouse_ira_buy_within_30d",
"unblock_date": "2026-05-12"
}
],
"warnings": []
}