Skip to content

Operations and troubleshooting

Command reference

make target Underlying command Changes mail?
make preview python3 runner.py --no-model No
make classify python3 runner.py No (paid if mail is pending)
make apply python3 runner.py --apply Yes, after a confirmation prompt
make review python3 triage.py apply-plan No
make retry-review python3 runner.py --retry-review No (paid)
make summary cat state/summary.json No
make usage python3 runner.py --usage-report No
make discover python3 mail_setup.py discover No
make rules-preview python3 mail_setup.py preview No
make doctor scripts/setup-macos.sh --check No

The engine also exposes lower-level commands:

python3 triage.py run                       # deterministic dry run, last 7 days
python3 triage.py run --apply               # deterministic changes, no classification call
python3 triage.py pending                   # new pending batch
python3 triage.py decide                    # validate and preview current manual decisions
python3 triage.py sync --apply              # sync completed tasks only
python3 triage.py undo -n 3 --apply         # revert last 3 journal batches
python3 runner.py --window '' --limit 8000  # preview whole-inbox processing (paid if pending)

Reports, journal and undo

Dry-run triage.py writes a per-message state/dry-run.csv; decide merges into it. --report <file> also writes a CSV when applying. Metadata headers are cached locally; unread flags in historical reports may be stale. The runner instead produces a compact deterministic summary and the same dry-run plan.

state/journal.jsonl supports undo for label changes/trash; undo does not delete created tasks. An applied operation plan is renamed state/plan.applied-<timestamp>.json.

Retrying held mail

Still-ambiguous cases are held for review without repeat paid attempts that day. To deliberately retry:

python3 runner.py --retry-review       # paid retry, dry run (make retry-review)
python3 runner.py --retry-review --apply

Troubleshooting

Start with make doctor; it names each missing piece and how to fix it.

Symptom Fix
gws authorization missing or expired Stop unattended work, then gws auth status and gws auth login --services gmail,tasks (or re-run make setup), then preview again
Classifier CLI not logged in copilot login or claude auth login
Claude Code refused because of ANTHROPIC_BASE_URL You are probably inside a Claude Code session; run from a normal terminal
Model access or CLI flag errors Verify the installed CLI flags (see Choosing the classifier CLI) and permitted model names in classifier.json
Stale batch / decisions rejected Regenerate pending and decisions; batches expire after 24 hours
gws is a different tool brew uninstall gws, then brew install googleworkspace-cli
macOS Python is 3.9 make setup installs python@3.13; or run make PYTHON=/path/to/python3 …
Exit code 2 Not an error: mail awaits classification or review

Do not bypass validation, replay legacy files, or use blanket tool permissions.

Local model evaluation (Laya)

ADR 0001: Laya versus Luna records the measured local experiment, all category outcomes, latency, confidence limitations, costs and reproduction commands. Production remains on Luna.

Laya's local typed-decision runtime was tested, not a chat model in LM Studio. It was faster and had no hosted inference fee, but the zero-shot baseline got 9/16 synthetic categories right versus Luna's 16/16. A concise exploratory schema improved Laya to 11/16, still missing Action categories and showing high-confidence errors. Real-mail agreement was measured separately; without human reference labels it is not an accuracy result.

The optional benchmark dependencies are isolated in .venv-benchmark and pinned in tools/benchmark/requirements.lock. They are not required by the scheduled runner. tools/benchmark.py never applies decisions. It persists classification-only evidence under state/; no raw mail or task text is exported. Running the offline tests needs no model downloads. The historical ADR records the original personalized-rules experiment; benchmarking the starter rules is a different configuration.