Skip to content

Local Waifu 0.1.16

Choose a tag to compare

@lumizone lumizone released this 21 May 09:11
· 4 commits to main since this release

Internal-quality release driven by a 4-agent backend audit. No
new user-facing features — just hardening every production-risk
finding the audit surfaced.

Fixed (BLOCKER class)

  • Migration runner crash window. The DB migration tracker row
    used to be inserted after the migration body's COMMIT. A crash
    in the millisecond between those two writes would leave the
    schema modified but the migration unrecorded; the next launch
    would re-attempt the same ALTER TABLE ... ADD COLUMN, fail
    (SQLite has no IF NOT EXISTS for ADD COLUMN), and brick the
    database. The body + bookkeeping now commit atomically.
  • Fire-and-forget tasks. commands/feedback.rs was using raw
    tokio::spawn instead of tauri::async_runtime::spawn for the
    background consolidation pass — broke the codebase convention.
    Swapped. (Telegram dispatcher keeps tokio::spawn because its
    is_finished() API powers status reporting — panic visibility
    is already covered by the global panic hook.)

Fixed (production hardening)

  • Marker parser robustness. [[MEM ...]], [[THREAD ...]],
    [[BLOCK ...]] parsers used to terminate on the first ]]
    encountered — if the model mirrored user text containing ]]
    (markdown footnotes, nested quotes), the marker would fire on
    the wrong delimiter and either drop a real call or swallow
    user-visible content. Now any [[ inside the body is treated
    as nested content and the marker is preserved verbatim.
  • Lorebook write validation. lorebook_upsert had no length
    caps; a user could push a 50 MB entry into SQLite. Now bounded
    at 200 chars (title), 4,000 chars (content), 32 keywords ×
    80 chars each. Update path also now correctly returns the
    original created_at instead of now, and rejects updates
    against an ID that doesn't belong to the active character.
  • Anticipations table runaway. The pruning DELETE used
    LIMIT = MAX_UNCONSUMED - parsed.len() which could go negative
    if those constants ever drifted. Clamped to >= 0.
  • DB lock starvation. anticipations::build_context used to
    hold the SQLite mutex across two prepared-statement iterations.
    Concurrent chat turns now don't block on the background
    reflection pass.
  • Ollama non-stream timeout. The shared client default 120 s
    was killing background drift detection / KG extraction /
    consolidation calls on Power-tier 27B models (real responses
    take 2-5 min). Per-request override raised to 10 min on the
    non-streaming path.
  • License gate for background loops. Daily consolidation +
    sleep-time anticipations now check license::gate::current_status
    and skip the LLM call entirely for Locked / TrialExpired /
    NotStarted users. They can't chat with the app anyway — no
    reason to burn their local CPU/GPU on reflection passes for a
    product they haven't paid for.

Audit deferrals

  • Retention sweep on memories / chat_messages / entities /
    open_threads / relationship_milestones.
    All grow unboundedly.
    Realistic ceiling for a daily-use install: ~100 MB after a year.
    Acceptable for v0.1.x; will land as a configurable retention
    policy in v0.2.