The shape ledger's divergence readout flagged _gate_setting (#4385).
Reading it turned up a family with no canon: floor_for,
get_duplicate_threshold, get_plan_match_threshold and _gate_setting each
parsed a stored string, fell back to the default (never 0) and clamped
into [lo, 1].
- services/settings.bounded_float(raw, default, lo=0, hi=1) is the pure
parse, fallback and clamp. Each caller keeps its own get_setting read,
so tests patching get_setting per module still take effect, and
_gate_setting still fails open on an unreadable setting.
- SettingsView.saveKbInject: eleven inline Math.min/Math.max clamps and
the local gateAt become one asBar(v, d, lo = 0), mirroring the server.
No behaviour change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reading the 28 route modules against each other and against the MCP tools:
- routes/notes.py carried a PUT and a PATCH handler that were the same
function minus the supersedes contract on one of them — one handler now
serves both verbs, so both carry it.
- The two _attach_supersession copies (REST + MCP) become
supersession_svc.attach_relations(uid, note_id, data, hint=) — the seam
the two surfaces must agree through; only the agent surface adds the
one-sentence reading hint.
- Three local _uid() wrappers over g.user.id → scribe.auth.get_current_user_id
like every other module; design_systems' private _not_found → routes.utils.
not_found; the four "********" literals → settings_svc.SECRET_MASK with the
read/write contract written once.
- routes/plugin.py: the project_id/repo resolution block and the
comma-separated id parse were copied into three endpoints — _project_scope()
and _int_list() now.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a daily off-hours VACUUM (ANALYZE) over the high-churn tables the
retention/purge sweeps churn (app_logs, notifications, token tables, notes,
note_versions), on top of Postgres autovacuum, to reclaim bloat left by the
nightly bulk DELETEs and keep planner stats fresh.
- services/db_maintenance.py: run_maintenance() over a closed table allowlist
via an AUTOCOMMIT connection (VACUUM can't run in a txn); per-table summary
persisted as the db_maintenance_last_run admin setting.
- services/db_maintenance_scheduler.py: BackgroundScheduler cron (default
04:00 UTC, after the 03:30 trash purge); enabled-gate checked at fire time;
live reschedule on hour change. Wired into app.py start/stop.
- routes/admin.py: admin-only GET/PUT /api/admin/db-maintenance + POST /run.
- settings.py: set_admin_setting() (write-side of get_admin_setting) for
out-of-request writes.
- SettingsView.vue: admin 'Database maintenance' card — enable toggle, run-hour
(UTC), Run-now, last-run summary.
- Tests: allowlist is closed, VACUUM issued per table, one failure doesn't
abort the rest, summary persisted; route/scheduler/service surface.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Settings install command had a <your-scribe-repo> placeholder — not
copyable. Add an instance-global 'plugin_marketplace_url' setting (admin sets
it to the app's own repo) that every user's MCP Access reads, so the
/plugin marketplace add command is copyable out of the box. Keeps it universal
(each deployment configures its own repo) rather than hardcoding one.
- services/settings.get_admin_setting(key): admin-scoped global read.
- routes/plugin: GET /api/plugin/marketplace-url (any user) + PUT (admin).
- SettingsView: Admin → 'Plugin marketplace' field to set it; MCP Access
marketplace field falls back to the configured value.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Renames src/fabledassistant -> src/scribe and all imports, plus the
default DB name and DB user/password (fabled -> scribe) in config +
compose. 952 refs / 154 files. Reverses the old 'internal name stays
fabledassistant' convention.
Code-only: live databases are still physically named 'fabledassistant'.
Deployed environments must set POSTGRES_DB / POSTGRES_USER (or rename the
DB) since the defaults now resolve to 'scribe'. Repo (FabledScribe), git
host (fabledsword), MCP (fabled-git) and the image name (fabledscribe)
are intentionally unchanged.
ruff check src/ clean locally; CI (typecheck + pytest) is the gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>