• v26.08.22.1 — project inception (decide what a project inherits)
    CI & Build / Python lint (push) Successful in 5s
    CI & Build / Plugin hooks (push) Successful in 12s
    CI & Build / integration (push) Successful in 27s
    CI & Build / TypeScript typecheck (push) Successful in 33s
    CI & Build / Python tests (push) Successful in 1m5s
    CI & Build / Build & push image (push) Successful in 15s
    Stable

    bvandeusen released this 2026-08-22 00:56:27 -04:00 | 131 commits to main since this release

    Project inception (milestone 297, PR #124). A project's inheritance is a decision, not a default: create_project takes what the project inherits (always-on rulebooks to exclude, rulebooks to subscribe, design system, seed the standard Systems); enter_project asks the owner of an undecided project until decide_project_inception records it; the New-project modal gains a second step and the project page an inception card; the Rules tab shows excluded always-on rulebooks. Exclusions are total across every rule surface, including the SessionStart block. Existing projects are stamped inherit-all (legacy) — no new asks for them.

    Migration 0085. Backup v10. Plugin 0.1.38.

    🤖 Generated with Claude Code

    Downloads
  • v26.08.21.2 — ledger first-contact fixes
    CI & Build / Python lint (push) Successful in 4s
    CI & Build / Plugin hooks (push) Successful in 7s
    CI & Build / integration (push) Successful in 25s
    CI & Build / TypeScript typecheck (push) Successful in 33s
    CI & Build / Python tests (push) Successful in 57s
    CI & Build / Build & push image (push) Successful in 14s
    Stable

    bvandeusen released this 2026-08-21 18:10:06 -04:00 | 140 commits to main since this release

    Fixes found on the first refresh under v26.08.21.1 (PR #123, issue #2877):

    • One-line CSS rules fingerprint their own declarations (they hashed to sha1("") and grouped 68 unrelated one-liners as one "copy" in the derive readout).
    • The proposer writes uses edges for already-judged consumers too (consumer maps were missing every call site classified before the edge table existed).
    • 0084 migration docstring reworded (extractor false positive).

    No new migrations.

    🤖 Generated with Claude Code

    Downloads
  • v26.08.21.1 — shape audit consolidation + ledger follow-ups
    CI & Build / Build & push image (push) Successful in 15s
    CI & Build / TypeScript typecheck (push) Successful in 23s
    CI & Build / Plugin hooks (push) Successful in 8s
    CI & Build / Python lint (push) Successful in 3s
    CI & Build / Python tests (push) Successful in 57s
    CI & Build / integration (push) Successful in 21s
    Stable

    bvandeusen released this 2026-08-21 15:39:45 -04:00 | 142 commits to main since this release

    Scribe shape audit (milestone 296) — code consolidation across tests/models/mcp/routes/services/frontend (PRs #120, #121): tests/helpers.py + conftest fixtures; iso() + model mixins; rules_payload; attach_relations; route helpers; start_periodic / ScheduledJob / hash_token / backup row builders; auth-shared.css, apiErrorMessage, dateFormat helpers, modal canon in components.css (fixes ConfirmDialog unstyled, #2856).

    Ledger follow-ups (milestone 294, PR #122) — proposer v3 (language-family gate, reference stoplist, same-project semantic); list_shapes(compact=) + classify_shapes_by_rule; the scoped bucket; derive ranking by body identity + CSS declaration fingerprints; bind_repo(ref=) (migration 0082); reason_code catalogue (0083); case-insensitive repo lookup; code_shape_uses consumption edges (0084); backup v9. Plugin 0.1.37.

    Migrations 0082–0084 run on deploy.

    🤖 Generated with Claude Code

    Downloads
  • v26.06.03
    CI & Build / Python lint (push) Successful in 2s
    CI & Build / TypeScript typecheck (push) Successful in 32s
    CI & Build / Python tests (push) Successful in 45s
    CI & Build / Build & push image (push) Successful in 14s
    Stable

    bvandeusen released this 2026-06-03 12:51:04 -04:00 | 460 commits to main since this release

    First release under the no-suffix CalVer scheme (rule 47). Covers everything merged to main since v26.06.01.2.

    Features

    • Stored Processes — reusable saved prompts (note_type=process) with create/list/get/update + resolve_process; Knowledge-view editor (PR #55).
    • Dashboard — new /dashboard landing ("what to work on") with / redirect; Knowledge becomes the Browse surface (PR #55).
    • MCP _INSTRUCTIONS hardening — agents now drive task lifecycle (in_progress → log → done + landing dev-logs), keep a Scribe-rules pointer in host memory when a project subscribes to a rulebook, and proactively bootstrap project context at session start (PR #56).

    Changes

    • update_task gains milestone_id=-1 (and project_id=-1) to CLEAR an FK; 0 still leaves unchanged (PR #56).
    • CI now gates + builds main as well as dev; main publishes the immutable :<sha> image, :latest + :<version> are release-only (PRs #56–#57).

    🤖 Generated with Claude Code

    Downloads
  • v26.06.01.2 — Rules consolidation + project rule suppressions
    CI & Build / Python lint (push) Successful in 3s
    CI & Build / TypeScript typecheck (push) Successful in 33s
    CI & Build / Python tests (push) Successful in 48s
    CI & Build / Build & push image (push) Successful in 22s
    Stable

    bvandeusen released this 2026-06-01 08:11:45 -04:00 | 492 commits to main since this release

    Rules subsystem matures from "additive cross-project rulebooks only" into a full first-class system: always-on rulebooks, project-scoped rules, an active-project handshake, and per-project suppression of inherited rules. Two PRs since v26.06.01.1.

    PR #53 — Rules consolidation (4 slices)

    Problem. Rules in Scribe were pull-only; CLAUDE.md and ~/.claude/.../memory/feedback_*.md were session-start auto-loaded. The MCP prompt never insisted that Claude fetch Scribe rules eagerly or codify new rules in Scribe rather than the competing stores. Result: Scribe rules silently lost.

    • S1 + S2 — always_on rulebook flag (commit 658348f)

      • Migration 0058: rulebooks.always_on boolean. FabledSword family rulebook seeded always_on=true at migrate-time.
      • New MCP tool list_always_on_rules() returns the union of standing rules for the user in one round-trip, no active-project needed.
      • _INSTRUCTIONS rewritten: at session start, call list_always_on_rules; treat as binding. Rules codified in Scribe via create_rule, never in CLAUDE.md or feedback files. Those stores are now scoped to user-facts and codebase onboarding respectively.
      • Frontend: badge on always-on rulebooks in RulebookListPane; toggle in RulebookDetailPane header.
    • S3 — Project-scoped rules (commit 43a860c)

      • Migration 0059: rules.project_id (nullable FK, CASCADE), rules.topic_id made nullable, CHECK ck_rule_topic_xor_project enforces exactly-one.
      • New MCP tool create_project_rule(project_id, statement, title?, why?, how_to_apply?) — no rulebook ceremony for project-only rules.
      • get_applicable_rules adds a project_rules field surfaced through get_project, get_task (plan), start_planning.
      • Trash cascade: deleting a project soft-deletes its project-scoped rules along with the rest.
      • REST: POST /api/projects/<id>/rules; frontend Rules-tab gains a "Project rules" section with inline create form.
    • S4 — enter_project handshake (commit c546921)

      • New MCP tool enter_project(project_id) composes get_project + get_applicable_rules + milestone-summary + open-tasks + recent-notes into one round-trip. Intended for once-per-session use when work is project-scoped.
      • _INSTRUCTIONS directs Claude to call it before any project-scoped mutation.
      • No schema change; pure composition over existing services.

    PR #54 — Project rule + topic suppressions (commit 7861607)

    Lets a project mute individual rules or whole topics from rulebooks it subscribes to, without unsubscribing the rulebook. Use case: subscribe to a broad rulebook (e.g. a future "FabledSword design system") but opt out of one or two rules — or a whole topic — that doesn't apply to a non-frontend project.

    • Schema (migration 0060): two new pure m2m association tables — project_rule_suppressions and project_topic_suppressions. PKs on both columns; FKs CASCADE. No deleted_at — pure associations, hard-deleted on project trash.
    • MCP tools (4): suppress_rule_for_project, unsuppress_rule_for_project, suppress_topic_for_project, unsuppress_topic_for_project. All idempotent; verify project + rule/topic ownership. Project-scoped rules (Rule.project_id) are deliberately not suppressible — delete them instead.
    • REST: POST/DELETE /api/projects/<pid>/suppressions/{rules,topics}/<id>.
    • Service projection: get_applicable_rules runs two extra queries and emits suppressed_rules + suppressed_topics as detail objects (id + title + rulebook context, not bare IDs — so the UI doesn't need a follow-up lookup). The main rules list is filtered SQL-side so truncated reflects post-suppression count. Rules now carry topic_id + rulebook_id columns for the UI affordance.
    • Propagation: suppressed_rules and suppressed_topics surface through get_project, enter_project, get_task (plan), and start_planning — Claude sees what's muted from any standard handshake.
    • Frontend: "× skip" affordance reveals on hover for each rule row and each topic header in ProjectRulesTab.vue; collapsed "Suppressed (N)" section at the bottom with per-item "↻ re-enable" buttons. groupByRulebookAndTopic refactored to id-bearing structs.
    • Trash cascade: project deletion hard-deletes the project's suppression rows so a later restore starts fresh.

    Migrations

    • 0058rulebooks.always_on boolean, default false; seeds FabledSword family to true.
    • 0059rules.project_id (FK CASCADE), rules.topic_id nullable, CHECK ck_rule_topic_xor_project. Down-migration requires migrating/deleting project-scoped rules first.
    • 0060project_rule_suppressions and project_topic_suppressions association tables.

    All three run automatically on first boot.

    Verification

    • CI green on c546921 (S4 — after rebuild; original run flaked on npm-cache action), 43a860c (S3), 658348f (S1+S2), and 7861607 (suppressions).
    • Migrations 0058/0059/0060 applied cleanly to the dev environment; operator confirmed the suppression UI and MCP tools work end-to-end on dev (migration in dev worked, 2026-06-01).
    • Tests cover: list_always_on_rules, update_rulebook(always_on=…), create_project_rule (required-fields + title-derivation + explicit-title), enter_project composed shape + 404, get_applicable_rules with project_rules + suppressed_rules + suppressed_topics, project-cascade trash hitting 7 tables (was 4 before S3, 5 after S3, 7 with this release).

    Deploy notes

    • Three migrations run automatically on container start.
    • After deploy, MCP reconnects pick up the new _INSTRUCTIONS; on the next project-scoped turn Claude should call list_always_on_rules and enter_project. Eyeball the tool-call transcript on first use.
    • No content migration: rule-shaped entries in CLAUDE.md and feedback memory files stay where they are. The prompt reroutes NEW rules to Scribe going forward.

    What this release does NOT do

    • No supersede semantics — a project rule can't yet replace a rulebook rule for one project (only mute it). Pure suppression covers the stated use case; replacement can land later.
    • No write-scope enforcement on bearer tokens — mutating tools still ignore the read/write scope on api_keys. M1 work, tracked separately.
    • No share-aware filtering on list_trash / restore / purge — same M1 carve-out.
    Downloads
  • v26.06.01.1 — MCP plan-prompt tune + Flutter docs removal
    CI & Build / Python lint (push) Successful in 3s
    CI & Build / TypeScript typecheck (push) Successful in 33s
    CI & Build / Python tests (push) Successful in 45s
    CI & Build / Build & push image (push) Successful in 24s
    Stable

    bvandeusen released this 2026-06-01 00:13:58 -04:00 | 498 commits to main since this release

    Small follow-up release on top of v26.05.30.1. Two commits, docs/prompt only — no schema, no behavior changes to running services.

    MCP plan-instruction tightening

    The Plans paragraph in the MCP server's _INSTRUCTIONS previously ended with a quiet footer ("not in local .md files") that lost to the much louder superpowers brainstorming / writing-plans skill flow, whose terminal steps save to docs/superpowers/specs/*.md and docs/superpowers/plans/*.md. Reordered so start_planning is named as the FIRST action when beginning non-trivial work, with an explicit override of those .md paths and coverage extended to specs as well as plans (matches rulebook rule 27).

    Effect: when Claude reconnects after the new image deploys, it will reach for start_planning before any markdown plan file by default.

    Flutter companion-app docs removed

    The Flutter app no longer adds value over web/PWA access. In-repo surface stripped:

    • deleted docs/android-app.md
    • dropped Android App row from README docs table and from the README feature paragraph
    • dropped the two Flutter-related roadmap bullets from docs/features.md
    • removed the "Flutter app port" subsection from docs/design-system.md

    The standalone fabled_app repo is untouched here; archival of that repo is a separate operation on that repo.

    Deploy notes

    No migration. No data changes. Pull the new :latest image; restart. Done.

    Verification

    CI green on commit fd20b67 (typecheck / lint / tests / image build all ) before merge.

    Downloads
  • bvandeusen released this 2026-05-29 22:51:28 -04:00 | 501 commits to main since this release

    118 commits since v26.05.13.1. Headline: the MCP-first pivot lands in production, and three new feature arcs land on top of it.

    MCP-first pivot (Phases 7-10)

    Scribe is now a pure data store. Claude via the in-app /mcp endpoint is the only LLM. ~30,000 lines deleted across backend, frontend, and infrastructure.

    • New endpoint/mcp mounted alongside /api/* via FastMCP's streamable-HTTP transport. ASGI middleware authenticates via bearer tokens from the existing api_keys table; rejects with 401 before FastMCP sees the request. Per-request user_id ContextVar for tool handlers.
    • 34 MCP tools covering notes, tasks, projects, milestones, events, typed entities (person/place/list), search.
    • Embeddings: Ollama → fastembed. In-process ONNX (BAAI/bge-small-en-v1.5, 384d). First boot downloads the model to /data/fastembed-cache once. Migration 0052 cleared the existing 768d note_embeddings; startup re-embeds existing notes.
    • Phase 7 — frontend strip: removed /chat, /journal, /workspace/:projectId, voice overlay, the chat-style home view. Surfaces that survived: /knowledge, /notes, /tasks, /projects, /calendar, /graph, /settings, /rules, /trash.
    • Phase 8 — backend deletion: /api/chat/*, /api/journal/*, /api/voice/*, /api/push/*, /api/quick-capture, /api/fable-mcp/* and their backing services. The standalone fable-mcp stdio wheel removed.
    • Phase 9 (migration 0053): dropped conversations, messages, generation_tool_log, moments + junction tables, moment_embeddings, pending_curator_actions, push_subscriptions, weather_cache, rss_item_embeddings.
    • Phase 10 (migration 0054): dropped image_cache; removed Ollama service config and frontend orphans.
    • Rebrand: "Scribe" user-facing; fabledassistant internal package name kept stable.
    • Settings → MCP Access tab: create write or read-only tokens (fmcp_<32-byte-base64url> format), shown once, sent as Authorization: Bearer <token>. Configurable MCP server name + scope.

    Rulebook system (May 2026 milestone)

    Cross-project standards: Rulebook → Topic → Rule, sibling to Project, with project subscriptions.

    • Migration 0055rulebooks, rulebook_topics, rules, project_rulebook_subscriptions. Each rule is a statement + why + how_to_apply triple.
    • 16 MCP tools for rulebook/topic/rule CRUD plus subscribe/unsubscribe. get_project(id) returns applicable_rules + subscribed_rulebooks (capped at 50).
    • /rules three-pane UI (rulebooks → topics → rules) with rule editor; project Rules tab grouped by rulebook → topic.
    • Seeded: the external FabledRulebook (12 topic .md files) imported into the "FabledSword family" rulebook — 12 topics, 43 rules. Fabled Assistant project subscribed.

    Plans-as-tasks (May 2026 milestone)

    Plans are first-class without a new entity: a Task with task_kind="plan".

    • Migration 0056 — additive notes.task_kind column (work | plan, default work, CHECK-constrained).
    • MCP: start_planning(project_id, title) seeds a plan template + returns applicable_rules. get_task surfaces applicable_rules for plan-tasks only.
    • UI: "Start planning" button on /projects/:id; Applicable Rules panel in the task editor; Knowledge view's Plans facet + plan badge.
    • Rulebook hook: rule 27 reframed ("Planning artifacts live in Scribe, never in git"); ritual rule 45 added ("Build plans via start_planning, not .md") — the rulebook itself points planning at Scribe.

    Soft-delete / Trash / Retention (May 2026 milestone)

    Deletes become recoverable.

    • Migration 0057deleted_at TIMESTAMPTZ NULL + deleted_batch_id TEXT NULL (+ ix_<t>_deleted_at) on 7 tables (notes, events, projects, milestones, rulebooks, rulebook_topics, rules). SoftDeleteMixin in models/base.py.
    • Cascade: project → milestones + tasks/notes; rulebook → topics → rules; restore brings the whole batch back together (reverses the prior FK SET-NULL behavior so a restored project comes back with its children).
    • MCP surface: existing delete_* flipped to soft (return {deleted_batch_id, message}); added delete_task, delete_project, delete_milestone (closes the CRUD-asymmetry gap); added list_trash, restore, purge_trash (confirmed-gated hard delete).
    • REST: /api/trash (GET list, POST /<batch>/restore, DELETE /<batch> for purge). All entity DELETE handlers flipped to call trash.delete.
    • Retention: trash_retention_days setting (default 90, 0 = keep forever). Daily 03:30 UTC APScheduler purge cron.
    • UI: /trash view (batches summary, Restore, Delete-permanently, Empty trash, empty state) + Settings → General → "Trash retention" field. g+x keyboard shortcut.

    MCP transport — stateless HTTP

    mcp/server.py sets stateless_http=True on the FastMCP instance. The default stateful streamable-HTTP transport handed Claude Code an Mcp-Session-Id; after a container redeploy the server rejected the now-unknown id with 404, and Claude Code doesn't re-initialize on 404 (issue #60949), so the connection stranded until manual /mcp retry. Stateless makes every request self-contained (bearer-auth only); the next message after a redeploy reconnects automatically — no manual intervention.

    Pre-pivot work in the diff (removed by the pivot)

    The diff includes substantial work that landed on dev before the MCP pivot and was subsequently removed by it: curator phases C1-C5 (additive-only curator tools, authority routing, Needs Review panel), journal closeout + captures panel, TTS migration (kokoro → piper) + voice admin UI, diagnostics for crash investigation, model-bench tooling (bench_ollama.py), user-controlled think mode. These commits remain in history but the surfaces they touched no longer exist in production.

    Deploy notes

    • Destructive migrations. 0052/0053/0054 drop large chunks of pre-pivot data (chat conversations, journal moments, voice settings, weather cache, image cache, push subscriptions, rss item embeddings). Back up before deploy if anything in those tables matters.
    • Migration sequence: 0044 → 0057 run automatically on first boot.
    • Compose stack reduces to two services: app + db. No Ollama, no Redis, no background worker.
    • MCP access: create a bearer token via Settings → MCP Access after deploy. Configure in Claude Code via claude mcp add --transport http scribe <BASE_URL>/mcp with the token in the bearer header.
    • First-boot fastembed download: ~150 MB ONNX model goes to /data/fastembed-cache; persists across container restarts.

    Verification

    • All backend pytest tests pass (CI green on commit 2f577fe).
    • /trash UI + Settings retention field device-checked by operator 2026-05-29.
    • MCP reconnect verified after the stateless-HTTP fix (Portainer redeploy → next message reconnected without manual /mcp retry).
    • Trash dogfood (via MCP this session): throwaway project cascade-delete → list_trashrestore → re-delete → purge_trash round-trip; single delete_tasklist_trashrestore round-trip. Both passed.

    Known follow-ups

    Captured as plan-task 183 on project 3, milestone M1 "Authorization & share-correctness (May 2026)":

    • Share-debt in soft-delete: list_trash / restore / purge ignore user_id (cross-user leak under multi-user); retention reads user 1's setting globally rather than a per-instance admin value; collaborator-delete semantics undefined.
    • Cosmetic papercuts: purge_trash rowcount is off-by-one when the batch holds parent + children whose DB-level FK has ON DELETE CASCADE (end state correct, count off by one); trash items show type: "note" for is_task=True tasks.
    • MCP write-scope enforcement — mutating tools currently ignore the read/write token scope.

    These feed the M1 foundation work. See project 3 milestones for the full M1→M4 roadmap (authz → audit → review surface → retrieval quality).

    Downloads
  • bvandeusen released this 2026-05-13 19:28:34 -04:00 | 620 commits to main since this release

    Forty-five commits across four feature groups: the journal closeout work from earlier in the day, the LLM tool-use fixes from the 2026-05-08 prod conversation investigation, and two new fully-specced projects (task-as-durable-record and note version pinning).

    Project A — Task as durable record

    The task body becomes the canonical record of what happened, not just what was initially asked.

    • Schema: new description field on Note (user-stated goal). New consolidated_at timestamp. Migration 0044 copies existing body content into description for existing tasks.
    • Body semantics shift for tasks: body is now LLM-maintained, written by a background consolidation pipeline from the task's accumulated work logs. Description is user-owned and never edited by the pipeline. Embeddings continue to index title + body, so semantic search now hits the work-done summary rather than the initial goal text — tasks become discoverable by what was actually done.
    • Consolidation pipeline: debounced after 3 new log_work entries, OR fired immediately on task status transition to done/cancelled. Uses background_model so the chat model's KV cache stays warm. Per-task asyncio lock prevents concurrent runs. Failures leave body untouched; next trigger retries.
    • Tool layer enforcement: create_note tool drops the body argument when status is set (i.e., creating a task); update_note tool rejects body writes on tasks with an error that nudges toward log_work. HTTP routes still accept body so the frontend editor works pre-consolidation.
    • Manual control: POST /api/tasks/:id/consolidate endpoint + auto_consolidate_tasks user setting (General tab toggle). Manual endpoint bypasses the toggle.
    • Frontend: "Goal" textarea added above the body in TaskEditorView; TaskViewerView renders the Goal block subordinate to the body. TipTap body editor is gated to read-only once consolidated_at is set; a "Re-consolidate" button calls the manual endpoint.

    Project B — Note version pinning

    The existing rolling 50-version snapshot system grows two retention tiers above the baseline.

    • Schema: pin_kind (NULL = rolling, 'auto', 'manual') and pin_label columns on note_versions. Migration 0045 leaves all existing rows as rolling.
    • Three FIFO buckets:
      • Rolling autosaves: cap 50 per note (unchanged from before, but the prune query now filters pin_kind IS NULL so pinned rows are not counted).
      • Auto-pinned: cap 25 per note. A daily 03:00 UTC APScheduler job walks each note's version chain and pins versions that are followed by another version ≥2 days later (or the latest version if it's ≥2 days old with no successor). Auto-label is generated from the stability window.
      • Manually declared: unlimited. Never pruned.
    • API: POST /api/notes/:id/versions/:vid/pin accepts an optional label; DELETE /api/notes/:id/versions/:vid/pin clears pin_kind/pin_label (downgrades back to rolling — does NOT delete the row).
    • Backup export/import roundtrip: both new fields persist through /api/export and restore. .get() on import preserves backward compatibility with older backup files.
    • Frontend: HistoryPanel.vue extended with kind-aware badges (filled circle = manual pin, half-filled = auto-pin), label rendering, and a pin/unpin/edit-label control row above the diff view.

    LLM tool-use fixes (2026-05-08 investigation)

    Five fixes shipped together because they share a root failure mode in colloquial project/task references.

    • search_projects_tool now returns success: True so the dispatcher doesn't mislabel valid results as errors.
    • list_notes strips filler type-nouns (task, tasks, note, notes, project, projects) from q before the ILIKE AND-filter; search_notes tool description nudges the model to use type/project parameters instead of stuffing those words into query.
    • New shared score_project_match helper unifies ranking between search_projects_tool and resolve_project. Tiered scoring (1.0 exact / 0.85 substring either way / 0.70 query-in-description / SequenceMatcher fallback against the title). score_project_match also strips project / projects filler from queries so "famous supply project" substring-matches "Famous-Supply Work topics" at 0.85 instead of 0.228.
    • record_moment tool description tightens task_titles / note_titles to require values from a prior search_notes call in the same turn.
    • Journal calibration prompt + chat tool_lines static block both gain a "search-first when user references existing work" clause.

    Journal closeout

    Nightly per-user closeout extracts profile observations from the day's journal.

    • services/journal_closeout.py runs once per user per day at the user's day_rollover_hour. Reads yesterday's /journal conversation, filters out daily_prep-metadata messages at the SQL layer (hard data-exclusion guard against the briefing-era feedback-loop pathology), and asks the background model for 2–5 bullet observations. Appends to observations_raw.
    • closeout_enabled config key (default ON) wired to Settings → Profile.
    • GET /api/profile/observations + collapsible "Recent observations (14)" panel for transparency.
    • Catch-up on startup if today's day_rollover_hour already passed and yesterday's entry is missing.

    Deploy notes

    • Two schema migrations (0044, 0045) — both additive and safe.
    • No frontend build required beyond standard CI.
    • The auto-pin scan first fires at 03:00 UTC after deploy. Existing notes won't see auto-pinned versions until then (or earlier if you manually pin via the new UI).
    • The consolidation pipeline only fires for tasks that get new log_work entries OR status transitions after deploy. Existing tasks retain their current body until they accumulate enough new logs to trigger consolidation.

    Verification

    • All backend pytest tests pass (CI green on PR #50)
    • Migrations apply cleanly on the dev environment
    • End-to-end on the dev environment: task description roundtrip, consolidation trigger after 3 work logs, body editor gating, version pinning + auto-pin scan, journal closeout settings UI

    Known follow-ups

    • Project D (fable-dev #167) — journal prompt simplification + WRITE_FALSE_CLAIM reconciler + tool description tightening + model-swap safety check. Recommended next; high priority. M effort.
    • Project E (fable-dev #168) — pipeline decoupling to enable journal model swap. Deferred.
    Downloads
  • bvandeusen released this 2026-05-12 21:32:01 -04:00 | 643 commits to main since this release

    Two threads — one rebuilds a half-deleted feature, the other patches an LLM-misuse failure mode caught in prod.

    Journal closeout for profile observations

    The briefing tear-down on 2026-04-25 deleted the closeout job but left the read path: build_profile_context still injects learned_summary into journal-prep and chat system prompts. Result was a stale frozen summary plus no path to refresh it. This release reinstates the write side with a hard guard against the briefing-era feedback loop.

    • services/journal_closeout.py (new) — runs once per user per day at the configured day_rollover_hour. Reads yesterday's /journal conversation, filters out messages with msg_metadata.kind="daily_prep" at the SQL layer, asks the background LLM for 2–5 bullet observations focused on user-side patterns (NOT structured fields like name/job/expertise — those are still the update_profile tool's lane), and appends to observations_raw via the existing append_observations.
    • Hard data-exclusion guard — the daily-prep block is structurally invisible to the closeout LLM. The previous briefing-era closeout sent all messages with role labels and relied on the prompt to say "user revealed"; that's the shape that caused the feedback loop. Instruction-only role separation isn't reliable on local Ollama models against long transcripts.
    • background_model for the LLM call — keeps chat-model KV cache warm.
    • closeout_enabled config key (default ON), wired to a Settings → Profile toggle. New GET /api/profile/observations endpoint and collapsible "Recent observations (14)" panel for transparency.
    • Catch-up on startup — if today's day_rollover_hour already passed and observations_raw has no entry for yesterday, runs once.

    LLM tool-use fixes from 2026-05-08 journal session

    Prod conversation #282 exposed three concrete code bugs and two behavioral patterns. All five fixes shipped together because they share a root failure mode: when the user names a project/task colloquially, the model fails to find it, fails to trust valid matches, or skips searching entirely and fabricates a moment.

    • search_projects returns success: True — the dispatcher reads this key to set status on the conversation history entry. Missing key → result labeled status: error even when data was returned → model second-guessed valid matches. One-line fix.
    • list_notes strips type-nouns from qtask, tasks, note, notes, project, projects are filtered out of the keyword-search tokens before the AND-ILIKE filter is built. Closed the case where "find the sebring secondary task" returned 7 unrelated tasks because task #95's title and body don't contain the word "task". search_notes tool description also tells the LLM to use type / project parameters instead of stuffing those words into query.
    • score_project_match shared helper (new in tools/_helpers.py) — tiered scoring (1.0 exact title / 0.85 substring either way / 0.70 query-in-description / SequenceMatcher-against-title fallback). Strips project / projects filler from the query so "famous supply project" substring-matches "Famous-Supply Work topics" at 0.85 instead of 0.228 SequenceMatcher floor. Both search_projects_tool and resolve_project use the helper — single source of truth, no drift between the LLM-facing surface and the create-task resolver.
    • record_moment descriptiontask_titles / note_titles must come from a prior search_notes call in the same turn. Prevents the project-name-as-task-title misuse that triggered the original investigation.
    • Search-first heuristic added to both JOURNAL_CALIBRATION and the chat tool_lines static block: when the user references existing work, call search_notes first; create or record only after no match surfaces and the user confirms.

    Verification

    • 178+ backend pytest tests pass (CI)
    • tests/test_journal_closeout.py — filter, run_for_user happy + skip paths, scheduler register/disable, catch-up
    • tests/test_tool_use_fixes.py_strip_type_nouns, score_project_match tiers, search_projects_tool ranks substring above SequenceMatcher, resolve_project finds colloquial match
    • Tool-use fixes verified end-to-end against fable-dev MCP: search_projects shows status: success, score 1.0 for exact-match substring; search_notes correctly split query="tool-use" / type="task" and returned the right task
    • Search-first heuristic verified in journal UI

    Deploy notes

    • No schema migrations; deploy-only
    • After deploy, optionally click Reset Learned Data in Settings → Profile to wipe the stale briefing-era learned_summary. The new closeout will rebuild from clean observations starting tomorrow's prep cycle.

    Out of scope (follow-ups)

    • Manual verification of the closeout toggle / observations panel (PR test plan checkbox left unchecked — covered separately)
    • Hybrid BM25 + embedding search (replaces semantic-only)
    • "Did you mean X?" UI surface for project matches in the 0.40–0.85 score band
    Downloads
  • bvandeusen released this 2026-04-29 22:08:38 -04:00 | 667 commits to main since this release

    Highlights

    EventSlideOver recurrence UI

    The calendar event modal now exposes the existing Event.recurrence RRULE column. A "Repeat" select offers None / Daily / Weekly / Monthly / Yearly presets that map to/from canonical RRULE strings.

    CalDAV-imported rules with extra parts (e.g. FREQ=WEEKLY;BYDAY=MO,WE,FR) are preserved — they surface as a disabled "Custom" option with the raw rule shown read-only, so they remain visible in the UI without us silently destroying them.

    Journal weather wiring fixes

    Closes two real gaps that caused empty weather sites in the journal panel:

    1. Saving locations now triggers a fetch. PUT /api/journal/config kicks off a background refresh_location_cache for every saved location with valid lat/lon. Previously, newly-entered sites had no cache row (or a stale one) until the user manually clicked the panel's refresh button.

    2. Orphaned cache rows are filtered. Migration 0040 deleted the briefing-era briefing_config setting but left weather_cache rows untouched. Those briefing-era rows showed up as ghost tabs in the new journal weather panel. get_cached_weather_rows now accepts a valid_keys filter; the journal weather routes and prep pipeline pass currently-configured location keys, so only cache rows that match an actively-configured location surface in the UI or the daily prep.

    Commits

    • 36cd08c feat(events): expose recurrence presets in EventSlideOver
    • c33cab7 fix(journal): wire weather refresh on config save; drop orphaned cache rows

    Verification

    • 153 backend pytest tests pass
    • Frontend vue-tsc --noEmit clean
    • CI green on dev before merge
    Downloads