Astraea v1.0 — multi-agent WA family-law assistant
Eight specialist agents over a 16-book verified WA law corpus (RAG with citations), per-user document vault, WA court-form PDF auto-fill, comms missions with DV safety guard, no-KYC auth, TTS. Self-hosted: Flask + SQLite + Ollama, stdlib-only RAG. Includes README, LICENSE (MIT + not-legal-advice notice), DEPLOY runbook, .gitignore.
This commit is contained in:
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# User data — never commit
|
||||||
|
astraea.db
|
||||||
|
astraea.db.*
|
||||||
|
uploads/
|
||||||
|
indexes/
|
||||||
|
index.json
|
||||||
|
*.db.bak*
|
||||||
|
|
||||||
|
# Python
|
||||||
|
venv/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.egg-info/
|
||||||
|
|
||||||
|
# Env / secrets
|
||||||
|
.env
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Working artifacts
|
||||||
|
benchmark-results.json
|
||||||
|
benchmark.out
|
||||||
|
polish-log.json
|
||||||
|
polish.out
|
||||||
|
polish-backups/
|
||||||
26
DEPLOY.md
Normal file
26
DEPLOY.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Deploying / updating Astraea on CT150
|
||||||
|
|
||||||
|
# The live app is at /opt/astraea on CT150 (10.30.20.160), proxmox host 10.30.20.85.
|
||||||
|
# NEVER delete the SQLite db (astraea.db) or uploads/ — that's user data.
|
||||||
|
|
||||||
|
# --- Deploy new code (from your machine) ---
|
||||||
|
# 1. Back up on the CT first:
|
||||||
|
ssh root@10.30.20.160 'cd /opt/astraea && tar czf /root/astraea-backup-$(date +%F-%H%M).tar.gz --exclude venv . && ls /root/astraea-backup*'
|
||||||
|
# 2. Copy changed files:
|
||||||
|
scp app.py agents.py root@10.30.20.160:/opt/astraea/
|
||||||
|
# 3. Restart:
|
||||||
|
ssh root@10.30.20.160 'systemctl restart astraea && sleep 2 && curl -s localhost:5000/api/health'
|
||||||
|
|
||||||
|
# --- Full rebuild from scratch (new container) ---
|
||||||
|
# See README "Deploy on Proxmox LXC" section.
|
||||||
|
|
||||||
|
# --- Backup user data (run weekly or before any risky change) ---
|
||||||
|
ssh root@10.30.20.160 'cd /opt/astraea && tar czf /root/astraea-USERDATA-$(date +%F).tar.gz astraea.db uploads indexes'
|
||||||
|
|
||||||
|
# --- Restore user data ---
|
||||||
|
# scp the tarball back, extract, chown -R root:root, systemctl restart astraea
|
||||||
|
|
||||||
|
# --- Note on remote-side editing pitfall ---
|
||||||
|
# Files on CT150 are owned by uid 501 (dragged from a macOS copy). If edits
|
||||||
|
# from the Mac fail with permission errors: ssh in and chown -R root:root /opt/astraea
|
||||||
|
# (do NOT chown the sqlite db while the service is running).
|
||||||
33
LICENSE
Normal file
33
LICENSE
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 drjones (Indiana Holmes Sr)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
ADDITIONAL NOTICE — NOT LEGAL ADVICE:
|
||||||
|
|
||||||
|
This software provides legal INFORMATION for educational purposes only. It is
|
||||||
|
not a law firm, does not provide legal advice, and creates no attorney-client
|
||||||
|
relationship. Legal information is not the same as legal advice — the
|
||||||
|
application of law varies with individual facts and jurisdiction. Always
|
||||||
|
consult a licensed attorney in your jurisdiction for advice about your
|
||||||
|
specific situation. The authors and contributors accept no liability for
|
||||||
|
actions taken based on output produced by this software.
|
||||||
194
README.md
Normal file
194
README.md
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
# ⚖️ Astraea — Self-Hosted AI Family-Law Assistant for Washington State
|
||||||
|
|
||||||
|
> **Eight specialist AI agents. Grounded in verified Washington law. Your documents never leave your network.**
|
||||||
|
|
||||||
|
Astraea is a self-hosted, multi-agent legal information assistant focused on Washington State family law: divorce, custody, child support, maintenance, property division, protection orders, and mediation. It combines retrieval-augmented generation (RAG) over a curated library of state-specific legal references with tool-calling LLM agents, PDF form auto-fill, per-user document vaults, and optional Twilio/SMTP communications — all running on your own hardware with your own models.
|
||||||
|
|
||||||
|
**Live instance:** https://astraea.thetempleofdoom.com
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Why Astraea
|
||||||
|
|
||||||
|
People going through divorce or custody battles face three problems: lawyers are expensive, court forms are intimidating, and generic chatbots hallucinate the law. Astraea attacks all three:
|
||||||
|
|
||||||
|
1. **Grounded answers, not vibes.** Every agent answers from a curated book library (185KB of verified WA-law reference material citing RCW chapters, controlling case law like *In re Marriage of Littlefield*, and WashingtonLawHelp.org). Responses carry citations back to the source chunks.
|
||||||
|
2. **Specialists, not one generic bot.** Eight agents, each with its own system prompt, book subset, and focus — from the intake Navigator to the Protection Orders specialist with built-in DV-safety guardrails.
|
||||||
|
3. **Self-hosted and private.** Runs on a Proxmox LXC container, talks to a LAN Ollama server, and stores everything in a local SQLite database. No cloud APIs, no data leaving your network.
|
||||||
|
|
||||||
|
> ⚠️ **Not legal advice.** Astraea is an educational and organizational tool. It does not replace a licensed Washington attorney. This notice is embedded in the product, the agents' system prompts, and every source book.
|
||||||
|
|
||||||
|
## The Eight Specialists
|
||||||
|
|
||||||
|
| Agent | Focus |
|
||||||
|
|---|---|
|
||||||
|
| 🧭 **Navigator** | Intake — where to start, which specialist next, the overall WA process |
|
||||||
|
| ⚖️ **Divorce & Dissolution** | No-fault dissolution, legal separation, the 90-day wait, decrees |
|
||||||
|
| 🧒 **Child Custody & Parenting** | Parenting plans, best-interest factors, relocation, nonparental custody |
|
||||||
|
| 💰 **Child Support** | Washington's economic table, imputation, deviations |
|
||||||
|
| 🤝 **Spousal Maintenance** | Alimony factors, duration, modification |
|
||||||
|
| 🏠 **Property & Debt** | Characterization, fair-and-equitable division, QDROs |
|
||||||
|
| 🛡️ **Protection** | Protection orders, no-contact orders, DV safety planning |
|
||||||
|
| 🕊️ **Mediation & ADR** | Mediation, arbitration, collaborative law, settlement strategy |
|
||||||
|
|
||||||
|
## Feature Highlights
|
||||||
|
|
||||||
|
- **Multi-agent RAG chat** — stdlib-only vector engine (embeddings via Ollama, cosine retrieval, cached disk index, incremental rebuild when a book changes)
|
||||||
|
- **Per-user document vault** — upload filings/correspondence/evidence; text extracted (PDF/DOCX/TXT), chunked, embedded, and retrieved as `[USER DOC]` context in chats
|
||||||
|
- **PDF form auto-fill** — 10+ Washington court form packets (FL All-Cases series) with field detection (`detect_fields.py` + `pymupdf` overlay) and LLM-assisted mapping from your case facts
|
||||||
|
- **Document generation** — declarations, letters, timelines generated from your case context
|
||||||
|
- **Comms missions with DV safety guard** — Twilio SMS/voice + SMTP email with a hard block on contacting anyone protected by a no-contact/protection order, plus formal message templates
|
||||||
|
- **No-KYC auth** — username/password only, per-user profiles with an "About Me" context system, 12 professional avatars
|
||||||
|
- **Text-to-speech** — every answer speakable via edge-tts
|
||||||
|
- **Conversation memory** — per-agent persisted history in SQLite
|
||||||
|
- **Zero cloud dependencies** — Flask + SQLite + Ollama; the RAG engine is pure stdlib (urllib + json + math)
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
Browser ──HTTPS──▶│ nginx (TLS termination, fleet tunnel) │
|
||||||
|
└───────────────┬─────────────────────────┘
|
||||||
|
│ :5000
|
||||||
|
┌───────────────▼───────────────┐
|
||||||
|
│ Astraea (Flask + gunicorn) │
|
||||||
|
│ ├─ agents.py 8 specialists │
|
||||||
|
│ ├─ rag.py vector engine │
|
||||||
|
│ ├─ store.py SQLite DAL │
|
||||||
|
│ ├─ userdocs.py doc vault │
|
||||||
|
│ ├─ forms.py PDF autofill │
|
||||||
|
│ ├─ comms.py Twilio/SMTP │
|
||||||
|
│ └─ tts.py edge-tts │
|
||||||
|
└───────────────┬───────────────┘
|
||||||
|
│ LAN
|
||||||
|
┌───────────────▼───────────────┐
|
||||||
|
│ Ollama host (GPU) │
|
||||||
|
│ ├─ qwen3.8fast orchestrator │
|
||||||
|
│ ├─ ornith-1.5:9b fallback │
|
||||||
|
│ └─ nomic-embed embeddings │
|
||||||
|
└───────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Repo layout**
|
||||||
|
|
||||||
|
```
|
||||||
|
├── app.py # Flask app: routes, chat orchestration, uploads (757 lines)
|
||||||
|
├── agents.py # The 8 specialist definitions (system prompts, book subsets)
|
||||||
|
├── rag.py # Stdlib-only RAG: chunk → embed → cosine retrieve → cite
|
||||||
|
├── store.py # SQLite: users, sessions, profiles, settings, messages, files
|
||||||
|
├── documents.py # Generated-document engine
|
||||||
|
├── detect_fields.py # PDF form field detection (pymupdf)
|
||||||
|
├── forms.py # Court-form fill + overlay endpoints
|
||||||
|
├── comms.py # Twilio SMS/voice + SMTP, no-contact safety guard
|
||||||
|
├── tts.py # edge-tts speech synthesis
|
||||||
|
├── userdocs.py # Per-user document vault ingestion
|
||||||
|
├── books/ # 16 curated WA family-law reference books (the RAG corpus)
|
||||||
|
├── templates/ # Landing page + app SPA (vanilla JS, no build step)
|
||||||
|
├── static/ # Assets
|
||||||
|
├── astraea.service # systemd unit (gunicorn, 2 workers × 4 threads)
|
||||||
|
├── astraea-nginx.conf # nginx site config
|
||||||
|
└── benchmark_models.py # Model-selection benchmark harness
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
- Python 3.11+ (3.9 works with minor tweaks — stdlib-only core)
|
||||||
|
- An [Ollama](https://ollama.com) server reachable on your LAN with:
|
||||||
|
- `qwen3.8fast` (or any tool-calling model) — orchestrator
|
||||||
|
- `ornith-1.5:9b` (or any reliable instruct model) — fallback
|
||||||
|
- `nomic-embed-text-v2-moe` — embeddings
|
||||||
|
- Optional: `pymupdf` for PDF form filling, `edge-tts` for speech
|
||||||
|
|
||||||
|
### Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone http://10.30.20.149:3000/drjones/astraea.git
|
||||||
|
cd astraea
|
||||||
|
python3 -m venv venv && source venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
pip install pymupdf edge-tts # optional features
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure
|
||||||
|
|
||||||
|
Point the agents at your Ollama host in `agents.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
OLLAMA_URL = "http://your-ollama-host:11434"
|
||||||
|
RAG_MODEL = "qwen3.8fast" # tool-calling orchestrator
|
||||||
|
GENERAL_MODEL = "ornith-1.5:9b" # fallback
|
||||||
|
EMBED_MODEL = "nomic-embed-text-v2-moe"
|
||||||
|
```
|
||||||
|
|
||||||
|
The vector index builds on first boot (background thread) and caches to `index.json`.
|
||||||
|
|
||||||
|
### Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python app.py # dev, port 5000
|
||||||
|
# or production:
|
||||||
|
gunicorn --workers 2 --threads 4 --bind 0.0.0.0:5000 --timeout 240 app:app
|
||||||
|
```
|
||||||
|
|
||||||
|
Register a user, answer the About-Me prompts (this becomes silent context for every agent), and start with the **Navigator**.
|
||||||
|
|
||||||
|
### Deploy on Proxmox LXC (how we run it)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pct create 150 local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst \
|
||||||
|
--hostname astraea --memory 2048 --cores 2 \
|
||||||
|
--net0 name=eth0,bridge=vmbr0,ip=10.30.20.160/24,gw=10.30.20.1 \
|
||||||
|
--rootfs poolmaster:8 --unprivileged 1
|
||||||
|
pct start 150
|
||||||
|
# then: install python3-venv, clone, pip install, cp astraea.service /etc/systemd/system/
|
||||||
|
systemctl enable --now astraea
|
||||||
|
```
|
||||||
|
|
||||||
|
nginx TLS termination + Cloudflare Tunnel gives the public URL. See `astraea-nginx.conf`.
|
||||||
|
|
||||||
|
## The RAG Corpus
|
||||||
|
|
||||||
|
`books/` contains 16 reference books (~185KB) written as structured Markdown, each with inline citations to RCW statutes, Washington case law, and WashingtonLawHelp.org:
|
||||||
|
|
||||||
|
- `divorce-dissolution.md` + 3 case-law companions (characterization, division, maintenance)
|
||||||
|
- `custody-parenting.md`, `parentage.md`, `guardianship.md`
|
||||||
|
- `child-support.md`, `spousal-maintenance.md`, `property-debt.md`
|
||||||
|
- `protection-orders.md`, `mediation-adr.md`, `modification-enforcement.md`
|
||||||
|
- `overview-intake.md`, `committed-relationships.md`, `adoption.md`
|
||||||
|
|
||||||
|
The index is chunked at ~1200 chars, embedded once, and rebuilt incrementally per-book on change. Retrieval returns top-k chunks as `[SOURCE n]` blocks the agent must cite.
|
||||||
|
|
||||||
|
## Safety Design
|
||||||
|
|
||||||
|
- **Not-legal-advice notice** in the product UI, agent system prompts, and every book header
|
||||||
|
- **No-contact safety guard** (`comms.py`): if a user's profile indicates a protection/no-contact order is in effect, outbound Twilio/SMTP actions toward the protected party are hard-blocked with a safety message
|
||||||
|
- **No-KYC by design**: no emails, no phone numbers required at signup; users stay anonymous
|
||||||
|
- **Local-only data**: uploads, chats, and profiles live in one SQLite file on your container
|
||||||
|
|
||||||
|
## Known Limitations & Roadmap
|
||||||
|
|
||||||
|
- [ ] **Password hashing is unsalted SHA-256** — needs bcrypt/argon2 with a transparent re-hash-on-login migration (next security release)
|
||||||
|
- [ ] Session tokens don't expire — add TTL + refresh
|
||||||
|
- [ ] No rate limiting on auth/chat endpoints
|
||||||
|
- [ ] Single-node SQLite — Postgres adapter for multi-user scale
|
||||||
|
- [ ] Books cover WA only — contributions for other states welcome (keep the citation format!)
|
||||||
|
- [ ] Streaming responses (currently full-answer)
|
||||||
|
|
||||||
|
## Operations
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl status astraea # service health
|
||||||
|
curl localhost:5000/api/health # app health + index status
|
||||||
|
curl -X POST localhost:5000/api/reindex # force full RAG rebuild
|
||||||
|
```
|
||||||
|
|
||||||
|
Database backup: `cp astraea.db astraea.db.bak-$(date +%F)` — everything (users, chats, vault metadata) is in that one file. Uploads live in `uploads/`.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
Built by [drjones](https://github.com/drjonesxxx1) on a Proxmox homelab with local Ollama models — no cloud APIs were harmed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Licensed under MIT — see [LICENSE](LICENSE). Legal reference material is educational, cites public sources, and is not legal advice.*
|
||||||
154
agents.py
Normal file
154
agents.py
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Astraea — Washington State family-law specialist agents.
|
||||||
|
Each agent scopes a domain and pulls from its own set of 'books' (markdown sources).
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Backend Ollama (nightmare, RTX 4080 SUPER). Renumbers after reboot — .29 as of Sept 2026.
|
||||||
|
OLLAMA_URL = "http://10.30.20.29:11434"
|
||||||
|
EMBED_MODEL = "nomic-embed-text-v2-moe:latest" # semantic embeddings (verified on nightmare)
|
||||||
|
RAG_MODEL = "qwen3.8fast:latest" # tool-calling orchestrator (web search + comms + RAG answer)
|
||||||
|
GENERAL_MODEL = "ornith-1.5:9b" # fallback (reliable, grounded, also tool-capable)
|
||||||
|
|
||||||
|
BOOKS_DIR = "/opt/astraea/books"
|
||||||
|
|
||||||
|
# Each agent: id, name, emoji, tagline, description, books (list of .md filenames), system prompt.
|
||||||
|
AGENTS = [
|
||||||
|
{
|
||||||
|
"id": "navigator",
|
||||||
|
"name": "Navigator",
|
||||||
|
"emoji": "\U0001F9ED",
|
||||||
|
"tagline": "Where to start",
|
||||||
|
"description": "Point you at the right specialist and walk through the Washington family-law process, from residency to final decree.",
|
||||||
|
"books": ["overview-intake.md", "mediation-adr.md", "adoption.md"],
|
||||||
|
"accent": "#7dd3fc",
|
||||||
|
"system": (
|
||||||
|
"You are the Navigator, the intake specialist for Astraea, a Washington State "
|
||||||
|
"family-law assistant. Help a person understand the overall WA family-law process: "
|
||||||
|
"residency requirements, the steps of a dissolution (petition, service, the 90-day "
|
||||||
|
"waiting period, temporary orders, settlement, trial), court fees and fee waivers, "
|
||||||
|
"and whether they need a lawyer. Most importantly, identify which specialist they "
|
||||||
|
"should talk to next and tell them so."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "divorce",
|
||||||
|
"name": "Divorce & Dissolution",
|
||||||
|
"emoji": "\u2696\uFE0F",
|
||||||
|
"tagline": "Ending a marriage",
|
||||||
|
"description": "No-fault dissolution, legal separation, the 90-day wait, grounds, residency, and the decree.",
|
||||||
|
"books": ["divorce-dissolution.md", "divorce-case-law-characterization.md",
|
||||||
|
"divorce-case-law-division.md", "divorce-case-law-maintenance.md"],
|
||||||
|
"accent": "#a5b4fc",
|
||||||
|
"system": (
|
||||||
|
"You are the Divorce & Dissolution specialist for Astraea, a Washington State "
|
||||||
|
"family-law assistant. Washington is a no-fault divorce state. Explain dissolution of "
|
||||||
|
"marriage and legal separation under RCW 26.09: residency requirements, the petition, "
|
||||||
|
"service, the 90-day waiting period, temporary orders, default, and the final decree. "
|
||||||
|
"Be clear about what 'no-fault' means and what the court can and cannot decide. "
|
||||||
|
"Where relevant, cite the controlling Washington case law (e.g. 'In re Marriage of "
|
||||||
|
"Littlefield') alongside the statute — not just the RCW."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "custody",
|
||||||
|
"name": "Child Custody & Parenting",
|
||||||
|
"emoji": "\U0001F9D2",
|
||||||
|
"tagline": "Parenting plans",
|
||||||
|
"description": "Residential schedules, best-interest factors, restrictions, relocation, and nonparental custody.",
|
||||||
|
"books": ["custody-parenting.md", "parentage.md", "guardianship.md"],
|
||||||
|
"accent": "#86efac",
|
||||||
|
"system": (
|
||||||
|
"You are the Child Custody & Parenting Plans specialist for Astraea, a Washington "
|
||||||
|
"State family-law assistant. Explain parenting plans and residential schedules under "
|
||||||
|
"RCW 26.09.184-.191: the best-interest-of-the-child factors, restrictions under "
|
||||||
|
"RCW 26.09.191 (domestic violence, abuse, etc.), decision-making, relocation, "
|
||||||
|
"and nonparental custody. Note: nonparental custody is now minor guardianship under "
|
||||||
|
"chapter 11.130 RCW and relatives' visitation under chapter 26.11 RCW (the old "
|
||||||
|
"RCW 26.10 was repealed in 2020). Always center the child's safety and best interests."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "child-support",
|
||||||
|
"name": "Child Support",
|
||||||
|
"emoji": "\U0001F4B5",
|
||||||
|
"tagline": "The support schedule",
|
||||||
|
"description": "Income-shares calculation, the WA economic table, deviations, imputed income, and modification.",
|
||||||
|
"books": ["child-support.md", "modification-enforcement.md"],
|
||||||
|
"accent": "#fcd34d",
|
||||||
|
"system": (
|
||||||
|
"You are the Child Support specialist for Astraea, a Washington State family-law "
|
||||||
|
"assistant. Explain Washington's child support schedule under RCW 26.19: the "
|
||||||
|
"income-shares model, the economic table, how income is combined, standard "
|
||||||
|
"calculations and deviations, imputed income, healthcare and special expenses, and "
|
||||||
|
"how to modify an existing order. Give practical guidance, not a legal ruling."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "maintenance",
|
||||||
|
"name": "Spousal Maintenance",
|
||||||
|
"emoji": "\U0001F54A\uFE0F",
|
||||||
|
"tagline": "Alimony in WA",
|
||||||
|
"description": "Maintenance factors, duration, and when spousal support is ordered or modified.",
|
||||||
|
"books": ["spousal-maintenance.md", "modification-enforcement.md", "divorce-case-law-maintenance.md"],
|
||||||
|
"accent": "#f0abfc",
|
||||||
|
"system": (
|
||||||
|
"You are the Spousal Maintenance (alimony) specialist for Astraea, a Washington State "
|
||||||
|
"family-law assistant. Explain maintenance under RCW 26.09.090: the statutory factors "
|
||||||
|
"(length of marriage, need, ability to pay, age/health, standard of living), how "
|
||||||
|
"duration is typically set, and modification or termination of maintenance awards. "
|
||||||
|
"Be honest that maintenance is fact-specific and discretionary."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "property",
|
||||||
|
"name": "Property & Debt",
|
||||||
|
"emoji": "\U0001F3E0",
|
||||||
|
"tagline": "Community property",
|
||||||
|
"description": "Separate vs. community property, characterization, division, pensions, and marital debt.",
|
||||||
|
"books": ["property-debt.md", "committed-relationships.md"],
|
||||||
|
"accent": "#fdba74",
|
||||||
|
"system": (
|
||||||
|
"You are the Property & Debt Division specialist for Astraea, a Washington State "
|
||||||
|
"family-law assistant. Washington is a community property state. Explain the "
|
||||||
|
"difference between separate and community property and debt (RCW 26.16), how assets "
|
||||||
|
"are characterized, the court's 'just and equitable' division standard (RCW 26.09.080), "
|
||||||
|
"pensions/retirement and QDROs, and how marital debt is allocated."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "protection",
|
||||||
|
"name": "Protection Orders",
|
||||||
|
"emoji": "\U0001F6E1\uFE0F",
|
||||||
|
"tagline": "Safety orders",
|
||||||
|
"description": "Domestic violence, anti-harassment, sexual assault, and extreme-risk protection orders (RCW 7.105).",
|
||||||
|
"books": ["protection-orders.md"],
|
||||||
|
"accent": "#fca5a5",
|
||||||
|
"system": (
|
||||||
|
"You are the Protection Orders & Safety specialist for Astraea, a Washington State "
|
||||||
|
"family-law assistant. Washington consolidated protection orders under RCW 7.105 "
|
||||||
|
"(effective July 1, 2022): domestic violence protection orders, civil anti-harassment, "
|
||||||
|
"sexual assault protection orders, stalking, and extreme risk protection orders. "
|
||||||
|
"Explain how to file, what each order does, and safety resources. If someone describes "
|
||||||
|
"immediate danger, tell them to call 911 and offer crisis resources."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "mediation",
|
||||||
|
"name": "Mediation & ADR",
|
||||||
|
"emoji": "\U0001F91D",
|
||||||
|
"tagline": "Settle, don't fight",
|
||||||
|
"description": "Mandatory mediation, settlement conferences, arbitration, collaborative law, and guardians ad litem.",
|
||||||
|
"books": ["mediation-adr.md"],
|
||||||
|
"accent": "#67e8f9",
|
||||||
|
"system": (
|
||||||
|
"You are the Mediation & Alternative Dispute Resolution specialist for Astraea, a "
|
||||||
|
"Washington State family-law assistant. Explain mandatory mediation in WA family law, "
|
||||||
|
"settlement conferences, arbitration, collaborative law, parenting-plan mediation, and "
|
||||||
|
"the role of guardians ad litem. Help people resolve disputes without a contested trial."
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
AGENT_BY_ID = {a["id"]: a for a in AGENTS}
|
||||||
|
|
||||||
|
DISCLAIMER = "Astraea gives practical legal guidance grounded in Washington statutes and case law. It is not a substitute for a licensed Washington attorney, who can advise on the specifics of your case."
|
||||||
757
app.py
Normal file
757
app.py
Normal file
@@ -0,0 +1,757 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Astraea — the world's best Washington divorce-law attorney.
|
||||||
|
Multi-agent RAG + no-KYC profiles (About Me) + document prep + comms missions + TTS.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from flask import Flask, jsonify, render_template, request, send_file, Response
|
||||||
|
|
||||||
|
import rag
|
||||||
|
import store
|
||||||
|
import comms
|
||||||
|
import documents
|
||||||
|
import forms
|
||||||
|
import tts
|
||||||
|
import userdocs
|
||||||
|
from agents import AGENTS, AGENT_BY_ID, OLLAMA_URL, RAG_MODEL, GENERAL_MODEL
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# ── Professional avatars (gradient + glyph, rendered client-side) ──
|
||||||
|
AVATARS = [
|
||||||
|
{"id": 0, "glyph": "\u2696", "c1": "#6ea8ff", "c2": "#a78bfa", "label": "Scales"},
|
||||||
|
{"id": 1, "glyph": "\U0001F3DB", "c1": "#7dd3fc", "c2": "#0ea5e9", "label": "Court"},
|
||||||
|
{"id": 2, "glyph": "\U0001F6E1", "c1": "#4ade80", "c2": "#059669", "label": "Shield"},
|
||||||
|
{"id": 3, "glyph": "\U0001F4DC", "c1": "#f5d06f", "c2": "#d97706", "label": "Scroll"},
|
||||||
|
{"id": 4, "glyph": "\U0001F54A", "c1": "#f0abfc", "c2": "#c026d3", "label": "Dove"},
|
||||||
|
{"id": 5, "glyph": "\U0001F4BC", "c1": "#fdba74", "c2": "#ea580c", "label": "Briefcase"},
|
||||||
|
{"id": 6, "glyph": "\U0001F512", "c1": "#67e8f9", "c2": "#0891b2", "label": "Key"},
|
||||||
|
{"id": 7, "glyph": "\u2731", "c1": "#fda4af", "c2": "#e11d48", "label": "Star"},
|
||||||
|
{"id": 8, "glyph": "\U0001F3AF", "c1": "#a3e635", "c2": "#65a30d", "label": "Target"},
|
||||||
|
{"id": 9, "glyph": "\U0001F525", "c1": "#f87171", "c2": "#b91c1c", "label": "Flame"},
|
||||||
|
{"id": 10, "glyph": "\U0001F31F", "c1": "#fbbf24", "c2": "#b45309", "label": "Glow"},
|
||||||
|
{"id": 11, "glyph": "\U0001F9ED", "c1": "#5eead4", "c2": "#0d9488", "label": "Compass"},
|
||||||
|
]
|
||||||
|
|
||||||
|
UPLOAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "uploads")
|
||||||
|
FILE_CATEGORIES = ["Court filings", "Correspondence", "Financial", "Agreements",
|
||||||
|
"Evidence", "Medical & safety", "Generated forms", "Other"]
|
||||||
|
|
||||||
|
|
||||||
|
def _docx_text(path):
|
||||||
|
import zipfile as _z
|
||||||
|
try:
|
||||||
|
with _z.ZipFile(path) as z:
|
||||||
|
xml = z.read("word/document.xml").decode("utf-8", "ignore")
|
||||||
|
out = []
|
||||||
|
for p in re.split(r"</w:p>", xml):
|
||||||
|
ts = re.findall(r"<w:t[^>]*>(.*?)</w:t>", p)
|
||||||
|
if ts:
|
||||||
|
out.append("".join(ts))
|
||||||
|
return "\n".join(out)
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _reindex_user(uid):
|
||||||
|
"""Rebuild a user's document vector index in a background thread."""
|
||||||
|
try:
|
||||||
|
userdocs.build(uid, store.list_files(uid), UPLOAD_DIR)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _autofill_values(uid, form_id):
|
||||||
|
"""Map the user's About Me + display name onto a form's fields via the LLM."""
|
||||||
|
spec = forms.FORM_NAMES.get(form_id)
|
||||||
|
fm = forms.FIELDMAP.get(form_id, [])
|
||||||
|
if not spec or not fm:
|
||||||
|
return {}
|
||||||
|
profile = store.get_profile(uid) if uid else {}
|
||||||
|
about = profile.get("about_me", "")
|
||||||
|
display = profile.get("display_name", "")
|
||||||
|
# filter to a manageable set: all text fields + checkboxes with a real label (capped)
|
||||||
|
text_fields = [f for f in fm if f.get("type") != "checkbox"]
|
||||||
|
cb_fields = [f for f in fm if f.get("type") == "checkbox" and f.get("label", "").strip()]
|
||||||
|
cb_fields = cb_fields[:40]
|
||||||
|
sel = text_fields + cb_fields
|
||||||
|
field_desc = ", ".join(
|
||||||
|
f"{f['key']} ({f.get('label', '')})" + (" [CB]" if f.get('type') == 'checkbox' else "")
|
||||||
|
for f in sel
|
||||||
|
)
|
||||||
|
prompt = (
|
||||||
|
"You are filling a Washington State court form. Map the user's facts to the form fields.\n"
|
||||||
|
f"Form: {spec['name']}\n"
|
||||||
|
f"Fields: {field_desc}\n\n"
|
||||||
|
f"User's display name: {display or '(not set)'}\n"
|
||||||
|
f"User's information (About Me):\n{about or '(not provided)'}\n\n"
|
||||||
|
"Output ONLY valid JSON. For text fields, output the value (or empty string if unknown). "
|
||||||
|
"For CHECKBOX fields, output 'X' ONLY when the user's facts clearly and unambiguously "
|
||||||
|
"match that option; otherwise output ''. Be conservative — do not guess. "
|
||||||
|
"Petitioner is the user themself (use the display name); respondent is their spouse."
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
resp = _ollama_chat(RAG_MODEL, [{"role": "user", "content": prompt}],
|
||||||
|
temperature=0.1, num_predict=1600)
|
||||||
|
content = resp.get("message", {}).get("content", "")
|
||||||
|
m = re.search(r"\{.*\}", content, re.DOTALL)
|
||||||
|
vals = json.loads(m.group(0)) if m else {}
|
||||||
|
except Exception:
|
||||||
|
vals = {}
|
||||||
|
return {f["key"]: str(vals.get(f["key"], "") or "") for f in sel}
|
||||||
|
|
||||||
|
|
||||||
|
def _save_pdf_to_vault(uid, path, name):
|
||||||
|
data = open(path, "rb").read()
|
||||||
|
fn = f"gen_{uid}_{uuid.uuid4().hex[:8]}.pdf"
|
||||||
|
with open(os.path.join(UPLOAD_DIR, fn), "wb") as f:
|
||||||
|
f.write(data)
|
||||||
|
return store.add_file(uid, "Generated forms", fn, name + ".pdf",
|
||||||
|
"application/pdf", len(data))
|
||||||
|
|
||||||
|
|
||||||
|
# ── Ollama helpers ──
|
||||||
|
def _ollama_chat(model, messages, tools=None, num_ctx=24000, num_predict=1400,
|
||||||
|
temperature=0.2, timeout=180):
|
||||||
|
payload = {"model": model, "messages": messages, "stream": False, "think": False,
|
||||||
|
"options": {"temperature": temperature, "num_predict": num_predict, "num_ctx": num_ctx}}
|
||||||
|
if tools:
|
||||||
|
payload["tools"] = tools
|
||||||
|
req = urllib.request.Request(f"{OLLAMA_URL}/api/chat", data=json.dumps(payload).encode(),
|
||||||
|
headers={"Content-Type": "application/json"})
|
||||||
|
op = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||||
|
with op.open(req, timeout=timeout) as r:
|
||||||
|
return json.loads(r.read().decode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def _web_search(query, limit=5):
|
||||||
|
"""Search the live web via local SearXNG (separate service, not the LLM)."""
|
||||||
|
try:
|
||||||
|
url = f"http://10.30.20.35:6969/search?q={urllib.parse.quote(query)}&format=json"
|
||||||
|
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (Astraea)"})
|
||||||
|
op = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||||
|
with op.open(req, timeout=15) as r:
|
||||||
|
data = json.loads(r.read().decode("utf-8"))
|
||||||
|
out = []
|
||||||
|
for it in data.get("results", [])[:limit]:
|
||||||
|
out.append({"title": it.get("title", ""), "url": it.get("url", ""),
|
||||||
|
"snippet": (it.get("content") or "")[:400]})
|
||||||
|
return out
|
||||||
|
except Exception as e:
|
||||||
|
return [{"error": f"web search failed: {e}"}]
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_sources(text):
|
||||||
|
text = re.split(r"\n\s*(?:Sources|References|Citations)\s*:\s*\n", text, flags=re.I)[0]
|
||||||
|
return text.strip()
|
||||||
|
|
||||||
|
|
||||||
|
# ── About Me injection (designated plainly, used silently) ──
|
||||||
|
def about_me_block(about_me):
|
||||||
|
if not about_me or not about_me.strip():
|
||||||
|
return ""
|
||||||
|
return (
|
||||||
|
"\n\n=== ABOUT THE USER (PRIVATE CONTEXT) ===\n"
|
||||||
|
f"{about_me.strip()}\n"
|
||||||
|
"=== END ABOUT THE USER ===\n"
|
||||||
|
"This is private background about the user's situation. Use it silently to tailor your "
|
||||||
|
"answer to their facts. NEVER reference it, quote it, summarize it back, or say anything "
|
||||||
|
"like 'based on what you shared'. Answer ONLY what the user asks."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ── Comms tools (agent can invoke to send SMS/email/call) ──
|
||||||
|
TOOLS = [
|
||||||
|
{"type": "function", "function": {
|
||||||
|
"name": "send_sms", "description": "Send a professionally-worded text message on the user's behalf.",
|
||||||
|
"parameters": {"type": "object", "properties": {
|
||||||
|
"recipient": {"type": "string", "description": "recipient phone number in E.164 (e.g. +14255550123)"},
|
||||||
|
"message": {"type": "string", "description": "the message text"}},
|
||||||
|
"required": ["recipient", "message"]}}},
|
||||||
|
{"type": "function", "function": {
|
||||||
|
"name": "send_email", "description": "Send a formal email on the user's behalf.",
|
||||||
|
"parameters": {"type": "object", "properties": {
|
||||||
|
"recipient": {"type": "string", "description": "recipient email address"},
|
||||||
|
"subject": {"type": "string"}, "body": {"type": "string"}},
|
||||||
|
"required": ["recipient", "subject", "body"]}}},
|
||||||
|
{"type": "function", "function": {
|
||||||
|
"name": "make_call", "description": "Place a phone call that speaks a message.",
|
||||||
|
"parameters": {"type": "object", "properties": {
|
||||||
|
"recipient": {"type": "string", "description": "recipient phone number in E.164"},
|
||||||
|
"message": {"type": "string", "description": "the message to speak"}},
|
||||||
|
"required": ["recipient", "message"]}}},
|
||||||
|
{"type": "function", "function": {
|
||||||
|
"name": "web_search", "description": "Search the live web for current facts, statutes, or answers not in your reference documents. Use when you don't know something or need up-to-date information.",
|
||||||
|
"parameters": {"type": "object", "properties": {
|
||||||
|
"query": {"type": "string", "description": "the search query"}},
|
||||||
|
"required": ["query"]}}},
|
||||||
|
{"type": "function", "function": {
|
||||||
|
"name": "fill_form", "description": "Fill an official Washington court form with the user's info and save the filled PDF to their case documents. Use when the user asks to fill out / prepare / complete paperwork. Form ids: fl200=Summons, fl201=Petition, fl001=Confidential Info, fl140=Parenting Plan, wscss=Child Support Worksheets, fl131=Financial Declaration, fl231=Findings & Conclusions, fl241=Final Divorce Order, fl211=Response, fl223=Motion for Temporary Order.",
|
||||||
|
"parameters": {"type": "object", "properties": {
|
||||||
|
"form_id": {"type": "string", "description": "the form id, e.g. fl201"},
|
||||||
|
"values": {"type": "object", "description": "optional explicit field values; omitted fields auto-fill from the user's profile"}},
|
||||||
|
"required": ["form_id"]}}},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _execute_tool(name, args, settings, uid, display_name):
|
||||||
|
recipient = args.get("recipient", "")
|
||||||
|
if comms.no_contact_blocked(settings, recipient):
|
||||||
|
return ("BLOCKED: a no-contact order is in effect for this person. No message was sent. "
|
||||||
|
"Inform the user you cannot contact this person due to the no-contact order.")
|
||||||
|
try:
|
||||||
|
if name == "send_sms":
|
||||||
|
comms.send_sms(settings, recipient, comms.sms_template(args.get("message", "")))
|
||||||
|
return f"SMS sent to {recipient}."
|
||||||
|
if name == "send_email":
|
||||||
|
body = comms.email_template(display_name, recipient, args.get("subject", ""), args.get("body", ""))
|
||||||
|
comms.send_email(settings, recipient, args.get("subject", ""), body)
|
||||||
|
return f"Email sent to {recipient}."
|
||||||
|
if name == "make_call":
|
||||||
|
twiml_url = request.host_url.rstrip("/") + "/twilio/voice"
|
||||||
|
comms.make_call(settings, recipient, twiml_url)
|
||||||
|
return f"Call initiated to {recipient}."
|
||||||
|
if name == "web_search":
|
||||||
|
res = _web_search(args.get("query", ""), limit=5)
|
||||||
|
if not res:
|
||||||
|
return "No results found."
|
||||||
|
return json.dumps(res, ensure_ascii=False)
|
||||||
|
if name == "fill_form":
|
||||||
|
form_id = args.get("form_id", "")
|
||||||
|
if not forms.FORM_NAMES.get(form_id):
|
||||||
|
return "Unknown form. Use one of: " + ", ".join(forms.FORM_NAMES.keys())
|
||||||
|
values = args.get("values") or {}
|
||||||
|
auto = _autofill_values(uid, form_id)
|
||||||
|
merged = {k: str(values.get(k) or auto.get(k) or "") for k in auto}
|
||||||
|
path = forms.fill_form(form_id, merged)
|
||||||
|
if not path:
|
||||||
|
return "Could not generate the form."
|
||||||
|
_save_pdf_to_vault(uid, path, forms.FORM_NAMES[form_id]["name"])
|
||||||
|
return (f"Filled '{forms.FORM_NAMES[form_id]['name']}' and saved it to the user's "
|
||||||
|
f"Case Documents. Tell the user to open their profile → Case documents to download it.")
|
||||||
|
except Exception as e:
|
||||||
|
return f"ERROR sending {name}: {e}"
|
||||||
|
return "Unknown tool."
|
||||||
|
|
||||||
|
|
||||||
|
# ── RAG chat with tool-calling ──
|
||||||
|
def safety_block(settings):
|
||||||
|
if not (settings.get("domestic_violence") or settings.get("protection_order")):
|
||||||
|
return ""
|
||||||
|
lines = ["\n\n=== SAFETY CONTEXT (PRIVATE — CRITICAL) ==="]
|
||||||
|
if settings.get("domestic_violence"):
|
||||||
|
lines.append("DOMESTIC VIOLENCE IS A FACTOR IN THIS CASE.")
|
||||||
|
if settings.get("protection_order"):
|
||||||
|
lines.append("An active protection order is in effect (RCW 7.105).")
|
||||||
|
notes = (settings.get("safety_notes") or "").strip()
|
||||||
|
if notes:
|
||||||
|
lines.append(f"Safety notes: {notes}")
|
||||||
|
lines.append("=== END SAFETY CONTEXT ===")
|
||||||
|
lines.append(
|
||||||
|
"Prioritize the user's safety above all else. Apply RCW 26.09.191 restrictions on "
|
||||||
|
"residential time and decision-making where domestic violence or abuse is a factor. "
|
||||||
|
"Recommend protection orders, supervised visitation, and safety planning where "
|
||||||
|
"appropriate. NEVER advise or facilitate direct contact with the abusive or restrained party."
|
||||||
|
)
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_answer(agent, message, history, about_me, settings, uid, display_name):
|
||||||
|
top = rag.retrieve(message, agent["id"], top_k=5)
|
||||||
|
context_blocks = []
|
||||||
|
citations = []
|
||||||
|
seen = set()
|
||||||
|
for i, (score, c) in enumerate(top, 1):
|
||||||
|
key = (c["source"], c["title"])
|
||||||
|
if key in seen:
|
||||||
|
continue
|
||||||
|
seen.add(key)
|
||||||
|
context_blocks.append(f"[{i}] ({c['source']} — {c['title']})\n{c['text']}")
|
||||||
|
citations.append({"source": c["source"], "title": c["title"],
|
||||||
|
"score": round(score, 3), "snippet": c["text"][:260]})
|
||||||
|
|
||||||
|
# user's own uploaded documents (per-user vector index) — retrieve alongside case law
|
||||||
|
if uid:
|
||||||
|
try:
|
||||||
|
for d in userdocs.retrieve(uid, message, top_k=4):
|
||||||
|
context_blocks.append(f"[USER DOC — {d['source']}]\n{d['text']}")
|
||||||
|
citations.append({"source": d["source"], "title": "your document",
|
||||||
|
"score": d["score"], "snippet": d["text"][:260]})
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
system = (
|
||||||
|
f"{agent['system']}\n\n"
|
||||||
|
"Rules:\n"
|
||||||
|
"- Give REAL, practical legal advice: state the rule, apply it to the user's facts, "
|
||||||
|
"tell them what to do next.\n"
|
||||||
|
"- Answer DIRECTLY. Do not restate the question or narrate your reasoning.\n"
|
||||||
|
"- Ground every legal claim in the reference documents. Cite the RCW section AND the "
|
||||||
|
"controlling case law by name.\n"
|
||||||
|
"- Be plain-English, specific to Washington State.\n"
|
||||||
|
"- Items marked '[USER DOC]' are the user's own uploaded files — reference them by name "
|
||||||
|
"to ground your answer in their actual case.\n"
|
||||||
|
"- You may use the available tools (send_sms / send_email / make_call) ONLY when the "
|
||||||
|
"user explicitly asks you to contact someone on their behalf.\n"
|
||||||
|
"- If you don't know the answer, or need current/up-to-date facts not in the reference "
|
||||||
|
"documents, use the web_search tool and answer from those results.\n"
|
||||||
|
+ about_me_block(about_me)
|
||||||
|
+ safety_block(settings)
|
||||||
|
)
|
||||||
|
|
||||||
|
user = ("REFERENCE DOCUMENTS:\n\n" + "\n\n".join(context_blocks)
|
||||||
|
+ f"\n\nUSER QUESTION: {message}")
|
||||||
|
|
||||||
|
messages = [{"role": "system", "content": system}]
|
||||||
|
if history:
|
||||||
|
for turn in history[-6:]:
|
||||||
|
if turn.get("role") in ("user", "assistant"):
|
||||||
|
messages.append({"role": turn["role"], "content": turn["content"]})
|
||||||
|
messages.append({"role": "user", "content": user})
|
||||||
|
|
||||||
|
try:
|
||||||
|
resp = _ollama_chat(RAG_MODEL, messages, tools=TOOLS)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
resp = _ollama_chat(GENERAL_MODEL, messages)
|
||||||
|
except Exception:
|
||||||
|
return {"answer": "The legal engine is temporarily unavailable. Try again shortly.",
|
||||||
|
"citations": [], "grounded": False}
|
||||||
|
|
||||||
|
# tool-calling loop
|
||||||
|
for _ in range(3):
|
||||||
|
msg = resp.get("message", {})
|
||||||
|
tool_calls = msg.get("tool_calls") or []
|
||||||
|
if not tool_calls:
|
||||||
|
break
|
||||||
|
messages.append(msg)
|
||||||
|
for tc in tool_calls:
|
||||||
|
fn = tc.get("function", {})
|
||||||
|
name = fn.get("name", "")
|
||||||
|
args = fn.get("arguments", {})
|
||||||
|
if isinstance(args, str):
|
||||||
|
try:
|
||||||
|
args = json.loads(args)
|
||||||
|
except Exception:
|
||||||
|
args = {}
|
||||||
|
result = _execute_tool(name, args, settings, uid, display_name)
|
||||||
|
messages.append({"role": "tool", "content": result})
|
||||||
|
resp = _ollama_chat(RAG_MODEL, messages, tools=TOOLS)
|
||||||
|
|
||||||
|
final = resp.get("message", {})
|
||||||
|
answer = (final.get("content") or final.get("thinking") or "").strip()
|
||||||
|
if not answer:
|
||||||
|
# transient empty response — fall back to the reliable model (no tools)
|
||||||
|
try:
|
||||||
|
resp = _ollama_chat(GENERAL_MODEL, messages)
|
||||||
|
answer = (resp.get("message", {}).get("content") or "").strip()
|
||||||
|
except Exception:
|
||||||
|
answer = ""
|
||||||
|
answer = _strip_sources(answer)
|
||||||
|
return {"answer": answer, "citations": citations, "grounded": bool(context_blocks)}
|
||||||
|
|
||||||
|
|
||||||
|
# ── Auth helpers ──
|
||||||
|
def _uid():
|
||||||
|
token = request.headers.get("Authorization", "").replace("Bearer ", "")
|
||||||
|
return store.user_from_token(token)
|
||||||
|
|
||||||
|
|
||||||
|
def _profile_or_none():
|
||||||
|
uid = _uid()
|
||||||
|
return uid, store.get_profile(uid) if uid else None, store.get_settings(uid) if uid else {}
|
||||||
|
|
||||||
|
|
||||||
|
# ── Pages ──
|
||||||
|
@app.route("/")
|
||||||
|
def landing():
|
||||||
|
return render_template("landing.html", agents=AGENTS)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/app")
|
||||||
|
def app_page():
|
||||||
|
return render_template("index.html", agents=AGENTS)
|
||||||
|
|
||||||
|
|
||||||
|
# ── Auth API ──
|
||||||
|
@app.route("/api/register", methods=["POST"])
|
||||||
|
def api_register():
|
||||||
|
d = request.get_json(force=True, silent=True) or {}
|
||||||
|
u = (d.get("username") or "").strip().lower()
|
||||||
|
p = (d.get("password") or "").strip()
|
||||||
|
if len(u) < 2 or len(u) > 30 or not u.isalnum():
|
||||||
|
return jsonify({"error": "Username must be 2-30 alphanumeric chars"}), 400
|
||||||
|
if len(p) < 4:
|
||||||
|
return jsonify({"error": "Password must be 4+ chars"}), 400
|
||||||
|
c = store._conn()
|
||||||
|
if c.execute("SELECT id FROM users WHERE username=?", (u,)).fetchone():
|
||||||
|
c.close()
|
||||||
|
return jsonify({"error": "Username taken"}), 409
|
||||||
|
c.close()
|
||||||
|
uid = store.create_user(u, p)
|
||||||
|
return jsonify({"ok": True, "token": store.issue_token(uid), "username": u})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/login", methods=["POST"])
|
||||||
|
def api_login():
|
||||||
|
d = request.get_json(force=True, silent=True) or {}
|
||||||
|
u = (d.get("username") or "").strip().lower()
|
||||||
|
p = (d.get("password") or "").strip()
|
||||||
|
uid = store.authenticate(u, p)
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "Invalid username or password"}), 401
|
||||||
|
return jsonify({"ok": True, "token": store.issue_token(uid), "username": u})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/me")
|
||||||
|
def api_me():
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"authenticated": False}), 401
|
||||||
|
return jsonify({"authenticated": True, "profile": store.get_profile(uid),
|
||||||
|
"settings": store.get_settings(uid)})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/profile", methods=["GET", "POST"])
|
||||||
|
def api_profile():
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "auth required"}), 401
|
||||||
|
if request.method == "GET":
|
||||||
|
return jsonify(store.get_profile(uid))
|
||||||
|
d = request.get_json(force=True, silent=True) or {}
|
||||||
|
prof = store.set_profile(uid, display_name=d.get("display_name"),
|
||||||
|
avatar=d.get("avatar"), about_me=d.get("about_me"))
|
||||||
|
return jsonify(prof)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/profile/pic", methods=["POST"])
|
||||||
|
def api_profile_pic():
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "auth required"}), 401
|
||||||
|
f = request.files.get("file")
|
||||||
|
if not f or not f.filename:
|
||||||
|
return jsonify({"error": "no file"}), 400
|
||||||
|
data = f.read()
|
||||||
|
if len(data) > 5 * 1024 * 1024:
|
||||||
|
return jsonify({"error": "image too large (max 5 MB)"}), 400
|
||||||
|
ext = os.path.splitext(f.filename)[1].lower()
|
||||||
|
if ext not in (".png", ".jpg", ".jpeg", ".gif", ".webp"):
|
||||||
|
return jsonify({"error": "unsupported type — use PNG/JPG/GIF/WebP"}), 400
|
||||||
|
up = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static", "uploads")
|
||||||
|
os.makedirs(up, exist_ok=True)
|
||||||
|
fn = f"u{uid}{ext}"
|
||||||
|
with open(os.path.join(up, fn), "wb") as out:
|
||||||
|
out.write(data)
|
||||||
|
prof = store.set_profile(uid, profile_pic=f"/static/uploads/{fn}")
|
||||||
|
return jsonify(prof)
|
||||||
|
|
||||||
|
|
||||||
|
# ── Document vault ──
|
||||||
|
@app.route("/api/files", methods=["GET", "POST"])
|
||||||
|
def api_files():
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "auth required"}), 401
|
||||||
|
if request.method == "GET":
|
||||||
|
return jsonify({"categories": FILE_CATEGORIES, "files": store.list_files(uid)})
|
||||||
|
cat = request.form.get("category", "Other")
|
||||||
|
if cat not in FILE_CATEGORIES:
|
||||||
|
cat = "Other"
|
||||||
|
f = request.files.get("file")
|
||||||
|
if not f or not f.filename:
|
||||||
|
return jsonify({"error": "no file"}), 400
|
||||||
|
data = f.read()
|
||||||
|
if len(data) > 25 * 1024 * 1024:
|
||||||
|
return jsonify({"error": "file too large (max 25 MB)"}), 400
|
||||||
|
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
||||||
|
ext = os.path.splitext(f.filename)[1].lower()[:12]
|
||||||
|
fn = f"{uid}_{uuid.uuid4().hex[:12]}{ext}"
|
||||||
|
with open(os.path.join(UPLOAD_DIR, fn), "wb") as out:
|
||||||
|
out.write(data)
|
||||||
|
fid = store.add_file(uid, cat, fn, f.filename, f.mimetype or "", len(data))
|
||||||
|
threading.Thread(target=_reindex_user, args=(uid,), daemon=True).start()
|
||||||
|
return jsonify({"ok": True, "id": fid, "category": cat})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/files/reindex", methods=["POST"])
|
||||||
|
def api_files_reindex():
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "auth required"}), 401
|
||||||
|
n = userdocs.build(uid, store.list_files(uid), UPLOAD_DIR)
|
||||||
|
return jsonify({"ok": True, "chunks": n})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/files/status")
|
||||||
|
def api_files_status():
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "auth required"}), 401
|
||||||
|
return jsonify(userdocs.status(uid))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/files/<int:fid>/raw")
|
||||||
|
def api_file_raw(fid):
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "auth required"}), 401
|
||||||
|
rec = store.get_file(uid, fid)
|
||||||
|
if not rec:
|
||||||
|
return jsonify({"error": "not found"}), 404
|
||||||
|
path = os.path.join(UPLOAD_DIR, rec["filename"])
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return jsonify({"error": "missing"}), 404
|
||||||
|
return send_file(path, mimetype=rec["mime"] or "application/octet-stream",
|
||||||
|
as_attachment=False, download_name=rec["orig_name"])
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/files/<int:fid>/text")
|
||||||
|
def api_file_text(fid):
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "auth required"}), 401
|
||||||
|
rec = store.get_file(uid, fid)
|
||||||
|
if not rec:
|
||||||
|
return jsonify({"error": "not found"}), 404
|
||||||
|
path = os.path.join(UPLOAD_DIR, rec["filename"])
|
||||||
|
ext = os.path.splitext(rec["orig_name"])[1].lower()
|
||||||
|
if ext == ".docx":
|
||||||
|
return jsonify({"text": _docx_text(path), "mime": "text/plain"})
|
||||||
|
if ext in (".txt", ".md", ".csv", ".log", ".json", ".html", ".xml"):
|
||||||
|
try:
|
||||||
|
with open(path, "r", encoding="utf-8", errors="replace") as fh:
|
||||||
|
return jsonify({"text": fh.read()[:200000], "mime": "text/plain"})
|
||||||
|
except Exception:
|
||||||
|
return jsonify({"text": "", "mime": "text/plain"})
|
||||||
|
return jsonify({"text": "", "mime": rec["mime"] or ""})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/files/<int:fid>", methods=["DELETE"])
|
||||||
|
def api_file_delete(fid):
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "auth required"}), 401
|
||||||
|
rec = store.get_file(uid, fid)
|
||||||
|
if rec:
|
||||||
|
p = os.path.join(UPLOAD_DIR, rec["filename"])
|
||||||
|
if os.path.exists(p):
|
||||||
|
try:
|
||||||
|
os.remove(p)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
store.delete_file(uid, fid)
|
||||||
|
return jsonify({"ok": True})
|
||||||
|
|
||||||
|
|
||||||
|
# ── Form filling (official WA court forms via pymupdf overlay) ──
|
||||||
|
@app.route("/api/forms")
|
||||||
|
def api_forms():
|
||||||
|
return jsonify(forms.list_forms())
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/forms/autofill", methods=["POST"])
|
||||||
|
def api_forms_autofill():
|
||||||
|
uid = _uid()
|
||||||
|
d = request.get_json(force=True, silent=True) or {}
|
||||||
|
form_id = d.get("form_id", "")
|
||||||
|
if not forms.FORM_NAMES.get(form_id):
|
||||||
|
return jsonify({"error": "unknown form"}), 400
|
||||||
|
values = _autofill_values(uid, form_id)
|
||||||
|
return jsonify({"form_id": form_id, "values": values})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/forms/fill", methods=["POST"])
|
||||||
|
def api_forms_fill():
|
||||||
|
uid = _uid()
|
||||||
|
d = request.get_json(force=True, silent=True) or {}
|
||||||
|
form_id = d.get("form_id", "")
|
||||||
|
values = d.get("values", {})
|
||||||
|
path = forms.fill_form(form_id, values)
|
||||||
|
if not path:
|
||||||
|
return jsonify({"error": "could not fill form"}), 400
|
||||||
|
return send_file(path, mimetype="application/pdf", as_attachment=True,
|
||||||
|
download_name=f"{form_id}_filled.pdf")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/settings", methods=["GET", "POST"])
|
||||||
|
def api_settings():
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "auth required"}), 401
|
||||||
|
if request.method == "GET":
|
||||||
|
return jsonify(store.get_settings(uid))
|
||||||
|
d = request.get_json(force=True, silent=True) or {}
|
||||||
|
s = store.set_settings(uid, **d)
|
||||||
|
# never echo the auth token back in full
|
||||||
|
if "twilio_auth_token" in s:
|
||||||
|
s["twilio_auth_token"] = "••••" if s["twilio_auth_token"] else ""
|
||||||
|
if "smtp_pass" in s:
|
||||||
|
s["smtp_pass"] = "••••" if s["smtp_pass"] else ""
|
||||||
|
return jsonify(s)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/avatars")
|
||||||
|
def api_avatars():
|
||||||
|
return jsonify(AVATARS)
|
||||||
|
|
||||||
|
|
||||||
|
# ── Chat ──
|
||||||
|
@app.route("/api/chat", methods=["POST"])
|
||||||
|
def api_chat():
|
||||||
|
uid = _uid()
|
||||||
|
d = request.get_json(force=True, silent=True) or {}
|
||||||
|
agent_id = (d.get("agent_id") or "navigator").strip()
|
||||||
|
message = (d.get("message") or "").strip()
|
||||||
|
client_history = d.get("history") or []
|
||||||
|
agent = AGENT_BY_ID.get(agent_id)
|
||||||
|
if not agent:
|
||||||
|
return jsonify({"error": "Unknown agent"}), 400
|
||||||
|
if not message:
|
||||||
|
return jsonify({"error": "Message required"}), 400
|
||||||
|
message = message[:4000]
|
||||||
|
profile = store.get_profile(uid) if uid else {"about_me": "", "display_name": ""}
|
||||||
|
settings = store.get_settings(uid) if uid else {}
|
||||||
|
# persisted history for authed users; client history as fallback for anonymous
|
||||||
|
history = store.get_conversation(uid, agent_id) if uid else client_history
|
||||||
|
t0 = time.time()
|
||||||
|
result = _build_answer(agent, message, history, profile.get("about_me", ""),
|
||||||
|
settings, uid, profile.get("display_name", ""))
|
||||||
|
if uid and result.get("answer"):
|
||||||
|
store.save_message(uid, agent_id, "user", message)
|
||||||
|
store.save_message(uid, agent_id, "assistant", result["answer"], result.get("citations"))
|
||||||
|
result["latency_ms"] = round((time.time() - t0) * 1000)
|
||||||
|
result["agent"] = agent_id
|
||||||
|
return jsonify(result)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/conversation/<agent_id>", methods=["GET", "DELETE"])
|
||||||
|
def api_conversation(agent_id):
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "auth required"}), 401
|
||||||
|
if agent_id not in AGENT_BY_ID:
|
||||||
|
return jsonify({"error": "unknown agent"}), 400
|
||||||
|
if request.method == "DELETE":
|
||||||
|
store.clear_conversation(uid, agent_id)
|
||||||
|
return jsonify({"ok": True})
|
||||||
|
return jsonify({"history": store.get_conversation(uid, agent_id)})
|
||||||
|
|
||||||
|
|
||||||
|
# ── Documents ──
|
||||||
|
@app.route("/api/documents/types")
|
||||||
|
def api_doc_types():
|
||||||
|
return jsonify([{"id": k, "name": v["name"], "form": v["form"], "description": v["description"]}
|
||||||
|
for k, v in documents.DOC_TYPES.items()])
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/documents", methods=["POST"])
|
||||||
|
def api_documents():
|
||||||
|
uid = _uid()
|
||||||
|
d = request.get_json(force=True, silent=True) or {}
|
||||||
|
doc_type = (d.get("doc_type") or "").strip()
|
||||||
|
instructions = (d.get("instructions") or "").strip()
|
||||||
|
if doc_type not in documents.DOC_TYPES:
|
||||||
|
return jsonify({"error": "Unknown document type"}), 400
|
||||||
|
profile = store.get_profile(uid) if uid else {"about_me": ""}
|
||||||
|
# retrieve relevant context for grounding
|
||||||
|
top = rag.retrieve(documents.DOC_TYPES[doc_type]["name"], "divorce", top_k=4)
|
||||||
|
ctx = "\n\n".join(f"{c['text']}" for s, c in top)
|
||||||
|
try:
|
||||||
|
text = documents.generate(doc_type, profile.get("about_me", ""), instructions, ctx)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": f"generation failed: {e}"}), 500
|
||||||
|
return jsonify({"ok": True, "document": text,
|
||||||
|
"name": documents.DOC_TYPES[doc_type]["name"]})
|
||||||
|
|
||||||
|
|
||||||
|
# ── TTS ──
|
||||||
|
@app.route("/api/voices")
|
||||||
|
def api_voices():
|
||||||
|
return jsonify({"voices": tts.list_voices(), "default": "aria"})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/speak", methods=["POST"])
|
||||||
|
def api_speak():
|
||||||
|
d = request.get_json(force=True, silent=True) or {}
|
||||||
|
text = (d.get("text") or "").strip()
|
||||||
|
voice = d.get("voice") or "aria"
|
||||||
|
if not text:
|
||||||
|
return jsonify({"error": "no text"}), 400
|
||||||
|
try:
|
||||||
|
path = tts.synthesize(text, voice)
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"error": str(e)}), 500
|
||||||
|
return send_file(path, mimetype="audio/mpeg")
|
||||||
|
|
||||||
|
|
||||||
|
# ── Missions (manual comms) ──
|
||||||
|
@app.route("/api/mission", methods=["POST"])
|
||||||
|
def api_mission():
|
||||||
|
uid = _uid()
|
||||||
|
if not uid:
|
||||||
|
return jsonify({"error": "auth required"}), 401
|
||||||
|
d = request.get_json(force=True, silent=True) or {}
|
||||||
|
mtype = d.get("type")
|
||||||
|
recipient = d.get("recipient") or ""
|
||||||
|
settings = store.get_settings(uid)
|
||||||
|
profile = store.get_profile(uid)
|
||||||
|
if comms.no_contact_blocked(settings, recipient):
|
||||||
|
return jsonify({"ok": False, "error": "Blocked: a no-contact order is in effect for this person."}), 403
|
||||||
|
try:
|
||||||
|
if mtype == "sms":
|
||||||
|
comms.send_sms(settings, recipient, comms.sms_template(d.get("message", "")))
|
||||||
|
elif mtype == "email":
|
||||||
|
body = comms.email_template(profile.get("display_name", ""), recipient,
|
||||||
|
d.get("subject", ""), d.get("body", ""))
|
||||||
|
comms.send_email(settings, recipient, d.get("subject", ""), body)
|
||||||
|
elif mtype == "call":
|
||||||
|
comms.make_call(settings, recipient, request.host_url.rstrip("/") + "/twilio/voice")
|
||||||
|
else:
|
||||||
|
return jsonify({"ok": False, "error": "unknown type"}), 400
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"ok": False, "error": str(e)}), 500
|
||||||
|
return jsonify({"ok": True})
|
||||||
|
|
||||||
|
|
||||||
|
# ── Twilio voice webhook (TwiML <Say>) ──
|
||||||
|
@app.route("/twilio/voice", methods=["GET", "POST"])
|
||||||
|
def twilio_voice():
|
||||||
|
msg = request.values.get("message", "This is an automated message from a legal assistant.")
|
||||||
|
twiml = (f'<?xml version="1.0" encoding="UTF-8"?>'
|
||||||
|
f'<Response><Say voice="Polly.Joanna">{msg}</Say></Response>')
|
||||||
|
return Response(twiml, mimetype="text/xml")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/health")
|
||||||
|
def health():
|
||||||
|
return jsonify({"ok": True, "agent_count": len(AGENTS), "index": rag.index_status()})
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/index/status")
|
||||||
|
def index_status():
|
||||||
|
return jsonify(rag.index_status())
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/api/reindex", methods=["POST"])
|
||||||
|
def reindex():
|
||||||
|
try:
|
||||||
|
idx = rag.build_index()
|
||||||
|
return jsonify({"ok": True, "chunks": len(idx)})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"ok": False, "error": str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
store.init_db()
|
||||||
|
rag.start_background_index()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(host="0.0.0.0", port=5000, threaded=True)
|
||||||
14
astraea-nginx.conf
Normal file
14
astraea-nginx.conf
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:5000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_read_timeout 300;
|
||||||
|
proxy_send_timeout 300;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
astraea.service
Normal file
13
astraea.service
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Astraea — Washington family-law specialist agents (Flask + RAG)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
WorkingDirectory=/opt/astraea
|
||||||
|
ExecStart=/opt/astraea/venv/bin/gunicorn --workers 2 --threads 4 --bind 0.0.0.0:5000 --timeout 240 app:app
|
||||||
|
Restart=always
|
||||||
|
RestartSec=3
|
||||||
|
Environment=PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
92
benchmark_models.py
Normal file
92
benchmark_models.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Astraea model benchmark — feed identical retrieved facts to each candidate model
|
||||||
|
and compare grounded, practical legal answers. Picks the best RAG answer lane.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
sys.path.insert(0, "/opt/astraea")
|
||||||
|
import rag # reuse the retrieval pipeline (embed + cosine top-k)
|
||||||
|
|
||||||
|
OLLAMA = "http://10.30.20.29:11434"
|
||||||
|
OUT = "/opt/astraea/benchmark-results.json"
|
||||||
|
|
||||||
|
MODELS = [
|
||||||
|
"granite4.2:latest",
|
||||||
|
"qwen3.8fast:latest",
|
||||||
|
"ornith-1.5:9b",
|
||||||
|
"gemma4:26b-a4b-it-q4_K_M",
|
||||||
|
"nemotron-3.5-lightning:30b-a3b-q4_K_M",
|
||||||
|
]
|
||||||
|
|
||||||
|
# (agent_id, question) — each has a real answer in the current index.
|
||||||
|
QUESTIONS = [
|
||||||
|
("property", "My spouse bought a house before we married, but we paid the mortgage from our joint account for 15 years. When we divorce, is the house community or separate property?"),
|
||||||
|
("maintenance", "We're divorcing after a 22-year marriage. I stayed home to raise our kids and have no income. Am I entitled to spousal maintenance, and what factors does the court weigh?"),
|
||||||
|
("divorce", "What factors does a Washington court consider when deciding how to divide our property and debts, and can one spouse get more than half?"),
|
||||||
|
("divorce", "I just moved to Washington three weeks ago and want to file for divorce from my spouse who lives in Oregon. Can I file here, and is there a waiting period?"),
|
||||||
|
]
|
||||||
|
|
||||||
|
SYSTEM = (
|
||||||
|
"You are a licensed Washington State family-law attorney giving a real client direct, "
|
||||||
|
"practical legal advice. Base every legal claim on the reference documents. Cite the RCW "
|
||||||
|
"section AND any controlling case law by name. Be specific and actionable: state the rule, "
|
||||||
|
"apply it to the client's facts, and tell them what to do next. Do not hedge or refuse to "
|
||||||
|
"answer. If the exact scenario is not in the documents, give the closest governing principle "
|
||||||
|
"and note it needs attorney review."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def chat(model, system, user):
|
||||||
|
payload = {
|
||||||
|
"model": model,
|
||||||
|
"messages": [{"role": "system", "content": system},
|
||||||
|
{"role": "user", "content": user}],
|
||||||
|
"stream": False,
|
||||||
|
"think": False,
|
||||||
|
"options": {"temperature": 0.2, "num_predict": 1400, "num_ctx": 16384},
|
||||||
|
}
|
||||||
|
req = urllib.request.Request(f"{OLLAMA}/api/chat", data=json.dumps(payload).encode(),
|
||||||
|
headers={"Content-Type": "application/json"})
|
||||||
|
op = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||||
|
with op.open(req, timeout=300) as r:
|
||||||
|
d = json.loads(r.read().decode("utf-8"))
|
||||||
|
m = d.get("message", {})
|
||||||
|
return (m.get("content") or m.get("thinking") or "").strip()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if not rag._INDEX:
|
||||||
|
print("loading index from cache...", flush=True)
|
||||||
|
rag._INDEX.extend(rag.build_index())
|
||||||
|
print(f"index: {len(rag._INDEX)} chunks", flush=True)
|
||||||
|
results = []
|
||||||
|
for agent_id, q in QUESTIONS:
|
||||||
|
top = rag.retrieve(q, agent_id, top_k=5)
|
||||||
|
ctx = "\n\n".join(
|
||||||
|
f"[{i+1}] ({c['source']} — {c['title']}):\n{c['text']}"
|
||||||
|
for i, (s, c) in enumerate(top)
|
||||||
|
)
|
||||||
|
print(f"\n=== Q: {q[:70]}... ({len(top)} chunks) ===", flush=True)
|
||||||
|
for model in MODELS:
|
||||||
|
t0 = time.time()
|
||||||
|
try:
|
||||||
|
ans = chat(model, SYSTEM, f"REFERENCE DOCUMENTS:\n\n{ctx}\n\nQUESTION: {q}")
|
||||||
|
dt = round(time.time() - t0, 1)
|
||||||
|
print(f" {model}: {len(ans)} chars in {dt}s", flush=True)
|
||||||
|
results.append({"question": q, "agent": agent_id, "model": model,
|
||||||
|
"answer": ans, "latency_s": dt,
|
||||||
|
"sources": [c["source"] for s, c in top]})
|
||||||
|
except Exception as e:
|
||||||
|
print(f" {model}: ERROR {e}", flush=True)
|
||||||
|
results.append({"question": q, "agent": agent_id, "model": model, "error": str(e)})
|
||||||
|
json.dump(results, open(OUT, "w"), indent=2)
|
||||||
|
print(f"\n=== done -> {OUT} ===")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
102
books/adoption.md
Normal file
102
books/adoption.md
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
# Washington State Adoption Law — A Practical Guide to Chapter 26.33 RCW
|
||||||
|
|
||||||
|
Adoption in Washington is governed mainly by chapter 26.33 of the Revised Code of Washington (RCW 26.33). Its purpose is to provide stable homes for children, and the guiding principle in every case is **the best interest of the child** (RCW 26.33.010). Adoption cases are heard in **superior court** (RCW 26.33.020(7)), and a petition may be filed in the county where the petitioner lives or where the child lives (RCW 26.33.030).
|
||||||
|
|
||||||
|
## Who Can Adopt and Who Can Be Adopted
|
||||||
|
|
||||||
|
Washington's eligibility rules are broad:
|
||||||
|
|
||||||
|
- **Any person may be adopted, regardless of age or residence** — including adults (RCW 26.33.140(1)).
|
||||||
|
- **Any person who is legally competent and at least 18 years old may be an adoptive parent** (RCW 26.33.140(2)). The statute sets no minimum income, home ownership, or marital requirements. Married petitioners must both join the petition (RCW 26.33.150(4)); single parents may adopt. The decree must state whether the petitioners are spouses, a stepparent, or a single parent (RCW 26.33.250(1)(c)).
|
||||||
|
- An adoption **may not be delayed or denied because of the race, color, or national origin** of the adoptive parent or the child, though an agency may consider a child's cultural background when deciding whether a particular placement is in the child's best interest (RCW 26.33.045).
|
||||||
|
|
||||||
|
## The Home Study (Preplacement Report)
|
||||||
|
|
||||||
|
Washington's "home study" is called a **preplacement report**. In most cases, **a child cannot be placed with prospective adoptive parents until a preplacement report has been filed with the court** (RCW 26.33.180). It is prepared by a licensed adoption agency, the Department of Children, Youth, and Families (DCYF), a court-approved individual, or a qualified court employee (RCW 26.33.190(1)). The report must investigate the **home environment, family life, health, facilities, and resources** of the prospective parent and recommend whether the person is fit to adopt (RCW 26.33.190(2)). It must confirm that key topics were discussed with the family: adoption as a lifelong commitment, a child's possible feelings of loss and identity confusion, sibling relationships, telling the child about the adoption, and the child's racial, ethnic, and cultural heritage (RCW 26.33.190(2)(a)–(f)). It must also include a **background check** — state and national criminal records, including fingerprint-based checks of national databases — plus a review of **child abuse and neglect registries** for every state where the applicants or any other adult in the home lived in the prior five years (RCW 26.33.190(3)). Agencies may charge a reasonable fee, which can be reduced or waived for hardship (RCW 26.33.190(4)). After placement, the court orders a **post-placement report** on the child's health, the home, and whether the placement serves the child's best interest, due within 60 days (RCW 26.33.200).
|
||||||
|
|
||||||
|
**Exceptions:** no reports are required when adopting a spouse's child (step-parent adoption) or when the adoptee is 18 or older — unless the court orders otherwise (RCW 26.33.220).
|
||||||
|
|
||||||
|
## The Adoption Process Step by Step
|
||||||
|
|
||||||
|
1. **Petition.** The prospective adoptive parent files a petition in superior court stating the parties' names and addresses, the child's name, gender, and birth information if known, whether the Indian Child Welfare Act applies, and who has custody. Any signed consents are filed with it (RCW 26.33.150).
|
||||||
|
2. **Preplacement report** (home study) is filed before the child is placed (RCW 26.33.180, RCW 26.33.190).
|
||||||
|
3. **Placement, then post-placement report** — ordered when the petition is filed and due within 60 days (RCW 26.33.200).
|
||||||
|
4. **Hearing.** After the reports are filed, the court schedules a hearing. Hearings are **closed to the public and held without a jury**; contested hearings are recorded, and attendees may not disclose identifying information (RCW 26.33.060). If the consents are valid (or properly dispensed with) and the adoption is in the child's best interest, the court **enters a decree of adoption** (RCW 26.33.240).
|
||||||
|
5. **Decree and birth certificate.** The decree must state the child's original and new names, the adoption type (spouses, stepparent, or single parent), and the information for a new birth certificate (RCW 26.33.250). After the appeal period, the clerk sends a certified copy to the state registrar, who issues a new birth certificate — or forwards it to another state or country's records office if the child was born elsewhere (RCW 26.33.280, RCW 26.33.290).
|
||||||
|
|
||||||
|
Appeals of a decree are decided on an **accelerated (fast) basis**, and later challenges are limited to very narrow grounds — the law intends decrees to be final so families can rely on them (RCW 26.33.260(2)–(4)).
|
||||||
|
|
||||||
|
## Consent and Relinquishment
|
||||||
|
|
||||||
|
**Consent to adoption is required from:** the child, if 14 or older; the child's parents (including an alleged genetic parent) if the child is under 18; the agency or department holding custody, if the child was relinquished to them; and the child's legal guardian (RCW 26.33.160(1)).
|
||||||
|
|
||||||
|
A consent must be **signed under penalty of perjury** and must state that it has no legal effect until a court approves it, that it is revocable before court approval, and that the signer understands their parental rights will be permanently terminated (RCW 26.33.160(4)). A parent or alleged genetic parent must sign in front of an adult witness of their own choosing, identified by name, address, and relationship in the document (RCW 26.33.160(6)). A consent may be valid even if it does not name the adoptive parent (RCW 26.33.160(5)).
|
||||||
|
|
||||||
|
**Relinquishment** is the voluntary surrender of custody (RCW 26.33.020(14)). A parent, the department, or an agency may file a petition to relinquish a child to the department, an agency, or specific prospective adoptive parents, with the parent's written consent attached (RCW 26.33.080). The hearing cannot occur sooner than **48 hours after birth or the signing of the consents, whichever is later**. The court must find the consent validly executed and relinquishment in the child's best interest; if approved, the recipient is appointed legal guardian and the court enters an order terminating the parent-child relationship (RCW 26.33.090).
|
||||||
|
|
||||||
|
**The court must appoint a guardian ad litem for any parent under 18**, who investigates whether the consent was signed voluntarily and with understanding (RCW 26.33.070).
|
||||||
|
|
||||||
|
## Birth Parent Rights and Revoking Consent
|
||||||
|
|
||||||
|
Birth parents have strong procedural protections:
|
||||||
|
|
||||||
|
- A consent **cannot even be presented to the court until 48 hours after it is signed or 48 hours after the child's birth, whichever is later** (RCW 26.33.160(4)(d)).
|
||||||
|
- A consent is **revocable at any time before the court approves it** — a written revocation delivered or mailed to the court clerk is enough (RCW 26.33.160(2)(a)).
|
||||||
|
- After approval, revocation is very limited: within **one year**, only for **fraud or duress** by the person or agency requesting the consent, or for the signer's **lack of mental competency** (RCW 26.33.160(3)). A narrow 48-hour rule also applies: if the parent gave notice of revocation to the agency or person who sought the consent within 48 hours of birth, a written revocation delivered to the clerk within 48 hours after that notice is valid even after approval (RCW 26.33.160(2)(b)).
|
||||||
|
|
||||||
|
If a parent has **not** consented, their rights can be ended only through a **termination of parental rights** petition. The parent must get notice of the hearing at least **20 days** in advance in state, or **30 days** out of state; if personal service is impossible, notice by mail and newspaper publication is required (RCW 26.33.310(2)–(3)). Notice must tell the parent they have a right to a lawyer, including a court-appointed lawyer if indigent (RCW 26.33.110(3)(b)(i)), and notices should be in the parent's native language or through an interpreter if needed (RCW 26.33.310(5)).
|
||||||
|
|
||||||
|
Termination requires **clear, cogent, and convincing evidence** that it is in the child's best interest and that the parent has failed to perform parental duties in a way showing substantial lack of regard for parental obligations while withholding consent (RCW 26.33.120(1)). Termination ends all legal rights and duties between parent and child except past-due child support, and the terminated parent gets no notice of the later adoption (RCW 26.33.130). A natural parent who **unsuccessfully** tries to set aside an adoption must pay the adoptive parents' costs and attorney fees, and a parent who consented cannot bring such an action more than **one year** after the court approved the consent (RCW 26.33.360).
|
||||||
|
|
||||||
|
## Step-Parent Adoption
|
||||||
|
|
||||||
|
Adopting your spouse's child is the most common adoption in Washington, and the law makes it easier:
|
||||||
|
|
||||||
|
- The stepparent files the petition and the child's parent **joins in it** (RCW 26.33.150(4)).
|
||||||
|
- **No home study is required** — preplacement and post-placement reports are waived unless the court orders otherwise (RCW 26.33.220).
|
||||||
|
- The child's other parent must **consent** (RCW 26.33.160(1)(b)) or have their rights **terminated**. The stepparent may file the termination petition themselves when seeking to adopt their spouse's child (RCW 26.33.100(1)(c)). The other parent's consent can be dispensed with only on clear, cogent, and convincing evidence that the adoption is in the child's best interest — for example, a conviction for rape or incest against the child's other parent (RCW 26.33.170).
|
||||||
|
- When the decree is entered, the parent who is **not** married to the adopting stepparent and did not join the petition loses all rights and obligations toward the child, except past-due child support (RCW 26.33.260(1)); the parent who joined keeps their rights.
|
||||||
|
|
||||||
|
If the other parent objects or cannot be found, the case becomes contested and the notice and termination rules described above apply (RCW 26.33.110, RCW 26.33.120, RCW 26.33.310).
|
||||||
|
|
||||||
|
## Adopting an Adult
|
||||||
|
|
||||||
|
Washington allows adoption of adults: any person may be adopted regardless of age or residence (RCW 26.33.140(1)), and for an adoptee 18 or older **no home study or post-placement report is required** (RCW 26.33.220). The adoptee's own consent is required (RCW 26.33.160(1)(a)); the parents' consent is required only for adoptees under 18 (RCW 26.33.160(1)(b)).
|
||||||
|
|
||||||
|
The effect is significant: after the decree, the adopted adult becomes the legal heir of the adoptive parent, with inheritance rights equal to a natural child (RCW 26.33.260(1)), and a lawfully adopted child is **not** an heir of their natural parents for inheritance purposes (RCW 11.04.085). Adult adoption is often used to formalize long-standing family relationships, such as a stepparent and adult stepchild.
|
||||||
|
|
||||||
|
## Open vs. Closed Adoption
|
||||||
|
|
||||||
|
Washington law supports both open and closed adoptions.
|
||||||
|
|
||||||
|
- **Open adoption agreements are allowed and can be enforceable.** Parties may agree on contact between the child, birth parents, and siblings. To be enforceable, the terms must be written into a **court order** approved in writing by the parties, with a court finding the contact is in the child's best interest (RCW 26.33.295(1)–(2)). A violation is **not** a ground to undo the adoption, but the order can be enforced in a civil action with attorney fees to the prevailing party (RCW 26.33.295(3)–(4)).
|
||||||
|
|
||||||
|
**In a closed adoption, records stay sealed.** All records of the proceeding are sealed and can be opened only by court order for good cause (RCW 26.33.330). Files are confidential, but **nonidentifying information** (medical history, heritage, education, circumstances of the adoption, etc.) must be disclosed on request to an adoptive parent, adoptee, or birth parent (RCW 26.33.340; RCW 26.33.020(12)).
|
||||||
|
|
||||||
|
Search and reunion rules:
|
||||||
|
|
||||||
|
- An adopted person **21 or older** (or younger with the adoptive parent's permission), or a birth family member after the adoptee turns 21, may ask the court to appoint a **confidential intermediary** to search for and discreetly contact the other party (RCW 26.33.343).
|
||||||
|
- An adopted person **18 or older** may request a **noncertified copy of their original birth certificate**: for adoptions finalized after October 1, 1993, unless the birth parent filed a valid affidavit of nondisclosure or a contact preference form refusing release; earlier adoptions became available after June 30, 2014. An affidavit of nondisclosure **expires when the birth parent dies** (RCW 26.33.345).
|
||||||
|
- An adopted adult may file a certified statement with the Department of Health consenting to or refusing release of identifying information and whether they wish to be contacted (RCW 26.33.347).
|
||||||
|
|
||||||
|
## Special Situations
|
||||||
|
|
||||||
|
**Indian children.** Every petition must state whether the child is or may be an Indian child; if so, Washington's Indian Child Welfare Act (chapter 13.38 RCW) and the federal ICWA apply, including notice to the child's tribe (RCW 26.33.040). A parent's consent is valid only if signed more than **10 days after birth** and recorded before a court, and it may be withdrawn for any reason **until the final decree** (RCW 26.33.160(4)(h)); relinquishment and termination hearings also cannot occur sooner than 10 days after birth (RCW 26.33.090, RCW 26.33.110). An adoption does not affect rights or benefits an Indian child derives from tribal descent (RCW 26.33.270).
|
||||||
|
|
||||||
|
**Children from foster care.** For a "hard to place" child, the court considers DCYF's adoption support agreement for payments and services to the family (RCW 26.33.320); DCYF adoption support rules are in WAC 388-27.
|
||||||
|
|
||||||
|
**Out-of-state documents.** A consent, relinquishment, or termination order valid where signed, and consistent with due process, is valid in Washington (RCW 26.33.050).
|
||||||
|
|
||||||
|
**Placement reports.** Before placement, the placing party must provide a complete medical report and a family and social history report, with birth parents' identities kept confidential (RCW 26.33.350, RCW 26.33.380).
|
||||||
|
|
||||||
|
## What the Decree of Adoption Changes
|
||||||
|
|
||||||
|
The decree creates a new legal family: the child becomes, "to all intents and purposes," the legal child and heir of the adoptive parent, with the right to inherit from and through them, and is freed from the rights and obligations of the former parents except past-due child support (RCW 26.33.260(1)). Benefits the child receives from third parties, agencies, the state, or the federal government are not lost (RCW 26.33.270), and a new birth certificate is issued showing the adoptive parents (RCW 26.33.250, RCW 26.33.280, RCW 26.33.290).
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
Adoption has permanent consequences, and the state courts website does not post adoption forms — it directs families to DCYF and to attorneys. County superior courts (such as King County) publish self-help adoption packets, and WashingtonLawHelp.org offers free plain-language guidance. Because consents, terminations, and decrees are difficult or impossible to undo later, most people have a Washington family law attorney review their case.
|
||||||
|
|
||||||
|
*Sources: chapter 26.33 RCW (verified against the official text on app.leg.wa.gov); RCW 11.04.085; WAC 388-27 (adoption support rules); Washington State Courts adoption forms page (courts.wa.gov).*
|
||||||
75
books/child-support.md
Normal file
75
books/child-support.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Washington State Child Support
|
||||||
|
|
||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
## The Big Picture
|
||||||
|
|
||||||
|
Every parent in Washington has a legal duty to support their children. Child support is set using the **Washington State Child Support Schedule**, codified in chapter 26.19 RCW, which applies in every county, in court and in administrative proceedings, and for both temporary and permanent orders (RCW 26.19.035(1)). The schedule is a formula, not a judge's guess: it uses an **economic table** tied to the parents' combined monthly net income and the number of children (RCW 26.19.011(1), (5); RCW 26.19.020).
|
||||||
|
|
||||||
|
Support can be set two ways: by a superior court judge or commissioner in a family law case, or **administratively by the Division of Child Support (DCS)**, a state agency that uses the same worksheets and schedule. A DCS order is just as enforceable as a court order (WashingtonLawHelp.org, "How is child support set?", reviewed 06/03/2026).
|
||||||
|
|
||||||
|
## How the Calculation Works, Step by Step
|
||||||
|
|
||||||
|
1. **Determine each parent's gross monthly income.** "Gross income" includes essentially everything: salaries, wages, commissions, bonuses, overtime, second jobs, self-employment income, rent, royalties, dividends, interest, trust income, capital gains, pensions, workers' compensation, unemployment, disability insurance benefits, Social Security benefits, and maintenance (alimony) actually received (RCW 26.19.071(3)).
|
||||||
|
|
||||||
|
2. **Deduct allowed expenses to get net monthly income.** Allowed deductions include federal and state income taxes, FICA, mandatory pension plan payments, mandatory union or professional dues, mandatory state insurance premiums, state industrial insurance premiums, court-ordered maintenance actually paid, up to $5,000 per year in voluntary retirement contributions that show a pre-existing pattern, and normal business expenses and self-employment taxes for self-employed people (RCW 26.19.071(5)).
|
||||||
|
|
||||||
|
3. **Combine the two net incomes and look up the economic table.** The table lists a **basic child support obligation per child** for combined monthly net income from $2,200 up through $50,000+ (RCW 26.19.020). For combined net income of $50,000 or less, the table amount is **presumptive** — the court orders it unless a specific, written reason for deviation applies. Above $50,000 per month combined, the court may order more than the table amount, but only with written findings of fact (RCW 26.19.065(3)).
|
||||||
|
|
||||||
|
4. **Split the obligation in proportion to income.** Each parent's share of the basic obligation equals their share of the combined net income. The parent with the child most of the time satisfies their share by having the child live with them; the other parent makes a monthly "support transfer payment" (RCW 26.19.080(1); RCW 26.19.011(10)).
|
||||||
|
|
||||||
|
**Verification and paperwork.** Both parents must provide the preceding two years' tax returns and current pay stubs, and must complete **child support worksheets** under penalty of perjury. The court will not accept incomplete worksheets, and every support order must state both the standard calculation amount and the amount actually ordered, with written findings for any deviation (RCW 26.19.071(2); RCW 26.19.035(3)–(4)).
|
||||||
|
|
||||||
|
## What Counts as Income — and What Doesn't
|
||||||
|
|
||||||
|
Income of a **new spouse or domestic partner**, income of other adults in the household, child support received for other children, gifts, prizes, TANF, SSI, food stamps, and similar public assistance are **excluded** from gross income — though they must still be disclosed. Likewise, overtime or second-job income beyond 40 hours per week can be excluded when it is worked to provide for a current family's needs or to retire past relationship or child support debt, if the court finds the income will stop once the debts are paid (RCW 26.19.071(4)).
|
||||||
|
|
||||||
|
**Imputed income.** A parent who is voluntarily unemployed or voluntarily underemployed can have income **imputed** to them — that is, the court counts income they could be earning. The law gives a priority order: full-time earnings at the current rate of pay; then at a historical rate (using Employment Security data); then a past rate; then 32 hours per week at minimum wage for a parent recently on public assistance or released from incarceration; then full-time at minimum wage for someone with no meaningful earnings history; and finally census median income. Income is **not** imputed to a parent who is gainfully employed full-time unless the court finds they are *purposely* underemployed to reduce support, and it is not imputed to an unemployable parent (RCW 26.19.071(6)).
|
||||||
|
|
||||||
|
## Expenses Beyond the Basic Amount
|
||||||
|
|
||||||
|
The economic table does **not** include health care costs, day care, or certain special expenses. These are added on top and shared in the same proportion as the basic obligation:
|
||||||
|
|
||||||
|
- **Health care costs** — medical, dental, orthodontia, vision, chiropractic, mental health treatment, and prescriptions (RCW 26.19.080(2)).
|
||||||
|
- **Day care and special child-rearing expenses** — such as tuition and long-distance transportation for visits (RCW 26.19.080(3)).
|
||||||
|
- **Medical support** — every child support order must require both parents to provide medical support: health care coverage that is accessible and available at reasonable cost, plus cash medical support (a proportionate share of premiums, capped at 25% of the basic support obligation) and a share of uninsured medical expenses (RCW 26.09.105). Coverage can be employer or union insurance or public health coverage (RCW 26.09.105(2); RCW 26.18.170).
|
||||||
|
|
||||||
|
## Deviations From the Standard Calculation
|
||||||
|
|
||||||
|
The standard calculation is presumed correct, but the court may deviate for specific, statutorily recognized reasons — and must state them in written findings. Recognized reasons include a **significant amount of residential time** with the paying parent; children from other relationships; extraordinary debt not voluntarily incurred; special needs of disabled children; special medical, educational, or psychological needs; nonrecurring income (like bonuses or overtime); possession of substantial wealth; significant disparity in living costs; and tax-planning considerations. The parents' mere agreement is **not** by itself a valid reason to deviate (RCW 26.19.075).
|
||||||
|
|
||||||
|
## Limits and Safety Valves
|
||||||
|
|
||||||
|
The schedule contains built-in protections for low- and moderate-income payers (RCW 26.19.065):
|
||||||
|
|
||||||
|
- **Self-support reserve.** Support generally cannot reduce the paying parent's net income below **180% of the federal poverty guideline for a one-person family** — except for the presumptive minimum payment (below).
|
||||||
|
- **Presumptive minimum.** When a parent's net income is below 180% of the federal poverty guideline, the order is generally **$50 per child per month** unless the parent proves even that would be unjust in their case.
|
||||||
|
- **45% cap.** A parent's total support obligation for all their children cannot exceed **45% of net income** except for good cause (such as substantial wealth, day care expenses, or special medical/educational needs).
|
||||||
|
- **Ability to pay.** For a parent with public assistance income or who is incarcerated with no assets, support can be set at $0 in appropriate circumstances (WashingtonLawHelp.org, "How is child support set?").
|
||||||
|
|
||||||
|
## How Long Does Support Last?
|
||||||
|
|
||||||
|
Child support continues until the child is **emancipated** — which in practice means age 18, unless the order extends it so a child still in high school can **finish high school** (RCW 26.09.170(3), (8)(b)). Support can also be ordered for a **dependent adult child** under RCW 26.09.100 (RCW 26.19.035(1)(f)).
|
||||||
|
|
||||||
|
**Postsecondary (college) support** is possible but is **advisory, not mandatory**: the court decides whether a dependent child's postsecondary educational expenses should be supported, looking at the parents' expectations, resources, and education levels, and the child's aptitudes. To receive it, the child must enroll in an accredited academic or vocational school, actively pursue study, stay in good academic standing, and share academic records with both parents. Support generally ends at the child's **23rd birthday** except for exceptional circumstances, and payments are usually made directly to the school (RCW 26.19.090).
|
||||||
|
|
||||||
|
## Changing (Modifying) a Support Order
|
||||||
|
|
||||||
|
- **Substantial change of circumstances.** Either party can petition to modify support at any time based on a substantially changed circumstance — but voluntary unemployment or underemployment by itself is not a substantial change (RCW 26.09.170(1), (5)).
|
||||||
|
- **24-month adjustment.** Once 24 months have passed since the order (or the last adjustment), either parent can ask for an adjustment based simply on changes in income or changes in the economic table — no substantial change required (RCW 26.09.170(9)).
|
||||||
|
- **Other paths.** Modification is also available after one year for severe economic hardship, to extend support through high school, or to add an automatic adjustment provision; and a change of more than 30% that causes hardship can be phased in two equal steps (RCW 26.09.170(8)–(9)).
|
||||||
|
- **Incarceration.** A parent serving a sentence longer than six months can seek to abate support to $10 per month per order, with restoration available after release (RCW 26.09.170(6)–(7); see also RCW 26.09.320).
|
||||||
|
- **Procedure.** Modification starts with a petition and worksheets (a $20 filing fee applies to dissolution cases); the responding party has 20 days to answer (60 if out of state) or face default; and the matter is usually decided on affidavits and worksheets rather than live testimony (RCW 26.09.175).
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Support orders are enforced through chapter 26.18 RCW and, for cases handled by the state, chapter 74.20A RCW. The **Division of Child Support (DCS)** can collect support through income withholding, intercepts, license actions, and other remedies, and the state is subrogated to the family's right to support when public assistance is paid (RCW 74.20A.030). Parents can also apply to DCS for enforcement services at any time (RCW 26.18.170(5)). All support orders must be registered with the **Washington State Support Registry** with a confidential information form (RCW 26.23.050).
|
||||||
|
|
||||||
|
## Where to Get Help
|
||||||
|
|
||||||
|
- **Washington State Child Support Schedule (WSCSS)** — the definitions, standards, instructions, economic table, and worksheets, free at courts.wa.gov/forms (RCW 26.19.050).
|
||||||
|
- **Division of Child Support** — dshs.wa.gov/dcs; 1-800-442-KIDS (1-800-442-5437) for enforcement services.
|
||||||
|
- **WashingtonLawHelp.org** — free guides and fillable Child Support Worksheets and Orders (Northwest Justice Project).
|
||||||
|
- **CLEAR legal aid hotline** — 1-888-201-1014 (WashingtonLawHelp.org).
|
||||||
|
|
||||||
|
*Sources: RCW 26.19.011, 26.19.020, 26.19.035, 26.19.050, 26.19.065, 26.19.071, 26.19.075, 26.19.080, 26.19.090, 26.09.100 (cross-reference), 26.09.105, 26.09.170, 26.09.175, 26.09.320 (cross-reference), 26.18, 26.18.170, 26.23.050, 74.20A, all verified against app.leg.wa.gov; WashingtonLawHelp.org "How is child support set?" (06/03/2026). Note: RCW 26.19.110 was repealed and is not cited.*
|
||||||
98
books/committed-relationships.md
Normal file
98
books/committed-relationships.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# Committed Intimate Relationships (CIRs) in Washington State
|
||||||
|
|
||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
This book explains Washington's judge-made law of committed intimate relationships: what a CIR is, how courts decide one exists, how property is divided when one ends, and how a CIR differs from marriage. "Committed intimate relationship" (CIR) and the older term "meretricious relationship" describe the same doctrine.
|
||||||
|
|
||||||
|
## What Is a Committed Intimate Relationship?
|
||||||
|
|
||||||
|
A CIR is "a stable, marital-like relationship where both parties cohabit with knowledge that a lawful marriage between them does not exist" (Connell v. Francisco, 127 Wn.2d 339, 346, 898 P.2d 831 (1995), quoting In re Marriage of Lindsey, 101 Wn.2d 299, 304, 678 P.2d 328 (1984)).
|
||||||
|
|
||||||
|
The doctrine grew out of In re Marriage of Lindsey, 101 Wn.2d 299, 678 P.2d 328 (1984). Before Lindsey, property acquired while living together unmarried was presumed to belong to whoever held title — the "Creasman presumption" (Creasman v. Boyle, 31 Wn.2d 345, 196 P.2d 835 (1948)). Courts carved out exceptions (implied partnership, constructive trust, tracing) to avoid unfair results (In re Marriage of Pennington, 142 Wn.2d 592, 599-600, 14 P.3d 764 (2000)). Lindsey overruled Creasman and required courts to "examine the relationship and the property accumulations and make a just and equitable disposition of the property" (Lindsey, 101 Wn.2d at 304, quoting Latham v. Hennessey, 87 Wn.2d 550, 554, 554 P.2d 1057 (1976)). Connell v. Francisco then set out the modern framework.
|
||||||
|
|
||||||
|
For decades courts called these relationships "meretricious." In Olver v. Fowler, 161 Wn.2d 655, 657 n.1, 168 P.3d 348 (2007), the Supreme Court recognized the term's negative connotation and substituted "committed intimate relationship," which "accurately describes the status of the parties and is less derogatory" (citing Peffley-Warner v. Bowen, 113 Wn.2d 243, 247 n.5, 778 P.2d 1022 (1989)). Some appellate courts also use the neutral phrase "equity relationship" (In re Meretricious Relationship of Long, 158 Wn. App. 919, 922-25, 244 P.3d 26 (2010); In re Domestic Partnership of Walsh, 183 Wn. App. 830, 834 n.1, 335 P.3d 984 (2014)).
|
||||||
|
|
||||||
|
## This Is Judge-Made (Case) Law, Not Statute
|
||||||
|
|
||||||
|
There is no RCW chapter that creates or defines committed intimate relationships. The doctrine is "a judicial one," developed "in the absence of" statute (Olver v. Fowler, 161 Wn.2d 655, 671 (2007) (Sanders, J., dissenting)). It is a line of Washington Supreme Court and Court of Appeals decisions, mainly Lindsey (1984), Connell (1995), Pennington (2000), and Olver (2007).
|
||||||
|
|
||||||
|
What the legislature has done is different: it wrote marriage law and, in 2008, extended community-property rules to state-registered domestic partnerships (RCW 26.16.030; Laws of 2008, ch. 6, § 604, discussed in Walsh, 183 Wn. App. at 845). The marriage dissolution statute, RCW 26.09.080, applies "in a proceeding for dissolution of the marriage or domestic partnership" — by its terms it does not cover unmarried cohabitants. Courts use RCW 26.09.080 and the separate/community definitions in RCW 26.16.010-.030 only "by analogy" (Connell, 127 Wn.2d at 351). Because the doctrine is judge-made, it can change with new Supreme Court decisions.
|
||||||
|
|
||||||
|
## How Courts Decide a CIR Existed: The Connell Factors
|
||||||
|
|
||||||
|
Connell set out five non-exclusive factors for deciding whether a relationship is a CIR: (1) continuous cohabitation, (2) duration of the relationship, (3) purpose of the relationship, (4) pooling of resources and services for joint projects, and (5) the intent of the parties (Connell, 127 Wn.2d at 346; Pennington, 142 Wn.2d at 601-05). Later courts repeat the same five factors (In re Kelly, 170 Wn. App. 722, 733 (2012); Muridan v. Redl, No. 49436-1-II, 413 P.3d 1072 (Wash. Ct. App. Mar. 27, 2018)).
|
||||||
|
|
||||||
|
Key rules about the factors:
|
||||||
|
|
||||||
|
- No single factor decides the case, and the list is not exhaustive (Pennington, 142 Wn.2d at 602).
|
||||||
|
- The factors must not be applied in a "hypertechnical" way; each case turns on its own facts (Muridan, slip op.; Long, 158 Wn. App. at 926).
|
||||||
|
- A relationship does not have to be "long term" to qualify, but duration is "a significant factor" (Connell, 127 Wn.2d at 346). A short relationship can qualify only if several substantial factors are present (Lindsey, 101 Wn.2d at 304-05).
|
||||||
|
- In Pennington, the Supreme Court rejected CIR claims in both consolidated cases: one relationship had sporadic cohabitation, gaps in shared expenses, and one partner married to someone else for years; the other had equivocal intent and little pooling (Pennington, 142 Wn.2d at 603-05).
|
||||||
|
- The doctrine applies to same-sex couples. The Court of Appeals "h[e]ld that the meretricious relationship doctrine should be extended to same-sex couples" in Gormley v. Robertson, 120 Wn. App. 31, 83 P.3d 1042 (2004) (the Supreme Court in Vasquez v. Hawthorne, 145 Wn.2d 103, 33 P.3d 735 (2001), had vacated and remanded a same-sex case on the record, without deciding the merits).
|
||||||
|
- The doctrine can even apply after both partners die, to divide assets between their estates (Olver, 161 Wn.2d at 657).
|
||||||
|
|
||||||
|
Evidence that supports a CIR finding includes: living together continuously, children born or raised together, engagement, shared bank accounts, joint projects, holding out as a family, and naming each other in wills or as insurance beneficiaries (see Connell, 127 Wn.2d at 343-45; Muridan, slip op.).
|
||||||
|
|
||||||
|
## Dividing Property When a CIR Ends
|
||||||
|
|
||||||
|
Once a court finds a CIR, it evaluates each party's interest in property acquired during the relationship and makes a just and equitable division (Connell, 127 Wn.2d at 349). The critical rules:
|
||||||
|
|
||||||
|
- Only property acquired during the relationship is before the court. Property each partner owned before the relationship — and property received during it by gift or inheritance, with its rents and profits — stays with its owner (Connell, 127 Wn.2d at 350-51, applying the definitions in RCW 26.16.010-.030 by analogy).
|
||||||
|
- All property acquired during the relationship is presumed owned by both partners, regardless of whose name is on the title. The presumption can be rebutted with evidence that the asset was bought with separate-property funds (Connell, 127 Wn.2d at 351-52).
|
||||||
|
- The division must be "just and equitable," not necessarily 50/50 (Pennington, 142 Wn.2d at 602; Koher v. Morgan, 93 Wn. App. 398, 968 P.2d 920 (1998)).
|
||||||
|
- A partner's labor is treated like a community asset: earnings during the relationship are relationship property (Koher, 93 Wn. App. at 402).
|
||||||
|
- If relationship funds or labor increase the value of one partner's separate property, the relationship may be entitled to reimbursement for those contributions (Connell, 127 Wn.2d at 352; In re Marriage of Lindemann, 92 Wn. App. 64, 70, 960 P.2d 966 (1998); Walsh, 183 Wn. App. at 842).
|
||||||
|
- Reviewing courts defer to the trial court: whether a CIR existed is reviewed de novo, but the property division is reviewed for abuse of discretion (Muridan, slip op.; Byerley v. Cail, 183 Wn. App. 677, 684-85, 334 P.3d 108 (2014)).
|
||||||
|
- In Muridan, a couple who never married but lived together over six years, had a child, and tried IVF together was found to be in a CIR; assets acquired during the relationship, including an interest in a marijuana business, were divided as "community-like" property (Muridan, slip op.).
|
||||||
|
|
||||||
|
The unjust-enrichment idea drives the whole doctrine: the point is to prevent one partner from keeping everything at the end of the relationship (Pennington, 142 Wn.2d at 602; see also In re Estate of Borghi, 167 Wn.2d 480, 483-84, 219 P.3d 932 (2009)).
|
||||||
|
|
||||||
|
## No Palimony and No Maintenance for Unmarried Partners
|
||||||
|
|
||||||
|
Washington does not recognize "palimony" — court-ordered support for an unmarried ex-partner. Maintenance is a statutory remedy limited to dissolution of marriage or domestic partnership: RCW 26.09.090 authorizes maintenance orders "for either spouse or either domestic partner" in dissolution proceedings. In Connell, the Supreme Court stressed that a CIR "is not the same as a marriage" and that marital property laws "do not directly apply" to unmarried couples (Connell, 127 Wn.2d at 349). Courts likewise held that attorney-fee awards under RCW 26.09.140, available in dissolution actions, are not available in CIR cases (Western Community Bank v. Helmer, 48 Wn. App. 694, 699, 740 P.2d 359 (1987); Foster v. Thilges, 61 Wn. App. 880, 888, 812 P.2d 523 (1991)).
|
||||||
|
|
||||||
|
## The Three-Year Deadline to File
|
||||||
|
|
||||||
|
A claim to divide CIR property is an equitable claim governed by the three-year statute of limitations in RCW 4.16.080(3) (actions "upon a contract or liability, express or implied, which is not in writing"). The clock starts when the CIR ends, not when one partner later "ousts" the other or when a specific asset is sold. That rule comes from In re Kelly, 170 Wn. App. 722, 734 (2012), where the court held that a CIR claim "accrues when a CIR ends." Courts may also dismiss stale claims under the doctrine of laches (Kelly, 170 Wn. App. at 731). Practical rule: sue within three years of the breakup or cohabitation ending.
|
||||||
|
|
||||||
|
## Cohabitation Agreements ("Living Together Contracts")
|
||||||
|
|
||||||
|
Unmarried couples can control their own property rights by written agreement. Washington courts have long divided property under contract principles when couples made express agreements, including agreements made before or during cohabitation (Foster v. Thilges, 61 Wn. App. at 884-86; see the contract-theory exception discussed in Pennington, 142 Wn.2d at 599-600). A written agreement can decide who owns what, how expenses are shared, what happens to jointly bought items, and whether any support-like payments will be made at the end. It can also waive or limit CIR claims. Such contracts are generally enforceable like any contract, subject to ordinary defenses (fraud, duress, unconscionability) and to the statute of frauds for real property transfers. WashingtonLawHelp.org (Northwest Justice Project) publishes a plain-language guide and form called the "Living Together Contract" for unmarried partners ("Make a Living Together Contract," reviewed June 23, 2025). An agreement signed while the relationship is healthy is the most reliable way to avoid CIR litigation later.
|
||||||
|
|
||||||
|
## How a CIR Differs From Marriage
|
||||||
|
|
||||||
|
Washington does not have common-law marriage; a valid marriage requires a license and ceremony (WashingtonLawHelp.org, "Ending your relationship when you're not married," reviewed Apr. 24, 2026). A CIR is deliberately not marriage:
|
||||||
|
|
||||||
|
- Property: married couples hold community property by statute (RCW 26.16.030), and in divorce all property — community and separate — is before the court (RCW 26.09.080). In a CIR, only community-like property acquired during the relationship is divided (Connell, 127 Wn.2d at 350-51).
|
||||||
|
- Support: spouses can receive maintenance (RCW 26.09.090); CIR partners cannot.
|
||||||
|
- Inheritance: a surviving spouse inherits automatically if there is no will (RCW 11.04.015). A CIR partner has no intestate rights; the Supreme Court confirmed an unmarried partner is not a "surviving spouse" for inheritance purposes (Peffley-Warner v. Bowen, 113 Wn.2d at 250).
|
||||||
|
- Other statutory rights: unmarried cohabitants are not "spouses" for unemployment marital-status benefits (Davis v. Dep't of Employment Sec., 108 Wn.2d 272, 278-79, 737 P.2d 1262 (1987)), wrongful-death claims (Roe v. Ludtke Trucking, Inc., 46 Wn. App. 816, 732 P.2d 1021 (1987)), or dissolution attorney fees (Helmer, 48 Wn. App. at 699).
|
||||||
|
- Status and deadlines: marriage and registered domestic partnership (RCW 26.60) are formal, registered statuses with full statutory rights; a CIR is a fact-based equitable status with a three-year filing deadline (RCW 4.16.080(3); Kelly, 170 Wn. App. at 734). Registered domestic partners are "treated the same as married spouses" for all purposes under state law (RCW 26.60.015). There is no time limit to file for divorce; CIR claims expire three years after the relationship ends.
|
||||||
|
|
||||||
|
## Practical Takeaways
|
||||||
|
|
||||||
|
- If you lived together in a marriage-like relationship and acquired property together, you may have CIR rights even though you never married (Connell, 127 Wn.2d 339).
|
||||||
|
- Keep records: who paid for what, joint accounts, joint projects, and how you held yourselves out as a couple matter for the five Connell factors.
|
||||||
|
- Act within three years of the end of the relationship (RCW 4.16.080(3); Kelly, 170 Wn. App. 722).
|
||||||
|
- You cannot get maintenance or automatic inheritance without marriage or a registered domestic partnership.
|
||||||
|
- A written cohabitation agreement is the best way to control the outcome in advance (WashingtonLawHelp.org, "Make a Living Together Contract").
|
||||||
|
- If you jointly own real estate but never had a CIR, partition or quiet-title actions may still be available (WashingtonLawHelp.org, "Ending your relationship when you're not married").
|
||||||
|
|
||||||
|
## Key Cases at a Glance
|
||||||
|
|
||||||
|
- In re Marriage of Lindsey, 101 Wn.2d 299, 678 P.2d 328 (1984) — created the doctrine; overruled the title-holder presumption.
|
||||||
|
- Connell v. Francisco, 127 Wn.2d 339, 898 P.2d 831 (1995) — defined the relationship, listed the five factors, created the community-like presumption, limited division to relationship-acquired property.
|
||||||
|
- In re Marriage of Pennington, 142 Wn.2d 592, 14 P.3d 764 (2000) — applied the factors strictly; no CIRs found in either case.
|
||||||
|
- Gormley v. Robertson, 120 Wn. App. 31, 83 P.3d 1042 (2004) — doctrine extended to same-sex couples (after Vasquez v. Hawthorne, 145 Wn.2d 103, 33 P.3d 735 (2001), remanded without a merits ruling).
|
||||||
|
- Olver v. Fowler, 161 Wn.2d 655, 168 P.3d 348 (2007) — adopted the term "committed intimate relationship"; doctrine applied between deceased partners' estates.
|
||||||
|
- In re Kelly, 170 Wn. App. 722 (2012) — three-year statute of limitations (RCW 4.16.080(3)); claim accrues when the CIR ends.
|
||||||
|
- In re Domestic Partnership of Walsh, 183 Wn. App. 830, 335 P.3d 984 (2014) — CIR factors and the limits of the community-property analogy restated.
|
||||||
|
- Muridan v. Redl, No. 49436-1-II, 413 P.3d 1072 (Wash. Ct. App. Mar. 27, 2018) — modern application of the five factors; community-like property divided after a six-plus-year relationship.
|
||||||
|
- Peffley-Warner v. Bowen, 113 Wn.2d 243, 778 P.2d 1022 (1989) — no intestate inheritance rights for unmarried partners.
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- Washington Supreme Court and Court of Appeals opinions as cited above (full texts reviewed via CourtListener bulk data and the Washington State Courts opinion archive).
|
||||||
|
- RCW 26.09.080, 26.09.090, 26.09.140, 26.16.010-.030, 26.16.220, 26.04.010, 26.60.015, 26.60.030, 4.16.080, 11.04.015 (leg.wa.gov).
|
||||||
|
- WashingtonLawHelp.org (Northwest Justice Project): "Ending your relationship when you're not married" (reviewed Apr. 24, 2026); "Make a Living Together Contract" (reviewed June 23, 2025).
|
||||||
|
- Stokes Lawrence, "How Washington Recognizes Property Rights Between Unmarried Partners" (2023); University of Washington Law Review, "An Analysis of Cohabitant Property Rights under Connell v. Francisco" (1999).
|
||||||
99
books/custody-parenting.md
Normal file
99
books/custody-parenting.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# Washington State Child Custody and Parenting Plans
|
||||||
|
|
||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
## The Big Picture
|
||||||
|
|
||||||
|
In Washington, parents in a divorce (dissolution), legal separation, or parentage case usually get a court order called a **Parenting Plan**. Courts here generally do not use the words "custody" and "visitation." Instead, a parenting plan covers two things: (1) a **residential schedule** — which parent the child lives with on given days, including holidays, vacations, and birthdays — and (2) **decision-making authority** — who makes major decisions about the child's education, health care, and religious upbringing (RCW 26.09.184(2), (5)–(6)).
|
||||||
|
|
||||||
|
The governing law is the Parenting Act of 1987, now codified in chapter 26.09 RCW. The state's core policy: "the best interests of the child shall be the standard by which the court determines and allocates the parties' parental responsibilities," and the relationship between the child and each parent "should be fostered unless inconsistent with the child's best interests." The statute is explicit that **residential time and financial support are equally important** parts of parenting arrangements (RCW 26.09.002).
|
||||||
|
|
||||||
|
## What a Parenting Plan Must Contain
|
||||||
|
|
||||||
|
By law, a permanent parenting plan must include provisions for resolving future disputes between the parents, allocation of decision-making authority, and a residential schedule that says in which parent's home each child will reside on given days of the year — including holidays, family birthdays, vacations, and other special occasions (RCW 26.09.184(2), (6)).
|
||||||
|
|
||||||
|
The plan also spells out a **dispute resolution process** other than court action — for example counseling, mediation, or arbitration. Preference is given to carrying out the existing plan, and a parent who misuses or frustrates the process without good reason can be ordered to pay the other parent's attorney fees and financial sanctions (RCW 26.09.184(4)).
|
||||||
|
|
||||||
|
Two practical points built into the law:
|
||||||
|
|
||||||
|
- Either parent may make **emergency decisions** affecting the child's health or safety regardless of how decision-making is allocated (RCW 26.09.184(5)(a)).
|
||||||
|
- Each parent makes **day-to-day decisions** while the child is with that parent (RCW 26.09.184(5)(b)).
|
||||||
|
- If one parent violates the plan, that does **not** excuse the other parent from following it; noncompliance can lead to a **contempt of court** finding (RCW 26.09.184(7); RCW 26.09.160).
|
||||||
|
|
||||||
|
## How a Judge Decides the Residential Schedule
|
||||||
|
|
||||||
|
When parents cannot agree, the court builds the residential schedule from the factors in RCW 26.09.187(3). Washington law gives **the greatest weight to one factor: the relative strength, nature, and stability of the child's relationship with each parent**. The other factors are:
|
||||||
|
|
||||||
|
- The parents' agreements (if made knowingly and voluntarily);
|
||||||
|
- Each parent's past and potential future performance of **parenting functions** — defined in RCW 26.09.004 — including which parent took greater responsibility for the child's daily needs;
|
||||||
|
- The child's emotional needs and developmental level;
|
||||||
|
- The child's relationship with siblings and other significant adults, and involvement with school and activities;
|
||||||
|
- The wishes of the parents and of a child who is mature enough to express reasoned, independent preferences; and
|
||||||
|
- Each parent's employment schedule, with accommodations consistent with those schedules (RCW 26.09.187(3)(a)).
|
||||||
|
|
||||||
|
Washington does not presume any particular split of time, and the court must not draw presumptions from any temporary parenting plan entered earlier in the case (RCW 26.09.191(2)(a)).
|
||||||
|
|
||||||
|
## Safety Limits and Restrictions
|
||||||
|
|
||||||
|
Parents are responsible for protecting their children's health and well-being, and RCW 26.09.191 requires or allows the court to **limit a parent's residential time** when certain conduct exists.
|
||||||
|
|
||||||
|
**Conduct that requires limits** (RCW 26.09.191(4)(a)–(b)):
|
||||||
|
|
||||||
|
- Willful abandonment that continues for an extended period;
|
||||||
|
- Physical abuse, or a pattern of emotional abuse, of a child;
|
||||||
|
- A history of acts of domestic violence (as defined in RCW 7.105.010), an assault causing grievous bodily harm or fear of such harm, or any sexual assault;
|
||||||
|
- Sexual abuse of a child; and
|
||||||
|
- Knowingly residing with a person who has engaged in such conduct.
|
||||||
|
|
||||||
|
**Conduct that may lead to limits** (RCW 26.09.191(4)(c)) includes neglect or substantial nonperformance of parenting functions; long-term emotional or physical impairment; long-term drug or alcohol impairment; absence or substantial impairment of emotional ties; "abusive use of conflict" that endangers the child's psychological development; and withholding the child from the other parent for a protracted period without good cause. Note: reporting abuse or seeking a protection order in good faith ("protective actions") does not count as abusive use of conflict (RCW 26.09.191(3)(a), (e)).
|
||||||
|
|
||||||
|
**What the court can order** (RCW 26.09.191(4)(d)):
|
||||||
|
|
||||||
|
- **Supervised visitation**, with a presumption that supervision is by a **professional supervisor** — a lay supervisor is allowed only in limited circumstances (geographic isolation or demonstrated indigency) (RCW 26.09.191(4)(d)(i)(A));
|
||||||
|
- **Evaluations or treatment** (for domestic violence perpetration, substance use, mental health, or anger management), with residential time possibly conditioned on completion;
|
||||||
|
- **No contact** at all, if limits would not adequately protect the child.
|
||||||
|
|
||||||
|
When a parent has committed abandonment, abuse, domestic violence, or assault, the court must also order **sole decision-making** for the other parent and **no dispute resolution except court action** (RCW 26.09.191(5)). Where domestic violence has been found, there is a rebuttable presumption of sole decision-making, and the court must **not require face-to-face mediation** or other interventions that force the parties into the same space (RCW 26.09.191(6)(b)). The court may decline to impose these limits only with express written findings based on **clear and convincing evidence**, addressing specific factors such as current risk and the parent's acknowledgment of past harm (RCW 26.09.191(6)). Cases involving a parent convicted of a sex offense against a child are governed by the stricter rules of RCW 26.09.192.
|
||||||
|
|
||||||
|
## Temporary Orders While the Case Is Pending
|
||||||
|
|
||||||
|
Family law cases can take many months. While the case is pending, either party may ask for **temporary orders** — including temporary support and orders restraining a party from removing a child from the court's jurisdiction, or from disturbing the other party (RCW 26.09.060). Emergency (ex parte) orders issued without notice to the other side may last no more than 14 days, or up to 24 days by court order in specified circumstances (RCW 26.09.060(3)).
|
||||||
|
|
||||||
|
## Moving With a Child (Relocation)
|
||||||
|
|
||||||
|
Washington has a detailed relocation statute, RCW 26.09.405 through 26.09.560, that applies to parenting plans and residential orders. If you want to move with the child, you generally must give the other parent (and anyone else with court-ordered residential time) a **written notice of intended relocation at least 60 days before the move**, served personally or by mail requiring a return receipt. If you did not know the required information in time, notice must be given no more than 5 days after you learn it. The notice must state the reasons for the move, the new address (or as much as is known), and a warning — in exact statutory language — that the other person has **30 days to file and serve an objection**, or the relocation will be permitted (RCW 26.09.440).
|
||||||
|
|
||||||
|
A person objecting must file an objection (a petition to modify the parenting plan or similar proceeding) **within 30 days** of receiving the notice. After an objection is filed, the relocating person generally cannot move the child's principal residence without a court order while the objection period or a scheduled hearing is pending (RCW 26.09.480).
|
||||||
|
|
||||||
|
There is a **rebuttable presumption that the relocation will be permitted**. The objecting person must show the detrimental effect of the move outweighs its benefit, using 11 unweighted factors in RCW 26.09.520 — including the strength of the child's relationships, the good faith of each party, the child's age and needs, the quality of life and opportunities in each location, and the feasibility of the other parent also relocating (RCW 26.09.520).
|
||||||
|
|
||||||
|
## Changing (Modifying) a Parenting Plan
|
||||||
|
|
||||||
|
Final parenting plans are meant to be stable. The court will not modify one unless, based on facts arising since the plan (or unknown at the time), there has been a **substantial change in the circumstances of the child or the nonmoving parent, and the modification is necessary to serve the child's best interests** (RCW 26.09.260(1)). The court keeps the existing residential schedule unless the parents agree, the child has been integrated into the petitioner's family with the other parent's consent, the child's present environment is detrimental, or the nonmoving parent has been found in contempt at least twice in three years for failing to comply with residential provisions or convicted of custodial interference (RCW 26.09.260(2)–(3)).
|
||||||
|
|
||||||
|
There are important exceptions:
|
||||||
|
|
||||||
|
- **Minor adjustments** — changes of no more than 24 days per year, changes driven by the other parent's move or involuntary work-schedule change, or increases up to 90 overnights per year in limited cases — can be made on a showing of substantial change without the full best-interests inquiry (RCW 26.09.260(5)).
|
||||||
|
- **Relocation-based changes** proceed under the relocation statute without a separate adequate-cause hearing (RCW 26.09.260(6)).
|
||||||
|
- The court may **reduce or restrict contact** at any time if needed to protect the child, using the limitation criteria of RCW 26.09.191 and 26.09.192 (RCW 26.09.260(4)).
|
||||||
|
|
||||||
|
## Unmarried Parents
|
||||||
|
|
||||||
|
If the parents were never married, legal parentage must generally be established before a parenting plan can be entered. Parentage is established by a voluntary **Acknowledgment of Parentage**, or through a court proceeding to adjudicate parentage under Washington's Uniform Parentage Act, chapter 26.26A RCW. An order determining parentage can include child support and, "on the same basis as provided in chapter 26.09 RCW," residential provisions — though a full parenting plan is not required unless a party asks for one (RCW 26.26B.020(6)–(7)). After an acknowledgment is beyond rescission, a parent can start a judicial proceeding "In re the parenting and support of [child]" to obtain a parenting plan and support order (RCW 26.26B.130).
|
||||||
|
|
||||||
|
## Non-Parents: Guardianship and Visitation
|
||||||
|
|
||||||
|
A person who is **not a legal parent cannot get a parenting plan** in Washington. Since 2021, what used to be called "non-parent custody" is obtained through a **minor guardianship** case under chapter 11.130 RCW, where the court can award a residential schedule for the child. A relative (such as a grandparent) who wants the right to visit a child can separately petition for **nonparental visitation** under chapter 26.11 RCW ("Nonparental child visitation — relatives") (WashingtonLawHelp.org, Parenting Plan Guide, reviewed 07/25/2025).
|
||||||
|
|
||||||
|
## Which Court Can Hear the Case (Jurisdiction)
|
||||||
|
|
||||||
|
Washington can make an initial custody determination only if it is the child's **home state** — generally where the child has lived with a parent or person acting as a parent for the six months before the case is filed — or under limited fallback rules involving significant connections and substantial evidence (RCW 26.27.201). If the child has lived in another state within the last six months, or another state has already issued custody orders, talk to a lawyer before filing (WashingtonLawHelp.org, Parenting Plan Guide).
|
||||||
|
|
||||||
|
## Where to Get Help
|
||||||
|
|
||||||
|
- **WashingtonLawHelp.org** — free plain-language guides and form packets (Northwest Justice Project), including the Parenting Plan Guide and Relocation Guide.
|
||||||
|
- **courts.wa.gov/forms** — official pattern forms for parenting plans, temporary orders, and modifications.
|
||||||
|
- **legalvoice.org** (Legal Voice) and **WSBA.org** (Washington State Bar Association) — referral services and legal resources.
|
||||||
|
- **Washington State CLEAR hotline 1-888-201-1014** — free legal aid intake (WashingtonLawHelp.org).
|
||||||
|
|
||||||
|
*Sources: RCW 26.09.002, 26.09.004, 26.09.060, 26.09.160, 26.09.184, 26.09.187, 26.09.191, 26.09.192, 26.09.260, 26.09.405–26.09.560 (including 26.09.440, 26.09.480, 26.09.520), 26.27.201, 26.26A, 26.26B.020, 26.26B.130, 26.11, 11.130, all verified against app.leg.wa.gov; WashingtonLawHelp.org Parenting Plan Guide (07/25/2025).*
|
||||||
158
books/divorce-case-law-characterization.md
Normal file
158
books/divorce-case-law-characterization.md
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
# Washington State Divorce Case Law: Characterization, Commingling, and Transmutation
|
||||||
|
|
||||||
|
Washington is a community property state. Under RCW 26.16.030, property acquired during marriage — and property acquired before marriage by either spouse's labor — is community property; everything each spouse owned before marriage, plus gifts and inheritances received during marriage, is separate property (RCW 26.16.010). Two working rules flow from these statutes: **property acquired during marriage is presumed to be community property**, and **the character of property — separate or community — is fixed at the date it was acquired**. At divorce, RCW 26.09.080 requires the court to make a "just and equitable" division of all property, both community and separate. The cases below show how Washington courts apply these rules when money gets mixed, titles get changed, and things appreciate.
|
||||||
|
|
||||||
|
## The Acquisition-Time Rule: Character Is Fixed When Property Is Acquired
|
||||||
|
|
||||||
|
### In re Marriage of Chumbley, 150 Wn.2d 1, 74 P.3d 129 (2003)
|
||||||
|
|
||||||
|
**Rule:** Property acquired during marriage has the same character as the funds used to purchase it, and separate property "will remain separate property through changes and transitions, if the separate property remains traceable and identifiable."
|
||||||
|
|
||||||
|
**Facts:** During her marriage, Mary Beckmann earned stock options through her employment. She exercised the options three ways: once with an employer loan, once with money from her separate account, and once by selling part of the purchased stock to pay for the rest. The dispute was whether the resulting stock was community or separate.
|
||||||
|
|
||||||
|
**Why it matters:** Chumbley is Washington's clearest statement that characterization follows the *source of funds* at acquisition. Because the options were community (earned during marriage) and some of the purchase money was separate, the court held the stock "will be divided upon the rates of separate and community contributions" — a proportional, mortgage-rule-style apportionment. It is also the leading modern citation for the traceability rule: changing an asset's form does not change its character if the separate property can still be traced.
|
||||||
|
|
||||||
|
### Borghi v. Gilroy (In re Estate of Borghi), 167 Wn.2d 480, 219 P.3d 932 (2009)
|
||||||
|
|
||||||
|
**Rule:** "The character of property as separate or community is established at acquisition," and spouses "cannot be deprived of their separate property solely because it is used during the marriage to acquire additional property."
|
||||||
|
|
||||||
|
**Facts:** Jeanette Borghi acquired real property before she married Robert Borghi; the property was later titled in both of their names. After her death, her estate and her son Arthur Gilroy fought over whether the property was community or Jeanette's separate property.
|
||||||
|
|
||||||
|
**Why it matters:** Borghi confirms, at the Supreme Court level, that the separate-property right is "as sacred as is the right in their community property" (quoting Guye v. Guye, 63 Wash. 340, 352, 115 P. 731 (1911)). A spouse cannot lose separate property merely because it was used during the marriage — the inquiry is always back to what the property was at acquisition and whether its separate character can be traced.
|
||||||
|
|
||||||
|
### Guye v. Guye, 63 Wash. 340, 115 P. 731 (1911)
|
||||||
|
|
||||||
|
**Rule:** A spouse's right in his or her separate property is as sacred as the right in community property.
|
||||||
|
|
||||||
|
**Why it matters:** Guye is the 1911 foundation for Washington's protection of separate property, still quoted verbatim by the modern Supreme Court in Borghi. It anchors the rule that use of separate property during marriage does not, by itself, convert it into community property.
|
||||||
|
|
||||||
|
### In re Marriage of Mueller, 140 Wn. App. 498, 167 P.3d 568 (2007)
|
||||||
|
|
||||||
|
**Rule:** A legal claim or cause of action is characterized by when the property interest in it was acquired — not merely when the lawsuit was filed.
|
||||||
|
|
||||||
|
**Facts:** The husband was a qui tam (False Claims Act) whistleblower. The dispute was whether he acquired a property interest in the qui tam action when he learned the material facts (before the 2001 marriage) or only when he filed the lawsuit in 2003 (after the marriage). A separate issue concerned an oral property agreement allegedly made during the marriage.
|
||||||
|
|
||||||
|
**Why it matters:** Mueller shows the acquisition-time rule applied to intangible rights — lawsuits, causes of action, and settlements. In a divorce, the timing of when a right *accrued* can determine whether the recovery is separate or community. It also stands for the practical point that characterization disputes are reviewed de novo on appeal while the underlying findings are reviewed for substantial evidence.
|
||||||
|
|
||||||
|
## Gifts, Inheritance, and Rebutting the Community Presumption
|
||||||
|
|
||||||
|
### Cummings v. Anderson, 94 Wn.2d 135, 614 P.2d 1283 (1980)
|
||||||
|
|
||||||
|
**Rule:** The party claiming property is separate can rebut the community-property presumption by showing the asset was acquired by gift or inheritance, with separate funds or separate credit, or from the traceable rents, issues, and profits of separate property.
|
||||||
|
|
||||||
|
**Why it matters:** Cummings lists the standard rebuttal toolkit in Washington. Any client who received a gift or inheritance during marriage, or bought something with pre-marriage money, must be prepared to show the source and the paper trail — the same three routes Cummings identifies.
|
||||||
|
|
||||||
|
### In re Marriage of Skarbek, 100 Wn. App. 444, 997 P.2d 447 (2000)
|
||||||
|
|
||||||
|
**Rule:** A rebuttable presumption arises that property acquired during marriage with separate funds is a gift to the community — but merely depositing separate funds into a joint bank account is *not* an acquisition of property, so no gift presumption attaches to the deposit itself.
|
||||||
|
|
||||||
|
**Facts:** John Skarbek deposited separate funds into a joint account during the marriage. He was able to trace and identify the separate portions of the account, and the court held those remained separate.
|
||||||
|
|
||||||
|
**Why it matters:** Skarbek is the practical commingling-in-bank-accounts case. The key distinction: buying an asset with separate money raises the gift-to-community presumption; parking separate money in a joint account does not. If the separate dollars can still be identified, they stay separate. If they cannot, the asset or account is treated as community.
|
||||||
|
|
||||||
|
## Commingling and Tracing
|
||||||
|
|
||||||
|
### In re Marriage of White, 105 Wn. App. 545, 20 P.3d 481 (2001)
|
||||||
|
|
||||||
|
**Rule:** "Commingling" of separate and community funds may give rise to a presumption that all of the property is community property; the spouse claiming a separate share must then trace the separate funds with sufficient certainty.
|
||||||
|
|
||||||
|
**Facts:** The dispute concerned funds — including a 401(k) account — acquired partly before and partly after the marriage, with separate and community contributions mixed together.
|
||||||
|
|
||||||
|
**Why it matters:** White is the workhorse citation for what happens when the paper trail disappears. Once separate and community money are mixed beyond the point where a court can distinguish or apportion them, the mixed mass loses its separate character and is treated as community. Careful account records are the only defense.
|
||||||
|
|
||||||
|
### Schwarz v. Schwarz, 192 Wn. App. 180, 368 P.3d 173 (2016)
|
||||||
|
|
||||||
|
**Rule:** On dissolution, the trial court must actually characterize each disputed asset — retirement accounts, investment accounts, and similar holdings — as community or separate based on the source of the funds, rather than distributing by label or convenience.
|
||||||
|
|
||||||
|
**Facts:** Susan Champagne challenged the property distribution on dissolution of her marriage to Damian Schwarz, specifically the trial court's characterization and distribution of several retirement and other investment accounts.
|
||||||
|
|
||||||
|
**Why it matters:** Schwarz is the modern appellate reminder that characterization is a legal question courts must decide explicitly. For lawyers, it shows that a decree that skips the tracing analysis — or that assumes an account is all community because it was partly funded with community earnings — is vulnerable on appeal.
|
||||||
|
|
||||||
|
## Transmutation: When Separate Property Becomes Community Property
|
||||||
|
|
||||||
|
Washington recognizes **transmutation**: separate property can become community property when the parties' words or conduct show an intent to make that change — typically a gift of an interest to the community. A change in title alone does not automatically change character, but it can be strong evidence of intent.
|
||||||
|
|
||||||
|
### In re Marriage of Pearson-Maines, 70 Wn. App. 860, 855 P.2d 1210 (1993)
|
||||||
|
|
||||||
|
**Rule:** The character of property (including employee benefits and stock options) is determined when the property is acquired; commingling of separate and community funds may give rise to a presumption that all are community.
|
||||||
|
|
||||||
|
**Facts:** The case involved characterization of employment-related benefits and funds acquired at different points relative to the marriage.
|
||||||
|
|
||||||
|
**Why it matters:** Pearson-Maines is the Court of Appeals case the Supreme Court later relied on in Short for the acquisition-time rule, and it is frequently quoted for the commingling presumption. It bridges the transmutation and tracing doctrines: mixing can shift character, but the starting point is always when the property was acquired.
|
||||||
|
|
||||||
|
### In re Marriage of Olivares, 69 Wn. App. 324, 848 P.2d 1281 (1993)
|
||||||
|
|
||||||
|
**Rule:** Separate property placed into joint title with the other spouse during marriage can transmute into community property where the evidence shows intent to make it community property.
|
||||||
|
|
||||||
|
**Why it matters:** Olivares is commonly cited in the transmutation line of cases for the proposition that joint titling, combined with the parties' conduct, can convert separate property into community property. It is the flip side of the tracing cases: title and conduct matter when the source-of-funds trail is ambiguous.
|
||||||
|
|
||||||
|
### In re Marriage of Sedlock, 69 Wn. App. 484, 849 P.2d 1243 (1993)
|
||||||
|
|
||||||
|
**Rule:** The parties' agreement or course of conduct during the marriage can change the character of property from separate to community (transmutation).
|
||||||
|
|
||||||
|
**Why it matters:** Sedlock, decided the same year as Olivares and Pearson-Maines, rounds out the transmutation trilogy. Together these cases warn that a spouse who keeps separate property segregated and documented preserves its character, while a spouse who treats it as jointly owned may lose the separate label.
|
||||||
|
|
||||||
|
### In re Marriage of Glorfield, 27 Wn. App. 358, 617 P.2d 1051 (1980)
|
||||||
|
|
||||||
|
**Rule:** Separate property may be converted into community property by an effective gift or agreement between the spouses.
|
||||||
|
|
||||||
|
**Why it matters:** Glorfield is the older Court of Appeals authority for transmutation-by-intent. It is still cited for the basic point that characterization is not frozen forever — a valid gift or agreement between spouses changes it.
|
||||||
|
|
||||||
|
## Appreciation of Separate Property
|
||||||
|
|
||||||
|
### In re Marriage of Elam, 97 Wn.2d 811, 650 P.2d 213 (1982)
|
||||||
|
|
||||||
|
**Rule:** "Any increase in the value of separate property is presumed to be separate property," and the presumption "may be rebutted by direct and positive evidence that the increase is attributable to community funds or labors."
|
||||||
|
|
||||||
|
**Facts:** The Elams' dissolution required the court to sort the value of separately owned property that had appreciated during the marriage from any portion of that increase attributable to the marital community's efforts.
|
||||||
|
|
||||||
|
**Why it matters:** Elam sets a deliberately high bar for converting appreciation into community property. The spouse claiming the community share must produce "direct and positive" evidence — speculation that community effort probably helped is not enough. In practice, when community funds or labor *do* increase separate property's value, the community gets a claim for reimbursement, often protected by an equitable lien.
|
||||||
|
|
||||||
|
### In re Marriage of Lindemann, 92 Wn. App. 64, 960 P.2d 966 (1998)
|
||||||
|
|
||||||
|
**Rule:** The rents, income, profits, and increase in the value of separate property are presumed to remain separate; that presumption is overcome only by "direct and positive" evidence that the increase came from community labor or contributions.
|
||||||
|
|
||||||
|
**Why it matters:** Lindemann extends Elam to income streams. Dividends, rents, and passive appreciation of separate assets stay separate in Washington (unlike some community property states). The spouse claiming otherwise must prove the community's causal contribution directly.
|
||||||
|
|
||||||
|
## Stock Options and Other Future Rights
|
||||||
|
|
||||||
|
### In re Marriage of Short, 125 Wn.2d 865, 890 P.2d 12 (1995)
|
||||||
|
|
||||||
|
**Rule:** The characterization of employee stock options as separate or community property under RCW 26.16 depends upon when the options were acquired — options acquired during the marriage are community property.
|
||||||
|
|
||||||
|
**Facts:** Robert Short was a Microsoft employee whose May 1990 and November 1990 stock options were at issue. The trial court apportioned portions of the exercised-option profits and unexercised shares to the wife as community property and gave the rest to Robert as separate. The Court of Appeals reversed in part, holding the options were entirely community property and that the decree's nonmodifiable maintenance provision was invalid; the Supreme Court resolved the characterization question under the acquisition-time rule.
|
||||||
|
|
||||||
|
**Why it matters:** Short is Washington's stock-option case, now applied to RSUs and every other employment-based future right. Options granted during marriage for services performed during marriage are community property even if they vest or are exercised later. The case also produced the practical "time rule"-style apportionment courts use for rights spanning both sides of the marriage.
|
||||||
|
|
||||||
|
## Dividing Separate Property at Divorce
|
||||||
|
|
||||||
|
### In re Marriage of Konzen, 103 Wn.2d 470, 693 P.2d 97 (1985)
|
||||||
|
|
||||||
|
**Rule:** In a dissolution, the trial court may award one spouse's separate property to the other where a just and equitable division requires it; separate property is "no longer entitled to special treatment," and no single factor — such as the character of the property — may be given greater weight as a matter of law.
|
||||||
|
|
||||||
|
**Facts:** Konzen addressed whether a spouse's separate property could be invaded at dissolution, and rejected the argument that such an award was limited to narrowly defined "exceptional" situations.
|
||||||
|
|
||||||
|
**Why it matters:** Konzen is the reason Washington divorce lawyers cannot tell a client, "your separate property is untouchable." RCW 26.09.080 authorizes a just and equitable division of *all* property. The character of the asset still matters enormously — courts generally respect separate property — but it is a factor weighed in the overall division, not a shield.
|
||||||
|
|
||||||
|
### In re Marriage of Littlefield, 133 Wn.2d 39, 940 P.2d 1362 (1997)
|
||||||
|
|
||||||
|
**Rule:** A trial court's property distribution is reviewed for abuse of discretion — "manifestly unreasonable or based on untenable grounds or untenable reasons" — and a disproportionate division must be supported by written findings under RCW 26.09.080.
|
||||||
|
|
||||||
|
**Facts:** The Littlefields' dissolution involved characterization and distribution of the family's assets, including farm property, with a dispute over whether the trial court's award to one spouse was properly supported by findings.
|
||||||
|
|
||||||
|
**Why it matters:** Littlefield is the standard-of-review anchor for every Washington property-division appeal. The spouse challenging a distribution bears a heavy burden on appeal, and trial courts that award property disproportionately must put the supporting findings on the record — without them, the decree is vulnerable to remand.
|
||||||
|
|
||||||
|
## Key Takeaways for Practice
|
||||||
|
|
||||||
|
- **Character is fixed at acquisition** (Chumbley, Borghi); the question is the source of funds, not later titles or forms.
|
||||||
|
- **Presumptions:** property acquired during marriage is presumed community; separate funds used to buy property during marriage are presumed a gift to the community (Skarbek).
|
||||||
|
- **Tracing:** separate property survives changes in form only while it remains "traceable and identifiable"; untraceable mixing means community property (Chumbley, White).
|
||||||
|
- **Transmutation:** separate property can become community through gift, agreement, or conduct showing intent (Glorfield, Sedlock, Olivares).
|
||||||
|
- **Appreciation:** increases in the value of separate property stay separate unless the other side proves community contribution with "direct and positive evidence" (Elam, Lindemann).
|
||||||
|
- **Division:** all property — including separate property — is subject to just and equitable division, with written findings required for disproportionate awards (Konzen, Littlefield; RCW 26.09.080).
|
||||||
|
|
||||||
|
## Research Notes
|
||||||
|
|
||||||
|
Citations were verified against CourtListener's Washington Supreme Court and Court of Appeals records (names, docket numbers, reporters, and dates) and cross-checked against Leagle's Washington case archive. Holdings were verified against the language of the opinions themselves or against quotations of the opinions in later Washington appellate decisions and Supreme Court filings (courts.wa.gov). Primary full-text sources used include the Municipal Research and Services Center archive (courts.mrsc.org), CourtListener, and Washington court filings. Cases proposed for this book whose subject matter did not match this topic — such as In re Marriage of Kovacs, 121 Wn.2d 795 (1993), which concerns residential placement of children rather than property characterization — were excluded.
|
||||||
143
books/divorce-case-law-division.md
Normal file
143
books/divorce-case-law-division.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
# Washington Divorce Case Law: Dividing Property — the "Just and Equitable" Standard
|
||||||
|
|
||||||
|
Washington is a community property state, but at divorce the labels "community" and "separate" only set the table. Under RCW 26.09.080, the court must make "such disposition of the property and the liabilities of the parties, either community or separate, as shall appear just and equitable," without regard to marital misconduct, considering at least four factors. This book collects the published Washington cases that show how the appellate courts actually apply that standard to the hardest division questions: unequal awards, characterization, professional goodwill, retirement benefits, businesses, and debt. Every citation below was verified against the published opinion text.
|
||||||
|
|
||||||
|
## The Standard and Its Four Factors
|
||||||
|
|
||||||
|
The statute directs the court to consider: (1) the nature and extent of the community property; (2) the nature and extent of the separate property; (3) the duration of the marriage; and (4) the economic circumstances of each spouse at the time the division becomes effective. The trial court has broad discretion, and its division is reversed only for a manifest abuse of discretion — for example, when the decree leaves a patent disparity in the parties' economic circumstances.
|
||||||
|
|
||||||
|
### In re Marriage of Konzen, 103 Wn.2d 470, 693 P.2d 97 (1985)
|
||||||
|
|
||||||
|
**Holding:** The trial court had the authority to award the wife a share of the husband's military retired pay even though the pay accrued entirely before the marriage and was his separate property, and doing so was not an abuse of discretion.
|
||||||
|
|
||||||
|
**Facts:** This was a second marriage for both spouses. Mr. Konzen had already retired from a 25-year Navy career and was receiving about $1,653 per month in military retired pay. Both parties were unemployed at trial, but the court found Mr. Konzen had a substantially and disproportionately greater earning capacity — he held undergraduate and master's degrees and had worked as a manager.
|
||||||
|
|
||||||
|
**Why it matters:** Konzen is the clearest statement that the just-and-equitable standard reaches separate property. The court may invade one spouse's separate assets when fairness and the parties' comparative economic circumstances require it.
|
||||||
|
|
||||||
|
### Friedlander v. Friedlander, 80 Wn.2d 293, 494 P.2d 208 (1972)
|
||||||
|
|
||||||
|
**Holding:** All property, community and separate, is before the trial court for distribution; the court affirmed awarding the wife substantially all of the community property while the husband kept his separate property, after striking down a prenuptial agreement the wife had signed without independent advice.
|
||||||
|
|
||||||
|
**Facts:** The husband's separate property consisted principally of his interests in the "Friedlander family enterprises," valued at about $1,041,183 when the marriage began. A prenuptial agreement the parties signed before marrying was held void in its inception because the wife received no independent advice and the agreement was unfair.
|
||||||
|
|
||||||
|
**Why it matters:** Decided under the former divorce statute but still cited today (for example, in Olivares, below) for the rule that the division power covers every asset before the court, and that characterization does not dictate the result.
|
||||||
|
|
||||||
|
## Unequal Division Is Allowed
|
||||||
|
|
||||||
|
Equal division is not required. The longer the marriage, the more likely a disproportionate division will be upheld — especially when one spouse is older, semi-retired, or in ill health and the other is employable.
|
||||||
|
|
||||||
|
### In re Marriage of Rockwell, 141 Wn. App. 235, 170 P.3d 572 (2007)
|
||||||
|
|
||||||
|
**Holding:** Affirmed a 60/40 division of the wife's federal pension in a 26-year marriage, including the trial court's consideration of the husband's future earning capacity and a $159,464 adjustment for Social Security benefits the wife would never receive; reversed only the trial court's use of the "subtraction method" to characterize and value the pension.
|
||||||
|
|
||||||
|
**Facts:** Peter and Carmen Rockwell were married from 1978 to 2004. Carmen spent 16 years in the federal civil service before the marriage and 24 more during it, retiring as a GS-15 executive under the Civil Service Retirement System — a pension that stands in place of Social Security. Peter was laid off at 48 and stopped seeking work in 2002. The trial court found the pension 92 percent community and 8 percent separate, then divided the community portion 60 percent to Carmen and 40 percent to Peter.
|
||||||
|
|
||||||
|
**Why it matters:** A blueprint for long-marriage divisions: unequal awards survive review, earning capacity and Social Security circumstances are legitimate factors, and pension valuation methods (here, the subtraction method) are scrutinized on appeal.
|
||||||
|
|
||||||
|
### In re Marriage of White, 105 Wn. App. 545, 20 P.3d 481 (2001)
|
||||||
|
|
||||||
|
**Holding:** When a spouse spends separate inheritance money to pay debts on community property, the assets remain community property — character is fixed at acquisition — but the contribution is a factor that can justify an unequal division. The trial court reached the right result for the wrong reasons, so the case was remanded for reconsideration.
|
||||||
|
|
||||||
|
**Facts:** Carol and Frank White married in 1973, separated in 1997. In 1993–94, Carol used $30,511 of an inheritance to pay off the family car ($4,000) and the family home ($26,511). The trial court characterized the $30,511 as still Carol's separate property and awarded her the first $26,511 of home value and the first $4,000 of car value.
|
||||||
|
|
||||||
|
**Why it matters:** The leading case on separate funds spent on community assets. There is no automatic transmutation and no burden-of-proof game about intent to make a gift; instead, the contribution is weighed as a distribution factor — here labeled an "unusually significant contribution."
|
||||||
|
|
||||||
|
## Characterization: Community vs. Separate
|
||||||
|
|
||||||
|
Before dividing, the court characterizes each asset. The source of the property and the date of acquisition guide the analysis, but the final division must be fair under all the circumstances.
|
||||||
|
|
||||||
|
### In re Marriage of Olivares, 69 Wn. App. 324, 848 P.2d 1281 (1993)
|
||||||
|
|
||||||
|
**Holding:** Affirmed the trial court's characterization of a promissory note assigned to both spouses by the husband's parents, and of a car titled in the wife's name, as community property — even though the marriage lasted under two years.
|
||||||
|
|
||||||
|
**Facts:** Third marriages for both parties; they married in December 1988 and separated in August 1990. The couple lived on Stephen's pre-marriage assets and gifts and loans from his parents. Through escrow, his elderly parents assigned a promissory note/deed of trust worth about $235,000 ($2,000 per month for 27 years), and the escrow agent put Theresa's name on the documents; payments went to an account bearing both spouses' Social Security numbers. Stephen also bought a 1968 Ford Mustang on his separate credit but placed the title in Theresa's name. The court found the note a gift to both spouses and the car a gift to Theresa, and awarded one-half of the note to each.
|
||||||
|
|
||||||
|
**Why it matters:** Gift characterization turns on donative intent shown by conduct — here, putting property in both names. The short duration of the marriage did not make the division an abuse of discretion.
|
||||||
|
|
||||||
|
### In re Marriage of DewBerry, 115 Wn. App. 351 (2003)
|
||||||
|
|
||||||
|
**Holding:** An oral agreement to treat income earned during marriage as separate property is enforceable where the trial court finds, by clear, cogent, and convincing evidence, that the parties fully performed their separate-property agreement during the marriage.
|
||||||
|
|
||||||
|
**Facts:** The DewBerrys agreed that their earnings would remain separate. Both were professionals — Carla left Arthur Andersen to become an associate in a Seattle law firm while George worked. The trial court found full performance of the agreement, characterized the property acquired during marriage accordingly, and allocated each spouse's separate property to the spouse who acquired it. The Court of Appeals affirmed the property division.
|
||||||
|
|
||||||
|
**Why it matters:** Wages earned during marriage are not inevitably community property. A fully performed separate-property agreement — even an oral one — can change the characterization of everything earned during the marriage.
|
||||||
|
|
||||||
|
### In re Marriage of Brown, 100 Wn.2d 729, 675 P.2d 1207 (1984)
|
||||||
|
|
||||||
|
**Holding:** Recovery for a personal injury inflicted on a married person by a third party is the separate property of the injured spouse, except to the extent the recovery compensates the community for lost wages (which would have been community property) or for injury-related expenses the community incurred.
|
||||||
|
|
||||||
|
**Facts:** Ronna Brown had a potential tort recovery from an injury sustained during her marriage. The Court of Appeals held the potential recovery was community property; the Supreme Court reversed and reinstated the trial court's disposition consistent with its new characterization rule.
|
||||||
|
|
||||||
|
**Why it matters:** The controlling characterization rule for personal-injury claims in a property division, and the source of the "wage-replacement" concept applied later in Brewer.
|
||||||
|
|
||||||
|
### In re Marriage of Brewer, 137 Wn.2d 756, 976 P.2d 102 (1999)
|
||||||
|
|
||||||
|
**Holding:** Monthly payments to a permanently disabled spouse under private disability insurance policies after dissolution are the separate property of the disabled spouse — even though the policies were acquired during the marriage and premiums were paid from community funds before being waived.
|
||||||
|
|
||||||
|
**Facts:** Michael Brewer became permanently disabled during the marriage. After dissolution, he received monthly disability benefits under policies bought during the marriage with community premiums. Applying the Brown wage-replacement rule, the Supreme Court affirmed in part and reversed in part, treating the post-dissolution benefits as compensation for the disabled spouse's future wage loss rather than as a divisible asset.
|
||||||
|
|
||||||
|
**Why it matters:** Distinguishes wage-replacement disability benefits (separate, not divisible) from other insurance proceeds, and remains the case to cite whenever disability income appears in a dissolution.
|
||||||
|
|
||||||
|
### In re Marriage of Zahm, 138 Wn.2d 213, 978 P.2d 498 (1999)
|
||||||
|
|
||||||
|
**Holding:** A trial court may not characterize a spouse's Social Security benefits as community property, because federal law makes those benefits indivisible and not subject to reassignment; the error was harmless here because the division was otherwise just and equitable, so the Court of Appeals was affirmed.
|
||||||
|
|
||||||
|
**Facts:** The Zahms married in 1978, divorced in 1987, reunited, and permanently separated in 1995. In the final dissolution, the trial court characterized two bank accounts and the Walla Walla home as community property and also listed the husband's Social Security benefits as community property. The Court of Appeals agreed the Social Security characterization was error but harmless.
|
||||||
|
|
||||||
|
**Why it matters:** Social Security benefits cannot be treated as a divisible asset — though they may still inform the economic-circumstances analysis under RCW 26.09.080.
|
||||||
|
|
||||||
|
## Professional Goodwill and Education
|
||||||
|
|
||||||
|
### In re Marriage of Fleege, 91 Wn.2d 324, 588 P.2d 1136 (1979)
|
||||||
|
|
||||||
|
**Holding:** The goodwill of a professional practice is an asset subject to division as part of the community property, even if it is not readily salable — the important consideration is whether the goodwill has value to the professional.
|
||||||
|
|
||||||
|
**Facts:** After a 32-year marriage, the husband's dental practice produced net profits of $106,452.61 in 1975. The wife, a trained dietician who had not worked during the marriage, presented two CPAs who valued the practice with a goodwill component; the trial court refused to include goodwill as a divisible asset.
|
||||||
|
|
||||||
|
**Why it matters:** Fleege established that professional goodwill is divisible property in Washington, and every later goodwill case builds on it.
|
||||||
|
|
||||||
|
### In re Marriage of Hall, 103 Wn.2d 236, 692 P.2d 175 (1984)
|
||||||
|
|
||||||
|
**Holding:** Professional goodwill is divisible, but the trial court must state on the record which factors and valuation method it used. Future earning potential is not a separate asset that offsets goodwill; instead, it is a substantial factor in making a just and equitable division. Reversed and remanded.
|
||||||
|
|
||||||
|
**Facts:** Both spouses were physicians. The husband, in private practice, was found to have goodwill worth $70,000; the wife, a salaried professor, was found to have none. The Supreme Court held the bare finding unsupported without articulated valuation factors and methods, and declined to treat the wife's future earning potential as an asset.
|
||||||
|
|
||||||
|
**Why it matters:** The framework for valuing goodwill (including the methods the opinion illustrates) and the rule that earning capacity is weighed as a factor, not valued as an asset.
|
||||||
|
|
||||||
|
### In re Marriage of Washburn, 101 Wn.2d 168, 677 P.2d 152 (1984)
|
||||||
|
|
||||||
|
**Holding:** Where one spouse supports the other through professional school in the mutual expectation that the community will enjoy the resulting financial benefit, but the marriage ends before that benefit is realized, the supporting spouse's contribution must be considered — as a factor in dividing property under RCW 26.09.080 or in awarding maintenance.
|
||||||
|
|
||||||
|
**Facts:** The Washburns married in 1971 while juniors in college; the wife worked while the husband pursued professional education, and the marriage dissolved before the expected payoff. In the consolidated Gillette case, the trial court had valued the wife's contribution to her husband's education and added a $19,000 lump-sum award, which was affirmed.
|
||||||
|
|
||||||
|
**Why it matters:** The remedy for education contributions when there is no professional goodwill yet to divide.
|
||||||
|
|
||||||
|
## Pensions, Retirement, and Military Benefits
|
||||||
|
|
||||||
|
### In re Marriage of Bulicek, 59 Wn. App. 630, 800 P.2d 394 (1990)
|
||||||
|
|
||||||
|
**Holding:** Pension benefits are property rights in the nature of deferred compensation and may be divided even when not yet in pay status; the trial court did not abuse its discretion in awarding the wife a percentage of the GTE pension on an as-received basis, or in its maintenance award.
|
||||||
|
|
||||||
|
**Facts:** George and Janet Bulicek married in 1963 and separated 22 years later. George's GTE pension was partly community (service during marriage) and partly separate (a 40-month pre-marriage service credit). At trial Janet earned about $720 per month and George about $2,500; Janet had serious health problems. The court divided the pension by a percentage payable as received, and awarded maintenance.
|
||||||
|
|
||||||
|
**Why it matters:** Shows a mixed-character pension being divided by percentage on an as-received basis, and how retirement division and maintenance work together to reach a fair result.
|
||||||
|
|
||||||
|
### In re Marriage of Kraft, 119 Wn.2d 438, 832 P.2d 871 (1992)
|
||||||
|
|
||||||
|
**Holding:** Military disability retirement pay may not be treated as an asset of the community in a dissolution property division, under the federal rule of Mansell v. Mansell; the trial court's division was reversed and remanded.
|
||||||
|
|
||||||
|
**Facts:** After a marriage of more than 20 years, the trial court included the husband's military disability retirement pay as a community asset in the distribution. The Supreme Court held that consideration of the disability pay was improper and remanded for reconsideration of the property distribution without it.
|
||||||
|
|
||||||
|
**Why it matters:** The line between military retired pay (which, per Konzen, can be distributed) and military disability pay (which, per federal law, cannot be treated as a divisible asset).
|
||||||
|
|
||||||
|
## Dividing Debts and Liabilities
|
||||||
|
|
||||||
|
The statute governs "the property and the liabilities of the parties, either community or separate," so debt division is part of the same just-and-equitable exercise — no separate, mechanical rule applies. The cases above show the working principles:
|
||||||
|
|
||||||
|
- **Debts paid with separate funds** do not change the character of the assets they secured (White), but the paying spouse's contribution is a factor supporting an unequal award in that spouse's favor.
|
||||||
|
- **Community debts and obligations are weighed with the assets** in the overall division — in Rockwell, the trial court considered the community debts alongside the pension, retirement accounts, and tangible property before approving a 60/40 split.
|
||||||
|
- **Separate property can be awarded across the line** (Konzen, Friedlander) when needed to balance the economic circumstances of the parties, including responsibility for liabilities.
|
||||||
|
|
||||||
|
A final practical note: these decisions were verified against the published opinions as they existed at the time of writing. Case law changes — before relying on any case here, check its current status and read the full opinion.
|
||||||
142
books/divorce-case-law-maintenance.md
Normal file
142
books/divorce-case-law-maintenance.md
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
# Washington State Divorce Case Law — Spousal Maintenance & Attorney Fees
|
||||||
|
|
||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
## About this book
|
||||||
|
|
||||||
|
This book explains Washington State divorce case law on **spousal maintenance** (often called alimony) and **attorney-fee awards**, using real appellate decisions. Every case was verified against the actual published opinion text, including its exact court, volume, reporter, page, and year. Citations follow standard Washington form: *In re Marriage of Smith*, 123 Wn.2d 456 (1999) (Supreme Court) or 123 Wn. App. 456 (1999) (Court of Appeals).
|
||||||
|
|
||||||
|
The governing statutes are **RCW 26.09.090** (maintenance), **RCW 26.09.170** (modification), and **RCW 26.09.140** (attorney fees).
|
||||||
|
|
||||||
|
## The framework: six factors, no formula
|
||||||
|
|
||||||
|
Washington has **no formula** for maintenance. A court "may" award maintenance to either spouse "in such amounts and for such periods of time as the court deems just," **without regard to marital misconduct**, after considering all relevant factors (RCW 26.09.090(1)): (1) the requesting spouse's financial resources, including property received in the division, and ability to meet needs independently; (2) time needed for education or training to find appropriate employment; (3) the standard of living during the marriage; (4) the duration of the marriage; (5) the requesting spouse's age, physical and emotional condition, and financial obligations; and (6) the other spouse's ability to pay while meeting their own needs.
|
||||||
|
|
||||||
|
Two rules frame everything. Maintenance is **not a matter of right** — its purpose is to support a spouse until that spouse can earn their own living or otherwise becomes self-supporting (*In re Marriage of Luckey*, 73 Wn. App. 201, 868 P.2d 189 (1994)). And the trial court's discretion is **wide**; the only limitation on amount and duration is that the award must be just in light of the factors (*In re Marriage of Bulicek*, 59 Wn. App. 630, 800 P.2d 394 (1990)).
|
||||||
|
|
||||||
|
## The landmark maintenance cases
|
||||||
|
|
||||||
|
### In re Marriage of Washburn — maintenance as a "flexible tool"
|
||||||
|
|
||||||
|
**In re Marriage of Washburn, 101 Wn.2d 168, 677 P.2d 152 (1984)** (Supreme Court, en banc). **Holding:** When one spouse supports the other through professional school expecting to share the resulting benefit, and the marriage ends first, the supporting spouse's contribution **must be considered** in dividing property (RCW 26.09.080) or in awarding maintenance (RCW 26.09.090).
|
||||||
|
|
||||||
|
**Facts:** In two consolidated cases, wives worked full time while their husbands attended veterinary school — Mrs. Washburn from 1974 to 1978, Mrs. Gillette through her husband's 1982 degree. The student husbands kept the degrees and future earning power; the supporting wives had little to show for their sacrifice.
|
||||||
|
|
||||||
|
**Why it matters:** Washburn is the most-cited maintenance case in Washington because of its famous language: maintenance "is not just a means of providing bare necessities, but rather a flexible tool by which the parties' standard of living may be equalized for an appropriate period of time." That principle drives the cases that follow.
|
||||||
|
|
||||||
|
### In re Marriage of Sheffer — standard of living is a paramount concern
|
||||||
|
|
||||||
|
**In re Marriage of Sheffer, 60 Wn. App. 51, 802 P.2d 817 (1990)** (Court of Appeals, Division One). **Holding:** In a long marriage where one spouse sacrificed a career for the family, the trial court must carefully weigh the marital standard of living and the parties' post-dissolution economic positions; failing to do so requires reversal.
|
||||||
|
|
||||||
|
**Facts:** After 30 years of marriage, Alfred earned about $4,900 per month, while Beverly — who had quit work as a secretary by agreement to raise their four children — would have about $844 per month once her three-year award expired.
|
||||||
|
|
||||||
|
**Why it matters:** Sheffer applies Washburn's "flexible tool" principle to a traditional long marriage, holding that "the standard of living of the parties during marriage and the parties' postdissolution economic condition are paramount concerns." The court reversed and remanded for a revised maintenance award, and awarded the wife appellate fees based on the income disparity.
|
||||||
|
|
||||||
|
### In re Marriage of Bulicek — duration can reach retirement
|
||||||
|
|
||||||
|
**In re Marriage of Bulicek, 59 Wn. App. 630, 800 P.2d 394 (1990)** (Court of Appeals, Division One). **Holding:** A maintenance award of $500 per month for one year, then $400 per month until the husband retired, was within the trial court's wide discretion in a 22-year marriage where the wife was in ill health.
|
||||||
|
|
||||||
|
**Facts:** George (52, healthy) earned about $2,500 per month; Janet (46, with numerous health problems) earned about $720 plus a $169 disability payment.
|
||||||
|
|
||||||
|
**Why it matters:** Bulicek rejects the idea that maintenance must always be brief and rehabilitative — Janet would "likely never achieve the postdissolution economic level of George." Bulicek also states the basic fee rule: fees rest on the requesting spouse's **need** and the other spouse's **ability to pay**, and "neither party is entitled to attorney's fees as a matter of right."
|
||||||
|
|
||||||
|
### In re Marriage of Luckey — no right to maintenance; discretion is wide
|
||||||
|
|
||||||
|
**In re Marriage of Luckey, 73 Wn. App. 201, 868 P.2d 189 (1994)** (Court of Appeals, Division Three). **Holding:** Denying maintenance beyond the first year of separation was not an abuse of discretion where the property division favored the wife and she received substantial support.
|
||||||
|
|
||||||
|
**Facts:** Robert and Patricia Luckey were married 14 years; Robert was a physician. The trial court valued his practice's goodwill at zero and denied Patricia maintenance beyond what was paid in the first year of separation.
|
||||||
|
|
||||||
|
**Why it matters:** Luckey collects the core standards: maintenance "is not awarded as a matter of right"; its purpose is support until the recipient becomes self-supporting; and the trial court's discretion is "wide," limited only by the requirement that the award be just under the statutory factors. The denial was affirmed because the property division favored the wife — $22,800 in first-year support, $785 per month child support, and a $21,000 payment.
|
||||||
|
|
||||||
|
### In re Marriage of Muhammad — fault is off-limits
|
||||||
|
|
||||||
|
**In re Marriage of Muhammad, 153 Wn.2d 795, 108 P.3d 779 (2005)** (Supreme Court, en banc). **Holding:** A trial court abuses its discretion when it considers marital fault — here, domestic violence allegations — in dividing property; the Supreme Court reversed and ordered a new trial before a new judge.
|
||||||
|
|
||||||
|
**Facts:** Cherry Gilbert (formerly Muhammad) and Dawud Muhammad married after about 20 months of cohabitation and separated in April 2001 amid domestic violence allegations against Muhammad, a deputy sheriff. The trial court appeared to weigh the fault allegations in dividing property. The Court of Appeals found no improper consideration of fault (119 Wn. App. 166, 79 P.3d 483 (2003)); the Supreme Court disagreed.
|
||||||
|
|
||||||
|
**Why it matters:** Muhammad is the leading modern statement of the **no-fault principle**. Maintenance awards are likewise made "without regard to marital misconduct" (RCW 26.09.090(1)), so Muhammad keeps fault — however serious — out of the award. The remedy was severe: a new trial with a new judge.
|
||||||
|
|
||||||
|
### In re Marriage of Mansour — no written findings required on every factor
|
||||||
|
|
||||||
|
**In re Marriage of Mansour, 126 Wn. App. 1, 106 P.3d 768 (2005)** (Court of Appeals, Division One). **Holding:** RCW 26.09.090 requires the court to **consider** the statutory factors, but not to make specific written findings on each one; denying maintenance was not an abuse of discretion where the wife received substantial home-sale proceeds.
|
||||||
|
|
||||||
|
**Facts:** Cindy and Ghassan Mansour divorced after a long marriage. The trial court awarded the wife mortgage payments until the house sold and two months of $1,500 relocation payments.
|
||||||
|
|
||||||
|
**Why it matters:** Mansour gives trial courts flexibility — no item-by-item factor findings are required — while confirming abuse-of-discretion review. It also states the fee standard: the court "must balance the needs of the spouse requesting them with the ability of the other spouse to pay," and may deny fees when the requesting spouse has resources (here, house proceeds). On appeal, the wife won fees after showing both merit and need.
|
||||||
|
|
||||||
|
### In re Marriage of Rockwell — earning capacity counts, but is not divisible property
|
||||||
|
|
||||||
|
**In re Marriage of Rockwell, 141 Wn. App. 235, 170 P.3d 572 (2007)** (Court of Appeals, Division One). **Holding:** In a 26-year marriage, a 60/40 property split favoring the older, retired, ill wife over the younger, employable husband was not an abuse of discretion; a court may compare the parties' future earning capacity as a factor, though it may not divide earning capacity itself as an asset.
|
||||||
|
|
||||||
|
**Facts:** Peter Rockwell, an engineer who stopped working after a 1999 layoff, challenged the 60/40 division in favor of Carmen, a retired federal executive drawing a substantial pension in lieu of Social Security.
|
||||||
|
|
||||||
|
**Why it matters:** Rockwell connects maintenance doctrine to property division. Washington courts aim to put parties "in roughly equal financial positions for the rest of their lives," and earning capacity — age, health, training, work history — is a legitimate factor in that assessment, so long as it is not converted into a divisible asset. The court did reverse the pension-valuation method used: even a fair result needs a correct methodology.
|
||||||
|
|
||||||
|
### In re Marriage of Anthony — a modern application
|
||||||
|
|
||||||
|
**In re Marriage of Anthony, 9 Wn. App. 2d 555, 446 P.3d 635 (2019)** (Court of Appeals, Division One). **Holding:** The decision to award maintenance to a long-time homemaker was affirmed, but the case was remanded because the trial court set the award without making findings on the parties' incomes or addressing the statutory factors in writing.
|
||||||
|
|
||||||
|
**Facts:** Joseph and Penny Anthony married in 1987. Penny, who never finished eighth grade, stayed home with their four children while Joseph served 22 years in the Marine Corps and later drove trucks, grossing about $7,807 per month.
|
||||||
|
|
||||||
|
**Why it matters:** Anthony shows the current appellate expectation: maintenance amounts must be traceable to actual income findings and the RCW 26.09.090 factors. The court also directed the trial court to address a pending RCW 26.09.140 fee request on remand.
|
||||||
|
|
||||||
|
## Duration: how long can maintenance last?
|
||||||
|
|
||||||
|
Duration is not set by formula. The governing question is what is "just" under the factors.
|
||||||
|
|
||||||
|
- **Short, rehabilitative awards** fit cases where education or training will make the recipient self-supporting (RCW 26.09.090(1)(b)). In *Washburn*, the Court spoke of allowing the supporting spouse "to share, temporarily" in the student spouse's earnings.
|
||||||
|
- **Long awards** fit long marriages where the recipient will never catch up. *Bulicek* approved $400 per month **until the husband retired**. In *Sheffer*, a three-year award was reversed because it ignored the recipient's permanent disadvantage.
|
||||||
|
- **Reconciliation does not end maintenance.** In *In re Marriage of Moody*, 137 Wn.2d 979, 976 P.2d 1240 (1999), the Supreme Court held that reconciliation did not invalidate a legal separation decree, its maintenance provisions, or a later agreed order extending payments.
|
||||||
|
- **Automatic termination events.** Under RCW 26.09.170(2), unless the parties agree otherwise in writing, future maintenance ends automatically on the death of either party or the recipient's remarriage (or registration of a new domestic partnership).
|
||||||
|
|
||||||
|
## Modification and termination
|
||||||
|
|
||||||
|
A maintenance award can be modified, but only prospectively and only on a showing of a **substantial change of circumstances** since the decree (RCW 26.09.170(1)).
|
||||||
|
|
||||||
|
- **Lambert v. Lambert, 66 Wn.2d 503, 403 P.2d 664 (1965)** (Supreme Court). **Holding:** A self-induced income decline is not a changed circumstance justifying modification. The optometrist husband voluntarily curtailed his earning capacity after the divorce; the Supreme Court reversed the reduction and reinstated the original award. The rule remains foundational: modification requires a change occurring after the decree that was not — and could not have been — litigated in the original action; a "voluntary reduction in income or self-imposed curtailment of earning capacity, absent a substantial showing of good faith," will not support modification.
|
||||||
|
- **In re Marriage of Spreen, 107 Wn. App. 341, 28 P.3d 769 (2001)** (Court of Appeals, Division Two). **Holding:** Worsened mental health (bipolar disorder making the wife unemployable) was a changed circumstance justifying an extension, but limiting it to one year because she had received maintenance "long enough" and government assistance was available was an abuse of discretion. Spreen requires actual application of the RCW 26.09.090 factors — arbitrary time caps and speculation about public assistance, with no evidence of what programs exist, are improper.
|
||||||
|
- **In re Marriage of Drlik, 121 Wn. App. 269, 87 P.3d 1192 (2004)** (Court of Appeals, Division Three). **Holding:** A court may **suspend** (not only modify or terminate) maintenance under RCW 26.09.170, but may not suspend it indefinitely on an unsupported finding that the payer might return to work. After a 38-year marriage, the husband paid $3,300 per month until age 70; stricken with brain cancer, he sought relief. The suspension authority was proper, but the indefinite, speculative basis required remand.
|
||||||
|
- **Moody** (above) adds that the parties can agree to modify maintenance terms post-decree, and such agreed orders bind the parties even after reconciliation.
|
||||||
|
|
||||||
|
## Attorney fees in divorce
|
||||||
|
|
||||||
|
Attorney-fee awards in dissolution are governed by **RCW 26.09.140**, which allows a court to order one party to pay the other's reasonable attorney fees "after considering the financial resources of both parties."
|
||||||
|
|
||||||
|
### The basic standard: need and ability
|
||||||
|
|
||||||
|
The trial court "must balance the needs of the spouse requesting them with the ability of the other spouse to pay" (*Mansour*, quoting *In re Marriage of Stenshoel*, 72 Wn. App. 800, 866 P.2d 635 (1993)). Fees are not a matter of right (*Bulicek*). The decision is discretionary; the challenger must show it was clearly untenable or manifestly unreasonable.
|
||||||
|
|
||||||
|
### In re Marriage of Knight — how fee awards are built and reviewed
|
||||||
|
|
||||||
|
**In re Marriage of Knight, 75 Wn. App. 721, 880 P.2d 71 (1994), review denied, 126 Wn.2d 1011 (1995)** (Court of Appeals, Division Two). **Holding:** A $149,000 fee award was affirmed where the court indicated its method, balanced needs against ability to pay, and considered the case's complexity.
|
||||||
|
|
||||||
|
**Facts:** The trial court valued Judy Knight's business (Ramtha Dialogues) with $792,000 of goodwill going to her husband, and awarded him roughly half his fees given the large economic disparity and the vigorous defense he faced.
|
||||||
|
|
||||||
|
**Why it matters:** Knight collects the mechanical rules: the court **must indicate on the record the method used to calculate the award**, must balance need against ability to pay, and should weigh (1) the factual and legal questions involved, (2) the time necessary to prepare and present the case, and (3) the amount and character of the property involved (*Abel v. Abel*, 47 Wn.2d 816 (1955)).
|
||||||
|
|
||||||
|
### In re Marriage of Van Camp — the lodestar formula is not required
|
||||||
|
|
||||||
|
**In re Marriage of Van Camp, 82 Wn. App. 339, 918 P.2d 509 (1996)** (Court of Appeals, Division Three). **Holding:** Dissolution courts are not required to use the lodestar method (hours × reasonable rate); they may instead apply the traditional dissolution factors — need, ability, and equity.
|
||||||
|
|
||||||
|
**Facts:** The wife's lawyers sought about $60,000 in fees; the husband's expert said $12,000–$13,500 was reasonable. The trial court reviewed time records, awarded roughly $37,000, and was affirmed.
|
||||||
|
|
||||||
|
### Fees based on intransigence (bad conduct)
|
||||||
|
|
||||||
|
Separate from need-and-ability fees, a court may award fees caused by a party's **intransigence** — foot-dragging, obstruction, or unnecessary motions that make the litigation unduly difficult.
|
||||||
|
|
||||||
|
- **In re Marriage of Greenlee, 65 Wn. App. 703, 829 P.2d 1120 (1992)** (Court of Appeals, Division One). **Holding:** When intransigence is established, **the requesting spouse's financial resources are irrelevant** — the award is an equitable remedy. The husband's obstructionist refusal to cooperate in refinancing the former family home justified fees for the wife, who had been forced to court to enforce the decree.
|
||||||
|
- **In re Marriage of Mattson, 95 Wn. App. 592, 976 P.2d 157 (1999)** (Court of Appeals, Division Two). **Holding:** Fee awards were affirmed on both RCW 26.09.140 need/ability grounds and intransigence grounds, where the husband's income "increased with each round of investigation" and his concealment forced costly discovery.
|
||||||
|
- **In re Marriage of Crosetto, 82 Wn. App. 545, 918 P.2d 954 (1996)** (Court of Appeals, Division Two). **Holding:** A pattern of obstruction (false abuse allegations, thwarting visitation) was intransigence; on remand the trial court was directed to **segregate** fees caused by intransigence from the fees the parties otherwise bear themselves.
|
||||||
|
|
||||||
|
### Fees on appeal
|
||||||
|
|
||||||
|
Appellate courts examine the **arguable merit** of the issues on appeal and the **financial resources** of the parties, under RCW 26.09.140 and RAP 18.1. In *Sheffer*, the wife won appellate fees based on income disparity; in *Mansour*, she earned them after showing merit, complying with RAP 18.1(b), and filing an affidavit of financial need.
|
||||||
|
|
||||||
|
## Practical takeaways
|
||||||
|
|
||||||
|
- Maintenance is discretionary, fact-driven, and never automatic — there is no formula.
|
||||||
|
- Long marriages, homemaker sacrifices, disability, and earning-power gaps favor longer or larger awards; *Washburn*, *Sheffer*, and *Bulicek* are the anchors.
|
||||||
|
- Courts may not consider fault in awarding maintenance (*Muhammad*), but may consider a spouse's intransigence in awarding fees (*Greenlee*, *Mattson*, *Crosetto*).
|
||||||
|
- Modification requires a genuine, post-decree change in circumstances; voluntary income reduction does not qualify (*Lambert*), but serious illness can (*Spreen*, *Drlik*).
|
||||||
|
- Fee awards balance need against ability to pay (*Knight*, *Mansour*); no lodestar formula is required (*Van Camp*).
|
||||||
|
- Maintenance ends automatically on death or the recipient's remarriage unless the decree or agreement says otherwise (RCW 26.09.170(2)); reconciliation does not void a separation decree's maintenance terms (*Moody*).
|
||||||
144
books/divorce-dissolution.md
Normal file
144
books/divorce-dissolution.md
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# Divorce (Dissolution) in Washington State
|
||||||
|
|
||||||
|
> For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
## What this book covers
|
||||||
|
|
||||||
|
This book explains how a marriage ends in Washington State: the legal paths available (divorce, legal separation, annulment), who can file, how the process works, and what a final court order can do. The rules are set out in Chapter 26.09 of the Revised Code of Washington (RCW). The same chapter applies to registered domestic partnerships — Washington law handles dissolution of a domestic partnership through the same court process, with some different form names (WashingtonLawHelp.org, *Divorce basics*).
|
||||||
|
|
||||||
|
Washington calls a divorce a **dissolution of marriage**. You get one by asking a superior court for a court order that ends your marriage. You cannot be divorced by private agreement — only a judge's signed final order ends a marriage in Washington (WashingtonLawHelp.org, *Divorce guide*).
|
||||||
|
|
||||||
|
## Washington is a no-fault state
|
||||||
|
|
||||||
|
You do not have to prove your spouse did something wrong. To get a divorce, the person filing (the petitioner) states in the petition that the marriage is **irretrievably broken** (RCW 26.09.030). This is a legal way of saying at least one spouse believes the marriage cannot be saved.
|
||||||
|
|
||||||
|
Key consequences of no-fault divorce:
|
||||||
|
|
||||||
|
- If your spouse does not want a divorce, you can still get one if the court finds the marriage irretrievably broken (RCW 26.09.030(c)).
|
||||||
|
- Fault — such as adultery, abandonment, or cruelty — is not a requirement for divorce, and it is generally not a factor in dividing property (RCW 26.09.080) or awarding spousal maintenance (RCW 26.09.090). Washington courts award both "without regard to misconduct."
|
||||||
|
- Washington does not recognize common-law marriage created in Washington, though a common-law marriage validly created in another state or country counts here (WashingtonLawHelp.org, *Divorce basics*).
|
||||||
|
|
||||||
|
## Who can file: residency and military service
|
||||||
|
|
||||||
|
Washington has no minimum length of residence. The court can hear a divorce petition when any one of these is true (RCW 26.09.030):
|
||||||
|
|
||||||
|
1. The petitioner or the respondent is a resident of Washington State;
|
||||||
|
2. The petitioner or the respondent is a member of the armed forces stationed in Washington; or
|
||||||
|
3. The petitioner or the respondent is married to a person who is a resident of Washington or is a service member stationed in Washington.
|
||||||
|
|
||||||
|
In practice, WashingtonLawHelp.org (*Divorce basics*) describes it this way: you can file when you or your spouse lives in Washington and plans to stay, or when you or your spouse is in the military and will be stationed here for at least 90 days after the case is filed.
|
||||||
|
|
||||||
|
Divorce cases are filed in **superior court** — there is a superior court in every county. File in the superior court of the county where you or your spouse lives; contact that court's clerk or family law facilitator to confirm local requirements (WashingtonLawHelp.org, *Divorce guide*; Washington Courts eService Center FAQ, "Filing for divorce in Washington State").
|
||||||
|
|
||||||
|
## Three ways to end a marriage
|
||||||
|
|
||||||
|
Washington law provides three kinds of family-law cases under Chapter 26.09 RCW:
|
||||||
|
|
||||||
|
### Dissolution (divorce)
|
||||||
|
|
||||||
|
This ends the marriage entirely. The decree is final when entered, subject to the right of appeal, and either party may remarry after the decree (RCW 26.09.150(1)).
|
||||||
|
|
||||||
|
### Legal separation
|
||||||
|
|
||||||
|
A decree of legal separation gives the court authority over the same issues as a divorce — parenting plan, child support, maintenance, and division of property and debts — but the marriage continues. You remain legally married. People often choose legal separation for religious reasons, or to preserve Social Security benefits that depend on being married (WashingtonLawHelp.org, *Legal separation basics*).
|
||||||
|
|
||||||
|
Differences from divorce that matter in practice:
|
||||||
|
|
||||||
|
- There is **no 90-day waiting period** for a legal separation (WashingtonLawHelp.org, *Legal separation basics*).
|
||||||
|
- You cannot remarry while separated — the decree can later be **converted to a divorce**. Either party may ask for conversion, but not earlier than six months after the separation decree is entered (RCW 26.09.150(2)(a)).
|
||||||
|
- A petitioner may ask for legal separation instead of dissolution in the original petition (RCW 26.09.030(d)).
|
||||||
|
|
||||||
|
### Declaration of invalidity (annulment)
|
||||||
|
|
||||||
|
An annulment is a court ruling that the marriage should be treated as though it never validly existed. A petition may be filed while both parties are living, by either or both parties (or a guardian for an incompetent spouse), when the marriage should not have been contracted for reasons including (RCW 26.09.040):
|
||||||
|
|
||||||
|
- Age of one or both parties (too young to marry);
|
||||||
|
- Lack of required parental or court approval;
|
||||||
|
- A prior, still-existing marriage or domestic partnership of one party;
|
||||||
|
- Consanguinity (the spouses are close blood relatives);
|
||||||
|
- A party lacked capacity to consent, because of mental incapacity or the influence of alcohol or other incapacitating substances;
|
||||||
|
- A party was induced to marry by force or duress, or by fraud involving the essentials of marriage.
|
||||||
|
|
||||||
|
A marriage entered in another place that was void or voidable under that place's law may also be declared invalid (RCW 26.09.040(4)(c)). Even in an annulment, the court has authority to award maintenance, order a parenting plan for minor children, and divide property (RCW 26.09.040(3)). Children born or conceived during the marriage stay legitimate even if the marriage is declared invalid (RCW 26.09.040(5)).
|
||||||
|
|
||||||
|
## The 90-day waiting period
|
||||||
|
|
||||||
|
Washington imposes a mandatory waiting period: the judge may not enter a decree of dissolution until **90 days have passed since the petition was filed and since the summons was served** (or first published) on the other spouse (RCW 26.09.030). This is the fastest any divorce can be finalized, even when both spouses agree on everything (WashingtonLawHelp.org, *Divorce guide*). Contested divorces take longer — often many months — because the judge must decide disputed issues, sometimes after a trial.
|
||||||
|
|
||||||
|
## The court process, step by step
|
||||||
|
|
||||||
|
WashingtonLawHelp.org's *Divorce guide* (reviewed May 2026) lays out the basic path:
|
||||||
|
|
||||||
|
1. **Prepare and file forms.** The core forms are the *Summons* (FL Divorce 200), the *Petition for Divorce (Dissolution)* (FL Divorce 201), and the *Confidential Information* form (FL All Family 001), which is for the court only and is never served on the other spouse (Washington Courts, "Court Forms: Divorce (Dissolution)"; WashingtonLawHelp.org, *Divorce guide*). Free guided form tools are available through Washington Forms Online.
|
||||||
|
2. **Pay the filing fee or get it waived.** The filing fee is currently **$364** (WashingtonLawHelp.org, *Divorce guide*). You can ask the judge to waive filing fees and surcharges under court rule **GR 34** if you receive TANF, SSI, HEN, food assistance, or federal poverty-related veterans' benefits; or your income is at or below 125% of the federal poverty guidelines; or basic living expenses keep you from paying (WashingtonLawHelp.org, *Ask the court for a fee waiver*; courts.wa.gov, GR 34).
|
||||||
|
3. **Serve your spouse.** Generally someone other than you must hand-deliver the papers. Personal service is not needed if your spouse signs a *Service Accepted* form or an *Agreement to Join Petition (Joinder)* (WashingtonLawHelp.org, *Divorce guide*; courts.wa.gov forms FL All Family 119, 117).
|
||||||
|
4. **Wait for a response.** A spouse served in Washington has 20 days to respond; 60 days if served outside Washington; 90 days if served by mail or publication; and (from September 1, 2025) 60 days for a person who receives the papers while in jail or prison (WashingtonLawHelp.org, *Divorce guide*). If there is no response, you can ask for an order of **default**, which lets the case proceed without the spouse's participation (WashingtonLawHelp.org, *Divorce guide*).
|
||||||
|
5. **Temporary orders, if needed** (see next section).
|
||||||
|
6. **Finalize.** If you agree on everything, you submit final orders (including *Findings and Conclusions About a Marriage*, FL Divorce 231, and the *Final Divorce Order*, FL Divorce 241) for the judge to sign. If you disagree, the judge decides after a trial. You are legally divorced when the judge signs the Final Divorce Order and it is filed; the court clerk then forwards a *Certificate of Dissolution* (DOH 422-027) to the State Registrar of Vital Statistics (WashingtonLawHelp.org, *Divorce guide*; courts.wa.gov forms page).
|
||||||
|
|
||||||
|
## Temporary orders while the case is pending
|
||||||
|
|
||||||
|
Either spouse can ask the judge for short-term orders that apply until the divorce is final (RCW 26.09.060). A motion for temporary maintenance or temporary child support must include an affidavit stating the facts and the amounts requested (RCW 26.09.060(1)). The court can also issue temporary restraining orders or preliminary injunctions that:
|
||||||
|
|
||||||
|
- Stop either party from transferring, hiding, or disposing of property outside the ordinary course of business (RCW 26.09.060(2)(a));
|
||||||
|
- Prohibit molesting or disturbing the other party or any child (RCW 26.09.060(2)(b));
|
||||||
|
- Keep one party away from the home, workplace, or school of the other party or the children (RCW 26.09.060(2)(c), (d));
|
||||||
|
- Stop a party from removing a child from the state (RCW 26.09.060(2)(e)).
|
||||||
|
|
||||||
|
For safety emergencies, a party may request a domestic violence or antiharassment **protection order** under Chapter 7.105 RCW. Temporary (ex parte) protection orders issued this way last up to 14 days, extendable to 24 days to consolidate hearings (RCW 26.09.060(3)). The court must consider firearm restrictions under RCW 9.41.800 and can order surrender of firearms and dangerous weapons (RCW 26.09.060(4)). Forms include the *Motion for Temporary Family Law Order and Restraining Order* (FL Divorce 223) and the *Motion for Immediate Restraining Order (Ex Parte)* (FL Divorce 221) (courts.wa.gov forms page). Temporary orders end when the final decree is entered (RCW 26.09.060(10)(c)).
|
||||||
|
|
||||||
|
## What the final decree can do
|
||||||
|
|
||||||
|
When entering a decree of dissolution, legal separation, or invalidity, the court must address, as applicable (RCW 26.09.050(1)):
|
||||||
|
|
||||||
|
- A **parenting plan** for any minor children (see next section);
|
||||||
|
- **Child support**;
|
||||||
|
- **Maintenance (spousal support)** for either spouse — see the companion book *Spousal Maintenance in Washington State*;
|
||||||
|
- **Division of property and debts**;
|
||||||
|
- Allocation of the children as federal tax exemptions;
|
||||||
|
- Any needed continuing **restraining orders** or protection orders under Chapter 7.105 RCW; and
|
||||||
|
- A **name change**, if a party requests restoration of a former name or a new name (see also RCW 26.09.150(3)).
|
||||||
|
|
||||||
|
## Dividing property and debts
|
||||||
|
|
||||||
|
Washington is a **community property** state. In general, property and debts acquired during the marriage are community property; property owned before the marriage, or received by gift or inheritance, is typically separate property. The court must divide the property and liabilities of the parties — both community and separate — in a way that is "just and equitable," considering (RCW 26.09.080):
|
||||||
|
|
||||||
|
1. The nature and extent of the community property;
|
||||||
|
2. The nature and extent of the separate property;
|
||||||
|
3. The length of the marriage; and
|
||||||
|
4. The economic circumstances of each spouse at the time of division, including the desirability of letting the parent with whom the children mostly live keep the family home for a reasonable time.
|
||||||
|
|
||||||
|
Two practical cautions:
|
||||||
|
|
||||||
|
- **Retirement benefits count.** Pensions, 401(k)s, IRAs, and similar accounts earned during the marriage are generally community property subject to division, even if one spouse never worked outside the home. Dividing many employer plans requires a special court order called a QDRO (Qualified Domestic Relations Order). Acting quickly after the decree matters — a delayed division can result in the account owner keeping everything (Legal Voice, *Divorce and Future Retirement Benefits*, updated June 2025).
|
||||||
|
- **Property division is final.** Unlike maintenance and child support, a property division generally cannot be modified later; a court may revisit it only under conditions that justify reopening a judgment (RCW 26.09.170(1)).
|
||||||
|
|
||||||
|
## Parenting plans and child support
|
||||||
|
|
||||||
|
In any case involving minor children, the best interests of the child is the standard the court applies (RCW 26.09.002). Every final decree with children includes a **permanent parenting plan** that sets residential time and decision-making authority (RCW 26.09.184; the criteria the court must use are in RCW 26.09.187 and RCW 26.09.191). Child support is set under Chapter 26.19 RCW using the Washington State Child Support Schedule, based on both parents' incomes (WashingtonLawHelp.org, *Divorce guide*).
|
||||||
|
|
||||||
|
## Settling your case: separation contracts
|
||||||
|
|
||||||
|
You and your spouse may resolve things by written agreement. A **separation contract** can cover maintenance, division of property, parenting, and child support (RCW 26.09.070(1)). When a divorce is filed, the contract is binding on the court unless the court finds it was unfair at the time it was made (RCW 26.09.070(3)). Notably, a separation contract may state that a maintenance provision cannot be modified later; without that language, maintenance terms can be modified like any decree provision (RCW 26.09.070(7)). Agreements on parenting plans and child support are always reviewable by the court for the child's best interests and compliance with the child support schedule (RCW 26.09.070(3)).
|
||||||
|
|
||||||
|
## Attorney fees and help
|
||||||
|
|
||||||
|
The court may order one party to pay a reasonable amount toward the other party's attorney fees and costs, based on the parties' financial resources (RCW 26.09.140). There is no right to a court-appointed lawyer in a divorce (WashingtonLawHelp.org, *Enforce your divorce decree*).
|
||||||
|
|
||||||
|
If your spouse ignores the final order, remedies include motions for contempt — for example, a parent who refuses to comply with a parenting plan can face contempt, attorney-fee awards, and civil penalties (RCW 26.09.160). Money judgments from a divorce can generally be enforced for 10 years and renewed (WashingtonLawHelp.org, *Enforce your divorce decree*).
|
||||||
|
|
||||||
|
Sources of help and forms:
|
||||||
|
|
||||||
|
- **WashingtonLawHelp.org** — free plain-language guides and Washington Forms Online (washingtonlawhelp.org, maintained by Northwest Justice Project).
|
||||||
|
- **Washington State Courts** — statewide court forms and the Court Directory: courts.wa.gov.
|
||||||
|
- **Legal Voice** — legal publications for Washington residents: legalvoice.org.
|
||||||
|
- **CLEAR** — statewide legal aid referral line: 1-888-201-1014 (courts.wa.gov forms page).
|
||||||
|
- **Family law facilitators** — some counties provide facilitators who help with paperwork (no legal advice).
|
||||||
|
- **Military spouses** — federal law gives active-duty service members extra protections, including postponement of hearings; check military status via the Defense Manpower Data Center (WashingtonLawHelp.org, *Divorce guide*).
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- RCW 26.09.002, 26.09.030, 26.09.040, 26.09.050, 26.09.060, 26.09.070, 26.09.080, 26.09.090, 26.09.140, 26.09.150, 26.09.160, 26.09.170, 26.09.184 — app.leg.wa.gov (Title 26, Chapter 26.09 RCW)
|
||||||
|
- WashingtonLawHelp.org (Northwest Justice Project): *Divorce guide* (reviewed 05/07/2026); *Divorce basics* (reviewed 09/03/2026); *Legal separation basics*; *Ask the court for a fee waiver*; *Enforce your divorce decree: Money and property issues* (reviewed 03/19/2026) — washingtonlawhelp.org
|
||||||
|
- Washington State Courts: *Court Forms: Divorce (Dissolution)* — courts.wa.gov/forms/?fa=forms.contribute&formID=13; eService Center FAQ *Filing for divorce in Washington State* — info.courts.wa.gov; General Rule 34 (fee waivers) — courts.wa.gov
|
||||||
|
- Legal Voice: *Divorce and Future Retirement Benefits* (updated June 2025) — legalvoice.org
|
||||||
103
books/guardianship.md
Normal file
103
books/guardianship.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
# Guardianship in Washington State: A Plain-English Guide to Chapter 11.130 RCW
|
||||||
|
|
||||||
|
## What This Book Covers
|
||||||
|
|
||||||
|
This book explains Washington's guardianship law, chapter 11.130 RCW — titled the "uniform guardianship, conservatorship, and other protective arrangements act" (RCW 11.130.005). It covers minor guardianship in depth, compares it with parenting plans, and closes with adult-guardianship basics. The minor-guardianship provisions took effect January 1, 2021 (RCW 11.130.915(2)); the act's overall effective date for adult guardianships and conservatorships is January 1, 2022 (RCW 11.130.915), and the chapter applies to proceedings begun after that date and to guardianships already in existence (RCW 11.130.910).
|
||||||
|
|
||||||
|
## Part 1 — Minor Guardianship
|
||||||
|
|
||||||
|
### What Minor Guardianship Is
|
||||||
|
|
||||||
|
A "guardian" is a person appointed by a court to make decisions about another person's personal affairs; a "minor" is an unemancipated person under eighteen (RCW 11.130.010(11), (20)). A person becomes a guardian for a minor only by court appointment — Washington has no guardianship created merely by private agreement (RCW 11.130.185(1)). The court issues "letters of office" certifying the guardian's authority; for a minor guardianship, the letters expire on the minor's eighteenth birthday (RCW 11.130.010(16); RCW 11.130.215(7)).
|
||||||
|
|
||||||
|
### The 2020–2021 Change: Nonparental Custody Became Minor Guardianship
|
||||||
|
|
||||||
|
Before January 1, 2021, a nonparent seeking custody of someone else's child filed a "nonparental custody" case under chapter 26.10 RCW. That chapter was repealed effective January 1, 2021 (2020 c 312 § 905, per the official disposition history of chapter 26.10 RCW). Today a nonparent obtains custody and decision-making authority through a minor guardianship; pre-repeal chapter 26.10 RCW orders remain valid, but modifications must follow chapter 11.130 RCW (RCW 11.130.245(1), (2)).
|
||||||
|
|
||||||
|
### Grounds: When a Court May Appoint a Guardian for a Minor
|
||||||
|
|
||||||
|
A court may appoint a guardian for a minor who does not already have one only if the appointment is in the minor's best interest and at least one of three grounds exists (RCW 11.130.185(2)):
|
||||||
|
|
||||||
|
- Each parent, after being fully informed of the nature and consequences of guardianship, consents (RCW 11.130.185(2)(a)).
|
||||||
|
- All parental rights have been terminated (RCW 11.130.185(2)(b)).
|
||||||
|
- There is clear and convincing evidence that no parent is willing or able to exercise "parenting functions" as defined in RCW 26.09.004 (RCW 11.130.185(2)(c)) — maintaining a loving, stable relationship; attending to daily needs such as feeding, clothing, supervision, and health care; attending to education; and exercising appropriate judgment (RCW 26.09.004(2)). The test is the parents' willingness and ability to care for the child — not which home is "better."
|
||||||
|
|
||||||
|
### Who May Petition and Where
|
||||||
|
|
||||||
|
"A person interested in the welfare of a minor, including the minor, may petition for appointment of a guardian for the minor" (RCW 11.130.190(1)); the petitioner need not be the person seeking to become guardian. The petition must state the petitioner's name, address, and relationship to the minor, and, so far as known: the minor's and parents' names and addresses; anyone with primary care or custody of the minor for at least sixty days in the last two years or seven hundred thirty days in the last five years; the legal basis, with the factual reasons in a separate declaration; the proposed guardian and why; the minor's property; interpreter needs; and any other pending proceeding about the child (RCW 11.130.190(2)). Venue is the county where the minor resides or is present, or where another custody or parental-rights proceeding is pending (RCW 11.130.030(1)).
|
||||||
|
|
||||||
|
### Who May Serve as Guardian
|
||||||
|
|
||||||
|
Any suitable person over age 21 — or a parent under 21 — may be appointed if not disqualified (RCW 11.130.090(1)). Disqualifiers include being under 18 (with exceptions); certain criminal convictions, though the court may still find a relative qualified after considering the facts; being a nonresident without a resident agent for service; being a corporation not authorized to act as fiduciary; or being found unsuitable by the court (RCW 11.130.090(1)(a)–(e)). Lay guardians must complete the free training video or webcast from the Administrative Office of the Courts (deferred up to 90 days in expedited cases) unless waived (RCW 11.130.090(2)). Before a final order, the court must obtain child-welfare records and state and national criminal-history checks for every proposed guardian and adult household member (RCW 11.130.210).
|
||||||
|
|
||||||
|
### Notice, Hearing, and the Child's Voice
|
||||||
|
|
||||||
|
The petitioner must personally serve the petition and notice on the minor (if 12 or older), each parent (or the nearest adult kin if there is none), any existing guardian or nonparental custodian under former chapter 26.10 RCW, and anyone else the court directs (RCW 11.130.195(1)(a)). Mail notice goes to a broader list including grandparents, adult siblings, prior caregivers, and any nominee of a parent or of the minor (RCW 11.130.195(1)(b)). The court may not grant the guardianship if the minor (12+) or the parents were not served, unless it finds by clear and convincing evidence that a parent cannot be located with due diligence or waived notice in a record (RCW 11.130.195(3)). When a parent cannot be served, or an unrepresented minor 12 or older is involved, the court appoints a court visitor to interview the parties and report (RCW 11.130.195(4)).
|
||||||
|
|
||||||
|
A minor 12 or older may request an attorney, whom the court may also appoint on a guardian ad litem's recommendation or its own motion; the attorney advocates the minor's wishes, or legal rights if the wishes are not reasonably ascertainable (RCW 11.130.200(1)–(2)). An indigent parent who objects is entitled to appointed counsel (RCW 11.130.200(5)–(6)). The minor may attend and participate in the hearing unless the court finds the minor cannot participate meaningfully or attendance would be harmful (RCW 11.130.205(1)).
|
||||||
|
|
||||||
|
### Whom the Court Appoints First
|
||||||
|
|
||||||
|
The court must appoint a person a parent nominated in a probated will or other sworn record (which may include a power of attorney) unless that is contrary to the minor's best interest; if parents nominate different people, the court chooses by best interest; otherwise it appoints the minor's own nominee, if the minor is 12 or older, unless contrary to the minor's best interest (RCW 11.130.215(2)). The court may create a "limited guardianship" granting fewer than all available powers (RCW 11.130.215(3)). The order must preserve the parent-child relationship through visitation unless restricted under RCW 26.09.191 or 26.09.192, and must state the rights each parent retains (RCW 11.130.215(4)). Each parent is entitled to notice if the guardian delegates custody, or if the court modifies, limits, or removes the guardian (RCW 11.130.215(5)).
|
||||||
|
|
||||||
|
### Emergency and Standby Guardians
|
||||||
|
|
||||||
|
The court may appoint an emergency guardian if that is likely to prevent substantial harm to the minor's health, safety, or welfare and no other person has the authority, ability, and willingness to act (RCW 11.130.225(1)). The appointment lasts at most 60 days, extendable once for up to 60 more (RCW 11.130.225(2)). Without notice, appointment is allowed only if the minor would be substantially harmed before a noticed hearing; notice must follow within 48 hours and a hearing within 5 days (RCW 11.130.225(4)). An emergency appointment is not a finding that grounds for a full guardianship exist (RCW 11.130.225(5)). Separately, a parent may nominate a "standby guardian," appointed on a finding that within two years no parent likely will be able or willing to perform parenting functions (RCW 11.130.220(2)–(3)); the standby guardian acts only when no parent is willing or able (RCW 11.130.220(1)).
|
||||||
|
|
||||||
|
### Guardian Duties for a Minor
|
||||||
|
|
||||||
|
A guardian for a minor is a fiduciary and, except as limited by the court, "has the duties and responsibilities of a parent regarding the minor's support, care, education, health, safety, and welfare" (RCW 11.130.230(1)). The guardian must act in the minor's best interest with reasonable care, diligence, and prudence; stay personally acquainted with the minor; use the minor's funds for current needs and conserve the rest; report and account if the court orders; inform the court of any change of dwelling; and consider the minor's preferences (RCW 11.130.230(1)–(2)). Unlike adult guardianships, minor guardianship has no automatic periodic reporting — a report is required only if the court orders one (RCW 11.130.230(2)(e)).
|
||||||
|
|
||||||
|
### Guardian Powers for a Minor
|
||||||
|
|
||||||
|
Except as limited by court order, a guardian has the powers a parent would have regarding the minor's support, care, education, health, safety, and welfare (RCW 11.130.235(1)): applying for benefits payable for the minor's support; taking custody and establishing the minor's dwelling within Washington (out-of-state moves require the relocation process of RCW 26.09.405 through 26.09.560 and court authorization); compelling child support; consenting to health care; and delegating age-appropriate responsibility to the minor (RCW 11.130.235(2)). Only if the minor has no parent may the court authorize the guardian to consent to adoption (RCW 11.130.235(3)). The court may also order parents to pay child support under chapter 26.19 RCW (RCW 11.130.255) and to maintain available employer-based health insurance under the conditions in RCW 11.130.260.
|
||||||
|
|
||||||
|
### An Alternative Short of Guardianship: Parental Delegation
|
||||||
|
|
||||||
|
A parent may delegate care, custody, or property powers over a minor by power of attorney for up to 24 months — except the power to consent to marriage or adoption (RCW 11.130.145) — allowing a temporary caregiver without a guardianship case.
|
||||||
|
|
||||||
|
### Court Oversight and Ending a Minor Guardianship
|
||||||
|
|
||||||
|
A minor guardianship terminates on the minor's death, adoption, emancipation, or attainment of majority, or when the court finds the basis for appointment no longer exists — unless termination would harm the minor and the minor's interest in continuing outweighs the parent's interest in restoring decision-making rights (RCW 11.130.240(1)). The minor, a parent, or any interested person may petition to terminate or modify the guardianship or remove the guardian (RCW 11.130.240(2)); the court may issue transitional orders when a guardianship ends (RCW 11.130.240(6)).
|
||||||
|
|
||||||
|
Anyone subject to a guardianship — or any person interested in the individual's welfare — who reasonably believes a guardian is breaching fiduciary duty may file a grievance with the court; the court must review it and may investigate, order reports, appoint a court visitor, or hold a hearing (RCW 11.130.140). Every guardianship petition must state whether the child is or may be an "Indian child" as defined in RCW 13.38.040; if so, the federal Indian Child Welfare Act and chapter 13.38 RCW apply, and the order must contain the required findings (RCW 11.130.250).
|
||||||
|
|
||||||
|
## Part 2 — Minor Guardianship vs. Parenting Plans
|
||||||
|
|
||||||
|
### Parenting Plans Are for Parents
|
||||||
|
|
||||||
|
A "parenting plan" allocates a child's care and decision-making between the child's own parents in a dissolution, legal separation, or parentage case; its objectives include the child's physical care, emotional stability, and each parent's authority and responsibilities (RCW 26.09.184(1)–(2)). Parents remain parents — a parenting plan does not appoint anyone.
|
||||||
|
|
||||||
|
A minor guardianship instead places a nonparent in a parent-like role without terminating parental rights — the order must preserve visitation unless restricted under RCW 26.09.191 or 26.09.192 (RCW 11.130.215(4)) — and ends at age 18 (RCW 11.130.215(7); RCW 11.130.240(1)(a)).
|
||||||
|
|
||||||
|
### De Facto Parentage Is a Different Path
|
||||||
|
|
||||||
|
A person who is not a legal parent but claims to have functioned as one may instead petition to be adjudicated a "de facto parent" under the Uniform Parentage Act, proving by a preponderance of the evidence residence with the child as a regular household member for a significant period, consistent caretaking, full parental responsibilities without expectation of pay, holding the child out as their own, and a bonded, dependent relationship that is parental in nature (RCW 26.26A.440(4)). De facto parentage makes the person a legal parent; guardianship makes the person a guardian while the child's parents stay parents.
|
||||||
|
|
||||||
|
## Part 3 — Adult Guardianship: The Basics
|
||||||
|
|
||||||
|
The same chapter 11.130 RCW governs adult guardianships, which replaced former chapter 11.88 RCW on January 1, 2022 (repealed by 2020 c 312 § 904, effective January 1, 2022, per its official disposition history).
|
||||||
|
|
||||||
|
### Grounds for an Adult Guardianship
|
||||||
|
|
||||||
|
A court may appoint a guardian for an adult only on clear and convincing evidence that the adult cannot meet essential requirements for physical health, safety, or self-care because the adult cannot receive and evaluate information or make or communicate decisions, even with supportive services, technological assistance, or supported decision making; that the appointment is necessary to prevent significant risk of harm; and that the adult's needs cannot be met by a protective arrangement instead of guardianship or another less restrictive alternative (RCW 11.130.265(1)). The court may not create a full guardianship if a limited guardianship or less restrictive alternative would work (RCW 11.130.265(2)). Age, eccentricity, poverty, or a diagnosis alone is not enough (RCW 11.130.265(3)).
|
||||||
|
|
||||||
|
### Who May Be Appointed
|
||||||
|
|
||||||
|
The court considers candidates in this order: a guardian currently acting in another jurisdiction; a person the adult nominated, including in a power of attorney; the adult's health care agent; a spouse or domestic partner; a relative or other individual who has shown special care and concern; then a certified professional guardian (RCW 11.130.305(1)). The order must state whether the adult retains the right to vote and to marry — the adult retains both unless the order contains findings supporting removal (RCW 11.130.310(1)–(2)).
|
||||||
|
|
||||||
|
### Duties, Powers, and Limits
|
||||||
|
|
||||||
|
An adult guardian is a fiduciary owing the highest duty of good faith, must promote the adult's self-determination, and may not substitute the guardian's own moral, religious, or philosophical values for the adult's (RCW 11.130.325(1)–(2)). The decision-making standard is "substituted judgment": make the decision the adult would make if able, unless that would unreasonably harm the adult, in which case act in the adult's best interest (RCW 11.130.325(4)–(5)). Core powers include establishing the adult's dwelling, consenting to health care, and receiving funds, benefits, and health information (RCW 11.130.330(1)). The guardian may not revoke or amend the adult's powers of attorney, and agents' decisions under those powers generally take precedence (RCW 11.130.335(1)). Involuntary commitment must follow chapters 10.77, 71.05, or 72.23 RCW (RCW 11.130.335(2)). Adults under guardianship retain the right to associate with people of their choosing unless the court authorizes a restriction (RCW 11.130.335(5)).
|
||||||
|
|
||||||
|
### Court Oversight
|
||||||
|
|
||||||
|
An adult guardian must file a care plan within 90 days of appointment; the court reviews and approves it (RCW 11.130.340(1), (4)). The guardian then files periodic reports — annual, biennial, or triennial, as the court sets — on the adult's condition, living arrangements, services, visits, and funds, which the court reviews for compliance and continued need (RCW 11.130.345(1)–(2), (5), (9)). A guardian may be removed for failure to perform duties or other good cause (RCW 11.130.350(1)), and the adult, the guardian, or an interested person may petition to terminate or modify the guardianship (RCW 11.130.355(1)). The grievance process of RCW 11.130.140 applies to adult guardianships too, and orders finding a professional guardian breached a fiduciary duty are sent to the Certified Professional Guardianship Board (RCW 11.130.140(5); RCW 11.130.670).
|
||||||
|
|
||||||
|
## Where to Learn More
|
||||||
|
|
||||||
|
- Official statute text: chapter 11.130 RCW — https://app.leg.wa.gov/rcw/default.aspx?cite=11.130
|
||||||
|
- Washington Law Help (Northwest Justice Project): "Minor guardianship guide" and "Children's rights in minor guardianship" — washingtonlawhelp.org
|
||||||
|
- Washington State Courts Guardian Portal — courts.wa.gov/guardianportal — forms, the free lay-guardian training module, and the Office of Guardianship and Elder Services.
|
||||||
109
books/mediation-adr.md
Normal file
109
books/mediation-adr.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# Washington Family Law: Mediation and Alternative Dispute Resolution (ADR)
|
||||||
|
|
||||||
|
> For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
This book explains the out-of-court ways Washington family law disputes get resolved: mediation, settlement conferences, parenting-plan dispute resolution, arbitration, collaborative law, and guardians ad litem. It is written for Washington State residents in plain English. Sources: RCW statutes at app.leg.wa.gov, WashingtonLawHelp.org, courts.wa.gov, King County Superior Court Local Family Law Rules, and WSBA materials. All citations were verified September 2026.
|
||||||
|
|
||||||
|
## What ADR means in a Washington family law case
|
||||||
|
|
||||||
|
"Alternative dispute resolution" (ADR) is any process that resolves your case without a judge deciding it at trial. In Washington family law, ADR is not just optional — courts are **directed to steer parties toward settlement**, and some form of ADR is often **required before trial**. The main processes are mediation, settlement conferences, arbitration, and collaborative law *(RCW 26.09.015; King County LFLR 16; RCW 26.09.184(4))*.
|
||||||
|
|
||||||
|
## Court-ordered mediation under RCW 26.09.015
|
||||||
|
|
||||||
|
In any divorce or legal separation proceeding, "the matter **may be set for mediation** of the contested issues before, or concurrent with, the setting of the matter for hearing" *(RCW 26.09.015(1))*. Key points from the statute:
|
||||||
|
|
||||||
|
- **Purpose:** reduce acrimony between the parties and "develop an agreement assuring the child's close and continuing contact with both parents." The mediator must use best efforts to settle the dispute *(RCW 26.09.015(1))*.
|
||||||
|
- **Who provides it:** each superior court may make a mediator available, using the most cost-effective mediation services readily available *(RCW 26.09.015(2)(a))*.
|
||||||
|
- **Reduced or waived fees:** counties **may** — and, when state funding is provided, **shall** — offer pre-decree and post-decree mediation at reduced or waived fees to parties within one year of the filing of the dissolution petition *(RCW 26.09.015(2)(b))*.
|
||||||
|
- **The child's interests:** the mediator must assess the needs and interests of the children involved and may interview a child when appropriate *(RCW 26.09.015(4))*.
|
||||||
|
- **Result:** any agreement reached in mediation is reported to the court and to counsel *(RCW 26.09.015(5))*.
|
||||||
|
|
||||||
|
Practical takeaway: mediation is voluntary in the sense that you negotiate freely, but the court can set your case for it, and participating in good faith is expected. If you reach agreement, the mediator reports it and the court can enter it as your final orders *(RCW 26.09.015(5); WashingtonLawHelp, "Divorce guide")*.
|
||||||
|
|
||||||
|
## Mediation when domestic violence or child abuse is involved
|
||||||
|
|
||||||
|
Mediation is **"generally inappropriate"** in cases involving domestic violence or child abuse. The law directs that, where appropriate, parties be given access to trained domestic violence advocates, and it allows mediation in such cases **only** when a victim requests it and the court makes a finding that mediation is appropriate under the circumstances, with the victim permitted to have a supporting person present *(RCW 26.09.016)*. In practice, local court rules also excuse parties from mandatory ADR in cases with documented domestic violence (see, e.g., King County LFLR 16 below). If you are a victim of domestic violence, tell the court and ask about these exceptions before agreeing to mediate *(RCW 26.09.016; King County LFLR 16(a))*.
|
||||||
|
|
||||||
|
## Confidentiality of mediation
|
||||||
|
|
||||||
|
Mediation in family law is governed by Washington's mediation confidentiality statute, **chapter 7.07 RCW** *(RCW 26.09.015(3)(a))*. Generally, mediation communications are privileged: what you say in mediation cannot be used against you in court, and a mediator can be compelled to testify only by court order for good cause *(RCW 26.09.015(3)(b); RCW 7.07.010)*. Exceptions: in **post-decree mediations mandated by a parenting plan**, mediation communications can be admitted in later proceedings for the limited purpose of proving abuse, neglect, abandonment, exploitation, or unlawful harassment of a child or family member *(RCW 26.09.015(3)(a)(i))*.
|
||||||
|
|
||||||
|
## Settlement conferences
|
||||||
|
|
||||||
|
A settlement conference is a meeting — usually with a judge, court commissioner, or volunteer attorney — where the parties try to settle the case on the eve of trial. It is more evaluative than mediation: the neutral may tell you how the case is likely to come out.
|
||||||
|
|
||||||
|
- WashingtonLawHelp notes that some courts **offer or require** settlement conferences before trial *(WashingtonLawHelp, "Divorce guide")*.
|
||||||
|
- **King County requires ADR:** "Except in cases involving domestic violence, or where waived by a court order, the parties in every case **shall participate in a settlement conference, mediation or other alternative dispute resolution process** conducted by a neutral third person no later than thirty (30) days before trial" *(King County LFLR 16(a))*. All parties and attorneys must personally attend and participate, come prepared with proposed final orders, a financial declaration, and (if parenting is at issue) a proposed parenting plan, and each party has a duty to act in good faith — failure can result in sanctions *(King County LFLR 16(b)–(d))*.
|
||||||
|
- King County also runs a **Volunteer Settlement Conference Program** that pairs low-income families with volunteer attorneys to resolve cases without trial *(King County Superior Court)*.
|
||||||
|
|
||||||
|
Because these rules are **local**, check your own county's "Local Family Law Rules" — most counties publish them on the county superior court website.
|
||||||
|
|
||||||
|
## Parenting-plan dispute resolution (RCW 26.09.184)
|
||||||
|
|
||||||
|
Every **permanent parenting plan** must include a section titled "Dispute Resolution" providing **"a process for resolving disputes, other than court action"** — such as counseling, mediation, or arbitration by a specified individual or agency — unless precluded or limited by the statutes protecting parents in domestic violence or abuse situations *(RCW 26.09.184(4), referencing RCW 26.09.187 and 26.09.191)*. The statute requires that:
|
||||||
|
|
||||||
|
- Preference be given to carrying out the parenting plan;
|
||||||
|
- Parents use the designated process for disputes about implementing the plan (except financial support disputes) unless there is an emergency;
|
||||||
|
- A written record be made of agreements and arbitration awards;
|
||||||
|
- A parent who **uses or frustrates the dispute resolution process without good reason** pays the other parent's attorney fees and financial sanctions; and
|
||||||
|
- The parties have a **right of review** from the dispute resolution process to the superior court *(RCW 26.09.184(4)(a)–(f))*.
|
||||||
|
|
||||||
|
This is why many parenting plans name a specific mediator or a "mediation-arbitration" provider for future disputes *(RCW 26.09.015(3)(a)(ii); RCW 26.09.184(4))*.
|
||||||
|
|
||||||
|
## Arbitration (Chapter 26.14 RCW — Uniform Family Law Arbitration Act)
|
||||||
|
|
||||||
|
Since 2023, Washington has a dedicated family law arbitration statute: the **Uniform Family Law Arbitration Act**, chapter 26.14 RCW *(RCW 26.14.005; 2023 c 61)*. Arbitration is like a private trial: the parties agree in writing to let a neutral arbitrator hear evidence and issue a **binding award**, which the court can confirm and enter as a judgment *(RCW 26.14.150, .200)*.
|
||||||
|
|
||||||
|
Important limits:
|
||||||
|
|
||||||
|
- An arbitrator **cannot** grant a divorce, legal separation, or annulment, terminate parental rights, grant an adoption or guardianship, or decide dependency status — only a judge can sign the final decree *(RCW 26.14.020(2))*.
|
||||||
|
- The act does **not** apply to protection orders (chapter 7.105 RCW), jurisdictional disputes, venue, or the court's authority to permanently modify a parenting plan based on a substantial change of circumstances *(RCW 26.14.020(3))*.
|
||||||
|
- Arbitrators must follow disclosure and qualification rules, and awards can be corrected, vacated, or appealed to the court *(RCW 26.14.070–.230)*.
|
||||||
|
|
||||||
|
Arbitration is chosen **by agreement of the parties**, typically written into a settlement agreement or parenting plan. It is usually faster and more private than court, but you give up a judge's decision and you generally pay the arbitrator's fees *(RCW 26.14.010, .040)*.
|
||||||
|
|
||||||
|
## Collaborative law (Chapter 7.77 RCW)
|
||||||
|
|
||||||
|
Collaborative law is a structured, voluntary process in which each spouse hires a lawyer, everyone signs a **collaborative law participation agreement**, and the group commits to negotiate a settlement **without going to court** *(RCW 7.77.010, .030)*. Key features under Washington's Uniform Collaborative Law Act:
|
||||||
|
|
||||||
|
- The agreement must be in writing, describe the matter, and identify each party's collaborative lawyer *(RCW 7.77.030)*.
|
||||||
|
- **Voluntary:** a tribunal cannot order a party into collaborative law over their objection, and either party may end the process at any time, with or without cause *(RCW 7.77.040(2), (6))*.
|
||||||
|
- **The disqualification rule:** if the process fails, the collaborative lawyers (and their law firms) are **disqualified** from representing the parties in court — which gives everyone a strong incentive to settle. The exception is asking a court to approve an agreement or to obtain an emergency order *(RCW 7.77.080)*.
|
||||||
|
- **Full, informal disclosure:** parties must exchange relevant information candidly without formal discovery *(RCW 7.77.100)*.
|
||||||
|
- **Emergency orders still available:** a court can issue emergency orders to protect health and safety during the process *(RCW 7.77.060)*.
|
||||||
|
- If a court case is already pending, filing the participation agreement operates as an application to **stay (pause) the proceeding** *(RCW 7.77.050)*.
|
||||||
|
|
||||||
|
Collaborative law often includes neutral experts — financial specialists, child specialists, or coaches — and ends with the court approving the parties' signed agreement *(RCW 7.77.070)*.
|
||||||
|
|
||||||
|
## Guardians ad litem (GALs)
|
||||||
|
|
||||||
|
A Guardian ad Litem (GAL) is a neutral person appointed by the court to represent the **best interests of a child** in a family law case *(RCW 26.12.175(1)(a))*.
|
||||||
|
|
||||||
|
- The GAL's role is to **investigate and report** factual information on the issues the court orders, and to make recommendations the court may weigh alongside the parties' positions. If a child expresses a preference about the parenting plan, the GAL must report it, along with whether the preference is voluntary and how well the child understands it *(RCW 26.12.175(1)(b))*.
|
||||||
|
- The GAL must file the report **at least 60 days before trial**, and the parties may file written responses *(RCW 26.12.175(1)(b)–(c))*.
|
||||||
|
- **Cost:** the court orders costs and fees to be paid by either or both parents according to their ability to pay; if both parents are indigent, the county bears the cost *(RCW 26.12.175(1)(d))*. Note that GAL fees are generally **not** covered by a GR 34 fee waiver *(WashingtonLawHelp, "Ask the court for a fee waiver")*.
|
||||||
|
- Separately, the court may order a **parenting evaluation** — an investigation and report concerning parenting arrangements — with or without a GAL *(RCW 26.09.220)*.
|
||||||
|
|
||||||
|
A GAL is not a lawyer for your child and is not your advocate; the GAL reports to the court about what is best for the child *(RCW 26.12.175(1)(b))*.
|
||||||
|
|
||||||
|
## Choosing the right process
|
||||||
|
|
||||||
|
| Situation | Usually best fit |
|
||||||
|
|---|---|
|
||||||
|
| You and your spouse can talk, mostly agree, want to save time/money | Mediation or settlement conference *(RCW 26.09.015; LFLR 16)* |
|
||||||
|
| You want to keep the case out of court entirely and have lawyers | Collaborative law *(RCW 7.77)* |
|
||||||
|
| You want a faster, private, binding decision and can agree on an arbitrator | Arbitration *(RCW 26.14)* |
|
||||||
|
| Post-divorce disputes about the parenting plan | The dispute resolution process named in your plan *(RCW 26.09.184(4))* |
|
||||||
|
| Domestic violence or child abuse | NO mediation unless you request it and the court approves; seek an advocate *(RCW 26.09.016)* |
|
||||||
|
| The court needs independent information about your child | Guardian ad Litem or parenting evaluation *(RCW 26.12.175; RCW 26.09.220)* |
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- RCW 26.09.015 (mediation proceedings), .016 (mediation and domestic violence), .184(4) (parenting plan dispute resolution), .187, .191, .220 — app.leg.wa.gov
|
||||||
|
- RCW 26.12.175 (guardians ad litem) — app.leg.wa.gov
|
||||||
|
- Chapter 26.14 RCW (Uniform Family Law Arbitration Act, 2023 c 61) — app.leg.wa.gov
|
||||||
|
- Chapter 7.77 RCW (Uniform Collaborative Law Act) — app.leg.wa.gov
|
||||||
|
- Chapter 7.07 RCW (mediation confidentiality) — app.leg.wa.gov
|
||||||
|
- King County Superior Court Local Family Law Rules, LFLR 16 (ADR) — kingcounty.gov
|
||||||
|
- WashingtonLawHelp.org — "Divorce guide" and "Ask the court for a fee waiver" (reviewed 05/07/2026)
|
||||||
|
- King County Superior Court — Settlement Conference Program
|
||||||
98
books/modification-enforcement.md
Normal file
98
books/modification-enforcement.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
# Modifying and Enforcing Washington Family Law Orders
|
||||||
|
|
||||||
|
This book is for Washington State residents who already have final orders — a divorce decree, parenting plan, child support order, or spousal maintenance (alimony) order — and need to change one or make the other side follow it. Every statute cite was verified September 2026 against the official text at app.leg.wa.gov.
|
||||||
|
|
||||||
|
## Modifying a parenting plan
|
||||||
|
|
||||||
|
A parenting plan is a court order and cannot be changed just because you are unhappy with it. The court may not modify a parenting plan unless it finds, based on facts that arose after the plan was entered (or were unknown to the court then), that (a) a **substantial change has occurred in the circumstances of the child or the nonmoving parent**, and (b) the modification is **in the best interest of the child and necessary** to serve that interest (RCW 26.09.260(1)). A parent's military duties alone are not a substantial change (RCW 26.09.260(1)).
|
||||||
|
|
||||||
|
Even with changed circumstances, the court **keeps the existing residential schedule** unless one of four things is shown (RCW 26.09.260(2)):
|
||||||
|
|
||||||
|
1. The **parents agree**;
|
||||||
|
2. The child has been **integrated into the moving parent's family with the other parent's consent**, in substantial deviation from the plan;
|
||||||
|
3. The child's **present environment is detrimental** to the child's physical, mental, or emotional health, and the harm of a change is outweighed by its advantage to the child; or
|
||||||
|
4. The nonmoving parent has been found **in contempt at least twice within three years** for failing the residential provisions, or has been **convicted of custodial interference**.
|
||||||
|
|
||||||
|
A custodial interference conviction (RCW 9A.40.060 or 9A.40.070) is itself a substantial change of circumstances (RCW 26.09.260(3)). The court may also reduce or restrict a parent's contact using the limitation criteria in RCW 26.09.191 and 26.09.192 (RCW 26.09.260(4)).
|
||||||
|
|
||||||
|
### Minor modifications and nonresidential changes
|
||||||
|
|
||||||
|
Smaller changes are easier. The court may make a **minor modification** to the residential schedule — one that does not change which parent the child lives with most of the time — on a substantial change in circumstances of **either parent or the child**, without the stricter factors above, if the change (RCW 26.09.260(5)): does not exceed **24 full days per calendar year**; results from the **non-primary parent moving** or an **involuntary work-schedule change** that makes the plan impractical; or adds up to no more than **90 overnights per year** when the plan does not give the non-primary parent reasonable time. A move by the parent the child lives with most is not a "minor modification" — that triggers the relocation rules below. Guardrails: a parent restricted under RCW 26.09.191 or 26.09.192 must show a change specifically related to the limitation (RCW 26.09.260(7)); a repeat 90-overnight motion within 24 months faces the stricter factors (RCW 26.09.260(5)); and a parent who has not completed court-ordered evaluations, treatment, or classes cannot expand time (RCW 26.09.260(9)). **Nonresidential aspects** of the plan (decision-making, dispute resolution) can be adjusted on a substantial change plus the child's best interest (RCW 26.09.260(10)). A **bad-faith motion** can cost the moving party the other parent's attorney fees and costs (RCW 26.09.260(13)).
|
||||||
|
|
||||||
|
### The "adequate cause" gate and venue
|
||||||
|
|
||||||
|
A party seeking to modify must submit an **affidavit setting forth facts** supporting the change; the court **denies the motion unless the affidavits establish "adequate cause"** for a hearing (RCW 26.09.270). The action may be filed in the county where the children live, the court that entered the order, or the county where the custodial parent lives (RCW 26.09.280).
|
||||||
|
|
||||||
|
## Modifying child support
|
||||||
|
|
||||||
|
A party may petition for child support modification based on **substantially changed circumstances at any time** (RCW 26.09.170(5)(a)). Two limits matter: modification applies **only to installments accruing after the petition** — no retroactive change (RCW 26.09.170(1)(a)) — and **voluntary unemployment or underemployment alone is not a substantial change** (RCW 26.09.170(5)(b)); a parent who quits or takes a voluntary pay cut may still have income imputed under the support schedule.
|
||||||
|
|
||||||
|
Some modifications skip the changed-circumstances requirement:
|
||||||
|
|
||||||
|
- **After one year**, an order may be modified without it for **severe economic hardship**, to extend support for a child still in high school past 18, or to add an **automatic adjustment** provision (RCW 26.09.170(8)).
|
||||||
|
- **After 24 months** from entry or the last adjustment, either party may seek adjustment based on **changed incomes or changes to the schedule's economic table**, by motion and worksheets (RCW 26.09.170(9)). A change over 30% causing significant hardship may be phased in two six-month steps (RCW 26.09.170(9)(c)).
|
||||||
|
- An order may be modified to add **incarceration abatement** (support reduced to $10 per month during confinement of six months or more) and again on release, without changed circumstances (RCW 26.09.170(6)–(7); RCW 26.09.320).
|
||||||
|
- **DCS** may seek modification when public assistance is paid or a review is requested, using a 15% deviation threshold (RCW 26.09.170(10)–(11)).
|
||||||
|
|
||||||
|
**Procedure.** Modification begins with a **petition and worksheets**; the filing fee is $20 (RCW 26.09.175(1)). The response is due in **20 days** (60 days out of state) or default judgment follows (RCW 26.09.175(4)). The case is normally decided **on affidavits and worksheets alone**; oral testimony requires extraordinary circumstances, and testimony may be remote (RCW 26.09.175(6)–(8)). The decree itself may require **automatic periodic adjustments** (RCW 26.09.100), and the support schedule applies in every modification proceeding (RCW 26.19.035(1), (3)).
|
||||||
|
|
||||||
|
## Modifying spousal maintenance
|
||||||
|
|
||||||
|
Maintenance is harder to change than child support. A decree respecting maintenance may be modified **only as to installments accruing after the petition** and **only on a showing of a substantial change of circumstances** (RCW 26.09.170(1)). The **property division** may not be modified at all unless conditions exist that would justify reopening a judgment — rare (RCW 26.09.170(1)). Maintenance **ends automatically** on the death of either party, or on the recipient's remarriage or registration of a new domestic partnership, unless the decree or a written agreement says otherwise (RCW 26.09.170(2)). Cohabitation with a new partner is not, by statute, a termination event.
|
||||||
|
|
||||||
|
## What is a "substantial change of circumstances"?
|
||||||
|
|
||||||
|
It means something real and meaningful that has happened since the order. The statutory anchors: for **parenting plans**, a substantial change in the circumstances of the **child or the nonmoving parent** (RCW 26.09.260(1)); for **maintenance**, a substantial change judged against what the court contemplated when it made the award (RCW 26.09.170(1)(b)); for **child support**, "substantially changed circumstances" — with the carve-out that voluntary unemployment or underemployment alone does not qualify (RCW 26.09.170(5)) and the one-year and 24-month paths that skip the requirement (RCW 26.09.170(8)–(9)). In practice, qualifying changes are usually significant income changes, a parent's move, a child's changing needs, or a serious problem in the child's environment.
|
||||||
|
|
||||||
|
## Relocation: when a move triggers modification
|
||||||
|
|
||||||
|
Washington's relocation scheme, RCW 26.09.405 through 26.09.560, applies to plans entered after June 8, 2000, and older orders silent on relocation (RCW 26.09.405). "Relocate" means a change in the child's principal residence, permanent or protracted (RCW 26.09.410).
|
||||||
|
|
||||||
|
**Notice.** A parent with majority residential time — or either parent in a substantially equal-time arrangement — must notify everyone entitled to residential time of an intended relocation (RCW 26.09.430). Notice is by personal service or return-receipt mail, at least **60 days before the move** (or within 5 days of learning the needed information), with specific reasons and a statutory warning that an objection must be filed within **30 days** or the move will be permitted (RCW 26.09.440). Failure to give notice is grounds for **sanctions, including contempt** (RCW 26.09.470).
|
||||||
|
|
||||||
|
**Objecting.** The other parent files a petition to modify the parenting plan (or another adequate proceeding) within 30 days, plus a three-day mail waiting period (RCW 26.09.480(1)). Once an objection is filed, the relocating parent generally **may not move the child** without a court order, and a hearing noticed within 15 days freezes the move (RCW 26.09.480(2)). If no one objects in time, the relocation is **permitted** (except for good cause) and the proposed revised schedule takes effect (RCW 26.09.500).
|
||||||
|
|
||||||
|
**The presumption.** There is a **rebuttable presumption the relocation will be permitted**; the objecting parent must show the move's detrimental effect outweighs its benefit to the child and the relocating person under eleven unweighted factors (RCW 26.09.520). But where parents have **substantially equal residential time** — 45% or more of the child's time with each parent — **there is no presumption**; the court decides in the child's best interests using the same factors (RCW 26.09.525). Relocation or objections made to harass or run up costs can draw **sanctions** (RCW 26.09.550); relocation hearings get docket priority (RCW 26.09.560).
|
||||||
|
|
||||||
|
**The modification hook.** A relocation dispute is itself a modification pathway: either parent may petition to modify the plan — including changing the child's primary residence — **without showing adequate cause other than the proposed relocation itself**. The court first decides whether to permit or restrain the move, then what change to make to the plan (RCW 26.09.260(6)).
|
||||||
|
|
||||||
|
## Enforcing parenting plans: contempt
|
||||||
|
|
||||||
|
If the other parent violates the plan, the remedy is a **contempt action**. Self-help is barred: one parent's violation does **not suspend** the other's obligations, and conditioning support on residential time is bad faith (RCW 26.09.160(1)). A parent who in bad faith fails to comply with residential provisions faces (RCW 26.09.160(2)):
|
||||||
|
|
||||||
|
- **Make-up time** equal to the time missed;
|
||||||
|
- Payment of the other parent's **court costs and reasonable attorney fees**;
|
||||||
|
- A **civil penalty of at least $100**;
|
||||||
|
- **Jail up to 180 days**, but only if the parent is presently **able** to comply and presently **unwilling**.
|
||||||
|
|
||||||
|
A **second violation within three years** doubles the make-up time and raises the penalty to at least $250 (RCW 26.09.160(3)). Present ability to comply is **presumed**; the accused parent must prove inability or a reasonable excuse by a preponderance of the evidence (RCW 26.09.160(4)). Monetary awards are collected like civil judgments (RCW 26.09.160(5)).
|
||||||
|
|
||||||
|
## Enforcing support: contempt and continuing jurisdiction
|
||||||
|
|
||||||
|
A parent who fails to pay support or maintenance faces a contempt action under chapter 7.21 RCW: petition or motion, order to show cause, and — if warned and absent — a **bench warrant** (RCW 26.18.050(1)–(3)). A parent claiming inability to pay must prove **due diligence** in seeking employment or otherwise becoming able to comply (RCW 26.18.050(4)). The court **retains continuing jurisdiction** to enforce until **all arrearages are paid** — arrears do not vanish when the child turns 18 (RCW 26.18.050(5)).
|
||||||
|
|
||||||
|
## Income withholding
|
||||||
|
|
||||||
|
Support is normally paid by automatic **income withholding**. A wage-assignment or income-withholding order is available if the support order allows immediate withholding, or if the payer is **more than 15 days past due in an amount of at least one month's obligation** (RCW 26.18.070(1)). The court directs the order to the employer and the clerk forwards it to the **Washington State Support Registry** within five days (RCW 26.18.080). Support orders must state that withholding may be taken against wages, earnings, assets, or benefits without further notice (RCW 26.23.050). A withholding order can be quashed or modified only for **extreme hardship or substantial injustice**; after 12 consecutive months of current payments the payer may move to terminate it unless the recipient shows good cause (RCW 26.18.140).
|
||||||
|
|
||||||
|
## Division of Child Support (DCS)
|
||||||
|
|
||||||
|
The **Division of Child Support (DCS)** runs the State Support Registry and must provide enforcement services whenever public assistance is paid, whenever a parent applies, or when payments route through the registry (RCW 26.23.045(1)). Services continue until a court orders payments out of the registry (RCW 26.23.045(2)). DCS has administrative enforcement tools beyond court process and can also seek modification in court (RCW 26.09.170(10)–(11)).
|
||||||
|
|
||||||
|
## License suspension for unpaid child support
|
||||||
|
|
||||||
|
If a parent falls out of compliance, DCS may serve a **notice of noncompliance** with intent to suspend licenses (RCW 74.20A.320(1)). The parent has **20 days** to: pay the arrears in full; request an administrative hearing (RCW 74.20A.322); arrange a payment schedule (RCW 74.20A.326); or **file a court action to modify support**, which stays suspension up to six months (RCW 74.20A.320(2)(b)). Otherwise DCS certifies noncompliance to the Department of Licensing and other agencies, and the parent's **driver's license — plus fishing, hunting, and occupational licenses — is suspended or not renewed** until DCS issues a release (RCW 74.20A.320(2)(c)). Support orders must warn that licenses may be suspended for noncompliance (RCW 26.23.050).
|
||||||
|
|
||||||
|
## Interest on unpaid support
|
||||||
|
|
||||||
|
Judgments for unpaid child support bear **12% interest** (RCW 4.56.110(2)), and DCS may assess **12% per year** on unpaid support accrued under registry orders, unless the order specifies a different rate (RCW 26.23.030(2)).
|
||||||
|
|
||||||
|
## Deadlines, back pay, and time limits
|
||||||
|
|
||||||
|
- **No back pay.** Modification applies only to installments accruing after the petition or motion (RCW 26.09.170(1)(a)) — so file promptly, and note the duty to pay is not suspended by the other side's misconduct (RCW 26.09.160(1)).
|
||||||
|
- **No waiting period to modify.** Child support modification may be sought "at any time" (RCW 26.09.170(5)(a)); parenting plan modification has no fixed waiting period but must pass the adequate-cause gate (RCW 26.09.270). The one-year and 24-month windows only concern modifications made *without* proving changed circumstances (RCW 26.09.170(8)–(9)).
|
||||||
|
- **Child support arrears last longest.** Collection may be brought within **10 years of the youngest child's 18th birthday** (RCW 4.16.020(3)), with execution available in that window (RCW 6.17.020(2)) — extendable in 10-year increments (RCW 6.17.020(3)).
|
||||||
|
- **Other judgments — including maintenance arrearages** — are generally enforceable for **10 years from entry**, renewable for another 10 by application within 90 days before expiration, granted as a matter of right (RCW 6.17.020(1), (3); RCW 4.16.020(2)). No judgment is enforceable beyond 20 years from entry except as provided in RCW 4.16.020(2) or (3) (RCW 6.17.020(7)).
|
||||||
|
- **Relocation deadlines are strict.** The 60-day notice and 30-day objection clocks bind; missing the objection deadline can mean the move is permitted and the proposed schedule takes effect (RCW 26.09.440; RCW 26.09.500).
|
||||||
123
books/overview-intake.md
Normal file
123
books/overview-intake.md
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
# Washington Family Law: Court Process Overview and Intake
|
||||||
|
|
||||||
|
> For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
This book explains how a family law case (mainly divorce) actually moves through Washington's courts, who can file, what it costs, whether you need a lawyer, and which specialist to consult for which problem. It is written for Washington State residents in plain English. Sources: RCW statutes at app.leg.wa.gov, WashingtonLawHelp.org, courts.wa.gov, WSBA, and Legal Voice. All citations were verified September 2026.
|
||||||
|
|
||||||
|
## The basics: what kind of case do you have?
|
||||||
|
|
||||||
|
Washington family law cases are filed in **superior court**, not district court. A proceeding for dissolution of marriage (divorce), dissolution of a state-registered domestic partnership, legal separation, or a declaration concerning the validity of a marriage may be filed "in the superior court of the county where the petitioner resides" *(RCW 26.09.010(2))*.
|
||||||
|
|
||||||
|
Washington is a **"no-fault" divorce state**. To get a divorce, you only need to allege that the marriage is "irretrievably broken" — you do not have to prove your spouse did anything wrong *(RCW 26.09.030; WashingtonLawHelp, "Divorce guide")*. If the other spouse denies the marriage is irretrievably broken, the court considers the circumstances and prospects for reconciliation, but can still enter a decree of dissolution *(RCW 26.09.030(1)(c))*.
|
||||||
|
|
||||||
|
There is **no jury** in these cases — civil practice rules apply except that trial by jury is dispensed with *(RCW 26.09.010(1))*.
|
||||||
|
|
||||||
|
A divorce case is titled "In re the marriage of ..." and the initial paperwork is called a **Petition**; the answering paperwork is a **Response** *(RCW 26.09.010(2), (4))*.
|
||||||
|
|
||||||
|
## Residency requirements
|
||||||
|
|
||||||
|
Washington's residency rules are among the most flexible in the country. A court can grant a divorce when the person filing (the petitioner) is any of the following:
|
||||||
|
|
||||||
|
1. A resident of Washington State;
|
||||||
|
2. A member of the armed forces stationed in Washington State; or
|
||||||
|
3. Married (or in a registered domestic partnership) to a Washington resident or to an armed-forces member stationed here *(RCW 26.09.030)*.
|
||||||
|
|
||||||
|
There is **no minimum length of time** you must live in Washington before filing — the statute requires only that you be a resident when you file, and it does not impose a waiting period of residence *(RCW 26.09.030)*. You also do not need to have been married in Washington *(RCW 26.09.030; WashingtonLawHelp, "Divorce guide")*.
|
||||||
|
|
||||||
|
## The court process timeline
|
||||||
|
|
||||||
|
A typical divorce in Washington moves through these stages *(WashingtonLawHelp, "Divorce guide", reviewed 05/07/2026; RCW 26.09.030)*:
|
||||||
|
|
||||||
|
### Step 1 — Prepare and file the Petition
|
||||||
|
|
||||||
|
You must use the official, state-approved family law forms — use of the approved forms is mandatory *(RCW 26.09.006)*. You can complete the forms free online through **Washington Forms Online** (a guided interview at courts.wa.gov) or download the packet from the "Dissolution (Divorce)" page at courts.wa.gov *(WashingtonLawHelp; courts.wa.gov/forms)*.
|
||||||
|
|
||||||
|
### Step 2 — File with the Superior Court Clerk
|
||||||
|
|
||||||
|
File the originals with the clerk in the county where the petitioner lives *(RCW 26.09.010(2))*. The clerk stamps your copies with the case number and filing date. Expect a filing fee (see "Court fees" below); many counties also allow electronic filing *(WashingtonLawHelp, "Divorce guide")*.
|
||||||
|
|
||||||
|
### Step 3 — Serve the other spouse
|
||||||
|
|
||||||
|
The other spouse must receive copies of the papers. Usually a third party personally serves them. Exceptions: your spouse can sign the "Service Accepted" form, or can sign an "Agreement to Join Petition" (joinder) if they agree to everything *(WashingtonLawHelp, "Divorce guide")*.
|
||||||
|
|
||||||
|
- Your spouse typically has **20 days** to respond after in-state personal service *(WashingtonLawHelp)*.
|
||||||
|
- If served **outside Washington**, they have **60 days** *(RCW 4.28.180; WashingtonLawHelp)*.
|
||||||
|
- If served by mail or publication, they have **90 days** *(WashingtonLawHelp)*.
|
||||||
|
|
||||||
|
If the respondent does not respond, you can ask the judge for an **order of default**, and the case can then be finished without the spouse's participation *(WashingtonLawHelp, "Divorce guide")*.
|
||||||
|
|
||||||
|
### Step 4 — Temporary orders, if needed
|
||||||
|
|
||||||
|
While the case is pending, either spouse can ask for temporary orders covering children, financial support, who pays which bills, use of property, and safety. The statute allows temporary maintenance or child support on motion with an affidavit, and temporary restraining orders or preliminary injunctions (for example, against disposing of property, disturbing the peace, or removing a child from the jurisdiction) *(RCW 26.09.060(1)–(6))*.
|
||||||
|
|
||||||
|
### Step 5 — The 90-day waiting period
|
||||||
|
|
||||||
|
Washington has a **mandatory 90-day waiting period**. The court cannot enter the final decree until **90 days have passed since the petition was filed AND since service of the summons** (or first publication) *(RCW 26.09.030)*. This is the minimum even if you and your spouse agree on everything; contested cases take longer *(WashingtonLawHelp, "Divorce guide")*.
|
||||||
|
|
||||||
|
### Step 6 — Information gathering and settlement
|
||||||
|
|
||||||
|
During the waiting period, spouses exchange financial information (informally or through formal "discovery"), negotiate, and often attend mediation or a settlement conference *(WashingtonLawHelp, "Divorce guide")*. Most cases settle.
|
||||||
|
|
||||||
|
### Step 7 — Final orders
|
||||||
|
|
||||||
|
- **By agreement:** both spouses sign the final orders (Findings, Decree, Parenting Plan, Child Support Order) and a judge reviews and signs them.
|
||||||
|
- **By default:** if the spouse never responded and default was entered.
|
||||||
|
- **By trial:** if you cannot agree, a judge decides at a trial. Some counties set a trial date when you file; others require you to request one *(WashingtonLawHelp, "Divorce guide")*.
|
||||||
|
|
||||||
|
You are divorced when the judge **signs the Final Divorce Order and it is filed** with the clerk; the clerk then forwards a Certificate of Dissolution to the State Registrar of Vital Statistics *(WashingtonLawHelp, "Divorce guide")*.
|
||||||
|
|
||||||
|
## Do you need a lawyer?
|
||||||
|
|
||||||
|
You are **not required** to have a lawyer — many people file for divorce without one ("pro se"), using the free official forms *(courts.wa.gov/forms; RCW 26.09.006)*. However, WashingtonLawHelp strongly recommends talking with a lawyer before filing, because a divorce can affect rights you may not know about — for example, a share of your spouse's pension or retirement benefits that you could lose if not protected in the final orders *(WashingtonLawHelp, "Divorce guide")*.
|
||||||
|
|
||||||
|
Middle options exist between full representation and going it alone:
|
||||||
|
|
||||||
|
- **Advice-only or document review:** pay a lawyer to review your papers or advise you, without hiring them for the whole case *(WashingtonLawHelp, "Divorce basics")*.
|
||||||
|
- **Limited-scope ("unbundled") services:** hire a lawyer for one task only. Find lawyers offering these services through the WSBA's **Find Legal Help** page *(wsba.org/for-the-public/find-legal-help)*.
|
||||||
|
- **Reduced-fee help:** the WSBA **Moderate Means Program** connects moderate-income people with lawyers offering reduced fees in family law *(wsba.org)*.
|
||||||
|
- **Free legal aid:** low-income residents can call the **CLEAR hotline at 1-888-201-1014** (weekdays 9:15 a.m.–12:15 p.m.); **in King County call 2-1-1** *(Northwest Justice Project, nwjustice.org/clear-hotline)*.
|
||||||
|
|
||||||
|
If your spouse has a lawyer, has significant assets or a pension, or if domestic violence is involved, obtaining a lawyer is especially important *(WashingtonLawHelp, "Divorce guide")*.
|
||||||
|
|
||||||
|
## Court fees and fee waivers
|
||||||
|
|
||||||
|
The base fee for filing the first document in a civil case (including divorce) is **$200** *(RCW 36.18.020(2)(a))*, plus statutory surcharges of **$40** and **$50** *(RCW 36.18.020(5)(c), (6))*. Counties with a courthouse facilitator program may add up to a **$20** surcharge on family law cases *(RCW 26.12.240)*. WashingtonLawHelp currently lists the total divorce filing fee as about **$364**, and notes superior court filing fees range from about $36 to $364 depending on case type — check your county clerk for the exact amount *(WashingtonLawHelp, "Divorce guide" and "Ask the court for a fee waiver", reviewed 05/07/2026)*.
|
||||||
|
|
||||||
|
### Fee waivers (GR 34)
|
||||||
|
|
||||||
|
If you cannot afford the fees, you can ask the judge to waive them under **General Rule 34**. You are eligible if you receive needs-based public benefits (TANF, SSI, SNAP/food stamps, federal poverty-related veterans' benefits, or similar), **or** your household income is **at or below 125% of the federal poverty guideline**, **or** your income is above that level but recurring basic living expenses leave you unable to pay, **or** other compelling circumstances exist *(GR 34; WashingtonLawHelp)*. A waiver covers **all required fees and surcharges**, and courts cannot charge a fee just to apply *(GR 34; WashingtonLawHelp, citing Jafar v. Webb)*. Optional services — such as mediation you choose or Guardian ad Litem fees — are generally **not** covered *(WashingtonLawHelp, "Ask the court for a fee waiver")*. Most protection order petitions are free to file *(WashingtonLawHelp)*.
|
||||||
|
|
||||||
|
## Which specialist for which problem
|
||||||
|
|
||||||
|
| Your problem | Who to consult |
|
||||||
|
|---|---|
|
||||||
|
| General divorce, property, parenting, trial | A **family law attorney** (wsba.org find-a-lawyer) |
|
||||||
|
| You and your spouse mostly agree; want help reaching an agreement | A **family law mediator** (see the companion book, "Mediation and ADR") |
|
||||||
|
| Child custody/parenting is contested; the court wants an independent view of the child's best interests | A **Guardian ad Litem (GAL)**, appointed by the court *(RCW 26.12.175)* |
|
||||||
|
| Court-ordered investigation of parenting arrangements (home studies, evaluations) | A court-ordered **parenting evaluator / investigator** *(RCW 26.09.220)* |
|
||||||
|
| Domestic violence, safety planning, protective orders | A **domestic violence advocate** through your local DV program; courts may connect parties to trained advocates *(RCW 26.09.016)*; National DV Hotline 1-800-799-7233 |
|
||||||
|
| Help filling out and filing forms without a lawyer | Your county's **courthouse facilitator** program, where available *(RCW 26.12.240)*; Washington Forms Online |
|
||||||
|
| Dividing retirement accounts, pensions, QDROs, business valuation | A **CPA or QDRO/retirement specialist** working with your attorney (practical guidance; not a statutory role) |
|
||||||
|
| Emotional support for you or the children | A licensed **therapist or counselor** (not a legal role; see Legal Voice resources) |
|
||||||
|
|
||||||
|
## Where to get help
|
||||||
|
|
||||||
|
- **WashingtonLawHelp.org** — free plain-language guides on divorce, custody, and support.
|
||||||
|
- **courts.wa.gov/forms** — official forms, instructions, and Washington Forms Online.
|
||||||
|
- **WSBA** (wsba.org/for-the-public/find-legal-help) — find a lawyer; Moderate Means Program.
|
||||||
|
- **CLEAR hotline** 1-888-201-1014 (weekdays 9:15 a.m.–12:15 p.m.); King County: 2-1-1.
|
||||||
|
- **Legal Voice** (legalvoice.org) — free legal rights resources.
|
||||||
|
- Your **county clerk or family law facilitator** for local procedures and fees.
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- RCW 26.09.010, .006, .016, .030, .060 — app.leg.wa.gov (Title 26, Chapter 26.09)
|
||||||
|
- RCW 4.28.180 (out-of-state service) — app.leg.wa.gov
|
||||||
|
- RCW 36.18.020 (clerk fees/surcharges) — app.leg.wa.gov
|
||||||
|
- RCW 26.12.240 (courthouse facilitator program) — app.leg.wa.gov
|
||||||
|
- GR 34 (fee waiver) — courts.wa.gov/court_rules/pdf/GR/GA_GR_34_00_00.pdf
|
||||||
|
- WashingtonLawHelp.org — "Divorce guide" and "Ask the court for a fee waiver" (both reviewed 05/07/2026)
|
||||||
|
- courts.wa.gov/forms — "Dissolution (Divorce)" forms page; Washington Forms Online / Guide & File
|
||||||
|
- wsba.org — "Find Legal Help"
|
||||||
|
- nwjustice.org — "CLEAR Hotline"
|
||||||
|
- legalvoice.org — legal resources for Washington residents
|
||||||
95
books/parentage.md
Normal file
95
books/parentage.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
# Parentage in Washington State: The Uniform Parentage Act
|
||||||
|
|
||||||
|
## What Parentage Is and Why It Matters
|
||||||
|
|
||||||
|
"Parentage" is the legal parent-child relationship. It is different from biology: biology matters in Washington, but it is not the only path to legal parentage — and sometimes not enough by itself. Once parentage is established, the relationship "applies for all purposes" — support, custody and parenting time, inheritance and benefits, medical decisions, and more — unless a different law says otherwise (RCW 26.26A.110). The law treats children of married and unmarried parents equally: "A parent-child relationship extends equally to every child and parent, regardless of the marital status of the parent" (RCW 26.26A.105).
|
||||||
|
|
||||||
|
In practice, an unmarried father whose parentage was never legally established has no legal right to custody or parenting time and no enforceable duty of support; once parentage is established, both parents gain rights *and* obligations. Only Washington superior courts may adjudicate parentage (RCW 26.26A.030), under chapter 26.26A RCW — Washington's version of the Uniform Parentage Act (UPA).
|
||||||
|
|
||||||
|
## Washington's Law: The Uniform Parentage Act
|
||||||
|
|
||||||
|
The Washington Uniform Parentage Act took effect **January 1, 2019** (RCW 26.26A.903) and applies even to cases already pending then (RCW 26.26A.902). The act is officially titled the "uniform parentage act" (RCW 26.26A.005) and uses these gender-neutral terms: an **acknowledged parent** became a parent by signing an acknowledgment form; an **adjudicated parent** is one a court has declared a parent; an **alleged genetic parent** is someone alleged to be the genetic parent whose parentage is not yet established (RCW 26.26A.010). The chapter governs every determination of parentage (RCW 26.26A.020), and rules written for a "father" also apply to a mother, and vice versa (RCW 26.26A.060).
|
||||||
|
|
||||||
|
## The Seven Ways Parentage Is Established
|
||||||
|
|
||||||
|
The law lists seven ways a parent-child relationship comes into existence (RCW 26.26A.100): giving birth; being a presumed parent; being adjudicated a parent; adopting the child; signing an acknowledgment of parentage; becoming a parent through assisted reproduction; or becoming a parent through a surrogacy agreement.
|
||||||
|
|
||||||
|
## Presumed Parents: Marriage, Domestic Partnership, and Four Years of Care
|
||||||
|
|
||||||
|
Washington law presumes someone is a parent — without any paperwork or court order — in several situations (RCW 26.26A.115(1)):
|
||||||
|
|
||||||
|
- The person was **married to, or in a state-registered domestic partnership with**, the person who gave birth, and the child is **born during** the marriage or partnership (RCW 26.26A.115(1)(a)(i)).
|
||||||
|
- The marriage or partnership ended (death, dissolution, annulment, invalidity, or legal separation), but the child is born **within 300 days** after it ended (RCW 26.26A.115(1)(a)(ii)).
|
||||||
|
- The couple **married or registered a domestic partnership after the birth**, and the person asserted parentage in a record filed with the state registrar of vital statistics, or agreed to be — and is — named as a parent on the birth record (RCW 26.26A.115(1)(a)(iii)).
|
||||||
|
- The person **lived in the same household with the child for the child's first four years** (allowing temporary absences) and openly held the child out as their own (RCW 26.26A.115(1)(b)).
|
||||||
|
|
||||||
|
A presumption can be overcome — but only by a court adjudication or a valid denial of parentage, not by argument or by a DNA test alone (RCW 26.26A.115(2)). After the child turns four, a presumption is harder to overcome: the court can do so only if the presumed parent is not the genetic parent **and** never lived with the child **and** never held the child out as their own, or if the child has more than one presumed parent (RCW 26.26A.435(2)).
|
||||||
|
|
||||||
|
## Acknowledgment of Parentage: The Hospital Form
|
||||||
|
|
||||||
|
The most common path for unmarried parents is the **acknowledgment of parentage** — the form offered at the hospital after birth — signed by the person who gave birth and the alleged genetic father (or a presumed parent or intended parent) (RCW 26.26A.200). It must be notarized or witnessed, state that the child has no other presumed or acknowledged parent, and state that the signers understand it is the same as a court adjudication of parentage that can be challenged only in limited circumstances, with challenges barred four years after it takes effect (RCW 26.26A.205). It can be signed before birth and is valid even if signed by a minor; it takes effect on the later of the birth or the filing with the state registrar of vital statistics (RCW 26.26A.215). Once filed, it "is equivalent to an adjudication of parentage" and gives the acknowledged parent all the rights and duties of a parent (RCW 26.26A.220). The Department of Health may charge a filing fee (RCW 26.26A.225), though signing at the hospital shortly after birth (within five days) is typically free (DSHS Division of Child Support).
|
||||||
|
|
||||||
|
A **denial of parentage** lets a presumed or alleged genetic parent state they are not the parent; it is valid only if someone else's acknowledgment is filed with it, it is notarized or witnessed, and the person has not previously acknowledged or been adjudicated a parent (RCW 26.26A.210). A valid denial is equivalent to an adjudication of nonparentage and discharges all parental rights and duties (RCW 26.26A.220(2)).
|
||||||
|
|
||||||
|
**Backing out.** A signer may rescind an acknowledgment or denial by filing a rescission with the state registrar within **60 days** of the effective date, or before the first court hearing about the child in a case the signer is a party to — whichever comes first (RCW 26.26A.235). After 60 days, for up to **four years**, a signer can still challenge, but only for **fraud, duress, or material mistake of fact** (RCW 26.26A.240).
|
||||||
|
|
||||||
|
## Genetic Testing
|
||||||
|
|
||||||
|
In a parentage case, the court **must order genetic testing** if a party files a sworn statement alleging a reasonable possibility they are — or are not — the genetic parent (RCW 26.26A.310(1)). Testing cannot be ordered while the child is still in the womb (RCW 26.26A.310(3)), and a testing order is enforceable by contempt (RCW 26.26A.310(8)). Exception: when a presumed parent or de facto parent claim exists, or someone challenges an acknowledgment, the court **may deny** testing after weighing the best-interest factors in RCW 26.26A.460(1)–(2) (RCW 26.26A.310(6)).
|
||||||
|
|
||||||
|
Testing must use methods experts reasonably rely on, at an accredited laboratory; a proper chain of custody makes results admissible without testimony (RCW 26.26A.315, .320). A person is identified as the genetic parent if testing shows at least a **99 percent probability of parentage** and a combined relationship index of at least 100 to 1 (RCW 26.26A.325(1)). A lab report is admissible in court; a party has 14 days to object (RCW 26.26A.425). If an alleged genetic parent **refuses** court-ordered testing, the court may adjudicate them the parent anyway (RCW 26.26A.430(2)(c)). Whoever requests testing generally pays up front; the Division of Child Support pays when it is providing services and may seek reimbursement (RCW 26.26A.330).
|
||||||
|
|
||||||
|
## Parentage Court Actions (Paternity Cases)
|
||||||
|
|
||||||
|
A parentage proceeding may be brought in superior court under the civil rules (RCW 26.26A.400). Those who can file include the child, the person who gave birth, anyone claiming to be a parent, anyone whose parentage is to be determined, the Division of Child Support, and adoption agencies (RCW 26.26A.405). A case may be started **before the child is born**, though any order is not enforced until birth (RCW 26.26A.480), and while it is pending the court can issue temporary child support, parenting-time, and protection orders (RCW 26.26A.470).
|
||||||
|
|
||||||
|
A case against an alleged genetic parent or a presumed parent must be started **before the child turns 18** — after that, only the adult child can bring it (RCW 26.26A.430(1), .435(1)). The court adjudicates an alleged genetic parent who is identified by genetic testing, admits parentage, refuses testing, defaults, or is found a parent on other evidence (RCW 26.26A.430(2)). The final order must identify the child, and the court may assess filing fees, attorney fees, and testing costs (RCW 26.26A.510); the signers and parties — and in some cases the child — are bound by the result (RCW 26.26A.515). One protection: a parent may ask the court to **preclude a person who committed sexual assault resulting in the pregnancy** from establishing parentage (RCW 26.26A.465).
|
||||||
|
|
||||||
|
## De Facto Parentage
|
||||||
|
|
||||||
|
Washington recognizes parents of the heart. A person who is not a biological or adoptive parent can be adjudicated a **de facto parent** if they prove seven things by a preponderance of the evidence (RCW 26.26A.440(4)):
|
||||||
|
|
||||||
|
1. They resided with the child as a regular member of the household for a significant period.
|
||||||
|
2. They engaged in consistent caretaking.
|
||||||
|
3. They undertook full, permanent parental responsibilities without expecting pay.
|
||||||
|
4. They held the child out as their own.
|
||||||
|
5. They established a bonded, dependent relationship with the child that is parental in nature.
|
||||||
|
6. Another parent fostered or supported that bonded relationship.
|
||||||
|
7. Continuing the relationship is in the child's best interest.
|
||||||
|
|
||||||
|
The claim must be filed before the child turns 18 and while the child is alive, with a verified pleading setting out specific facts; the court decides standing on an expedited basis (RCW 26.26A.440(2)–(3)). When two or more people have competing claims — for example, a presumed parent and a genetic parent — the court decides **in the best interest of the child**, weighing the child's age, how long each person acted as a parent, the nature of each relationship, the harm if a relationship is not recognized, the basis of each claim, and other equitable factors (RCW 26.26A.460(1)). Notably, a Washington court **may recognize more than two parents** if failing to do so would be detrimental to the child (RCW 26.26A.460(3)).
|
||||||
|
|
||||||
|
## Assisted Reproduction and Surrogacy
|
||||||
|
|
||||||
|
Washington has clear rules for families built with reproductive technology. A **sperm or egg donor is not a parent** of a child conceived by assisted reproduction (RCW 26.26A.605), while a person who **consents to assisted reproduction with the intent to be a parent is a parent** (RCW 26.26A.610). Consent should be in a written record signed by the person giving birth and the intended parent; even without a record, the court can find consent by clear and convincing evidence of a pre-conception agreement, or if the two lived together with the child for its first four years and both held the child out as their own (RCW 26.26A.615). Consent can be withdrawn in writing any time **before** the transfer that results in pregnancy (RCW 26.26A.630). If an intended parent dies between the transfer and the birth, their parentage can still be established (RCW 26.26A.635).
|
||||||
|
|
||||||
|
Surrogacy agreements are legal in Washington but heavily regulated: the surrogate must be at least 21, have previously given birth, and complete medical and mental-health evaluations with independent counsel (RCW 26.26A.705). In a **gestational** surrogacy (the surrogate is not genetically related to the child), each intended parent is a parent **by operation of law** at birth, and the surrogate and her spouse are not parents (RCW 26.26A.740). Separate validation rules govern **genetic** surrogacy agreements (RCW 26.26A.760 through 26.26A.785), and the chapter also regulates information about gamete and embryo donors (RCW 26.26A.800 through 26.26A.825).
|
||||||
|
|
||||||
|
## Unmarried Parents' Rights
|
||||||
|
|
||||||
|
An unmarried mother is a parent from the moment of birth — no paperwork needed (RCW 26.26A.100(1)). An unmarried father is only an **alleged genetic parent** until parentage is established; the law treats children of unmarried and married parents identically (RCW 26.26A.105). Practical routes: sign an **acknowledgment** at the hospital or later (RCW 26.26A.200), ask the **Division of Child Support** for free help (RCW 26.26A.405(5)), or file a **Petition to Decide Parentage** in superior court using the free state-approved forms (RCW 26.26A.400). Once parentage exists, both parents have equal standing to seek parenting time and decision-making — and both owe support.
|
||||||
|
|
||||||
|
## Why Parentage Matters for Custody and Child Support
|
||||||
|
|
||||||
|
Parentage is the gatekeeper to everything else in a family case. A parentage adjudication or acknowledgment creates the legal relationship "for all purposes" (RCW 26.26A.110), which means:
|
||||||
|
|
||||||
|
- **Custody and parenting time.** Only a legal parent can ask for a parenting plan (chapter 26.09 RCW); in a parentage action the court can issue temporary parenting-time orders (RCW 26.26A.470) and a final parenting plan.
|
||||||
|
- **Child support.** Only a legal parent owes — and can receive — child support, calculated under the Washington State Child Support Schedule in chapter 26.19 RCW. The Division of Child Support establishes parentage precisely so it can collect support (RCW 26.26A.405(5)).
|
||||||
|
- **Everything else.** Inheritance, benefits, insurance, and medical decisions flow from the parent-child relationship (RCW 26.26A.110).
|
||||||
|
|
||||||
|
If you are unsure whether parentage has been established, the free "Parentage guide" at WashingtonLawHelp.org explains the options, and the Division of Child Support can help without a lawyer.
|
||||||
|
|
||||||
|
## Key Citations
|
||||||
|
|
||||||
|
- Uniform Parentage Act short title and effective date — RCW 26.26A.005, .903
|
||||||
|
- Definitions — RCW 26.26A.010
|
||||||
|
- Establishment of parent-child relationship (seven paths) — RCW 26.26A.100
|
||||||
|
- Presumption of parentage — RCW 26.26A.115
|
||||||
|
- Acknowledgment and denial of parentage — RCW 26.26A.200 through 26.26A.265 (rescission: .235; challenge: .240)
|
||||||
|
- Genetic testing — RCW 26.26A.300 through 26.26A.355 (ordering: .310; results: .325)
|
||||||
|
- Proceedings to adjudicate parentage — RCW 26.26A.400 through 26.26A.515 (standing: .405; de facto parentage: .440; competing claims: .460; temporary orders: .470)
|
||||||
|
- Assisted reproduction — RCW 26.26A.600 through 26.26A.635
|
||||||
|
- Surrogacy agreements — RCW 26.26A.700 through 26.26A.785
|
||||||
|
- Donor information — RCW 26.26A.800 through 26.26A.825
|
||||||
117
books/property-debt.md
Normal file
117
books/property-debt.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
# Community Property and Debt Division in Washington State
|
||||||
|
|
||||||
|
Washington is a community property state — one of only a small number of states where most property and debts acquired during a marriage are owned equally by both spouses. Understanding the difference between "community" and "separate" property matters for divorce, debt collection, bankruptcy, and death. This book explains the basic rules.
|
||||||
|
|
||||||
|
## What Is Community Property?
|
||||||
|
|
||||||
|
**Property acquired during marriage is community property.** Under RCW 26.16.030, property that is not separate property and is acquired after marriage (or after registration of a state registered domestic partnership) by either spouse — or both — is community property. The law treats both spouses as having equal ownership, regardless of whose name is on the title or who earned the money.
|
||||||
|
|
||||||
|
- Both spouses can manage and control community property (RCW 26.16.030).
|
||||||
|
- Neither spouse can give away community property without the other's consent (RCW 26.16.030(2)).
|
||||||
|
- Neither spouse can sell, convey, or mortgage community real estate without the other spouse signing the deed or document (RCW 26.16.030(3)).
|
||||||
|
- Neither spouse can will away more than half of the community property (RCW 26.16.030(1)).
|
||||||
|
- State registered domestic partners are treated the same as spouses throughout this chapter (RCW 26.16.030).
|
||||||
|
|
||||||
|
**The key questions:** (1) When was the property acquired — before or during the marriage? (2) How was it acquired — by work, or by gift or inheritance?
|
||||||
|
|
||||||
|
## What Is Separate Property?
|
||||||
|
|
||||||
|
**Separate property belongs to one spouse only.** Under RCW 26.16.010, separate property is:
|
||||||
|
|
||||||
|
- Property owned before the marriage.
|
||||||
|
- Property acquired during the marriage by gift, bequest, devise, descent, or inheritance.
|
||||||
|
- The rents, issues, and profits of that separate property (RCW 26.16.010).
|
||||||
|
|
||||||
|
The same rule applies to registered domestic partners (RCW 26.16.020).
|
||||||
|
|
||||||
|
Separate property is **not** subject to the debts or contracts of the other spouse, and the owner can manage, sell, or encumber it without the other spouse joining in (RCW 26.16.010).
|
||||||
|
|
||||||
|
**Earnings while living apart.** Once spouses are living separate and apart, each spouse's earnings and accumulations are that spouse's separate property (RCW 26.16.140). This matters when a marriage breaks down before the divorce is filed.
|
||||||
|
|
||||||
|
## Characterizing Common Assets
|
||||||
|
|
||||||
|
The general rules produce the following results:
|
||||||
|
|
||||||
|
- **Wages, salaries, and business income earned during the marriage** are community property (RCW 26.16.030).
|
||||||
|
- **A house or car bought during the marriage with earned money** is community property, even if only one name is on the title (RCW 26.16.030).
|
||||||
|
- **A house or car owned before the marriage** is separate property, but if community money is used to pay down the mortgage during the marriage, the community may acquire an interest — a tracing question courts resolve on the facts (RCW 26.16.010; RCW 26.16.030).
|
||||||
|
- **Gifts and inheritances received during the marriage** are separate property of the recipient, including what that property earns (RCW 26.16.010).
|
||||||
|
- **Personal injury settlements** may be community or separate depending on what the award compensates — this is case-law-driven; see WashingtonLawHelp.org's community property guide (linked below) for the practical explanation.
|
||||||
|
- **Retirement and pension benefits** earned during the marriage are property that can be divided at divorce, even if the benefits will not be paid until later (In re Marriage of Brown, 98 Wn.2d 46, 653 P.2d 602 (1982)).
|
||||||
|
|
||||||
|
A spouse can change the character of property — for example, by transferring separate property into community property by agreement or gift. Washington case law generally allows spouses to change the character of property by their intent shown through words or conduct, subject to protecting creditors (see RCW 26.16.210 on the burden of proving good faith in transactions between spouses).
|
||||||
|
|
||||||
|
## Community Property Agreements
|
||||||
|
|
||||||
|
Spouses can make a written agreement that changes the status or disposition of all or part of their community property, to take effect on the death of either spouse. The agreement must be in writing, witnessed and acknowledged like a deed to real estate. It cannot defeat the rights of creditors, and a court can set it aside for fraud or other recognized equity grounds (RCW 26.16.120). This is a planning tool for married couples, not a way to hide assets from creditors.
|
||||||
|
|
||||||
|
## Debts: Who Owes What?
|
||||||
|
|
||||||
|
**Family expenses are chargeable to both spouses.** Expenses of the family and education of the children (including stepchildren) are chargeable upon the property of both spouses or either of them, and the spouses may be sued jointly or separately (RCW 26.16.205).
|
||||||
|
|
||||||
|
**Premarital debts.** Neither spouse is liable for debts the other incurred before the marriage, nor for the other's separate debts. The non-debtor spouse's separate property and the income from it are not available for those debts. However, the debtor spouse's own earnings and accumulations can be reached by creditors for premarital debts (RCW 26.16.200).
|
||||||
|
|
||||||
|
**The three-year judgment rule for separate debts.** A separate debt (other than child support or maintenance) cannot be collected against either spouse's earnings and accumulations unless the creditor reduces it to a judgment within three years of the marriage (RCW 26.16.200).
|
||||||
|
|
||||||
|
**Child support is different.** A parent's or stepparent's child support obligation can be collected from that person's separate property, earnings, and share of the community property. But funds in a community bank account that can be identified as the earnings of the non-obligated spouse are exempt from satisfying the debtor spouse's child support obligation (RCW 26.16.200).
|
||||||
|
|
||||||
|
**Torts (injuries).** If one spouse causes an injury, there is no recovery against the separate property of the other spouse, except where the other spouse would be jointly responsible even without the marriage (RCW 26.16.190).
|
||||||
|
|
||||||
|
**General rule for debts incurred during marriage.** Debts incurred by either spouse during the marriage for family or community purposes are community obligations for which community property can be reached — the statutes carve out only premarital debts, separate debts, and the other spouse's separate property (RCW 26.16.200; RCW 26.16.205). WashingtonLawHelp.org's community property and debt resources explain how this plays out with creditors.
|
||||||
|
|
||||||
|
## Dividing Property and Debts in Divorce
|
||||||
|
|
||||||
|
**The standard is "just and equitable" — not necessarily 50/50.** In a dissolution, legal separation, or declaration of invalidity, the court must divide the property and liabilities of the parties — both community and separate — as appears just and equitable, **without regard to marital misconduct** (RCW 26.09.080). The court considers, among other relevant factors:
|
||||||
|
|
||||||
|
1. The nature and extent of the community property.
|
||||||
|
2. The nature and extent of the separate property.
|
||||||
|
3. The duration of the marriage.
|
||||||
|
4. The economic circumstances of each spouse when the division takes effect, including the desirability of awarding the family home — or the right to live in it — to the spouse with whom the children live the majority of the time (RCW 26.09.080(1)–(4)).
|
||||||
|
|
||||||
|
Key practical points:
|
||||||
|
|
||||||
|
- **The court can divide separate property too.** RCW 26.09.080 expressly covers "either community or separate" property.
|
||||||
|
- **Debts are divided in the same proceeding.** The court's disposition covers "the liabilities of the parties" (RCW 26.09.080).
|
||||||
|
- **The division is between the spouses.** A divorce decree allocating a debt does not by itself stop a creditor from pursuing either spouse under the debt rules above; the decree gives the paying spouse rights against the other spouse. (See the creditor/debt statutes cited above and WashingtonLawHelp.org's debt resources.)
|
||||||
|
- **Maintenance (alimony) is a separate question.** Spousal support is decided under RCW 26.09.090 based on its own factors (financial resources, time needed for education or training, standard of living, duration of the marriage, age and health, and the paying spouse's ability to pay).
|
||||||
|
- **Property division is usually final.** After the decree, courts have limited power to reopen property division — a party should not assume the division can be changed later.
|
||||||
|
|
||||||
|
## Separation Contracts
|
||||||
|
|
||||||
|
Spouses can settle property, debts, support, and parenting issues by a written separation contract. In a later dissolution, the contract (except parenting plan terms) is binding on the court unless it finds the contract was unfair at the time it was executed (RCW 26.09.070). Such contracts are common and generally enforced.
|
||||||
|
|
||||||
|
## Unmarried Couples: Committed Intimate Relationships
|
||||||
|
|
||||||
|
Community property law applies only to married couples and registered domestic partners (RCW 26.16.030). Unmarried couples who live together are governed instead by the "committed intimate relationship" (CIR) doctrine — previously called "meretricious relationship" law.
|
||||||
|
|
||||||
|
Under Connell v. Francisco, 127 Wn.2d 339 (1995), when a stable, marriage-like relationship ends, the court can divide property acquired during the relationship in a just and equitable manner. Courts look at factors including: continuous cohabitation, the duration of the relationship, the purpose of the relationship, the pooling of resources and services for joint projects, and the intent of the parties (Connell, 127 Wn.2d at 346). WashingtonLawHelp.org has a dedicated "Committed Intimate Relationships" topic with practical guidance.
|
||||||
|
|
||||||
|
## Quasi-Community Property
|
||||||
|
|
||||||
|
"Quasi-community property" is property acquired while the couple lived in another state that **would have been** community property if Washington law had applied at acquisition. It matters mainly at death: half of the quasi-community property belongs to the surviving spouse, and half passes by will or intestacy as community property would (RCW 26.16.220; RCW 26.16.230).
|
||||||
|
|
||||||
|
## Where to Get Help
|
||||||
|
|
||||||
|
- **WashingtonLawHelp.org** — free plain-English guides: "Community Property in Washington State" (https://www.washingtonlawhelp.org/resource/community-property-in-washington-state) and "Divorce in Washington State" (https://www.washingtonlawhelp.org/resource/divorce-in-washington-state).
|
||||||
|
- **Washington State Courts** — court forms and self-help information: https://www.courts.wa.gov/forms/.
|
||||||
|
- **Washington State Bar Association** — lawyer referral and legal help resources: https://www.wsba.org/.
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- RCW 26.16.010, 26.16.020 (separate property) — app.leg.wa.gov
|
||||||
|
- RCW 26.16.030 (community property defined; management and control) — app.leg.wa.gov
|
||||||
|
- RCW 26.16.120 (community property agreements) — app.leg.wa.gov
|
||||||
|
- RCW 26.16.140 (earnings while living apart) — app.leg.wa.gov
|
||||||
|
- RCW 26.16.190 (tort liability between spouses) — app.leg.wa.gov
|
||||||
|
- RCW 26.16.200 (premarital and separate debts) — app.leg.wa.gov
|
||||||
|
- RCW 26.16.205 (family expense liability) — app.leg.wa.gov
|
||||||
|
- RCW 26.16.210 (burden of proof in interspousal transactions) — app.leg.wa.gov
|
||||||
|
- RCW 26.16.220, 26.16.230 (quasi-community property) — app.leg.wa.gov
|
||||||
|
- RCW 26.09.070 (separation contracts) — app.leg.wa.gov
|
||||||
|
- RCW 26.09.080 (disposition of property and liabilities — factors) — app.leg.wa.gov
|
||||||
|
- RCW 26.09.090 (maintenance — factors) — app.leg.wa.gov
|
||||||
|
- In re Marriage of Brown, 98 Wn.2d 46, 653 P.2d 602 (1982) (pensions as divisible property)
|
||||||
|
- Connell v. Francisco, 127 Wn.2d 339 (1995) (committed intimate relationships)
|
||||||
|
- WashingtonLawHelp.org; Washington State Courts (courts.wa.gov); WSBA (wsba.org)
|
||||||
122
books/protection-orders.md
Normal file
122
books/protection-orders.md
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
# Protection Orders in Washington State
|
||||||
|
|
||||||
|
Washington law gives people a fast, free civil court process to get protection from violence, harassment, stalking, sexual assault, and firearm danger. Since July 1, 2022, all protection orders are governed by one consolidated statute — chapter 7.105 RCW — which replaced and recodified the older separate laws, including the Domestic Violence Prevention Act (former chapter 26.50 RCW) and parts of the stalking protection order law (former chapter 26.52 RCW). The recodification took effect July 1, 2022 (see RCW 7.105.900; 2021 c 215).
|
||||||
|
|
||||||
|
## The Six Types of Protection Orders
|
||||||
|
|
||||||
|
RCW 7.105.100(1) establishes six types of protection order petitions:
|
||||||
|
|
||||||
|
1. **Domestic violence protection order (DVPO)** — requires domestic violence by an intimate partner or a family or household member (RCW 7.105.100(1)(a)).
|
||||||
|
2. **Sexual assault protection order** — requires nonconsensual sexual conduct, nonconsensual sexual penetration, or commercial sexual exploitation. A single incident is enough. No relationship to the respondent is required (RCW 7.105.100(1)(b)).
|
||||||
|
3. **Stalking protection order** — requires stalking. No relationship required (RCW 7.105.100(1)(c)).
|
||||||
|
4. **Vulnerable adult protection order** — requires that the protected person is a vulnerable adult who has been abandoned, abused, financially exploited, or neglected, or is threatened with those harms (RCW 7.105.100(1)(d)).
|
||||||
|
5. **Extreme risk protection order (ERPO)** — requires that the respondent poses a significant danger of injuring self or others with a firearm. Only an intimate partner, a family or household member, or a law enforcement agency can file (RCW 7.105.100(1)(e)).
|
||||||
|
6. **Antiharassment protection order** — requires unlawful harassment (a knowing and willful course of conduct, or a single violent act or threat, that seriously alarms the petitioner and serves no lawful purpose). No relationship required (RCW 7.105.100(1)(f); RCW 7.105.010(37)).
|
||||||
|
|
||||||
|
The court cannot dismiss a petition just because the conduct would also fit another order type — it must consider the petitioner's preferred type (RCW 7.105.100(5)).
|
||||||
|
|
||||||
|
## Key Definitions
|
||||||
|
|
||||||
|
- **Domestic violence** means physical harm, bodily injury, assault, or the infliction of fear of those harms; nonconsensual sexual conduct or penetration; coercive control; unlawful harassment; or stalking — committed by an intimate partner or a family or household member (RCW 7.105.010(10)).
|
||||||
|
- **Coercive control** means a pattern of behavior used to cause physical, emotional, or psychological harm that unreasonably interferes with a person's free will and personal liberty. Examples include property destruction, technology abuse, weapon intimidation, isolation, and psychological aggression (RCW 7.105.010(4)).
|
||||||
|
- **Family or household members** include people related by blood, marriage, domestic partnership, or adoption; people who currently or formerly lived together; people with a parent-child relationship (including stepparents, stepchildren, grandparents, grandchildren); and a person who is acting or has acted as a legal guardian (RCW 7.105.010(14)).
|
||||||
|
- **Intimate partners** include spouses and former spouses, domestic partners and former domestic partners, people who share a child (unless conceived through sexual assault), and people 13 or older who have or had a dating relationship (RCW 7.105.010(21)). A **dating relationship** is a social relationship of a romantic nature (RCW 7.105.010(9)).
|
||||||
|
- **Stalking** includes stalking under RCW 9A.46.110 and cyber harassment under RCW 9A.90.120 (RCW 7.105.010(35)).
|
||||||
|
- **Vulnerable adult** includes a person 60 or older who cannot care for themselves, or an adult under guardianship or conservatorship (RCW 7.105.010(38)).
|
||||||
|
|
||||||
|
## Who Can File
|
||||||
|
|
||||||
|
- Any adult can file for themselves; **minors 15 and older** can file without an adult (RCW 7.105.100(2)).
|
||||||
|
- Children under 15 must petition through an authorized adult (RCW 7.105.100(3)).
|
||||||
|
- A petitioner may also ask for protection of minor children and vulnerable adults in the household (RCW 7.105.100(1)(a)).
|
||||||
|
- Leaving your home does not affect your right to petition (RCW 7.105.105(7)).
|
||||||
|
|
||||||
|
## How to File: Where, Cost, and Forms
|
||||||
|
|
||||||
|
- Petitions may be filed in superior court or in district/municipal court, in person or electronically; filing systems must accept submissions any time of day (RCW 7.105.105(1)). Vulnerable adult protection orders are filed **only** in superior court (RCW 7.105.065).
|
||||||
|
- **No filing fees** are charged for protection order petitions — except antiharassment orders, which may carry a fee unless the respondent committed stalking, a hate crime, a violent act, sexual assault, or domestic violence, or the petitioner cannot pay (RCW 7.105.105(9)).
|
||||||
|
- No bond is required, service is free, and certified copies and forms are provided free of charge (RCW 7.105.105(8)–(9)).
|
||||||
|
- The petition must be signed under penalty of perjury and must disclose other pending litigation or existing protection/no-contact orders (RCW 7.105.105(3)–(4)).
|
||||||
|
- The petitioner may keep their address confidential if disclosing it would risk harm (RCW 7.105.105(10)).
|
||||||
|
- Standard forms are on the Washington Courts website: the Protection Orders packet (https://www.courts.wa.gov/forms/?fa=forms.contribute&formID=142) and the Extreme Risk Protection Order forms (https://www.courts.wa.gov/forms/?fa=forms.contribute&formID=106).
|
||||||
|
|
||||||
|
## Temporary (Ex Parte) Orders
|
||||||
|
|
||||||
|
A petitioner can ask for an immediate **ex parte temporary protection order** — issued without notice to the respondent — when serious immediate harm or irreparable injury could result before a hearing. The court may issue it the same day the petition is filed, and must review ex parte requests as a priority, no later than the following judicial day (RCW 7.105.305(1); RCW 7.105.105(12)).
|
||||||
|
|
||||||
|
- The temporary order is initially valid for up to **14 days**, extendable for good cause (RCW 7.105.100(8)).
|
||||||
|
- The full hearing must be set **within 14 days** of filing (RCW 7.105.305(4)).
|
||||||
|
- There is a rebuttable presumption that the petitioner's minor children are included in a temporary DVPO (RCW 7.105.100(8)).
|
||||||
|
|
||||||
|
## What an Order Can Do
|
||||||
|
|
||||||
|
RCW 7.105.310 lists the relief a court may grant, including:
|
||||||
|
|
||||||
|
- Restraining the respondent from violence, threats, stalking, harassment, and any contact — direct, indirect, or through third parties (RCW 7.105.310(1)(a)–(b)).
|
||||||
|
- Excluding the respondent from the shared residence, and from the petitioner's residence, workplace, school, or a child's school or day care (RCW 7.105.310(1)(c)–(d)).
|
||||||
|
- A distance restriction, presumptively at least **1,000 feet** from protected people and places (RCW 7.105.310(1)(e)).
|
||||||
|
- Temporary residential provisions for shared children, and suspending the respondent's contact under an existing parenting plan, without requiring a full parenting plan (RCW 7.105.310(1)(f)).
|
||||||
|
- Ordering perpetrator treatment, mental health or chemical dependency evaluation, electronic monitoring, use of a vehicle, and possession of essential personal effects — including pets (RCW 7.105.310(1)(g), (h), (l), (n), (o)).
|
||||||
|
- Requiring the respondent to pay court costs, service fees, and the petitioner's reasonable attorney fees (RCW 7.105.310(1)(j)).
|
||||||
|
- Ordering surrender of firearms, dangerous weapons, and any concealed pistol license (RCW 7.105.310(1)(m); RCW 9.41.800).
|
||||||
|
- Restricting abusive litigation and harassing filings (RCW 7.105.310(1)(p)).
|
||||||
|
|
||||||
|
## Firearms Surrender
|
||||||
|
|
||||||
|
When a qualifying protection order issues, the court must consider RCW 9.41.800 and order the respondent to surrender all firearms, dangerous weapons, and any concealed pistol license, and prohibit possessing, purchasing, or receiving them (RCW 7.105.310(1)(m); RCW 9.41.800). Orders that include weapons surrender require personal service by law enforcement (RCW 7.105.150(1)(a)–(b)). Law enforcement may stand by to help the petitioner recover possessions and firearms (RCW 7.105.320).
|
||||||
|
|
||||||
|
## The Full Hearing
|
||||||
|
|
||||||
|
At the full hearing, the court issues the order if the petitioner proves the required conduct by a **preponderance of the evidence** (more likely than not) (RCW 7.105.225(1)). The court may not deny a petition merely because the petitioner is a minor, did not report to police, has other court orders available, or there is another pending proceeding (RCW 7.105.225(2)).
|
||||||
|
|
||||||
|
## Duration and Renewal
|
||||||
|
|
||||||
|
- A full order is for a **fixed period of at least one year** (unless the petitioner requests a shorter period) or may be **permanent** (RCW 7.105.315(1)). A provision restraining contact with the respondent's own minor children is limited to one year (RCW 7.105.315(2)).
|
||||||
|
- The petitioner may file for renewal any time in the **90 days before expiration**. The hearing is set within 14 days. The petitioner does **not** need to prove current fear; the order is renewed unless the respondent proves a substantial change in circumstances and that the prohibited conduct will not resume. Renewals are for at least one year or permanent (RCW 7.105.405(1)–(4), (8)).
|
||||||
|
- The court may not deny renewal just because the respondent never violated the order, time passed, or the respondent moved away (RCW 7.105.405(6)).
|
||||||
|
|
||||||
|
## Violations: Enforcement and Penalties
|
||||||
|
|
||||||
|
- Violating restraint, exclusion, or distance provisions of a DVPO, sexual assault, stalking, or vulnerable adult order is a **gross misdemeanor** (RCW 7.105.450(1)).
|
||||||
|
- Police **must arrest without a warrant** anyone they have probable cause to believe violated those provisions (RCW 7.105.450(2)).
|
||||||
|
- An **assault that violates the order is a class C felony**, as is reckless conduct creating substantial risk of death or serious injury; a third violation (with two prior convictions) is also a class C felony (RCW 7.105.450(4)–(5)).
|
||||||
|
- A violation is also **contempt of court**, and the court can issue a show-cause order within 14 days (RCW 7.105.450(3), (7)).
|
||||||
|
- The respondent must know of the order to be penalized; police serve the order if needed (RCW 7.105.465).
|
||||||
|
- Clerks and law enforcement enter orders into statewide systems and the federal background check system the same or next judicial day; the order is enforceable statewide (RCW 7.105.325).
|
||||||
|
|
||||||
|
## Modification and Termination
|
||||||
|
|
||||||
|
A respondent can ask the court to modify or terminate a DVPO, sexual assault, stalking, or antiharassment order, but only once every 12 months, and the respondent must prove by a preponderance of the evidence a substantial change in circumstances showing the prohibited conduct will not resume. The petitioner has no burden to prove ongoing fear. The court may refuse to terminate a severe-order case regardless (RCW 7.105.500).
|
||||||
|
|
||||||
|
## Extreme Risk Protection Orders (ERPOs)
|
||||||
|
|
||||||
|
ERPOs address firearm danger, not necessarily abuse of the petitioner:
|
||||||
|
|
||||||
|
- Grounds: the respondent poses a **significant danger of causing personal injury to self or others** with a firearm (RCW 7.105.100(1)(e)).
|
||||||
|
- A temporary ERPO issues on **reasonable cause** after an ex parte review, and the full hearing follows; a full ERPO can last **one year** (RCW 7.105.330(2)–(3)).
|
||||||
|
- The court considers evidence including recent threats or violence, patterns within the past 12 months, violations of other orders, and firearm access (RCW 7.105.215(3)).
|
||||||
|
- The respondent must surrender all firearms and any concealed pistol license to law enforcement immediately; the license is revoked (RCW 7.105.340).
|
||||||
|
- The respondent can request a termination hearing once every 12 months and bears the burden of proving they do not pose a significant danger (RCW 7.105.505).
|
||||||
|
- Filing a knowingly false ERPO petition, or possessing a firearm while subject to an ERPO, is a gross misdemeanor; a repeat firearms violation is a class C felony (RCW 7.105.460).
|
||||||
|
- Surrendered firearms are returned only after a background check confirms eligibility and the order has ended (RCW 7.105.345).
|
||||||
|
|
||||||
|
## The Older Laws (Recodification Note)
|
||||||
|
|
||||||
|
Before July 1, 2022, domestic violence protection orders lived in chapter 26.50 RCW (the Domestic Violence Prevention Act) and stalking orders in chapter 26.52 RCW. Chapter 7.105 RCW, enacted by Laws of 2021, ch. 215, consolidated and recodified those laws effective July 1, 2022 — for example, former RCW 26.50.100 and 26.50.110 were repealed by 2021 c 215 § 170, effective July 1, 2022, and former RCW 26.52.070 now directs that violations be punished under RCW 7.105.450. Older cases and forms citing chapter 26.50 RCW are still useful background, but current citations should use chapter 7.105 RCW.
|
||||||
|
|
||||||
|
## If You Need an Order Right Now
|
||||||
|
|
||||||
|
1. **If you are in immediate danger, call 911.** Violations of qualifying orders require mandatory arrest (RCW 7.105.450(2)).
|
||||||
|
2. **Get forms and instructions** from the Washington Courts Protection Orders page (https://www.courts.wa.gov/forms/?fa=forms.contribute&formID=142) or at any courthouse — there is no filing fee in almost all cases (RCW 7.105.105(9)).
|
||||||
|
3. **Ask about advocacy help.** Domestic violence advocates at courthouses and community programs can help complete the paperwork. Free legal information is available from WashingtonLawHelp.org (see its "Protection Orders (2022 and later)" resource, https://www.washingtonlawhelp.org/resource/protection-orders-2022-and-later) and Legal Voice (https://www.legalvoice.org/). The National Domestic Violence Hotline is available 24/7 at 1-800-799-7233.
|
||||||
|
4. **Keep a certified copy of the order with you**, and report any violation to police; you can be protected even if you initiated contact — the order's provisions are on the respondent, and only the court can change the order (RCW 7.105.115(1)(a)).
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- Chapter 7.105 RCW (Civil Protection Orders) — app.leg.wa.gov (sections cited throughout: 7.105.010, 7.105.065, 7.105.100, 7.105.105, 7.105.110, 7.105.115, 7.105.150, 7.105.215, 7.105.225, 7.105.305, 7.105.310, 7.105.315, 7.105.320, 7.105.325, 7.105.330, 7.105.335, 7.105.340, 7.105.345, 7.105.350, 7.105.405, 7.105.450, 7.105.460, 7.105.465, 7.105.500, 7.105.505, 7.105.900)
|
||||||
|
- RCW 9.41.800 (firearms surrender) — app.leg.wa.gov
|
||||||
|
- Former chapters 26.50 and 26.52 RCW (recodified/repealed effective July 1, 2022 by 2021 c 215) — app.leg.wa.gov
|
||||||
|
- Washington State Courts — Protection Order forms (courts.wa.gov)
|
||||||
|
- WashingtonLawHelp.org; Legal Voice (legalvoice.org)
|
||||||
98
books/spousal-maintenance.md
Normal file
98
books/spousal-maintenance.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# Spousal Maintenance (Alimony) in Washington State
|
||||||
|
|
||||||
|
> For educational purposes only — not legal advice.
|
||||||
|
|
||||||
|
## What spousal maintenance is
|
||||||
|
|
||||||
|
Spousal maintenance — what many people call **alimony** or spousal support — is money one spouse is ordered to pay the other for financial support when a marriage or registered domestic partnership ends. Washington law uses the word **maintenance** (WashingtonLawHelp.org, *Divorce guide*).
|
||||||
|
|
||||||
|
Maintenance is **not automatic** and **not calculated by a formula**. A judge decides whether to award it, in what amount, and for how long, by weighing the factors in the statute (RCW 26.09.090). Either spouse — and either domestic partner — may be ordered to pay or may receive maintenance; there is no gender-based rule (RCW 26.09.090(1)).
|
||||||
|
|
||||||
|
Maintenance is separate from **child support**, which is money for raising children under 18 and is calculated under the Washington State Child Support Schedule in Chapter 26.19 RCW. A person can receive maintenance, child support, both, or neither.
|
||||||
|
|
||||||
|
## When maintenance can be awarded
|
||||||
|
|
||||||
|
A court may grant a maintenance order in any of these proceedings (RCW 26.09.090(1)):
|
||||||
|
|
||||||
|
- Dissolution of marriage or domestic partnership (divorce);
|
||||||
|
- Legal separation;
|
||||||
|
- Declaration of invalidity (annulment) — the court has the same authority to award maintenance in an annulment case (RCW 26.09.040(3)); and
|
||||||
|
- A later maintenance proceeding where a divorce was granted by a court that lacked personal jurisdiction over the absent spouse.
|
||||||
|
|
||||||
|
Maintenance can also be **temporary**: while a divorce is pending, either party can ask the judge for temporary maintenance with a motion and affidavit stating the facts and amounts requested (RCW 26.09.060(1)). Temporary orders end when the final decree is entered (RCW 26.09.060(10)(c)).
|
||||||
|
|
||||||
|
## The six factors a judge considers
|
||||||
|
|
||||||
|
When deciding maintenance, the court must consider all relevant factors, including but not limited to these six, from RCW 26.09.090(1), translated into plain English:
|
||||||
|
|
||||||
|
1. **The requesting spouse's financial resources** — the money and property the person asking for maintenance will have after the divorce, including any property awarded in the property division, and the ability to meet their own needs independently. The court also looks at whether child support received for a child living with that spouse already covers part of that spouse's needs (RCW 26.09.090(1)(a)).
|
||||||
|
2. **Time needed for education or training** — how long it would take the requesting spouse to get education or training to find work appropriate to their skills, interests, standard of living, and circumstances (RCW 26.09.090(1)(b)).
|
||||||
|
3. **The standard of living during the marriage** (RCW 26.09.090(1)(c)).
|
||||||
|
4. **The length of the marriage or domestic partnership** (RCW 26.09.090(1)(d)).
|
||||||
|
5. **The age, physical and emotional condition, and financial obligations** of the spouse requesting maintenance (RCW 26.09.090(1)(e)).
|
||||||
|
6. **The paying spouse's ability to pay** — whether the other spouse can meet their own needs and obligations while also paying maintenance (RCW 26.09.090(1)(f)).
|
||||||
|
|
||||||
|
Two things the court is told not to consider: misconduct (fault) is irrelevant — the order is made "without regard to misconduct" (RCW 26.09.090(1)) — and there is no presumption that maintenance must be awarded. The court "may" grant it, in "such amounts and for such periods of time as the court deems just" (RCW 26.09.090(1)).
|
||||||
|
|
||||||
|
## Who is likely to receive maintenance — practical picture
|
||||||
|
|
||||||
|
Because there is no formula, results vary case by case. WashingtonLawHelp.org's *Divorce guide* describes the practical pattern:
|
||||||
|
|
||||||
|
- Maintenance is **more likely** when the marriage was long, when the requesting spouse has a disability or stayed home to care for children while the other spouse worked, and when that spouse would have trouble finding a good job now.
|
||||||
|
- Being unemployed for a **long time** (especially after staying home with children) supports an award more than a temporary layoff.
|
||||||
|
- Maintenance is possible **even if the requesting spouse is working**, if the judge decides the paying spouse can afford it and the requesting spouse should be able to keep the standard of living established during the marriage.
|
||||||
|
- The judge weighs how long you were married, both spouses' financial situations after property and debt division, the time needed for education or training, the marital standard of living, and the age and health of the requesting spouse.
|
||||||
|
|
||||||
|
The factors from the statute (RCW 26.09.090(1)(a)–(f)) are the same checklist the judge works from.
|
||||||
|
|
||||||
|
## Amount and duration
|
||||||
|
|
||||||
|
Washington has **no guideline or formula** for maintenance amounts — unlike child support. The judge sets an amount and a duration that seem just based on the factors above (RCW 26.09.090(1)). In practice:
|
||||||
|
|
||||||
|
- **Shorter marriages** with a spouse who needs retraining may produce maintenance for a limited period — long enough to get education or training (the "time necessary" factor, RCW 26.09.090(1)(b)).
|
||||||
|
- **Long marriages**, especially where one spouse stayed home for years or has health problems, can produce maintenance of longer duration or without a fixed end date — but courts generally expect maintenance to be limited or at least periodically re-examined; outcomes depend heavily on the facts.
|
||||||
|
- The standard of living during the marriage matters, but the paying spouse's own needs and obligations are also weighed (RCW 26.09.090(1)(c), (f)).
|
||||||
|
|
||||||
|
## When maintenance ends
|
||||||
|
|
||||||
|
Unless the spouses agree in writing or the decree says otherwise, the obligation to pay future maintenance **ends automatically** when (RCW 26.09.170(2)):
|
||||||
|
|
||||||
|
- Either party dies; or
|
||||||
|
- The person receiving maintenance remarries or registers a new domestic partnership.
|
||||||
|
|
||||||
|
WashingtonLawHelp.org's *Divorce guide* states the same rule in plain terms: "Spousal support payments generally end when you remarry or die." An agreement or decree can provide otherwise, so read the final order carefully.
|
||||||
|
|
||||||
|
## Agreeing on maintenance: separation contracts
|
||||||
|
|
||||||
|
You and your spouse can agree about maintenance in a written **separation contract**, which may cover maintenance, property, parenting, and child support (RCW 26.09.070(1)). When a divorce case is filed, the contract binds the court unless the court finds it was **unfair at the time it was signed** (RCW 26.09.070(3)). A separation contract can also state that its maintenance terms may not be modified later; without that language, maintenance terms in the decree can be modified like any maintenance order (RCW 26.09.070(7)). If you are considering giving up a right to maintenance by agreement, get legal advice first.
|
||||||
|
|
||||||
|
## Modifying (changing) maintenance later
|
||||||
|
|
||||||
|
A maintenance award can be changed after the divorce, but only in limited ways (RCW 26.09.170(1)):
|
||||||
|
|
||||||
|
- A modification can apply **only to payments that come due after** the request for modification is filed — not to past-due installments (RCW 26.09.170(1)(a)).
|
||||||
|
- Generally, modification requires a **substantial change of circumstances** since the original order (RCW 26.09.170(1)(b)) — for example, a major change in either spouse's income, health, or needs.
|
||||||
|
- If the maintenance obligation was made **non-modifiable by agreement** (see previous section), the court will honor that (RCW 26.09.070(7)).
|
||||||
|
- By contrast, **property division** orders are essentially final and generally cannot be modified at all (RCW 26.09.170(1)).
|
||||||
|
|
||||||
|
## Enforcing a maintenance award
|
||||||
|
|
||||||
|
If your former spouse stops paying, your remedies depend on how the order is set up:
|
||||||
|
|
||||||
|
- **Through the state Support Registry:** If the court order says maintenance payments go through the state Support Registry and you have a child support case open with the Division of Child Support (DCS), DCS can collect maintenance for you, including some back payments (WashingtonLawHelp.org, *Enforce your divorce decree: Money and property issues*).
|
||||||
|
- **Maintenance only, no child support case:** DCS will not open a case. You can file a **motion for contempt**. If the judge finds your ex-spouse had the ability to pay and did not, the judge can order payment or impose fines; a spouse who proves inability to pay despite good-faith effort generally is not held in contempt (WashingtonLawHelp.org, *Enforce your divorce decree*).
|
||||||
|
- Money awards in a divorce order are enforceable like other civil judgments — typically for 10 years, with renewal available (WashingtonLawHelp.org, *Enforce your divorce decree*).
|
||||||
|
|
||||||
|
## Practical tips
|
||||||
|
|
||||||
|
- **Ask early.** If you will need support while the case is pending, move for temporary maintenance when you file or respond — use the *Motion for Temporary Family Law Order and Restraining Order* (FL Divorce 223) and file a *Financial Declaration* (FL All Family 131) so the court has your income and expense information (courts.wa.gov, "Court Forms: Divorce (Dissolution)").
|
||||||
|
- **Put it in the final order.** Whether you settle or go to trial, make sure the *Final Divorce Order* (FL Divorce 241) states the amount, start date, duration, how payments are made, and what events end maintenance.
|
||||||
|
- **Document ability to pay.** The paying spouse's ability to pay (RCW 26.09.090(1)(f)) matters; complete financial disclosures truthfully on both sides.
|
||||||
|
- **Fees can shift.** If the financial situations are lopsided, the court may order one party to help pay the other's attorney fees and costs (RCW 26.09.140).
|
||||||
|
- **Get advice.** Maintenance outcomes depend heavily on the specific facts. WashingtonLawHelp.org (maintained by Northwest Justice Project) has free guides; the CLEAR legal-aid referral line is 1-888-201-1014 (courts.wa.gov forms page).
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- RCW 26.09.040(3), 26.09.060, 26.09.070, 26.09.090, 26.09.140, 26.09.170 — app.leg.wa.gov (Title 26, Chapter 26.09 RCW)
|
||||||
|
- WashingtonLawHelp.org (Northwest Justice Project): *Divorce guide* (reviewed 05/07/2026), spousal support section; *Enforce your divorce decree: Money and property issues* (reviewed 03/19/2026) — washingtonlawhelp.org
|
||||||
|
- Washington State Courts: *Court Forms: Divorce (Dissolution)* — courts.wa.gov/forms/?fa=forms.contribute&formID=13
|
||||||
96
comms.py
Normal file
96
comms.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Astraea comms — Twilio (SMS + voice calls) + SMTP email, with a no-contact-order
|
||||||
|
safety guard and formal message templates. stdlib-only (urllib + smtplib + email).
|
||||||
|
"""
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import smtplib
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
|
||||||
|
TWILIO_API = "https://api.twilio.com/2010-04-01/Accounts"
|
||||||
|
|
||||||
|
|
||||||
|
def _twilio_request(settings, path, data):
|
||||||
|
sid = settings.get("twilio_sid", "")
|
||||||
|
token = settings.get("twilio_auth_token", "")
|
||||||
|
if not sid or not token:
|
||||||
|
raise RuntimeError("Twilio credentials not configured")
|
||||||
|
url = f"{TWILIO_API}/{sid}/{path}.json"
|
||||||
|
cred = base64.b64encode(f"{sid}:{token}".encode()).decode()
|
||||||
|
body = urllib.parse.urlencode(data).encode()
|
||||||
|
req = urllib.request.Request(url, data=body,
|
||||||
|
headers={"Authorization": f"Basic {cred}",
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"})
|
||||||
|
op = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||||
|
with op.open(req, timeout=30) as r:
|
||||||
|
return json.loads(r.read().decode())
|
||||||
|
|
||||||
|
|
||||||
|
def send_sms(settings, to, body):
|
||||||
|
return _twilio_request(settings, "Messages",
|
||||||
|
{"To": to, "From": settings.get("twilio_from", ""), "Body": body})
|
||||||
|
|
||||||
|
|
||||||
|
def make_call(settings, to, twiml_url):
|
||||||
|
return _twilio_request(settings, "Calls",
|
||||||
|
{"To": to, "From": settings.get("twilio_from", ""), "Url": twiml_url})
|
||||||
|
|
||||||
|
|
||||||
|
def send_email(settings, to, subject, body):
|
||||||
|
host = settings.get("smtp_host", "")
|
||||||
|
user = settings.get("smtp_user", "")
|
||||||
|
pwd = settings.get("smtp_pass", "")
|
||||||
|
frm = settings.get("email_from", "") or user
|
||||||
|
if not host or not user or not frm:
|
||||||
|
raise RuntimeError("Email (SMTP) not configured")
|
||||||
|
port = int(settings.get("smtp_port", 587) or 587)
|
||||||
|
msg = MIMEMultipart()
|
||||||
|
msg["Subject"] = subject
|
||||||
|
msg["From"] = frm
|
||||||
|
msg["To"] = to
|
||||||
|
msg.attach(MIMEText(body, "plain", "utf-8"))
|
||||||
|
s = smtplib.SMTP(host, port, timeout=30)
|
||||||
|
s.starttls()
|
||||||
|
s.login(user, pwd)
|
||||||
|
s.sendmail(frm, [to], msg.as_string())
|
||||||
|
s.quit()
|
||||||
|
return {"ok": True, "to": to, "subject": subject}
|
||||||
|
|
||||||
|
|
||||||
|
# ── Formal templates (the agent composes content; these wrap it professionally) ──
|
||||||
|
def email_template(display_name, recipient, subject, body):
|
||||||
|
return (
|
||||||
|
f"{recipient},\n\n"
|
||||||
|
f"{body}\n\n"
|
||||||
|
f"Respectfully,\n"
|
||||||
|
f"{display_name}\n\n"
|
||||||
|
f"---\nThis message was drafted on Astraea, an AI legal assistant, on my behalf. "
|
||||||
|
f"Please direct any reply to me directly."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sms_template(body):
|
||||||
|
return f"{body}\n\n— sent via Astraea legal assistant"
|
||||||
|
|
||||||
|
|
||||||
|
def call_script(greeting, purpose, body, closing):
|
||||||
|
return (
|
||||||
|
f"Good {greeting}. My name is [your name]. {purpose}.\n\n"
|
||||||
|
f"{body}\n\n"
|
||||||
|
f"{closing}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def no_contact_blocked(settings, recipient):
|
||||||
|
"""Return True if a no-contact order OR protection order is in effect AND the recipient
|
||||||
|
looks like the protected/restrained party. Domestic-violence protection orders (RCW 7.105)
|
||||||
|
carry the same no-contact force as a standalone no-contact order."""
|
||||||
|
if not (settings.get("no_contact_order") or settings.get("protection_order")):
|
||||||
|
return False
|
||||||
|
partner = (settings.get("partner_name") or "").strip().lower()
|
||||||
|
if not partner:
|
||||||
|
return True # order in effect but name unset — block to be safe
|
||||||
|
return partner in (recipient or "").lower()
|
||||||
149
detect_fields.py
Normal file
149
detect_fields.py
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Detect fillable blanks across WA court forms: underscore runs + the standard
|
||||||
|
caption (county/petitioner/respondent/case_no). Emits a field-map JSON."""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pymupdf
|
||||||
|
|
||||||
|
FORMS_DIR = "/opt/astraea/forms"
|
||||||
|
|
||||||
|
def lines_with_pos(page):
|
||||||
|
words = page.get_text("words")
|
||||||
|
groups = {}
|
||||||
|
for w in words:
|
||||||
|
x0, y0, x1, y1, word = w[0], w[1], w[2], w[3], w[4]
|
||||||
|
key = int(y0 / 5) * 5
|
||||||
|
groups.setdefault(key, []).append((x0, x1, word))
|
||||||
|
out = []
|
||||||
|
for y in sorted(groups):
|
||||||
|
ws = sorted(groups[y], key=lambda t: t[0])
|
||||||
|
text = " ".join(w for _, _, w in ws)
|
||||||
|
out.append({"y": y, "x0": ws[0][0], "x1": ws[-1][1], "text": text, "words": ws})
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def detect_caption(lines):
|
||||||
|
"""Find the standard caption fields. Returns list of {key, label, page, x, y}."""
|
||||||
|
fields = []
|
||||||
|
for ln in lines:
|
||||||
|
t = ln["text"]
|
||||||
|
# "County of" -> blank right after 'of'
|
||||||
|
if re.search(r"\bCounty\b.*\bof\b\s*$", t) and "Superior" in t:
|
||||||
|
ofx = None
|
||||||
|
for x0, x1, w in ln["words"]:
|
||||||
|
if w == "of":
|
||||||
|
ofx = x1
|
||||||
|
if ofx:
|
||||||
|
fields.append({"key": "county", "label": "County", "x": ofx + 6, "y": ln["y"] + 9})
|
||||||
|
# Petitioner ... case): -> name after the colon
|
||||||
|
if "Petitioner" in t and ("case" in t or "case):" in t) and ":" in t:
|
||||||
|
colonx = None
|
||||||
|
for x0, x1, w in ln["words"]:
|
||||||
|
if w.endswith(":"):
|
||||||
|
colonx = x1
|
||||||
|
if colonx and colonx < 400:
|
||||||
|
fields.append({"key": "petitioner", "label": "Petitioner (your full name)", "x": colonx + 6, "y": ln["y"] + 9})
|
||||||
|
# Respondent ... partner): -> name after colon
|
||||||
|
if "Respondent" in t and ":" in t:
|
||||||
|
colonx = None
|
||||||
|
for x0, x1, w in ln["words"]:
|
||||||
|
if w.endswith(":"):
|
||||||
|
colonx = x1
|
||||||
|
if colonx and colonx < 400:
|
||||||
|
fields.append({"key": "respondent", "label": "Respondent (spouse's full name)", "x": colonx + 6, "y": ln["y"] + 9})
|
||||||
|
# Case No.
|
||||||
|
if "Case No." in t:
|
||||||
|
nox = None
|
||||||
|
for x0, x1, w in ln["words"]:
|
||||||
|
if w in ("No.", "No"):
|
||||||
|
nox = x1
|
||||||
|
if nox:
|
||||||
|
fields.append({"key": "case_no", "label": "Case number", "x": nox + 6, "y": ln["y"] + 9})
|
||||||
|
return fields
|
||||||
|
|
||||||
|
|
||||||
|
def detect_underscores(pno, page):
|
||||||
|
"""Find underscore runs (blank fields) and their preceding label."""
|
||||||
|
fields = []
|
||||||
|
words = page.get_text("words")
|
||||||
|
groups = {}
|
||||||
|
for w in words:
|
||||||
|
x0, y0, x1, y1, word = w[0], w[1], w[2], w[3], w[4]
|
||||||
|
key = int(y0 / 5) * 5
|
||||||
|
groups.setdefault(key, []).append((x0, x1, y0, word))
|
||||||
|
for y in sorted(groups):
|
||||||
|
ws = sorted(groups[y], key=lambda t: t[0])
|
||||||
|
for i, (x0, x1, y0, word) in enumerate(ws):
|
||||||
|
if word.strip("_").strip() == "" and len(word.strip()) >= 4:
|
||||||
|
label = " ".join(w for _, _, _, w in ws[:i]).strip()
|
||||||
|
fields.append({"page": pno, "x": x0, "y": y + 9,
|
||||||
|
"label": label, "type": "text"})
|
||||||
|
return fields
|
||||||
|
|
||||||
|
|
||||||
|
def detect_checkboxes(pno, page):
|
||||||
|
"""Find checkbox glyphs (U+F0A8) and their labels. Type 'checkbox'."""
|
||||||
|
fields = []
|
||||||
|
words = page.get_text("words")
|
||||||
|
groups = {}
|
||||||
|
for w in words:
|
||||||
|
x0, y0, x1, y1, word = w[0], w[1], w[2], w[3], w[4]
|
||||||
|
key = int(y0 / 5) * 5
|
||||||
|
groups.setdefault(key, []).append((x0, x1, y0, word))
|
||||||
|
for y in sorted(groups):
|
||||||
|
ws = sorted(groups[y], key=lambda t: t[0])
|
||||||
|
for i, (x0, x1, y0, word) in enumerate(ws):
|
||||||
|
if "\uf0a8" in word:
|
||||||
|
label_words = []
|
||||||
|
for wx0, wx1, _, w in ws[i + 1:]:
|
||||||
|
if "\uf0a8" in w:
|
||||||
|
break
|
||||||
|
label_words.append(w)
|
||||||
|
label = " ".join(label_words).strip()
|
||||||
|
fields.append({"page": pno, "x": x0, "y": y0, "type": "checkbox",
|
||||||
|
"label": label})
|
||||||
|
return fields
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
all_fields = {}
|
||||||
|
for fn in sorted(os.listdir(FORMS_DIR)):
|
||||||
|
if not fn.endswith(".pdf"):
|
||||||
|
continue
|
||||||
|
fid = fn[:-4]
|
||||||
|
path = os.path.join(FORMS_DIR, fn)
|
||||||
|
try:
|
||||||
|
d = pymupdf.open(path)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{fid}: OPEN FAIL {e}", file=sys.stderr)
|
||||||
|
continue
|
||||||
|
fields = []
|
||||||
|
seen_keys = set()
|
||||||
|
for pno in range(min(len(d), 12)): # caption + all pages
|
||||||
|
if pno == 0:
|
||||||
|
lines = lines_with_pos(d[pno])
|
||||||
|
for f in detect_caption(lines):
|
||||||
|
f["page"] = 0
|
||||||
|
if f["key"] not in seen_keys:
|
||||||
|
seen_keys.add(f["key"])
|
||||||
|
fields.append(f)
|
||||||
|
for f in detect_underscores(pno, d[pno]):
|
||||||
|
f["key"] = "f_" + str(len(fields))
|
||||||
|
fields.append(f)
|
||||||
|
for f in detect_checkboxes(pno, d[pno]):
|
||||||
|
f["key"] = "cb_" + str(len(fields))
|
||||||
|
fields.append(f)
|
||||||
|
d.close()
|
||||||
|
all_fields[fid] = fields
|
||||||
|
out = "/opt/astraea/forms/fieldmap.json"
|
||||||
|
with open(out, "w") as f:
|
||||||
|
json.dump(all_fields, f, indent=1)
|
||||||
|
print(f"saved {out}: { {k: len(v) for k, v in all_fields.items()} }")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
139
documents.py
Normal file
139
documents.py
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Astraea document generation — prep Washington divorce paperwork on demand.
|
||||||
|
Each doc type carries its real WA form structure; the LLM fills it with the user's facts.
|
||||||
|
"""
|
||||||
|
import urllib.request
|
||||||
|
import json
|
||||||
|
|
||||||
|
OLLAMA_URL = "http://10.30.20.29:11434"
|
||||||
|
|
||||||
|
# WA family-law paperwork the specialist can draft. `sections` mirrors the real form.
|
||||||
|
DOC_TYPES = {
|
||||||
|
"petition": {
|
||||||
|
"name": "Petition for Dissolution of Marriage",
|
||||||
|
"form": "FL Divorce 201",
|
||||||
|
"description": "The filing that starts a divorce — party info, marriage details, grounds, and requests.",
|
||||||
|
"sections": [
|
||||||
|
"Caption (Superior Court of Washington, County of ___)",
|
||||||
|
"Parties (Petitioner / Respondent names)",
|
||||||
|
"Marriage information (date + place of marriage)",
|
||||||
|
"Residency basis (RCW 26.09.030)",
|
||||||
|
"Grounds: irretrievably broken",
|
||||||
|
"Children of the marriage (names, ages)",
|
||||||
|
"Requests: dissolution, property division, maintenance, parenting plan, fees",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"summons": {
|
||||||
|
"name": "Summons (Dissolution)",
|
||||||
|
"form": "FL Divorce 200",
|
||||||
|
"description": "Formal notice to the other spouse that a case has been filed and they must respond.",
|
||||||
|
"sections": [
|
||||||
|
"Caption", "Notice of case filing", "Response deadline (20/60 days)",
|
||||||
|
"Warning: default judgment if no response", "Certificate of service",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"parenting-plan": {
|
||||||
|
"name": "Parenting Plan",
|
||||||
|
"form": "FL All Family 140",
|
||||||
|
"description": "The residential schedule and decision-making plan (RCW 26.09.184).",
|
||||||
|
"sections": [
|
||||||
|
"Children covered", "Residential schedule (school year / holidays / summer)",
|
||||||
|
"Decision-making (joint or sole, RCW 26.09.187)", "Dispute resolution",
|
||||||
|
"Relocation restrictions (RCW 26.09.405-.560)", "Safety provisions if DV (RCW 26.09.191)",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"child-support": {
|
||||||
|
"name": "Child Support Order + Worksheet",
|
||||||
|
"form": "WSCSS / FL All Family 130",
|
||||||
|
"description": "The income-shares support calculation under RCW 26.19.",
|
||||||
|
"sections": [
|
||||||
|
"Combined monthly net income (each parent)", "Economic table lookup (RCW 26.19.020)",
|
||||||
|
"Basic support obligation", "Proportionate share split",
|
||||||
|
"Transfer payment amount", "Healthcare + special expenses", "Deviations (if any)",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"financial": {
|
||||||
|
"name": "Financial Declaration",
|
||||||
|
"form": "FL All Family 131",
|
||||||
|
"description": "Full disclosure of income, assets, debts, and expenses.",
|
||||||
|
"sections": ["Income (all sources)", "Assets (real + personal)", "Debts/liabilities",
|
||||||
|
"Monthly living expenses", "Separate property claims"],
|
||||||
|
},
|
||||||
|
"final-order": {
|
||||||
|
"name": "Findings of Fact, Conclusions of Law & Decree",
|
||||||
|
"form": "FL Divorce 241",
|
||||||
|
"description": "The final order that ends the marriage and sets the terms.",
|
||||||
|
"sections": [
|
||||||
|
"Findings of fact (residency, marriage, children)", "Conclusions of law (grounds)",
|
||||||
|
"Property + debt division (RCW 26.09.080)", "Maintenance award (RCW 26.09.090)",
|
||||||
|
"Parenting plan incorporation", "Child support order", "Name restoration",
|
||||||
|
"Attorney fees (RCW 26.09.140)",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"settlement": {
|
||||||
|
"name": "Separation / Settlement Agreement",
|
||||||
|
"form": "RCW 26.09.070",
|
||||||
|
"description": "A binding written agreement resolving all issues without trial.",
|
||||||
|
"sections": ["Recitals", "Property + debt division", "Maintenance",
|
||||||
|
"Parenting + support", "Disclosure of assets", "Mutual release",
|
||||||
|
"Enforcement + severability"],
|
||||||
|
},
|
||||||
|
"confidential": {
|
||||||
|
"name": "Confidential Information Form",
|
||||||
|
"form": "FL All Family 001",
|
||||||
|
"description": "Sealed identifying info (SSNs, DOBs) filed with every case.",
|
||||||
|
"sections": ["Party names + DOB + SSN", "Children (names/DOB)", "Protective address (if DV)"],
|
||||||
|
},
|
||||||
|
"letter": {
|
||||||
|
"name": "Formal Correspondence",
|
||||||
|
"form": "custom",
|
||||||
|
"description": "A professional letter to a spouse, the court, or counsel.",
|
||||||
|
"sections": ["Date + address", "Salutation", "Body (purpose)", "Closing + signature"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_prompt(doc_type, about_me, instructions, context):
|
||||||
|
dt = DOC_TYPES.get(doc_type)
|
||||||
|
if not dt:
|
||||||
|
return None, None
|
||||||
|
sections = "\n".join(f"- {s}" for s in dt["sections"])
|
||||||
|
system = (
|
||||||
|
"You are a meticulous Washington State family-law paralegal preparing court documents. "
|
||||||
|
"Draft the requested document as a clean, complete, court-ready text. Use the user's "
|
||||||
|
"ABOUT ME details and any reference context to fill in every field you can; where a "
|
||||||
|
"fact is unknown, insert a blank '[____]' placeholder — never invent names, dates, or "
|
||||||
|
"numbers. Follow the real Washington form structure precisely. Do not add legal advice "
|
||||||
|
"to the document itself."
|
||||||
|
)
|
||||||
|
user = (
|
||||||
|
f"DOCUMENT: {dt['name']} (form {dt['form']})\n"
|
||||||
|
f"PURPOSE: {dt['description']}\n\n"
|
||||||
|
f"REQUIRED SECTIONS:\n{sections}\n\n"
|
||||||
|
f"ABOUT ME (the client):\n{about_me or '(none provided)'}\n\n"
|
||||||
|
f"SPECIFIC INSTRUCTIONS:\n{instructions or '(draft a complete standard version)'}\n\n"
|
||||||
|
f"REFERENCE (Washington law):\n{context}\n\n"
|
||||||
|
"Draft the full document now, formatted for print."
|
||||||
|
)
|
||||||
|
return system, user
|
||||||
|
|
||||||
|
|
||||||
|
def generate(doc_type, about_me, instructions, context):
|
||||||
|
system, user = build_prompt(doc_type, about_me, instructions, context)
|
||||||
|
if not system:
|
||||||
|
return None
|
||||||
|
payload = {
|
||||||
|
"model": "ornith-1.5:9b",
|
||||||
|
"messages": [{"role": "system", "content": system},
|
||||||
|
{"role": "user", "content": user}],
|
||||||
|
"stream": False, "think": False,
|
||||||
|
"options": {"temperature": 0.2, "num_predict": 3000, "num_ctx": 24000},
|
||||||
|
}
|
||||||
|
req = urllib.request.Request(f"{OLLAMA_URL}/api/chat",
|
||||||
|
data=json.dumps(payload).encode(),
|
||||||
|
headers={"Content-Type": "application/json"})
|
||||||
|
op = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||||
|
with op.open(req, timeout=360) as r:
|
||||||
|
d = json.loads(r.read().decode("utf-8"))
|
||||||
|
m = d.get("message", {})
|
||||||
|
return (m.get("content") or m.get("thinking") or "").strip()
|
||||||
98
forms.py
Normal file
98
forms.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Astraea form filling — overlay the user's answers onto official WA court forms
|
||||||
|
with pymupdf (no PDF MCP exists; pymupdf is the editor). Field maps are
|
||||||
|
auto-detected from the flat forms and cached in forms/fieldmap.json.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import pymupdf
|
||||||
|
|
||||||
|
FORMS_DIR = "/opt/astraea/forms"
|
||||||
|
FIELDMAP_PATH = os.path.join(FORMS_DIR, "fieldmap.json")
|
||||||
|
|
||||||
|
FORM_NAMES = {
|
||||||
|
"fl200": {"name": "Summons (FL Divorce 200)", "file": "fl200.pdf",
|
||||||
|
"description": "Notifies your spouse the divorce case has started."},
|
||||||
|
"fl201": {"name": "Petition for Dissolution (FL Divorce 201)", "file": "fl201.pdf",
|
||||||
|
"description": "The main form that starts your divorce case."},
|
||||||
|
"fl001": {"name": "Confidential Information (FL All Family 001)", "file": "fl001.pdf",
|
||||||
|
"description": "Private contact/safety info for the court."},
|
||||||
|
"fl140": {"name": "Parenting Plan (FL All Family 140)", "file": "fl140.pdf",
|
||||||
|
"description": "Residential schedule and decision-making for your children."},
|
||||||
|
"wscss": {"name": "Child Support Worksheets (WSCSS)", "file": "wscss.pdf",
|
||||||
|
"description": "Calculates child support under RCW 26.19."},
|
||||||
|
"fl131": {"name": "Financial Declaration (FL All Family 131)", "file": "fl131.pdf",
|
||||||
|
"description": "Your income, assets, and debts."},
|
||||||
|
"fl231": {"name": "Findings & Conclusions (FL Divorce 231)", "file": "fl231.pdf",
|
||||||
|
"description": "The court's factual findings."},
|
||||||
|
"fl241": {"name": "Final Divorce Order (FL Divorce 241)", "file": "fl241.pdf",
|
||||||
|
"description": "The final decree ending your marriage."},
|
||||||
|
"fl211": {"name": "Response to Petition (FL Divorce 211)", "file": "fl211.pdf",
|
||||||
|
"description": "Your reply to your spouse's petition."},
|
||||||
|
"fl223": {"name": "Motion for Temporary Order (FL Divorce 223)", "file": "fl223.pdf",
|
||||||
|
"description": "Ask for temporary custody, support, or protection orders."},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _load_fieldmap():
|
||||||
|
try:
|
||||||
|
with open(FIELDMAP_PATH) as f:
|
||||||
|
return json.load(f)
|
||||||
|
except Exception:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
FIELDMAP = _load_fieldmap()
|
||||||
|
|
||||||
|
|
||||||
|
def list_forms():
|
||||||
|
out = []
|
||||||
|
for fid, spec in FORM_NAMES.items():
|
||||||
|
fm = FIELDMAP.get(fid, [])
|
||||||
|
fields = [{"key": f["key"], "label": f.get("label", f["key"]),
|
||||||
|
"type": f.get("type", "text")} for f in fm]
|
||||||
|
out.append({"id": fid, "name": spec["name"], "description": spec["description"],
|
||||||
|
"fields": fields})
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def fill_form(form_id, values):
|
||||||
|
"""Overlay `values` (dict field_key -> text) onto the form, return the filled PDF path."""
|
||||||
|
spec = FORM_NAMES.get(form_id)
|
||||||
|
fm = FIELDMAP.get(form_id, [])
|
||||||
|
if not spec:
|
||||||
|
return None
|
||||||
|
src = os.path.join(FORMS_DIR, spec["file"])
|
||||||
|
if not os.path.exists(src):
|
||||||
|
return None
|
||||||
|
fmap = {f["key"]: f for f in fm}
|
||||||
|
doc = pymupdf.open(src)
|
||||||
|
for key, text in values.items():
|
||||||
|
f = fmap.get(key)
|
||||||
|
if not f or not text:
|
||||||
|
continue
|
||||||
|
page = doc[f.get("page", 0)]
|
||||||
|
if f.get("type") == "checkbox":
|
||||||
|
v = str(text).strip().lower()
|
||||||
|
if v in ("x", "true", "yes", "1", "checked", "check", "on"):
|
||||||
|
x, y = f["x"], f["y"]
|
||||||
|
page.draw_line(pymupdf.Point(x + 1, y + 1), pymupdf.Point(x + 9, y + 9),
|
||||||
|
color=(0, 0, 0), width=1.3)
|
||||||
|
page.draw_line(pymupdf.Point(x + 9, y + 1), pymupdf.Point(x + 1, y + 9),
|
||||||
|
color=(0, 0, 0), width=1.3)
|
||||||
|
else:
|
||||||
|
text = str(text).strip()
|
||||||
|
if not text:
|
||||||
|
continue
|
||||||
|
page.insert_text((f["x"], f["y"]), text, fontsize=f.get("size", 11),
|
||||||
|
fontname="helv", color=(0, 0, 0))
|
||||||
|
out = os.path.join("/tmp", f"filled_{form_id}_{uuid.uuid4().hex[:8]}.pdf")
|
||||||
|
doc.save(out)
|
||||||
|
doc.close()
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def available_forms():
|
||||||
|
return list(FORM_NAMES.keys())
|
||||||
211
polish.py
Normal file
211
polish.py
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Astraea Polisher v2 — recursive qwen3.8fast self-improvement loop.
|
||||||
|
|
||||||
|
Goal: make the most visually appealing, premium, cinematic site possible.
|
||||||
|
Each iteration: read frontend -> qwen3.8fast critiques + rewrites toward GOAL
|
||||||
|
(contract = Jinja + chat API + external links) -> validate (JS/HTML id cross-check)
|
||||||
|
-> backup -> apply -> curl-verify -> auto-rollback on failure -> log. Retries a
|
||||||
|
failed generation up to 2x with targeted feedback.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import urllib.request
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
OLLAMA = "http://10.30.20.29:11434"
|
||||||
|
MODEL = "qwen3.8fast:latest"
|
||||||
|
TEMPLATE = "/opt/astraea/templates/index.html"
|
||||||
|
BACKUP_DIR = "/opt/astraea/polish-backups"
|
||||||
|
LOG_PATH = "/opt/astraea/polish-log.json"
|
||||||
|
ITERATIONS = int(sys.argv[1]) if len(sys.argv) > 1 else 6
|
||||||
|
|
||||||
|
GOAL = (
|
||||||
|
"Make this the most visually stunning, premium, cinematic, and emotionally "
|
||||||
|
"compelling family-law website on the internet. Obsess over typography "
|
||||||
|
"(scale, weight, letter-spacing), a refined dark color palette with a "
|
||||||
|
"confident accent, spacing and rhythm, motion and micro-interactions, depth "
|
||||||
|
"(gradients, glows, glass), and copy that is authoritative yet human. Target "
|
||||||
|
"the feel of a top-tier law firm's flagship site crossed with a futuristic AI "
|
||||||
|
"product — dark, elegant, trustworthy, memorable. Every iteration must be "
|
||||||
|
"strictly MORE appealing than the last."
|
||||||
|
)
|
||||||
|
|
||||||
|
CONTRACT = (
|
||||||
|
"HARD REQUIREMENTS — preserve these EXACTLY or the rewrite is invalid:\n"
|
||||||
|
"1. Keep the Jinja placeholders verbatim: `{{ agents | tojson }}` and "
|
||||||
|
"`{{ disclaimer | tojson }}`.\n"
|
||||||
|
"2. Keep `const AGENTS = {{ agents | tojson }};` and "
|
||||||
|
"`const DISCLAIMER = {{ disclaimer | tojson }};`.\n"
|
||||||
|
"3. Keep `fetch('/api/chat', ...)` and the request body {agent_id, message, history}.\n"
|
||||||
|
"4. Keep the Umami beacon `<script async src=\"https://analytics.thetempleofdoom.com/script.js\" "
|
||||||
|
"data-website-id=\"c8c2f69c-4448-40ab-9848-02179f10c001\"></script>`.\n"
|
||||||
|
"5. Keep the BMAC footer link to https://buymeacoffee.com/r26xrthzttg.\n"
|
||||||
|
"6. Keep every element id that the JavaScript references via getElementById / querySelector "
|
||||||
|
"matching the SAME id in the HTML (you may rename, but keep them consistent).\n"
|
||||||
|
"7. Output ONLY the complete valid HTML document (from <!DOCTYPE html> to </html>), "
|
||||||
|
"with no code fences and no commentary."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _p(*a, **k):
|
||||||
|
print(*a, **k, flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def chat(system, user, max_tokens=14000, temperature=0.65):
|
||||||
|
payload = {
|
||||||
|
"model": MODEL,
|
||||||
|
"messages": [{"role": "system", "content": system},
|
||||||
|
{"role": "user", "content": user}],
|
||||||
|
"stream": False, "think": False,
|
||||||
|
"options": {"temperature": temperature, "num_predict": max_tokens, "num_ctx": 24000},
|
||||||
|
}
|
||||||
|
req = urllib.request.Request(f"{OLLAMA}/api/chat", data=json.dumps(payload).encode(),
|
||||||
|
headers={"Content-Type": "application/json"})
|
||||||
|
op = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||||
|
with op.open(req, timeout=360) as r:
|
||||||
|
d = json.loads(r.read().decode("utf-8"))
|
||||||
|
m = d.get("message", {})
|
||||||
|
return (m.get("content") or m.get("thinking") or "").strip()
|
||||||
|
|
||||||
|
|
||||||
|
def extract_html(text):
|
||||||
|
"""Pull the HTML document out of a model reply (strip fences + prose)."""
|
||||||
|
# strip a code fence if present (any language tag)
|
||||||
|
m = re.search(r"```\w*\s*\n?(.*?)```", text, re.DOTALL)
|
||||||
|
if m:
|
||||||
|
text = m.group(1)
|
||||||
|
# find the <html ...> ... </html> block (with or without DOCTYPE)
|
||||||
|
m = re.search(r"(<html[^>]*>.*?</html>)", text, re.DOTALL | re.IGNORECASE)
|
||||||
|
if m:
|
||||||
|
html = m.group(1)
|
||||||
|
if not html.lower().lstrip().startswith("<!doctype"):
|
||||||
|
html = "<!DOCTYPE html>\n" + html
|
||||||
|
return html.strip()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def validate(html):
|
||||||
|
"""Return (ok, [problems]). External contract + internal JS/HTML id consistency."""
|
||||||
|
problems = []
|
||||||
|
if "{{ agents | tojson }}" not in html:
|
||||||
|
problems.append("missing {{ agents | tojson }}")
|
||||||
|
if "{{ disclaimer | tojson }}" not in html:
|
||||||
|
problems.append("missing {{ disclaimer | tojson }}")
|
||||||
|
if "const AGENTS =" not in html:
|
||||||
|
problems.append("missing const AGENTS")
|
||||||
|
if "const DISCLAIMER =" not in html:
|
||||||
|
problems.append("missing const DISCLAIMER")
|
||||||
|
if "fetch('/api/chat'" not in html:
|
||||||
|
problems.append("missing fetch('/api/chat')")
|
||||||
|
if "c8c2f69c-4448-40ab-9848-02179f10c001" not in html:
|
||||||
|
problems.append("missing Umami id")
|
||||||
|
if "buymeacoffee.com/r26xrthzttg" not in html:
|
||||||
|
problems.append("missing BMAC link")
|
||||||
|
for tag in ("html", "body", "script", "style"):
|
||||||
|
if html.count(f"<{tag}") != html.count(f"</{tag}>"):
|
||||||
|
problems.append(f"unbalanced <{tag}> tags")
|
||||||
|
# JS/HTML id cross-check
|
||||||
|
scripts = "\n".join(re.findall(r"<script[^>]*>(.*?)</script>", html, re.DOTALL))
|
||||||
|
refs = set(re.findall(r"getElementById\(['\"]([^'\"]+)['\"]\)", scripts))
|
||||||
|
refs |= set(re.findall(r"querySelector\(['\"]#([^'\"]+)['\"]\)", scripts))
|
||||||
|
refs |= set(re.findall(r"querySelectorAll\(['\"]#([^'\"]+)['\"]\)", scripts))
|
||||||
|
for r in refs:
|
||||||
|
if not re.search(rf"id\s*=\s*[\"']{re.escape(r)}[\"']", html):
|
||||||
|
problems.append(f"JS refs #{r} but no matching id in HTML")
|
||||||
|
return (len(problems) == 0, problems)
|
||||||
|
|
||||||
|
|
||||||
|
def shell(cmd):
|
||||||
|
return subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||||
|
|
||||||
|
|
||||||
|
def verify_live():
|
||||||
|
"""Check the served page returns 200 (stdlib urllib, no curl on the CT)."""
|
||||||
|
try:
|
||||||
|
req = urllib.request.Request("http://127.0.0.1:5000/")
|
||||||
|
op = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||||
|
with op.open(req, timeout=12) as r:
|
||||||
|
return r.status == 200
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def log_entry(entry):
|
||||||
|
log = []
|
||||||
|
if os.path.exists(LOG_PATH):
|
||||||
|
try:
|
||||||
|
log = json.load(open(LOG_PATH))
|
||||||
|
except Exception:
|
||||||
|
log = []
|
||||||
|
log.append(entry)
|
||||||
|
json.dump(log, open(LOG_PATH, "w"), indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
os.makedirs(BACKUP_DIR, exist_ok=True)
|
||||||
|
_p(f"=== Astraea Polisher v2 — {ITERATIONS} iterations, {MODEL} ===")
|
||||||
|
_p(f"GOAL: {GOAL[:80]}...")
|
||||||
|
|
||||||
|
for it in range(1, ITERATIONS + 1):
|
||||||
|
_p(f"\n--- iteration {it}/{ITERATIONS} ---")
|
||||||
|
current = open(TEMPLATE, encoding="utf-8").read()
|
||||||
|
system = f"You are a world-class front-end design engineer. OBJECTIVE: {GOAL}\n\n{CONTRACT}"
|
||||||
|
|
||||||
|
applied = False
|
||||||
|
problems = []
|
||||||
|
for attempt in range(1, 4):
|
||||||
|
feedback = ""
|
||||||
|
if attempt > 1:
|
||||||
|
feedback = f"\n\nYour previous attempt was REJECTED for: {problems}. Fix ONLY those issues and resubmit the complete HTML."
|
||||||
|
user = (
|
||||||
|
"Here is the CURRENT full HTML/CSS/JS. Rewrite it into a COMPLETE, "
|
||||||
|
"significantly more appealing HTML document. Output ONLY the HTML — no "
|
||||||
|
"commentary, no explanation, no code fences."
|
||||||
|
f"{feedback}\n\n=== CURRENT HTML ===\n{current}"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
reply = chat(system, user)
|
||||||
|
except Exception as e:
|
||||||
|
_p(f" LLM error: {e}")
|
||||||
|
break
|
||||||
|
new_html = extract_html(reply)
|
||||||
|
if not new_html:
|
||||||
|
_p(f" attempt {attempt}: could not extract HTML (reply len {len(reply)})")
|
||||||
|
continue
|
||||||
|
ok, problems = validate(new_html)
|
||||||
|
if not ok:
|
||||||
|
_p(f" attempt {attempt}: validation failed ({len(problems)}): {problems[:4]}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# backup + apply
|
||||||
|
ts = datetime.now().strftime("%Y%m%d-%H%M%S")
|
||||||
|
backup = os.path.join(BACKUP_DIR, f"index.html.bak-{ts}")
|
||||||
|
open(backup, "w", encoding="utf-8").write(current)
|
||||||
|
open(TEMPLATE, "w", encoding="utf-8").write(new_html)
|
||||||
|
shell("systemctl restart astraea")
|
||||||
|
time.sleep(4)
|
||||||
|
if verify_live():
|
||||||
|
_p(f" ✓ applied ({len(new_html)-len(current):+d} chars) — {backup}")
|
||||||
|
log_entry({"ts": ts, "iter": it, "attempt": attempt,
|
||||||
|
"delta_chars": len(new_html) - len(current)})
|
||||||
|
applied = True
|
||||||
|
else:
|
||||||
|
open(TEMPLATE, "w", encoding="utf-8").write(current)
|
||||||
|
shell("systemctl restart astraea")
|
||||||
|
_p(" ✗ live verify failed — rolled back")
|
||||||
|
log_entry({"ts": ts, "iter": it, "rollback": True})
|
||||||
|
break
|
||||||
|
|
||||||
|
if not applied:
|
||||||
|
_p(" (no change this iteration)")
|
||||||
|
|
||||||
|
_p(f"\n=== done. log: {LOG_PATH} ===")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
185
rag.py
Normal file
185
rag.py
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Semantic RAG engine for Astraea — chunk the law 'books', embed, and retrieve.
|
||||||
|
|
||||||
|
stdlib-only (urllib + json + math), no numpy needed. Embeddings and generation
|
||||||
|
run on the Ollama host (nightmare). The vector index is cached to disk and rebuilt
|
||||||
|
incrementally only when a book changes.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import threading
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
from agents import OLLAMA_URL, EMBED_MODEL, BOOKS_DIR, AGENTS
|
||||||
|
|
||||||
|
INDEX_PATH = "/opt/astraea/index.json"
|
||||||
|
|
||||||
|
|
||||||
|
def _post(url, payload, timeout=120):
|
||||||
|
"""POST JSON to Ollama with a no-proxy opener (safe on LAN)."""
|
||||||
|
req = urllib.request.Request(
|
||||||
|
url, data=json.dumps(payload).encode("utf-8"),
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
)
|
||||||
|
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
|
||||||
|
with opener.open(req, timeout=timeout) as r:
|
||||||
|
return json.loads(r.read().decode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def embed(text):
|
||||||
|
"""Return a float vector for `text` via the Ollama embeddings endpoint."""
|
||||||
|
d = _post(f"{OLLAMA_URL}/api/embeddings", {"model": EMBED_MODEL, "prompt": text})
|
||||||
|
v = d.get("embedding") or d.get("embeddings")
|
||||||
|
if isinstance(v, list) and v and isinstance(v[0], list):
|
||||||
|
v = v[0]
|
||||||
|
if not v:
|
||||||
|
raise RuntimeError("empty embedding returned")
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
def _cosine(a, b):
|
||||||
|
dot = sum(x * y for x, y in zip(a, b))
|
||||||
|
na = math.sqrt(sum(x * x for x in a))
|
||||||
|
nb = math.sqrt(sum(y * y for y in b))
|
||||||
|
return dot / (na * nb) if na and nb else 0.0
|
||||||
|
|
||||||
|
|
||||||
|
MAX_CHUNK = 1100 # chars — stay safely under the embedding model's context budget
|
||||||
|
|
||||||
|
|
||||||
|
def _split_long(text, limit):
|
||||||
|
"""Split text into pieces <= limit chars, preferring sentence boundaries."""
|
||||||
|
if len(text) <= limit:
|
||||||
|
return [text] if text.strip() else []
|
||||||
|
parts = re.split(r"(?<=[.!?])\s+", text)
|
||||||
|
out = []
|
||||||
|
cur = ""
|
||||||
|
for p in parts:
|
||||||
|
if cur and len(cur) + len(p) + 1 > limit:
|
||||||
|
out.append(cur)
|
||||||
|
cur = p
|
||||||
|
else:
|
||||||
|
cur = (cur + " " + p).strip() if cur else p
|
||||||
|
while len(cur) > limit:
|
||||||
|
out.append(cur[:limit])
|
||||||
|
cur = cur[limit:]
|
||||||
|
if cur.strip():
|
||||||
|
out.append(cur)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _chunk_markdown(text):
|
||||||
|
"""Split markdown into self-contained (title, body) chunks, each <= MAX_CHUNK chars."""
|
||||||
|
lines = text.splitlines()
|
||||||
|
chunks = []
|
||||||
|
cur_title = None
|
||||||
|
cur_buf = []
|
||||||
|
|
||||||
|
def flush():
|
||||||
|
nonlocal cur_title, cur_buf
|
||||||
|
if not cur_buf:
|
||||||
|
return
|
||||||
|
body = "\n".join(cur_buf).strip()
|
||||||
|
cur_buf = []
|
||||||
|
if not body:
|
||||||
|
return
|
||||||
|
for piece in _split_long(body, MAX_CHUNK):
|
||||||
|
chunks.append((cur_title or "Section", piece))
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
if re.match(r"^#{1,4}\s+", line):
|
||||||
|
flush()
|
||||||
|
cur_title = re.sub(r"^#{1,4}\s+", "", line).strip()
|
||||||
|
else:
|
||||||
|
cur_buf.append(line)
|
||||||
|
flush()
|
||||||
|
return chunks
|
||||||
|
|
||||||
|
|
||||||
|
def build_index():
|
||||||
|
"""Build (or load from cache) the vector index. Returns list of chunk dicts."""
|
||||||
|
index = []
|
||||||
|
book_files = []
|
||||||
|
for a in AGENTS:
|
||||||
|
for b in a["books"]:
|
||||||
|
path = os.path.join(BOOKS_DIR, b)
|
||||||
|
if os.path.exists(path):
|
||||||
|
book_files.append((path, a["id"]))
|
||||||
|
|
||||||
|
# Decide whether to reuse cache: index.json exists, embed model matches, and no book changed.
|
||||||
|
if os.path.exists(INDEX_PATH):
|
||||||
|
try:
|
||||||
|
with open(INDEX_PATH) as f:
|
||||||
|
cached = json.load(f)
|
||||||
|
if cached.get("embed_model") == EMBED_MODEL:
|
||||||
|
newest_book = max(os.path.getmtime(p) for p, _ in book_files) if book_files else 0
|
||||||
|
if os.path.getmtime(INDEX_PATH) >= newest_book:
|
||||||
|
return cached["chunks"]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for path, agent_id in book_files:
|
||||||
|
try:
|
||||||
|
with open(path, encoding="utf-8") as f:
|
||||||
|
text = f.read()
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
base = os.path.basename(path)
|
||||||
|
for title, body in _chunk_markdown(text):
|
||||||
|
# Embed the title + body together for best semantic match.
|
||||||
|
try:
|
||||||
|
vec = embed(f"{title}\n{body}")
|
||||||
|
except Exception:
|
||||||
|
continue # skip any chunk that fails to embed
|
||||||
|
index.append({
|
||||||
|
"agent": agent_id,
|
||||||
|
"source": base,
|
||||||
|
"title": title,
|
||||||
|
"text": body,
|
||||||
|
"vector": vec,
|
||||||
|
})
|
||||||
|
|
||||||
|
if index:
|
||||||
|
try:
|
||||||
|
with open(INDEX_PATH, "w") as f:
|
||||||
|
json.dump({"embed_model": EMBED_MODEL, "chunks": index}, f)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return index
|
||||||
|
|
||||||
|
|
||||||
|
def retrieve(query, agent_id, top_k=5):
|
||||||
|
"""Return top-k relevant chunks for `agent_id` via cosine similarity."""
|
||||||
|
qvec = embed(query)
|
||||||
|
scored = []
|
||||||
|
for c in _INDEX:
|
||||||
|
if c["agent"] != agent_id:
|
||||||
|
continue
|
||||||
|
scored.append((_cosine(qvec, c["vector"]), c))
|
||||||
|
scored.sort(key=lambda x: x[0], reverse=True)
|
||||||
|
return [(s, c) for s, c in scored[:top_k]]
|
||||||
|
|
||||||
|
|
||||||
|
# Global index, built lazily once in a background thread.
|
||||||
|
_INDEX = []
|
||||||
|
_index_lock = threading.Lock()
|
||||||
|
_index_ready = False
|
||||||
|
|
||||||
|
|
||||||
|
def start_background_index():
|
||||||
|
def _run():
|
||||||
|
global _index_ready
|
||||||
|
try:
|
||||||
|
idx = build_index()
|
||||||
|
with _index_lock:
|
||||||
|
_INDEX.clear()
|
||||||
|
_INDEX.extend(idx)
|
||||||
|
finally:
|
||||||
|
_index_ready = True
|
||||||
|
threading.Thread(target=_run, daemon=True).start()
|
||||||
|
|
||||||
|
|
||||||
|
def index_status():
|
||||||
|
return {"ready": _index_ready, "chunks": len(_INDEX)}
|
||||||
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
flask>=3.0
|
||||||
|
gunicorn>=21.2
|
||||||
BIN
static/hero.png
Normal file
BIN
static/hero.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
254
store.py
Normal file
254
store.py
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Astraea data layer — no-KYC auth, profiles (avatar + About Me), and settings.
|
||||||
|
SQLite at /opt/astraea/astraea.db. stdlib-only.
|
||||||
|
"""
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import secrets
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
DB_PATH = "/opt/astraea/astraea.db"
|
||||||
|
|
||||||
|
|
||||||
|
def _conn():
|
||||||
|
c = sqlite3.connect(DB_PATH)
|
||||||
|
c.row_factory = sqlite3.Row
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
|
def init_db():
|
||||||
|
c = _conn()
|
||||||
|
c.executescript("""
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT UNIQUE NOT NULL,
|
||||||
|
password_hash TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS sessions (
|
||||||
|
token TEXT PRIMARY KEY,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS profiles (
|
||||||
|
user_id INTEGER PRIMARY KEY,
|
||||||
|
display_name TEXT DEFAULT '',
|
||||||
|
avatar INTEGER DEFAULT 0,
|
||||||
|
about_me TEXT DEFAULT '',
|
||||||
|
profile_pic TEXT DEFAULT '',
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
user_id INTEGER PRIMARY KEY,
|
||||||
|
twilio_sid TEXT DEFAULT '',
|
||||||
|
twilio_auth_token TEXT DEFAULT '',
|
||||||
|
twilio_from TEXT DEFAULT '',
|
||||||
|
smtp_host TEXT DEFAULT '',
|
||||||
|
smtp_port INTEGER DEFAULT 587,
|
||||||
|
smtp_user TEXT DEFAULT '',
|
||||||
|
smtp_pass TEXT DEFAULT '',
|
||||||
|
email_from TEXT DEFAULT '',
|
||||||
|
no_contact_order INTEGER DEFAULT 0,
|
||||||
|
partner_name TEXT DEFAULT '',
|
||||||
|
domestic_violence INTEGER DEFAULT 0,
|
||||||
|
protection_order INTEGER DEFAULT 0,
|
||||||
|
safety_notes TEXT DEFAULT '',
|
||||||
|
tts_voice TEXT DEFAULT 'aria'
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS messages (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
agent_id TEXT NOT NULL,
|
||||||
|
role TEXT NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
citations TEXT DEFAULT '',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS files (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
category TEXT DEFAULT 'Other',
|
||||||
|
filename TEXT NOT NULL,
|
||||||
|
orig_name TEXT NOT NULL,
|
||||||
|
mime TEXT DEFAULT '',
|
||||||
|
size INTEGER DEFAULT 0,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
c.commit()
|
||||||
|
# migrations for pre-existing DBs
|
||||||
|
for col, ddl in [("domestic_violence", "INTEGER DEFAULT 0"),
|
||||||
|
("protection_order", "INTEGER DEFAULT 0"),
|
||||||
|
("safety_notes", "TEXT DEFAULT ''")]:
|
||||||
|
try:
|
||||||
|
c.execute(f"ALTER TABLE settings ADD COLUMN {col} {ddl}")
|
||||||
|
c.commit()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
c.execute("ALTER TABLE profiles ADD COLUMN profile_pic TEXT DEFAULT ''")
|
||||||
|
c.commit()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
c.close()
|
||||||
|
|
||||||
|
|
||||||
|
def hash_password(pw):
|
||||||
|
return hashlib.sha256(pw.encode()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def user_from_token(token):
|
||||||
|
if not token:
|
||||||
|
return None
|
||||||
|
c = _conn()
|
||||||
|
row = c.execute("SELECT user_id FROM sessions WHERE token=?", (token,)).fetchone()
|
||||||
|
c.close()
|
||||||
|
return row["user_id"] if row else None
|
||||||
|
|
||||||
|
|
||||||
|
def create_user(username, password):
|
||||||
|
c = _conn()
|
||||||
|
c.execute("INSERT INTO users (username, password_hash) VALUES (?,?)",
|
||||||
|
(username, hash_password(password)))
|
||||||
|
c.commit()
|
||||||
|
uid = c.execute("SELECT id FROM users WHERE username=?", (username,)).fetchone()["id"]
|
||||||
|
c.execute("INSERT OR IGNORE INTO profiles (user_id) VALUES (?)", (uid,))
|
||||||
|
c.execute("INSERT OR IGNORE INTO settings (user_id) VALUES (?)", (uid,))
|
||||||
|
c.commit()
|
||||||
|
c.close()
|
||||||
|
return uid
|
||||||
|
|
||||||
|
|
||||||
|
def issue_token(uid):
|
||||||
|
token = secrets.token_hex(32)
|
||||||
|
c = _conn()
|
||||||
|
c.execute("INSERT INTO sessions (token, user_id) VALUES (?,?)", (token, uid))
|
||||||
|
c.commit()
|
||||||
|
c.close()
|
||||||
|
return token
|
||||||
|
|
||||||
|
|
||||||
|
def authenticate(username, password):
|
||||||
|
c = _conn()
|
||||||
|
row = c.execute("SELECT id, password_hash FROM users WHERE username=?",
|
||||||
|
(username,)).fetchone()
|
||||||
|
c.close()
|
||||||
|
if row and row["password_hash"] == hash_password(password):
|
||||||
|
return row["id"]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_profile(uid):
|
||||||
|
c = _conn()
|
||||||
|
row = c.execute("SELECT * FROM profiles WHERE user_id=?", (uid,)).fetchone()
|
||||||
|
c.close()
|
||||||
|
if not row:
|
||||||
|
return {"display_name": "", "avatar": 0, "about_me": "", "profile_pic": ""}
|
||||||
|
return {"display_name": row["display_name"], "avatar": row["avatar"],
|
||||||
|
"about_me": row["about_me"], "profile_pic": row["profile_pic"] or ""}
|
||||||
|
|
||||||
|
|
||||||
|
def set_profile(uid, display_name=None, avatar=None, about_me=None, profile_pic=None):
|
||||||
|
cur = get_profile(uid)
|
||||||
|
dn = cur["display_name"] if display_name is None else display_name
|
||||||
|
av = cur["avatar"] if avatar is None else avatar
|
||||||
|
am = cur["about_me"] if about_me is None else about_me
|
||||||
|
pp = cur.get("profile_pic", "") if profile_pic is None else profile_pic
|
||||||
|
c = _conn()
|
||||||
|
c.execute("""INSERT INTO profiles (user_id, display_name, avatar, about_me, profile_pic, updated_at)
|
||||||
|
VALUES (?,?,?,?,?, CURRENT_TIMESTAMP)
|
||||||
|
ON CONFLICT(user_id) DO UPDATE SET
|
||||||
|
display_name=excluded.display_name, avatar=excluded.avatar,
|
||||||
|
about_me=excluded.about_me, profile_pic=excluded.profile_pic, updated_at=CURRENT_TIMESTAMP""",
|
||||||
|
(uid, dn, av, am, pp))
|
||||||
|
c.commit()
|
||||||
|
c.close()
|
||||||
|
return {"display_name": dn, "avatar": av, "about_me": am, "profile_pic": pp}
|
||||||
|
|
||||||
|
|
||||||
|
def get_settings(uid):
|
||||||
|
c = _conn()
|
||||||
|
row = c.execute("SELECT * FROM settings WHERE user_id=?", (uid,)).fetchone()
|
||||||
|
c.close()
|
||||||
|
if not row:
|
||||||
|
return {}
|
||||||
|
# strip auth secrets from the read-back for safety, but return everything to owner
|
||||||
|
return dict(row)
|
||||||
|
|
||||||
|
|
||||||
|
def save_message(uid, agent_id, role, content, citations=None):
|
||||||
|
c = _conn()
|
||||||
|
c.execute("INSERT INTO messages (user_id, agent_id, role, content, citations) VALUES (?,?,?,?,?)",
|
||||||
|
(uid, agent_id, role, content, json.dumps(citations or [])))
|
||||||
|
c.commit()
|
||||||
|
c.close()
|
||||||
|
|
||||||
|
|
||||||
|
def get_conversation(uid, agent_id, limit=60):
|
||||||
|
c = _conn()
|
||||||
|
rows = c.execute(
|
||||||
|
"SELECT role, content, citations FROM messages WHERE user_id=? AND agent_id=? ORDER BY id DESC LIMIT ?",
|
||||||
|
(uid, agent_id, limit)).fetchall()
|
||||||
|
c.close()
|
||||||
|
out = []
|
||||||
|
for r in reversed(rows):
|
||||||
|
try:
|
||||||
|
cit = json.loads(r["citations"]) if r["citations"] else []
|
||||||
|
except Exception:
|
||||||
|
cit = []
|
||||||
|
out.append({"role": r["role"], "content": r["content"], "citations": cit})
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def clear_conversation(uid, agent_id):
|
||||||
|
c = _conn()
|
||||||
|
c.execute("DELETE FROM messages WHERE user_id=? AND agent_id=?", (uid, agent_id))
|
||||||
|
c.commit()
|
||||||
|
c.close()
|
||||||
|
|
||||||
|
|
||||||
|
def set_settings(uid, **fields):
|
||||||
|
allowed = {"twilio_sid", "twilio_auth_token", "twilio_from", "smtp_host", "smtp_port",
|
||||||
|
"smtp_user", "smtp_pass", "email_from", "no_contact_order", "partner_name",
|
||||||
|
"domestic_violence", "protection_order", "safety_notes", "tts_voice"}
|
||||||
|
updates = {k: v for k, v in fields.items() if k in allowed}
|
||||||
|
if not updates:
|
||||||
|
return get_settings(uid)
|
||||||
|
cols = ", ".join(f"{k}=?" for k in updates)
|
||||||
|
c = _conn()
|
||||||
|
c.execute(f"UPDATE settings SET {cols} WHERE user_id=?", (*updates.values(), uid))
|
||||||
|
c.commit()
|
||||||
|
c.close()
|
||||||
|
return get_settings(uid)
|
||||||
|
|
||||||
|
|
||||||
|
# ── Document vault ──
|
||||||
|
def add_file(uid, category, filename, orig_name, mime, size):
|
||||||
|
c = _conn()
|
||||||
|
c.execute("INSERT INTO files (user_id, category, filename, orig_name, mime, size) VALUES (?,?,?,?,?,?)",
|
||||||
|
(uid, category, filename, orig_name, mime, size))
|
||||||
|
c.commit()
|
||||||
|
fid = c.execute("SELECT last_insert_rowid()").fetchone()[0]
|
||||||
|
c.close()
|
||||||
|
return fid
|
||||||
|
|
||||||
|
|
||||||
|
def list_files(uid):
|
||||||
|
c = _conn()
|
||||||
|
rows = c.execute("SELECT * FROM files WHERE user_id=? ORDER BY created_at DESC, id DESC", (uid,)).fetchall()
|
||||||
|
c.close()
|
||||||
|
return [dict(r) for r in rows]
|
||||||
|
|
||||||
|
|
||||||
|
def get_file(uid, fid):
|
||||||
|
c = _conn()
|
||||||
|
row = c.execute("SELECT * FROM files WHERE id=? AND user_id=?", (fid, uid)).fetchone()
|
||||||
|
c.close()
|
||||||
|
return dict(row) if row else None
|
||||||
|
|
||||||
|
|
||||||
|
def delete_file(uid, fid):
|
||||||
|
c = _conn()
|
||||||
|
c.execute("DELETE FROM files WHERE id=? AND user_id=?", (fid, uid))
|
||||||
|
c.commit()
|
||||||
|
c.close()
|
||||||
507
templates/index.html
Normal file
507
templates/index.html
Normal file
@@ -0,0 +1,507 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Astraea — Your Attorney</title>
|
||||||
|
<style>
|
||||||
|
:root { --bg:#070b1a; --panel:#101832; --panel2:#141d3d; --line:#233055; --text:#e8edfb; --muted:#8b98c4; --gold:#f5d06f; }
|
||||||
|
* { box-sizing:border-box; margin:0; padding:0; }
|
||||||
|
body { font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:radial-gradient(1200px 700px at 50% -10%,#18254f 0%,var(--bg) 60%); color:var(--text); min-height:100vh; }
|
||||||
|
canvas { position:fixed; inset:0; z-index:0; pointer-events:none; }
|
||||||
|
.app { position:relative; z-index:1; max-width:1120px; margin:0 auto; padding:0 20px 60px; }
|
||||||
|
header { padding:20px 0; display:flex; align-items:center; justify-content:space-between; gap:14px; }
|
||||||
|
.brand { display:flex; align-items:center; gap:11px; }
|
||||||
|
.logo { width:40px; height:40px; border-radius:11px; background:linear-gradient(135deg,#6ea8ff,#a78bfa); display:grid; place-items:center; font-size:20px; }
|
||||||
|
.brand h1 { font-size:20px; font-weight:800; }
|
||||||
|
.brand h1 span { color:var(--gold); }
|
||||||
|
.top-actions { display:flex; align-items:center; gap:10px; }
|
||||||
|
.avatar-sm { width:36px; height:36px; border-radius:50%; display:grid; place-items:center; font-size:17px; color:#fff; cursor:pointer; }
|
||||||
|
.btn { border:1px solid var(--line); background:var(--panel2); color:var(--text); border-radius:10px; padding:9px 15px; font-size:13.5px; font-weight:600; cursor:pointer; transition:.15s; }
|
||||||
|
.btn:hover { border-color:#3b82f6; }
|
||||||
|
.btn.primary { background:linear-gradient(135deg,#3b82f6,#6366f1); border:none; color:#fff; }
|
||||||
|
.hero { padding:12px 0 20px; }
|
||||||
|
.hero h2 { font-size:clamp(24px,4vw,34px); font-weight:800; letter-spacing:-.5px; }
|
||||||
|
.hero p { color:var(--muted); margin-top:8px; font-size:15px; }
|
||||||
|
.label { font-size:11.5px; text-transform:uppercase; letter-spacing:2px; color:var(--muted); margin:14px 0 12px; }
|
||||||
|
.grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(235px,1fr)); gap:13px; }
|
||||||
|
.card { background:linear-gradient(180deg,var(--panel2),var(--panel)); border:1px solid var(--line); border-radius:15px; padding:16px; cursor:pointer; position:relative; overflow:hidden; transition:.18s; }
|
||||||
|
.card::before { content:""; position:absolute; inset:0 0 auto 0; height:3px; background:var(--c,#7dd3fc); }
|
||||||
|
.card:hover { transform:translateY(-3px); border-color:var(--c,#7dd3fc); }
|
||||||
|
.card.active { border-color:var(--c,#7dd3fc); box-shadow:0 0 0 1px var(--c,#7dd3fc),0 14px 34px rgba(0,0,0,.5); }
|
||||||
|
.card .emoji { font-size:27px; }
|
||||||
|
.card h3 { font-size:15.5px; font-weight:700; margin:10px 0 3px; }
|
||||||
|
.card .tag { font-size:12px; color:var(--c,#7dd3fc); font-weight:600; margin-bottom:6px; }
|
||||||
|
.card p { font-size:12.5px; color:var(--muted); line-height:1.45; }
|
||||||
|
|
||||||
|
.chat-panel { margin-top:30px; background:linear-gradient(180deg,var(--panel2),var(--panel)); border:1px solid var(--line); border-radius:18px; display:flex; flex-direction:column; overflow:hidden; }
|
||||||
|
.chat-head { padding:14px 18px; border-bottom:1px solid var(--line); display:flex; align-items:center; gap:11px; flex-wrap:wrap; }
|
||||||
|
.chat-head .emoji { font-size:24px; }
|
||||||
|
.chat-head .who { font-weight:700; font-size:15px; }
|
||||||
|
.chat-head .scope { color:var(--muted); font-weight:500; font-size:12px; }
|
||||||
|
.chips { margin-left:auto; display:flex; gap:6px; flex-wrap:wrap; }
|
||||||
|
.chip { font-size:11.5px; padding:5px 10px; border-radius:999px; border:1px solid var(--line); color:var(--muted); background:transparent; cursor:pointer; white-space:nowrap; }
|
||||||
|
.chip.on { color:#06121f; background:var(--c,#7dd3fc); border-color:var(--c,#7dd3fc); font-weight:700; }
|
||||||
|
.messages { padding:20px; min-height:220px; max-height:520px; overflow-y:auto; display:flex; flex-direction:column; gap:15px; }
|
||||||
|
.msg { max-width:80%; display:flex; flex-direction:column; }
|
||||||
|
.msg.user { align-self:flex-end; align-items:flex-end; }
|
||||||
|
.msg.agent { align-self:flex-start; }
|
||||||
|
.bubble { padding:12px 15px; border-radius:15px; font-size:14.5px; line-height:1.6; white-space:pre-wrap; word-break:break-word; }
|
||||||
|
.msg.user .bubble { background:linear-gradient(135deg,#3b82f6,#6366f1); color:#fff; border-bottom-right-radius:4px; }
|
||||||
|
.msg.agent .bubble { background:var(--panel2); border:1px solid var(--line); border-bottom-left-radius:4px; }
|
||||||
|
.msg .who-label { font-size:11px; color:var(--muted); margin:0 4px 4px; }
|
||||||
|
.speak { margin-top:6px; background:none; border:none; color:var(--gold); cursor:pointer; font-size:12px; }
|
||||||
|
.citations { margin-top:9px; }
|
||||||
|
.cite-toggle { font-size:12px; color:var(--gold); cursor:pointer; background:none; border:none; }
|
||||||
|
.cite-list { margin-top:7px; display:none; flex-direction:column; gap:7px; }
|
||||||
|
.cite-list.open { display:flex; }
|
||||||
|
.cite { background:#0c142e; border:1px solid var(--line); border-radius:9px; padding:9px 11px; font-size:12px; color:var(--muted); }
|
||||||
|
.cite b { color:var(--text); display:block; margin-bottom:2px; }
|
||||||
|
.typing { display:inline-flex; gap:5px; padding:6px 2px; }
|
||||||
|
.typing i { width:7px; height:7px; border-radius:50%; background:var(--muted); animation:bl 1.2s infinite; }
|
||||||
|
.typing i:nth-child(2){ animation-delay:.2s; } .typing i:nth-child(3){ animation-delay:.4s; }
|
||||||
|
@keyframes bl { 0%,100%{opacity:.25;} 50%{opacity:1;} }
|
||||||
|
.composer { border-top:1px solid var(--line); padding:13px 15px; display:flex; gap:9px; align-items:flex-end; }
|
||||||
|
.composer textarea { flex:1; resize:none; background:#0c142e; border:1px solid var(--line); color:var(--text); border-radius:11px; padding:11px 13px; font-size:14.5px; font-family:inherit; line-height:1.5; max-height:130px; outline:none; }
|
||||||
|
.composer textarea:focus { border-color:var(--c,#7dd3fc); }
|
||||||
|
.send { border:none; border-radius:11px; padding:12px 18px; font-size:14px; font-weight:700; color:#06121f; background:var(--c,#7dd3fc); cursor:pointer; }
|
||||||
|
.send:disabled { opacity:.45; }
|
||||||
|
.tools-row { display:flex; gap:8px; padding:0 15px 12px; flex-wrap:wrap; }
|
||||||
|
.tools-row .btn { font-size:12.5px; padding:8px 13px; }
|
||||||
|
|
||||||
|
.modal-bg { position:fixed; inset:0; background:rgba(3,5,12,.8); z-index:50; display:none; align-items:center; justify-content:center; padding:20px; }
|
||||||
|
.modal-bg.open { display:flex; }
|
||||||
|
.modal { background:linear-gradient(180deg,var(--panel2),var(--panel)); border:1px solid var(--line); border-radius:18px; width:100%; max-width:620px; max-height:90vh; overflow-y:auto; padding:26px; }
|
||||||
|
.modal h2 { font-size:20px; font-weight:800; margin-bottom:4px; }
|
||||||
|
.modal .sub { color:var(--muted); font-size:13px; margin-bottom:18px; }
|
||||||
|
.modal .close { float:right; background:none; border:none; color:var(--muted); font-size:22px; cursor:pointer; }
|
||||||
|
.fld { margin-bottom:15px; }
|
||||||
|
.fld label { display:block; font-size:12.5px; color:var(--muted); margin-bottom:6px; }
|
||||||
|
.fld input, .fld textarea, .fld select { width:100%; background:#0c142e; border:1px solid var(--line); color:var(--text); border-radius:10px; padding:11px 13px; font-size:14px; outline:none; font-family:inherit; }
|
||||||
|
.fld textarea { min-height:110px; resize:vertical; }
|
||||||
|
.fld input:focus, .fld textarea:focus { border-color:#6366f1; }
|
||||||
|
.avatar-grid { display:grid; grid-template-columns:repeat(6,1fr); gap:10px; margin-bottom:18px; }
|
||||||
|
.avatar-opt { aspect-ratio:1; border-radius:50%; display:grid; place-items:center; font-size:22px; color:#fff; cursor:pointer; border:2px solid transparent; }
|
||||||
|
.avatar-opt.sel { border-color:#fff; box-shadow:0 0 0 3px rgba(255,255,255,.2); }
|
||||||
|
.progbar { height:6px; background:var(--panel2); border-radius:3px; overflow:hidden; margin:14px 0 18px; }
|
||||||
|
.progbar > div { height:100%; background:linear-gradient(90deg,#6ea8ff,#a78bfa); border-radius:3px; transition:width .2s; }
|
||||||
|
.intake-q { font-size:16px; font-weight:600; margin-bottom:12px; line-height:1.4; }
|
||||||
|
.intake-nav { display:flex; justify-content:space-between; margin-top:14px; gap:10px; }
|
||||||
|
.intake-nav .btn { flex:1; }
|
||||||
|
.modal.wide { width:min(880px,94vw); max-height:90vh; overflow:auto; }
|
||||||
|
.file-list { max-height:240px; overflow:auto; display:flex; flex-direction:column; gap:6px; }
|
||||||
|
.file-item { display:flex; align-items:center; gap:8px; padding:8px 10px; background:var(--panel2); border-radius:8px; font-size:13px; }
|
||||||
|
.file-item .nm { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
||||||
|
.file-item .cat { color:var(--muted); font-size:11px; }
|
||||||
|
.file-item button { background:none; border:1px solid var(--line); color:var(--text); border-radius:6px; padding:4px 9px; cursor:pointer; font-size:12px; }
|
||||||
|
.file-item button:hover { background:var(--line); }
|
||||||
|
.switch { display:flex; align-items:center; gap:10px; }
|
||||||
|
.switch input { width:auto; }
|
||||||
|
.grid2 { display:grid; grid-template-columns:1fr 1fr; gap:13px; }
|
||||||
|
.doc-result { background:#0c142e; border:1px solid var(--line); border-radius:12px; padding:16px; font-size:13px; white-space:pre-wrap; max-height:50vh; overflow-y:auto; line-height:1.6; margin-top:14px; }
|
||||||
|
footer { text-align:center; margin-top:26px; font-size:12px; color:var(--muted); }
|
||||||
|
footer a { color:var(--muted); }
|
||||||
|
@media (max-width:640px){ .msg{max-width:92%;} .grid2{grid-template-columns:1fr;} .avatar-grid{grid-template-columns:repeat(4,1fr);} }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="stars"></canvas>
|
||||||
|
<div class="app">
|
||||||
|
<header>
|
||||||
|
<div class="brand"><div class="logo">⚖️</div><div><h1>ASTRA<span>EA</span></h1></div></div>
|
||||||
|
<div class="top-actions">
|
||||||
|
<button class="btn" onclick="openDocs()">📄 Documents</button>
|
||||||
|
<button class="btn" onclick="openSettings()">⚙️ Settings</button>
|
||||||
|
<div class="avatar-sm" id="myAvatar" onclick="openProfile()" style="background:linear-gradient(135deg,#6ea8ff,#a78bfa)">⚖</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="hero">
|
||||||
|
<h2>Eight specialists. Your case.</h2>
|
||||||
|
<p>Ask anything. Every answer is grounded in Washington statute and verified case law — and it already knows your situation.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="label">Choose your specialist</div>
|
||||||
|
<div class="grid" id="grid"></div>
|
||||||
|
|
||||||
|
<section class="chat-panel" id="chatPanel">
|
||||||
|
<div class="chat-head">
|
||||||
|
<div class="emoji" id="headEmoji">🧭</div>
|
||||||
|
<div><div class="who" id="headName">Navigator</div><div class="scope" id="headScope">Where to start</div></div>
|
||||||
|
<div class="chips" id="chips"></div>
|
||||||
|
</div>
|
||||||
|
<div class="messages" id="messages"></div>
|
||||||
|
<div class="tools-row">
|
||||||
|
<button class="btn" onclick="openDocs()">📄 Prep a document</button>
|
||||||
|
<button class="btn" onclick="openFill()">📝 Fill a form</button>
|
||||||
|
<button class="btn" onclick="openMission()">📨 Send on my behalf</button>
|
||||||
|
<button class="btn" onclick="clearConv()">🗑 Clear chat</button>
|
||||||
|
</div>
|
||||||
|
<div class="composer">
|
||||||
|
<button class="btn" id="micBtn" onclick="toggleMic()" title="Speak" style="flex:0 0 auto">🎤</button>
|
||||||
|
<textarea id="input" rows="1" placeholder="Ask your attorney…"></textarea>
|
||||||
|
<button class="send" id="sendBtn">Ask</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>Astraea gives practical legal guidance grounded in Washington law — not a substitute for a licensed attorney. · <a href="https://buymeacoffee.com/r26xrthzttg" target="_blank" rel="noopener">☕ Support</a></footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Profile modal -->
|
||||||
|
<div class="modal-bg" id="profileModal"><div class="modal">
|
||||||
|
<button class="close" onclick="closeModal('profileModal')">×</button>
|
||||||
|
<h2>Your profile</h2><div class="sub">This context shapes every answer — your attorney already knows your situation.</div>
|
||||||
|
<div class="fld"><label>Display name</label><input id="pfName" placeholder="Your name"></div>
|
||||||
|
<div class="fld"><label>Display picture</label></div>
|
||||||
|
<div class="avatar-grid" id="avatarGrid"></div>
|
||||||
|
<div class="fld"><label>Or upload your own photo</label><input type="file" id="pfPic" accept="image/*" onchange="uploadPic()"><span id="pfPicPreview"></span></div>
|
||||||
|
<div class="fld"><label>About Me — everything your attorney should know</label><button class="btn" style="width:100%;margin-bottom:8px" onclick="openIntake()">✍️ Guided intake — answer questions step by step</button><textarea id="pfAbout" placeholder="…or write it freely here. e.g. Married 2011, two children (ages 9 and 12), separated June 2025, own a home in King County, spouse filed a parenting plan…"></textarea></div>
|
||||||
|
<div class="fld"><label>📁 Case documents — upload & organize your paperwork</label>
|
||||||
|
<div style="display:flex;gap:8px;margin-bottom:8px"><select id="fileCat" style="width:auto"></select><input type="file" id="fileInput" multiple onchange="uploadFiles()" style="flex:1"></div>
|
||||||
|
<div id="fileList" class="file-list"><div class="sub">No documents yet — upload your petition, orders, agreements, and evidence.</div></div>
|
||||||
|
</div>
|
||||||
|
<button class="btn primary" style="width:100%" onclick="saveProfile()">Save profile</button>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
|
<!-- Guided intake modal -->
|
||||||
|
<div class="modal-bg" id="intakeModal"><div class="modal">
|
||||||
|
<button class="close" onclick="closeModal('intakeModal')">×</button>
|
||||||
|
<h2>Guided intake</h2><div class="sub">Answer a few questions — your attorney uses these as context for every answer.</div>
|
||||||
|
<div class="progbar"><div id="intakeBar"></div></div>
|
||||||
|
<div class="intake-q" id="intakeQ"></div>
|
||||||
|
<input id="intakeA" placeholder="">
|
||||||
|
<div class="intake-nav">
|
||||||
|
<button class="btn" id="intakeBack" onclick="intakePrev()">← Back</button>
|
||||||
|
<button class="btn primary" id="intakeNext" onclick="intakeNext()">Next →</button>
|
||||||
|
</div>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
|
<!-- Document viewer modal -->
|
||||||
|
<div class="modal-bg" id="viewerModal"><div class="modal wide">
|
||||||
|
<button class="close" onclick="closeModal('viewerModal')">×</button>
|
||||||
|
<h2 id="viewerTitle"></h2>
|
||||||
|
<div id="viewerBody" style="margin-top:12px"></div>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
|
<!-- Fill a form modal -->
|
||||||
|
<div class="modal-bg" id="fillModal"><div class="modal">
|
||||||
|
<button class="close" onclick="closeModal('fillModal')">×</button>
|
||||||
|
<h2>Fill a court form</h2><div class="sub">Your answers are typed onto the official WA form PDF, ready to print and file.</div>
|
||||||
|
<div class="fld"><label>Form</label><select id="fillFormSelect"></select></div>
|
||||||
|
<button class="btn" style="width:100%;margin-bottom:12px" onclick="autofillForm()">✨ Auto-fill from my info</button>
|
||||||
|
<div id="fillFields"></div>
|
||||||
|
<button class="btn primary" style="width:100%" onclick="generateForm()">⬇ Generate filled PDF</button>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
|
<!-- Settings modal -->
|
||||||
|
<div class="modal-bg" id="settingsModal"><div class="modal">
|
||||||
|
<button class="close" onclick="closeModal('settingsModal')">×</button>
|
||||||
|
<h2>Settings</h2><div class="sub">Comms channels your attorney can use for missions, and safety.</div>
|
||||||
|
<div class="fld"><label>Twilio Account SID</label><input id="twSid" placeholder="AC…"></div>
|
||||||
|
<div class="fld"><label>Twilio Auth Token</label><input id="twToken" type="password" placeholder="••••"></div>
|
||||||
|
<div class="fld"><label>Twilio From number (E.164)</label><input id="twFrom" placeholder="+14255550123"></div>
|
||||||
|
<div class="grid2">
|
||||||
|
<div class="fld"><label>SMTP host</label><input id="smHost" placeholder="smtp.gmail.com"></div>
|
||||||
|
<div class="fld"><label>SMTP port</label><input id="smPort" value="587"></div>
|
||||||
|
</div>
|
||||||
|
<div class="grid2">
|
||||||
|
<div class="fld"><label>SMTP username</label><input id="smUser"></div>
|
||||||
|
<div class="fld"><label>SMTP password</label><input id="smPass" type="password"></div>
|
||||||
|
</div>
|
||||||
|
<div class="fld"><label>Email "from" address</label><input id="emFrom" placeholder="you@example.com"></div>
|
||||||
|
<div class="fld"><label>TTS voice</label><select id="ttsVoice"></select></div>
|
||||||
|
<div class="fld switch"><label style="margin:0">🔒 No-contact order in effect</label><input type="checkbox" id="ncOrder"></div>
|
||||||
|
<div class="fld"><label>Protected person's name (blocked from contact)</label><input id="ncName" placeholder="Full name of the person you must not contact"></div>
|
||||||
|
<div class="fld switch"><label style="margin:0">⚠️ Domestic violence is a factor in my case</label><input type="checkbox" id="dvFactor"></div>
|
||||||
|
<div class="fld switch"><label style="margin:0">🛡️ Active protection order in effect (RCW 7.105)</label><input type="checkbox" id="poOrder"></div>
|
||||||
|
<div class="fld"><label>Safety notes — threats, incidents, what you're afraid of</label><textarea id="safetyNotes" style="min-height:70px" placeholder="e.g. There has been physical violence. I'm afraid for my safety and the children's safety."></textarea></div>
|
||||||
|
<button class="btn primary" style="width:100%" onclick="saveSettings()">Save settings</button>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
|
<!-- Documents modal -->
|
||||||
|
<div class="modal-bg" id="docsModal"><div class="modal">
|
||||||
|
<button class="close" onclick="closeModal('docsModal')">×</button>
|
||||||
|
<h2>Prepare a document</h2><div class="sub">Court-ready Washington divorce paperwork, drafted to your facts.</div>
|
||||||
|
<div class="fld"><label>Document</label><select id="docType"></select></div>
|
||||||
|
<div class="fld"><label>Specific instructions (optional)</label><textarea id="docInst" placeholder="e.g. I want primary residential time with the children, propose alternate weekends for the other parent."></textarea></div>
|
||||||
|
<button class="btn primary" style="width:100%" id="docBtn" onclick="genDoc()">Generate document</button>
|
||||||
|
<div class="doc-result" id="docResult" style="display:none"></div>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
|
<!-- Mission modal -->
|
||||||
|
<div class="modal-bg" id="missionModal"><div class="modal">
|
||||||
|
<button class="close" onclick="closeModal('missionModal')">×</button>
|
||||||
|
<h2>Send on your behalf</h2><div class="sub">Your attorney will format this professionally. A no-contact order blocks protected recipients.</div>
|
||||||
|
<div class="fld"><label>Type</label><select id="msType"><option value="sms">Text (SMS)</option><option value="email">Email</option><option value="call">Phone call</option></select></div>
|
||||||
|
<div class="fld"><label>Recipient (phone E.164 or email)</label><input id="msRecipient" placeholder="+14255550123 or name@example.com"></div>
|
||||||
|
<div class="fld" id="msSubjectWrap" style="display:none"><label>Subject</label><input id="msSubject"></div>
|
||||||
|
<div class="fld"><label>Message</label><textarea id="msBody" placeholder="What do you want to say?"></textarea></div>
|
||||||
|
<button class="btn primary" style="width:100%" onclick="doMission()">Send</button>
|
||||||
|
<div id="msStatus" style="margin-top:12px;font-size:13px"></div>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const AGENTS = {{ agents | tojson }};
|
||||||
|
const token = localStorage.getItem('astraea_token') || '';
|
||||||
|
let active = AGENTS[0].id;
|
||||||
|
let avatars = [];
|
||||||
|
let myProfile = {display_name:'', avatar:0, about_me:''};
|
||||||
|
let mySettings = {};
|
||||||
|
const history = {};
|
||||||
|
AGENTS.forEach(a => history[a.id] = []);
|
||||||
|
|
||||||
|
// starfield
|
||||||
|
const cvs = document.getElementById('stars'), ctx = cvs.getContext('2d');
|
||||||
|
let stars = [];
|
||||||
|
(function size(){ cvs.width=innerWidth; cvs.height=innerHeight; stars=Array.from({length:120},()=>({x:Math.random()*cvs.width,y:Math.random()*cvs.height,r:Math.random()*1.3+.3,t:Math.random()*6.28,s:Math.random()*.02+.005})); })();
|
||||||
|
addEventListener('resize',()=>{cvs.width=innerWidth;cvs.height=innerHeight;});
|
||||||
|
(function draw(){ ctx.clearRect(0,0,cvs.width,cvs.height); for(const s of stars){ s.t+=s.s; const a=.3+Math.abs(Math.sin(s.t))*.7; ctx.beginPath(); ctx.arc(s.x,s.y,s.r,0,6.28); ctx.fillStyle=`rgba(190,210,255,${a})`; ctx.fill(); } requestAnimationFrame(draw); })();
|
||||||
|
|
||||||
|
function authHeaders(){ return token ? {'Authorization':'Bearer '+token} : {}; }
|
||||||
|
function accent(id){ const a=AGENTS.find(x=>x.id===id); return a?a.accent:'#7dd3fc'; }
|
||||||
|
function closeModal(id){ document.getElementById(id).classList.remove('open'); }
|
||||||
|
|
||||||
|
function renderGrid(){
|
||||||
|
const g=document.getElementById('grid'); g.innerHTML='';
|
||||||
|
AGENTS.forEach(a=>{ const c=document.createElement('div'); c.className='card'+(a.id===active?' active':''); c.style.setProperty('--c',a.accent); c.innerHTML=`<div class="emoji">${a.emoji}</div><h3>${a.name}</h3><div class="tag">${a.tagline}</div><p>${a.description}</p>`; c.onclick=()=>selectAgent(a.id,true); g.appendChild(c); });
|
||||||
|
}
|
||||||
|
function renderChips(){
|
||||||
|
const c=document.getElementById('chips'); c.innerHTML='';
|
||||||
|
AGENTS.forEach(a=>{ const b=document.createElement('button'); b.className='chip'+(a.id===active?' on':''); b.style.setProperty('--c',a.accent); b.textContent=a.emoji+' '+a.name.split(' & ')[0].split(' ')[0]; b.onclick=()=>selectAgent(a.id,false); c.appendChild(b); });
|
||||||
|
}
|
||||||
|
async function selectAgent(id,scroll){
|
||||||
|
active=id; const a=AGENTS.find(x=>x.id===id);
|
||||||
|
document.getElementById('headEmoji').textContent=a.emoji;
|
||||||
|
document.getElementById('headName').textContent=a.name;
|
||||||
|
document.getElementById('headScope').textContent=a.tagline;
|
||||||
|
const cp=document.getElementById('chatPanel'); cp.style.setProperty('--c',a.accent); cp.style.borderColor=a.accent+'55';
|
||||||
|
renderGrid(); renderChips();
|
||||||
|
const m=document.getElementById('messages'); m.innerHTML='';
|
||||||
|
if(token && !(history[id]||[]).length){
|
||||||
|
try{ const r=await fetch('/api/conversation/'+id,{headers:authHeaders()}); const d=await r.json(); history[id]=d.history||[]; }catch(e){}
|
||||||
|
}
|
||||||
|
(history[id]||[]).forEach(t=>addBubble(t.role,t.content,t.citations));
|
||||||
|
if(scroll) cp.scrollIntoView({behavior:'smooth',block:'start'});
|
||||||
|
}
|
||||||
|
async function clearConv(){
|
||||||
|
if(token){ try{ await fetch('/api/conversation/'+active,{method:'DELETE',headers:authHeaders()}); }catch(e){} }
|
||||||
|
history[active]=[]; document.getElementById('messages').innerHTML='';
|
||||||
|
}
|
||||||
|
let rec=null;
|
||||||
|
function toggleMic(){
|
||||||
|
const SR=window.SpeechRecognition||window.webkitSpeechRecognition;
|
||||||
|
const mb=document.getElementById('micBtn');
|
||||||
|
if(!SR){ mb.title='Voice input needs Chrome/Edge'; return; }
|
||||||
|
if(rec){ rec.stop(); rec=null; mb.classList.remove('on'); mb.style.borderColor=''; return; }
|
||||||
|
rec=new SR(); rec.lang='en-US'; rec.interimResults=false;
|
||||||
|
rec.onresult=e=>{ const t=e.results[0][0].transcript; const i=document.getElementById('input'); i.value=(i.value?i.value+' ':'')+t; };
|
||||||
|
rec.onend=()=>{ rec=null; mb.classList.remove('on'); mb.style.borderColor=''; };
|
||||||
|
rec.onerror=()=>{ rec=null; mb.classList.remove('on'); mb.style.borderColor=''; };
|
||||||
|
rec.start(); mb.classList.add('on'); mb.style.borderColor='#f87171';
|
||||||
|
}
|
||||||
|
function addBubble(role,text,citations){
|
||||||
|
const m=document.createElement('div'); m.className='msg '+role;
|
||||||
|
const l=document.createElement('div'); l.className='who-label'; l.textContent=role==='user'?'You':(AGENTS.find(x=>x.id===active)?.name||'Attorney');
|
||||||
|
const b=document.createElement('div'); b.className='bubble'; b.textContent=text;
|
||||||
|
m.appendChild(l); m.appendChild(b);
|
||||||
|
if(role==='assistant'){
|
||||||
|
const sp=document.createElement('button'); sp.className='speak'; sp.textContent='🔊 Listen'; sp.onclick=()=>speak(text); m.appendChild(sp);
|
||||||
|
if(citations&&citations.length){ const cw=document.createElement('div'); cw.className='citations'; const t=document.createElement('button'); t.className='cite-toggle'; t.textContent='🔗 '+citations.length+' source'+(citations.length>1?'s':''); const cl=document.createElement('div'); cl.className='cite-list'; citations.forEach(c=>{ const it=document.createElement('div'); it.className='cite'; it.innerHTML=`<b>${c.title}</b>${c.snippet}<div style="color:var(--gold);font-size:10.5px">${c.source}</div>`; cl.appendChild(it); }); t.onclick=()=>cl.classList.toggle('open'); cw.appendChild(t); cw.appendChild(cl); m.appendChild(cw); }
|
||||||
|
}
|
||||||
|
document.getElementById('messages').appendChild(m);
|
||||||
|
document.getElementById('messages').scrollTop=document.getElementById('messages').scrollHeight;
|
||||||
|
}
|
||||||
|
function typing(on){ document.querySelectorAll('.typing').forEach(e=>e.remove()); if(!on)return; const m=document.createElement('div'); m.className='msg agent'; const t=document.createElement('div'); t.className='typing'; t.innerHTML='<i></i><i></i><i></i>'; m.appendChild(t); document.getElementById('messages').appendChild(m); }
|
||||||
|
|
||||||
|
async function speak(text){
|
||||||
|
try{ const r=await fetch('/api/speak',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({text:text.replace(/\n+/g,'. ').substring(0,2500),voice:mySettings.tts_voice||'aria'})}); const b=await r.blob(); new Audio(URL.createObjectURL(b)).play(); }catch(e){}
|
||||||
|
}
|
||||||
|
async function send(){
|
||||||
|
const t=document.getElementById('input').value.trim(); if(!t)return;
|
||||||
|
document.getElementById('input').value='';
|
||||||
|
addBubble('user',t); history[active].push({role:'user',content:t});
|
||||||
|
typing(true); document.getElementById('sendBtn').disabled=true;
|
||||||
|
try{
|
||||||
|
const r=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json',...authHeaders()},body:JSON.stringify({agent_id:active,message:t,history:history[active].slice(0,-1)})});
|
||||||
|
const d=await r.json(); typing(false);
|
||||||
|
if(d.answer){ addBubble('assistant',d.answer,d.citations); history[active].push({role:'assistant',content:d.answer,citations:d.citations}); }
|
||||||
|
else addBubble('assistant',d.error||'Something went wrong.',null);
|
||||||
|
}catch(e){ typing(false); addBubble('assistant','Could not reach the engine.',null); }
|
||||||
|
finally{ document.getElementById('sendBtn').disabled=false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadAvatars(){ const r=await fetch('/api/avatars'); avatars=await r.json(); const g=document.getElementById('avatarGrid'); g.innerHTML=''; avatars.forEach(a=>{ const d=document.createElement('div'); d.className='avatar-opt'; d.style.background=`linear-gradient(135deg,${a.c1},${a.c2})`; d.textContent=a.glyph; d.title=a.label; d.onclick=()=>{ myProfile.avatar=a.id; renderAvatarSel(); }; g.appendChild(d); }); }
|
||||||
|
function renderAvatarSel(){ document.querySelectorAll('.avatar-opt').forEach((d,i)=>d.classList.toggle('sel',i===myProfile.avatar)); }
|
||||||
|
function renderMyAvatar(){ const el=document.getElementById('myAvatar'); if(myProfile.profile_pic){ el.textContent=''; el.style.background=`url('${myProfile.profile_pic}') center/cover`; } else { const a=avatars[myProfile.avatar]||avatars[0]; if(a){ el.style.background=`linear-gradient(135deg,${a.c1},${a.c2})`; el.textContent=a.glyph; } } }
|
||||||
|
async function uploadPic(){
|
||||||
|
const f=document.getElementById('pfPic').files[0];
|
||||||
|
if(!f) return;
|
||||||
|
const fd=new FormData(); fd.append('file',f);
|
||||||
|
const pv=document.getElementById('pfPicPreview'); pv.textContent='Uploading…';
|
||||||
|
try{ const r=await fetch('/api/profile/pic',{method:'POST',headers:authHeaders(),body:fd}); const d=await r.json();
|
||||||
|
if(d.error){ pv.textContent=d.error; pv.style.color='#f87171'; return; }
|
||||||
|
myProfile=d; renderMyAvatar(); pv.textContent='✓ uploaded'; pv.style.color='#4ade80';
|
||||||
|
}catch(e){ pv.textContent='Upload failed'; pv.style.color='#f87171'; }
|
||||||
|
}
|
||||||
|
const INTAKE=[
|
||||||
|
{label:"My full legal name", q:"What's your full legal name (first, middle, last)?", p:"Jane Marie Doe"},
|
||||||
|
{label:"My mailing address", q:"What's your current mailing address (street, city, state, ZIP)?", p:"123 Main St, Everett, WA 98201"},
|
||||||
|
{label:"Spouse's full legal name", q:"What's your spouse's full legal name?", p:"John Robert Doe"},
|
||||||
|
{label:"Spouse's mailing address", q:"What's your spouse's mailing address?", p:"456 Oak Ave, Everett, WA 98201"},
|
||||||
|
{label:"Marriage date & place", q:"When and where did you get married (exact date + city/county/state)?", p:"June 15, 2011, Seattle, WA"},
|
||||||
|
{label:"Separation date", q:"When did you separate (exact date)?", p:"June 1, 2025"},
|
||||||
|
{label:"Case number", q:"Has a case been filed? What's the case number (if you know it)?", p:"Not yet / 24-3-01234-31"},
|
||||||
|
{label:"Children (names, ages, DOBs)", q:"List each child's full name, age, and date of birth.", p:"Emma Grace Doe, 12, born 01/15/2013; Liam James Doe, 9, born 03/22/2016"},
|
||||||
|
{label:"Current custody", q:"Where do the children live now, and what's the current schedule?", p:"They live with me; dad has them every other weekend"},
|
||||||
|
{label:"Home / real estate", q:"Do you own a home or other real estate? Address, when bought, whose name is on the title/mortgage?", p:"House at 123 Main St, bought 2012, both names on title"},
|
||||||
|
{label:"Assets", q:"What are your main assets? (bank accounts, retirement, vehicles, business, investments)", p:"Joint savings ~$20k, my 401k ~$85k, two cars"},
|
||||||
|
{label:"Debts", q:"What are your main debts? (mortgage, credit cards, loans)", p:"Mortgage $280k remaining, $9k credit card, $12k car loan"},
|
||||||
|
{label:"My income & employer", q:"What's your job, employer, and annual gross income?", p:"Registered nurse, Providence, $72k/yr"},
|
||||||
|
{label:"Spouse's income & employer", q:"What's your spouse's job, employer, and annual gross income?", p:"Software engineer, Amazon, $95k/yr"},
|
||||||
|
{label:"Children's health insurance", q:"Who provides health insurance for the children, and through which employer?", p:"My employer (Providence) covers the kids"},
|
||||||
|
{label:"Case status", q:"Has either of you filed for divorce yet, and what does each side want (custody, support, property)?", p:"Spouse filed; wants full custody and to keep the house"},
|
||||||
|
{label:"Safety / DV", q:"Is there any domestic violence, abuse, or safety concern? Any protection orders in place?", p:"No / Yes — details"},
|
||||||
|
{label:"Other cases", q:"Are there any other court cases involving you, your spouse, or the children (criminal, CPS, other family law)?", p:"None"},
|
||||||
|
{label:"Goals & worries", q:"What outcome matters most to you? What are you most worried about?", p:"Keeping the kids with me and keeping the house"},
|
||||||
|
];
|
||||||
|
let intakeIdx=0; const intakeAnswers={};
|
||||||
|
function openIntake(){ intakeIdx=0; for(const k in intakeAnswers) delete intakeAnswers[k]; closeModal('profileModal'); renderIntake(); document.getElementById('intakeModal').classList.add('open'); }
|
||||||
|
function renderIntake(){ const q=INTAKE[intakeIdx]; document.getElementById('intakeQ').textContent=(intakeIdx+1)+'. '+q.q; const a=document.getElementById('intakeA'); a.value=intakeAnswers[intakeIdx]||''; a.placeholder=q.p; document.getElementById('intakeBar').style.width=Math.round((intakeIdx/(INTAKE.length-1))*100)+'%'; document.getElementById('intakeBack').style.visibility=intakeIdx===0?'hidden':'visible'; document.getElementById('intakeNext').textContent=intakeIdx===INTAKE.length-1?'Finish ✓':'Next →'; }
|
||||||
|
function intakeNext(){ intakeAnswers[intakeIdx]=document.getElementById('intakeA').value.trim(); if(intakeIdx<INTAKE.length-1){ intakeIdx++; renderIntake(); } else { finishIntake(); } }
|
||||||
|
function intakePrev(){ intakeAnswers[intakeIdx]=document.getElementById('intakeA').value.trim(); if(intakeIdx>0){ intakeIdx--; renderIntake(); } }
|
||||||
|
function finishIntake(){
|
||||||
|
const parts=[]; INTAKE.forEach((q,i)=>{ const v=(intakeAnswers[i]||'').trim(); if(v) parts.push(q.label+': '+v); });
|
||||||
|
document.getElementById('pfAbout').value=parts.join('\n');
|
||||||
|
closeModal('intakeModal'); document.getElementById('profileModal').classList.add('open');
|
||||||
|
}
|
||||||
|
let myFiles=[], myCats=['Other'];
|
||||||
|
function escapeHtml(s){ return (s||'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c])); }
|
||||||
|
function fmtSize(n){ return n>1048576?(n/1048576).toFixed(1)+' MB':n>1024?(n/1024).toFixed(0)+' KB':(n||0)+' B'; }
|
||||||
|
async function loadFiles(){
|
||||||
|
if(!token) return;
|
||||||
|
try{ const r=await fetch('/api/files',{headers:authHeaders()}); const d=await r.json(); myFiles=d.files||[]; myCats=d.categories||['Other'];
|
||||||
|
const sel=document.getElementById('fileCat'); if(sel){ sel.innerHTML=''; myCats.forEach(c=>{ const o=document.createElement('option'); o.value=c; o.textContent=c; sel.appendChild(o); }); }
|
||||||
|
renderFiles();
|
||||||
|
}catch(e){}
|
||||||
|
}
|
||||||
|
function renderFiles(){
|
||||||
|
const el=document.getElementById('fileList'); if(!el) return;
|
||||||
|
if(!myFiles.length){ el.innerHTML='<div class="sub">No documents yet — upload your petition, orders, agreements, and evidence.</div>'; return; }
|
||||||
|
const groups={}; myFiles.forEach(f=>{ (groups[f.category]=groups[f.category]||[]).push(f); });
|
||||||
|
el.innerHTML='';
|
||||||
|
Object.keys(groups).forEach(cat=>{
|
||||||
|
const h=document.createElement('div'); h.style.cssText='color:var(--gold);font-size:12px;font-weight:700;margin-top:10px'; h.textContent=cat+' ('+groups[cat].length+')'; el.appendChild(h);
|
||||||
|
groups[cat].forEach(f=>{
|
||||||
|
const it=document.createElement('div'); it.className='file-item';
|
||||||
|
const nm=document.createElement('span'); nm.className='nm'; nm.textContent=f.orig_name; it.appendChild(nm);
|
||||||
|
const sz=document.createElement('span'); sz.className='cat'; sz.textContent=fmtSize(f.size); it.appendChild(sz);
|
||||||
|
const v=document.createElement('button'); v.textContent='View'; v.onclick=()=>viewFile(f.id); it.appendChild(v);
|
||||||
|
const d=document.createElement('button'); d.textContent='✕'; d.onclick=()=>delFile(f.id); it.appendChild(d);
|
||||||
|
el.appendChild(it);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function uploadFiles(){
|
||||||
|
const fs=document.getElementById('fileInput').files; if(!fs.length) return;
|
||||||
|
const cat=document.getElementById('fileCat').value;
|
||||||
|
for(const f of fs){ const fd=new FormData(); fd.append('file',f); fd.append('category',cat); try{ await fetch('/api/files',{method:'POST',headers:authHeaders(),body:fd}); }catch(e){} }
|
||||||
|
document.getElementById('fileInput').value='';
|
||||||
|
await loadFiles();
|
||||||
|
}
|
||||||
|
async function delFile(id){ try{ await fetch('/api/files/'+id,{method:'DELETE',headers:authHeaders()}); }catch(e){} await loadFiles(); }
|
||||||
|
async function viewFile(id){
|
||||||
|
const rec=myFiles.find(f=>f.id===id); if(!rec) return;
|
||||||
|
const ext=(rec.orig_name.split('.').pop()||'').toLowerCase();
|
||||||
|
document.getElementById('viewerTitle').textContent=rec.orig_name;
|
||||||
|
const body=document.getElementById('viewerBody'); body.innerHTML='<div class="sub">Loading…</div>';
|
||||||
|
document.getElementById('viewerModal').classList.add('open');
|
||||||
|
if(['png','jpg','jpeg','gif','webp','svg'].includes(ext)){
|
||||||
|
const r=await fetch('/api/files/'+id+'/raw',{headers:authHeaders()}); const b=await r.blob(); const u=URL.createObjectURL(b);
|
||||||
|
body.innerHTML='<img src="'+u+'" style="max-width:100%;border-radius:8px">';
|
||||||
|
} else if(ext==='pdf'){
|
||||||
|
const r=await fetch('/api/files/'+id+'/raw',{headers:authHeaders()}); const b=await r.blob(); const u=URL.createObjectURL(b);
|
||||||
|
body.innerHTML='<iframe src="'+u+'" style="width:100%;height:70vh;border:0;border-radius:8px"></iframe>';
|
||||||
|
} else {
|
||||||
|
let txt=''; try{ const r=await fetch('/api/files/'+id+'/text',{headers:authHeaders()}); const d=await r.json(); txt=d.text||''; }catch(e){}
|
||||||
|
body.innerHTML='<pre style="white-space:pre-wrap;font-family:inherit;font-size:13px;line-height:1.55">'+escapeHtml(txt||'(no text preview — this file type downloads instead)')+'</pre>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function openProfile(){ document.getElementById('pfName').value=myProfile.display_name||''; document.getElementById('pfAbout').value=myProfile.about_me||''; renderAvatarSel(); document.getElementById('profileModal').classList.add('open'); loadFiles(); }
|
||||||
|
let allForms=[];
|
||||||
|
async function openFill(){
|
||||||
|
try{ const r=await fetch('/api/forms'); allForms=await r.json();
|
||||||
|
const sel=document.getElementById('fillFormSelect'); sel.innerHTML='';
|
||||||
|
allForms.forEach(f=>{ const o=document.createElement('option'); o.value=f.id; o.textContent=f.name; sel.appendChild(o); });
|
||||||
|
document.getElementById('fillFields').innerHTML='<div class="sub">Click "Auto-fill from my info" to pre-fill the fields.</div>';
|
||||||
|
document.getElementById('fillModal').classList.add('open');
|
||||||
|
}catch(e){}
|
||||||
|
}
|
||||||
|
async function autofillForm(){
|
||||||
|
const fid=document.getElementById('fillFormSelect').value;
|
||||||
|
const el=document.getElementById('fillFields'); el.innerHTML='<div class="sub">Filling…</div>';
|
||||||
|
const r=await fetch('/api/forms/autofill',{method:'POST',headers:{'Content-Type':'application/json',...authHeaders()},body:JSON.stringify({form_id:fid})});
|
||||||
|
const d=await r.json();
|
||||||
|
const f=allForms.find(x=>x.id===fid);
|
||||||
|
el.innerHTML='';
|
||||||
|
(f.fields||[]).forEach(fd=>{
|
||||||
|
const row=document.createElement('div'); row.style.cssText='margin-bottom:8px';
|
||||||
|
const lb=document.createElement('label'); lb.style.cssText='font-size:12px;color:var(--muted)'; lb.textContent=fd.label; row.appendChild(lb);
|
||||||
|
const inp=document.createElement('input'); inp.dataset.key=fd.key; inp.value=(d.values||{})[fd.key]||''; row.appendChild(inp);
|
||||||
|
el.appendChild(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function generateForm(){
|
||||||
|
const fid=document.getElementById('fillFormSelect').value;
|
||||||
|
const values={}; document.querySelectorAll('#fillFields input').forEach(i=>values[i.dataset.key]=i.value);
|
||||||
|
const r=await fetch('/api/forms/fill',{method:'POST',headers:{'Content-Type':'application/json',...authHeaders()},body:JSON.stringify({form_id:fid,values})});
|
||||||
|
if(!r.ok){ alert('Could not generate form'); return; }
|
||||||
|
const blob=await r.blob(); const url=URL.createObjectURL(blob);
|
||||||
|
const a=document.createElement('a'); a.href=url; a.download=fid+'_filled.pdf'; document.body.appendChild(a); a.click(); a.remove();
|
||||||
|
}
|
||||||
|
async function saveProfile(){
|
||||||
|
const body={display_name:document.getElementById('pfName').value, about_me:document.getElementById('pfAbout').value, avatar:myProfile.avatar};
|
||||||
|
const r=await fetch('/api/profile',{method:'POST',headers:{'Content-Type':'application/json',...authHeaders()},body:JSON.stringify(body)});
|
||||||
|
myProfile=await r.json(); renderMyAvatar(); closeModal('profileModal');
|
||||||
|
}
|
||||||
|
function openSettings(){
|
||||||
|
const s=mySettings; document.getElementById('twSid').value=s.twilio_sid||''; document.getElementById('twToken').value=''; document.getElementById('twFrom').value=s.twilio_from||''; document.getElementById('smHost').value=s.smtp_host||''; document.getElementById('smPort').value=s.smtp_port||587; document.getElementById('smUser').value=s.smtp_user||''; document.getElementById('emFrom').value=s.email_from||''; document.getElementById('ttsVoice').value=s.tts_voice||'aria'; document.getElementById('ncOrder').checked=!!s.no_contact_order; document.getElementById('ncName').value=s.partner_name||''; document.getElementById('dvFactor').checked=!!s.domestic_violence; document.getElementById('poOrder').checked=!!s.protection_order; document.getElementById('safetyNotes').value=s.safety_notes||'';
|
||||||
|
document.getElementById('settingsModal').classList.add('open');
|
||||||
|
}
|
||||||
|
async function saveSettings(){
|
||||||
|
const body={ twilio_sid:document.getElementById('twSid').value, twilio_from:document.getElementById('twFrom').value, smtp_host:document.getElementById('smHost').value, smtp_port:parseInt(document.getElementById('smPort').value||587), smtp_user:document.getElementById('smUser').value, email_from:document.getElementById('emFrom').value, tts_voice:document.getElementById('ttsVoice').value, no_contact_order:document.getElementById('ncOrder').checked?1:0, partner_name:document.getElementById('ncName').value, domestic_violence:document.getElementById('dvFactor').checked?1:0, protection_order:document.getElementById('poOrder').checked?1:0, safety_notes:document.getElementById('safetyNotes').value };
|
||||||
|
const tw=document.getElementById('twToken').value; if(tw) body.twilio_auth_token=tw;
|
||||||
|
const sp=document.getElementById('smPass').value; if(sp) body.smtp_pass=sp;
|
||||||
|
const r=await fetch('/api/settings',{method:'POST',headers:{'Content-Type':'application/json',...authHeaders()},body:JSON.stringify(body)});
|
||||||
|
mySettings=await r.json(); closeModal('settingsModal');
|
||||||
|
}
|
||||||
|
async function openDocs(){ const r=await fetch('/api/documents/types'); const types=await r.json(); const s=document.getElementById('docType'); s.innerHTML=''; types.forEach(t=>{ const o=document.createElement('option'); o.value=t.id; o.textContent=t.name+' ('+t.form+')'; s.appendChild(o); }); document.getElementById('docResult').style.display='none'; document.getElementById('docsModal').classList.add('open'); }
|
||||||
|
async function genDoc(){
|
||||||
|
document.getElementById('docBtn').disabled=true;
|
||||||
|
const body={doc_type:document.getElementById('docType').value, instructions:document.getElementById('docInst').value};
|
||||||
|
const r=await fetch('/api/documents',{method:'POST',headers:{'Content-Type':'application/json',...authHeaders()},body:JSON.stringify(body)});
|
||||||
|
const d=await r.json(); document.getElementById('docBtn').disabled=false;
|
||||||
|
const el=document.getElementById('docResult'); el.style.display='block'; el.textContent=d.document||d.error;
|
||||||
|
}
|
||||||
|
function openMission(){ document.getElementById('missionModal').classList.add('open'); }
|
||||||
|
document.getElementById('msType').onchange=function(){ document.getElementById('msSubjectWrap').style.display=this.value==='email'?'block':'none'; };
|
||||||
|
async function doMission(){
|
||||||
|
const type=document.getElementById('msType').value, recipient=document.getElementById('msRecipient').value;
|
||||||
|
const body={type,recipient,message:document.getElementById('msBody').value,subject:document.getElementById('msSubject').value,body:document.getElementById('msBody').value};
|
||||||
|
const r=await fetch('/api/mission',{method:'POST',headers:{'Content-Type':'application/json',...authHeaders()},body:JSON.stringify(body)});
|
||||||
|
const d=await r.json(); document.getElementById('msStatus').textContent=d.ok?'✓ Sent.':('✗ '+(d.error||'Failed')); document.getElementById('msStatus').style.color=d.ok?'#4ade80':'#fca5a5';
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('sendBtn').onclick=send;
|
||||||
|
document.getElementById('input').addEventListener('keydown',e=>{ if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();send();} });
|
||||||
|
|
||||||
|
async function boot(){
|
||||||
|
if(!token){ window.location.href='/'; return; }
|
||||||
|
try{ const r=await fetch('/api/me',{headers:authHeaders()}); if(!r.ok){ window.location.href='/'; return; } const d=await r.json(); myProfile=d.profile; mySettings=d.settings; }catch(e){}
|
||||||
|
await loadAvatars(); renderMyAvatar(); renderGrid(); renderChips(); selectAgent(active,false);
|
||||||
|
// populate tts voices
|
||||||
|
try{ const vr=await fetch('/api/voices'); const vd=await vr.json(); const s=document.getElementById('ttsVoice'); s.innerHTML=''; vd.voices.forEach(v=>{ const o=document.createElement('option'); o.value=v.id; o.textContent=v.label; s.appendChild(o); }); s.value=mySettings.tts_voice||'aria'; }catch(e){}
|
||||||
|
}
|
||||||
|
boot();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
124
templates/landing.html
Normal file
124
templates/landing.html
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Astraea — Your Washington Divorce Attorney</title>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
background: #05070f; color: #e8edfb; overflow-x: hidden; -webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
.hero {
|
||||||
|
position: relative; min-height: 100vh; display: flex; align-items: center;
|
||||||
|
background: radial-gradient(1200px 700px at 50% -10%, #16224a 0%, #070b1a 60%, #05070f 100%);
|
||||||
|
}
|
||||||
|
.hero-bg { position: absolute; inset: 0; overflow: hidden; }
|
||||||
|
.hero-bg img {
|
||||||
|
width: 100%; height: 100%; object-fit: cover; opacity: .55;
|
||||||
|
-webkit-mask-image: radial-gradient(ellipse at 50% 40%, #000 30%, transparent 75%);
|
||||||
|
mask-image: radial-gradient(ellipse at 50% 40%, #000 30%, transparent 75%);
|
||||||
|
}
|
||||||
|
.hero-overlay { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(5,7,15,.35), rgba(5,7,15,.9) 80%, #05070f); }
|
||||||
|
.wrap { position: relative; z-index: 2; width: 100%; max-width: 1180px; margin: 0 auto; padding: 40px 26px; }
|
||||||
|
header { display: flex; align-items: center; justify-content: space-between; }
|
||||||
|
.brand { display: flex; align-items: center; gap: 13px; }
|
||||||
|
.logo { width: 46px; height: 46px; border-radius: 13px; background: linear-gradient(135deg,#6ea8ff,#a78bfa); display: grid; place-items: center; font-size: 24px; box-shadow: 0 0 26px rgba(124,155,255,.55); }
|
||||||
|
.brand h1 { font-size: 24px; font-weight: 800; letter-spacing: .4px; }
|
||||||
|
.brand h1 span { color: #f5d06f; }
|
||||||
|
.pill { font-size: 12px; color: #aeb8d9; border: 1px solid #233055; padding: 6px 13px; border-radius: 999px; display: flex; align-items: center; gap: 7px; }
|
||||||
|
.dot { width: 8px; height: 8px; border-radius: 50%; background: #4ade80; box-shadow: 0 0 10px #4ade80; }
|
||||||
|
|
||||||
|
.hero-content { display: grid; grid-template-columns: 1.25fr .75fr; gap: 50px; align-items: center; padding: 60px 0 40px; }
|
||||||
|
.left h2 { font-size: clamp(34px, 5vw, 60px); line-height: 1.05; font-weight: 800; letter-spacing: -1px; }
|
||||||
|
.left h2 .grad { background: linear-gradient(90deg,#7dd3fc,#a78bfa,#f0abfc); -webkit-background-clip: text; background-clip: text; color: transparent; }
|
||||||
|
.left .sub { color: #aeb8d9; font-size: 18px; line-height: 1.6; margin-top: 20px; max-width: 560px; }
|
||||||
|
.left .sub b { color: #e8edfb; }
|
||||||
|
.agents-row { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 26px; }
|
||||||
|
.agent-chip { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; padding: 8px 13px; border-radius: 999px; border: 1px solid #233055; background: rgba(16,24,50,.6); }
|
||||||
|
.cta-row { margin-top: 34px; display: flex; gap: 14px; flex-wrap: wrap; }
|
||||||
|
.btn { border: none; border-radius: 13px; padding: 15px 28px; font-size: 16px; font-weight: 700; cursor: pointer; transition: transform .12s ease, box-shadow .2s ease; }
|
||||||
|
.btn-primary { background: linear-gradient(135deg,#3b82f6,#6366f1); color: #fff; box-shadow: 0 10px 30px rgba(59,130,246,.4); }
|
||||||
|
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 16px 40px rgba(59,130,246,.55); }
|
||||||
|
.btn-ghost { background: rgba(16,24,50,.6); color: #e8edfb; border: 1px solid #233055; }
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: linear-gradient(180deg, rgba(20,29,61,.9), rgba(16,24,50,.85));
|
||||||
|
border: 1px solid #233055; border-radius: 20px; padding: 28px;
|
||||||
|
box-shadow: 0 30px 70px rgba(0,0,0,.5); backdrop-filter: blur(8px);
|
||||||
|
}
|
||||||
|
.card h3 { font-size: 20px; font-weight: 700; margin-bottom: 4px; }
|
||||||
|
.card .hint { color: #8b98c4; font-size: 13px; margin-bottom: 20px; }
|
||||||
|
.field { margin-bottom: 15px; }
|
||||||
|
.field label { display: block; font-size: 12.5px; color: #aeb8d9; margin-bottom: 6px; }
|
||||||
|
.field input { width: 100%; background: #0c142e; border: 1px solid #233055; color: #e8edfb; border-radius: 11px; padding: 12px 14px; font-size: 15px; outline: none; }
|
||||||
|
.field input:focus { border-color: #6366f1; }
|
||||||
|
.err { color: #fca5a5; font-size: 13px; margin-bottom: 12px; min-height: 18px; }
|
||||||
|
.tabs { display: flex; gap: 4px; margin-bottom: 20px; background: #0c142e; border-radius: 11px; padding: 4px; }
|
||||||
|
.tabs button { flex: 1; border: none; background: transparent; color: #8b98c4; padding: 10px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 600; }
|
||||||
|
.tabs button.on { background: #1c2748; color: #e8edfb; }
|
||||||
|
footer { text-align: center; padding: 20px 0 40px; color: #5a658a; font-size: 12.5px; }
|
||||||
|
footer a { color: #8b98c4; }
|
||||||
|
@media (max-width: 860px) { .hero-content { grid-template-columns: 1fr; } }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="hero">
|
||||||
|
<div class="hero-bg"><img src="/static/hero.png" alt="Astraea"></div>
|
||||||
|
<div class="hero-overlay"></div>
|
||||||
|
<div class="wrap">
|
||||||
|
<header>
|
||||||
|
<div class="brand"><div class="logo">⚖️</div><div><h1>ASTRA<span>EA</span></h1></div></div>
|
||||||
|
<div class="pill"><span class="dot"></span> Washington family law, on your side</div>
|
||||||
|
</header>
|
||||||
|
<div class="hero-content">
|
||||||
|
<div class="left">
|
||||||
|
<h2>Your personal<br><span class="grad">divorce attorney</span>,<br>reinvented.</h2>
|
||||||
|
<p class="sub">Astraea is a constellation of <b>eight Washington State legal specialists</b> — grounded in the real Revised Code of Washington and <b>verified case law</b> — that give you sound legal advice on the facts, prep your court paperwork on demand, and even handle professional correspondence for you.</p>
|
||||||
|
<div class="agents-row">
|
||||||
|
{% for a in agents %}<div class="agent-chip">{{ a.emoji }} {{ a.name.split(' & ')[0].split(' ')[0] }}</div>{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="cta-row">
|
||||||
|
<button class="btn btn-primary" onclick="document.getElementById('auth').scrollIntoView({behavior:'smooth'})">Begin your case →</button>
|
||||||
|
<button class="btn btn-ghost" onclick="window.location.href='/app'">I have an account</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card" id="auth">
|
||||||
|
<h3>Welcome back</h3>
|
||||||
|
<div class="hint">No email, no verification. Just you and your counsel.</div>
|
||||||
|
<div class="tabs">
|
||||||
|
<button id="tabLogin" class="on" onclick="setTab('login')">Sign in</button>
|
||||||
|
<button id="tabRegister" onclick="setTab('register')">Create account</button>
|
||||||
|
</div>
|
||||||
|
<div class="err" id="err"></div>
|
||||||
|
<div class="field"><label>Username</label><input id="u" autocomplete="username"></div>
|
||||||
|
<div class="field"><label>Password</label><input id="p" type="password" autocomplete="current-password"></div>
|
||||||
|
<button class="btn btn-primary" style="width:100%" id="go">Sign in</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
Astraea provides practical legal guidance grounded in Washington statute and case law — not a substitute for a licensed attorney.
|
||||||
|
<br><a href="https://buymeacoffee.com/r26xrthzttg" target="_blank" rel="noopener">☕ Support Astraea</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
let mode = 'login';
|
||||||
|
function setTab(m){ mode = m; document.getElementById('tabLogin').classList.toggle('on', m==='login'); document.getElementById('tabRegister').classList.toggle('on', m==='register'); document.getElementById('go').textContent = m==='login' ? 'Sign in' : 'Create account'; }
|
||||||
|
async function go(){
|
||||||
|
const u = document.getElementById('u').value.trim(), p = document.getElementById('p').value;
|
||||||
|
const err = document.getElementById('err');
|
||||||
|
if(!u || !p){ err.textContent = 'Enter a username and password.'; return; }
|
||||||
|
const r = await fetch('/api/'+mode, {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({username:u, password:p})});
|
||||||
|
const d = await r.json();
|
||||||
|
if(!r.ok){ err.textContent = d.error || 'Something went wrong'; return; }
|
||||||
|
localStorage.setItem('astraea_token', d.token);
|
||||||
|
localStorage.setItem('astraea_user', d.username);
|
||||||
|
window.location.href = '/app';
|
||||||
|
}
|
||||||
|
document.getElementById('go').onclick = go;
|
||||||
|
document.getElementById('p').addEventListener('keydown', e => { if(e.key==='Enter') go(); });
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
33
tts.py
Normal file
33
tts.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""Astraea TTS — realistic neural voices via edge-tts (Microsoft neural, sounds human).
|
||||||
|
Structured so a local GPU TTS (Kokoro/XTTS on the Windows box) can be swapped in later.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
# Resolve edge-tts relative to the venv that runs this app (robust vs hardcoded paths).
|
||||||
|
EDGE_TTS_BIN = os.path.join(os.path.dirname(sys.executable), "edge-tts")
|
||||||
|
|
||||||
|
VOICES = {
|
||||||
|
"aria": {"name": "en-US-AriaNeural", "label": "Aria — confident (F)"},
|
||||||
|
"jenny": {"name": "en-US-JennyNeural", "label": "Jenny — warm (F)"},
|
||||||
|
"ana": {"name": "en-US-AnaNeural", "label": "Ana — calm (F)"},
|
||||||
|
"guy": {"name": "en-US-GuyNeural", "label": "Guy — professional (M)"},
|
||||||
|
"christopher": {"name": "en-US-ChristopherNeural", "label": "Christopher — deep (M)"},
|
||||||
|
"eric": {"name": "en-US-EricNeural", "label": "Eric — measured (M)"},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize(text, voice="aria", rate="-5%"):
|
||||||
|
v = VOICES.get(voice, VOICES["aria"])["name"]
|
||||||
|
text = text.replace("\n", " ").replace(" ", " ")[:4000]
|
||||||
|
out = tempfile.NamedTemporaryFile(suffix=".mp3", delete=False).name
|
||||||
|
subprocess.run([EDGE_TTS_BIN, "--voice", v, f"--rate={rate}", "--text", text,
|
||||||
|
"--write-media", out], capture_output=True, timeout=60, check=True)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def list_voices():
|
||||||
|
return [{"id": k, "label": v["label"]} for k, v in VOICES.items()]
|
||||||
Reference in New Issue
Block a user