The claude-obsidian merge carried a CPython 3.12 .pyc from running the
sync script in place. Bytecode is a build artifact; ignore it repo-wide.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Needed to leave a full backlog drain running unattended:
- Run lock (flock): enabling the timer fired an immediate catch-up run
(Persistent=true) while a manual run was about to start; without a
lock the two would race on state.json and usage.jsonl.
- OBSIDIAN_API_URL accepts a comma-separated list, first reachable wins,
so the permanent address and an interim port-forward share one config.
- Fix: inline "# comments" in config.env were parsed into values, so
BUDGET_USD_PER_RUN=0.50 # ... crashed float(). The shipped
config.env.example had exactly that shape on several lines -- anyone
copying it would have hit the crash. A '#' inside a value (URL fragment)
still survives. Tested against both the example and a real config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KF322NzozeaFjpgWvh4F4g
The user asked for usage to be monitored closely, and measuring it turned
up that most of the spend was avoidable.
Metering: every model call is appended to usage.jsonl with BILLED tokens
(modelUsage -- the visible `usage` turn undercounted by half) and the
CLI's list-price cost. `usage` reports 5h/24h/7d. Each call's cost is
estimated first and refused if it would cross the per-run or rolling 24h
budget, so budgets cannot be overshot. Removed --max-budget-usd: it
aborts after spending and discards the result ($0.26 lost in one call).
Cost, measured on one session ($0.057 -> $0.010, same summary content):
- alwaysThinkingEnabled=false: extended thinking was ~85% of output.
- DISABLE_PROMPT_CACHING=1: one-shot prompts were being written to the
1h cache at 2x input price.
- CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1: a hidden side request
re-read the whole prompt, doubling billed input (9,638 vs 4,663).
Summaries are cached until their note is written, so a vault failure
never re-pays; --prefetch summarizes while the vault is unreachable.
Offline tests (fake claude binary): budget refusal pre-flight, cache
reuse with zero calls, map-reduce, env/argv levers, billed-token ledger.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KF322NzozeaFjpgWvh4F4g
One note per Claude Code session (incremental, hourly user timer) and a
claude.ai data-export importer, written through the Obsidian Local REST API
plugin -- whose built-in /mcp/ server is also how Claude reads them back.
Transcripts are condensed (tool output dropped, one line per tool call,
~60x smaller), redacted, then summarized by headless `claude -p` on the
user's own plan. Three things learned the hard way are encoded here:
- --no-session-persistence, or every summary becomes a transcript that
the next run summarizes.
- A LiteLLM gateway passing through the caller's Claude subscription has
no key of its own, so a background job cannot use its claude-* models.
- Summaries share the plan's usage limits. A parallel ~100-session
backfill exhausted a session limit in minutes. So one job, a per-run
cap, and the run stops at the first usage-limit error (exit 75) instead
of failing the whole queue; a stop flag, because Future.cancel() cannot
stop work a thread already picked up. Covered by an offline test of
stop / resume / idempotent rerun.
Nothing environment-specific is committed: hosts, keys and paths live in
~/.config/claude-obsidian/ (this repo is public).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KF322NzozeaFjpgWvh4F4g