Finish the breakfix/issue-logging gap as a lightweight convention: when
recording a solved problem, capture symptom -> root cause -> fix and tag it
'issue' so it's findable instead of re-diagnosed. Pairs with the B9 trigger
('log when a problem is found'). No schema change — a structured note_type/
task_kind=issue is deferred to a joint schema pass with B7.
Refs plan 812 (B8 convention; B7 deferred).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the '(This instance's rules carry the specifics.)' pointer — universal
_INSTRUCTIONS must not assume this install has a particular rulebook. State the
ACL principle on its own so it holds for any Scribe install/fork.
Refs plan 812 (instance-agnostic product principle).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The MCP instructions are domain-neutral except a thin layer of dev vocabulary
and one project-specific paragraph (B10 audit, task 812). Make the data store's
own instructions serve any domain, and add the missing positive write-mandate.
B9 (neutralize):
- 'before writing code' -> 'before you dive in'
- Note examples 'dev-logs' -> 'logs of what happened'
- record trigger 'a merge, a shipped feature, a finished plan' + 'dev-log note'
-> 'finishing a task, or hitting/discovering a problem that changes direction'
(folds in B8: log pivots, not just wins; mirrors the static-tier wording)
- recall examples 'ticket/dev-log' -> 'task/prior note' (server + SKILL.md)
- 'Engineering and workflow rules' -> 'Workflow and standards rules'
- slim the 'developing Scribe itself' ACL paragraph to a neutral one-liner
(project-specific specifics already live in rules #47/#78)
A4 (write-mandate): state up front that Scribe is the system of record — record
work here, recall before acting, don't keep project work in local files.
Refs plan 812 (B9, A4, B8-trigger); B10 audit work-log.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Direction change (operator, see plan task #755 work-log): the plugin must
NOT depend on disabling a native Claude function to work. It earns its place
by steering behavior, not by toggling autoMemoryEnabled.
Memory doctrine (no dual-write):
- using-scribe SKILL.md gains "Scribe holds these functions — don't keep a
second copy": route rules/recall/planning to Scribe, don't also write them
to native auto-memory, never instruct disabling a native function, and
accept a "Scribe-shaped hole" if the plugin is removed (recover over time).
- mcp/server.py _INSTRUCTIONS: drop the paragraph that told the model to
create/refresh a "rules live in Scribe" pointer in CLAUDE.md / ~/.claude
memory. That was an active dual-write instruction; the SessionStart hook is
the bridge now. Replaced with the no-dual-write / no-settings-dependency
doctrine. Supersedes plan #755 Phase 6 ("set autoMemoryEnabled:false").
Project-scope discipline (stop cross-project bleed):
- using-scribe SKILL.md gains "Stay inside the active project's scope": pass
project_id to every read, only reference/offer work on the in-scope project,
ask before switching.
- _INSTRUCTIONS scope bullet extended from reads to referencing/offering, and
flags get_recent as cross-project.
- get_recent docstring gains a scope note steering to scoped list_* when a
project is active.
plugin.json 0.1.4 -> 0.1.5 so clients' caches actually refresh (re-shipping
under the same version does not bust the cache).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dashboard:
- 'Done recently' chip-cloud -> compact uniform list (Active-now row style),
showing 5 with inline expand to the rest (backend already returns up to 8).
- New 'Projects' rail card: each active project with 'N open · M done'.
Backend already computed done_count (dashboard.py) — now surfaced in the
/api/dashboard payload per active project.
MCP Access (Connect Claude / Claude Code):
- Progressive disclosure: lead with the pre-filled plugin-install snippet;
fold server name, scope, marketplace URL, and the MCP-only path into a
single 'Customize' expander. Desktop tab keeps its own server-name field.
- Marketplace URL now defaults to this instance's own repo via
config.PLUGIN_MARKETPLACE_URL (env-overridable); /api/plugin/marketplace-url
falls back to it, so the field + install snippet are pre-filled out of the
box instead of showing a generic placeholder.
Refs #761
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make the always-on / subscribed / project-rule distinction explicit at the
authoring surface so it can't silently regress (for this operator or other
users). Previously the tools said only 'cross-project rulebook rule' and a
bare 'subscribe a project' — nothing steered project-specific detail away
from shared rulebooks, which is how a Scribe-pinned rule ends up binding
every family project.
Principle encoded in 5 places: a rule's home is chosen by WHO it should bind,
and both rulebook tiers are SHARED so their rules stay general — they differ
in reach (all projects vs opt-in by theme), not generality. Project-specific
detail goes in create_project_rule.
- server.py MCP instructions: add the 3-tier authoring principle
- create_rule / create_rulebook / create_project_rule / subscribe_* docstrings
- using-scribe SKILL.md: a 'Where a new rule goes' note for the pull path
Refs #755
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The SessionStart hook asked for a project_id via plugin userConfig, which pins
one install to a single project — wrong for an operator working across many
repos/projects. Resolve the active project server-side from the working repo's
git remote instead (a stable identifier, not a dir-name guess).
- repo_bindings table (migration 0064) + RepoBinding model: (user, repo_key) ->
project, FKs CASCADE.
- services/repo_bindings: normalize_repo_key collapses ssh/https/scp/creds/port/
.git to host/owner/repo; resolve/set/list/delete.
- GET /api/plugin/context takes ?repo=<remote>; unbound repo -> a "bind this
repo" hint with a ready bind_repo() call. project_id kept as manual override.
- MCP tools: bind_repo / list_repo_bindings / unbind_repo.
- Hook sends ?repo=$(git remote get-url origin) URL-encoded; all project_id
handling removed. plugin.json drops the project_id userConfig (0.1.2 -> 0.1.3).
- Tests: normalize equivalence classes + unbound-hint rendering.
Refs task 755 (Scribe-as-plugin push channel).
Co-Authored-By: Claude Opus 4.8 (1M context) <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>
The v2 backup silently dropped the entire rulebook system (rulebooks, topics,
rules), the project subscription/suppression join tables, and events — so a
'full' backup wasn't. v3 adds all of them with FK re-mapping on restore, and a
_not_included field that names the still-deferred tables (ACL groups/shares,
api_keys, embeddings, transient/operational) so the gap is explicit, not silent.
restore_full_backup routes v2 and v3 through one path; v3-only sections are
guarded by data.get so a v2 payload still restores cleanly.
Tests: version/coverage constants, pure join-table row helpers, and the export
contract via a mocked session (CI has no DB; full round-trip is a manual check).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 1 of plan #755 (Scribe-as-plugin). Gives Scribe its own session-start
push channel so always-on rules + active-project context surface without being
asked — the gap behind 'I have to prompt for everything'.
- services/plugin_context.build_session_context: renders always-on rule titles
grouped by topic (under the 10k additionalContext cap; full text stays one
list_always_on_rules/get_rule call away) + optional project goal/open-task
count + a recall/update-over-create reflex line. Capped at 9000 chars.
- routes/plugin GET /api/plugin/context (login_required already accepts Bearer
fmcp_ keys; read scope suffices).
- tests: titles-not-statements, project scoping, length cap (pure mocks).
- scripts/scribe_session_context.sh: dogfood SessionStart hook, fail-open,
reads url+token from .mcp.json. Superseded in Phase 2 by the plugin-bundled
hook using userConfig.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The MCP surface advertised writing well but recall poorly, and project
scoping had no anchor that survived past enter_project's snapshot:
- search / list_notes dropped the project_id their services already
support, so a scoped search was impossible — every query swept all
projects and bled unrelated work into the session.
- The tool descriptions were mechanical ("Semantic search over the
user's notes and tasks") with no trigger telling Claude WHEN to reach
for them; the server instructions were all write-discipline and said
nothing about searching before answering or starting work.
Changes:
- search, list_notes: add project_id param, wired to the service.
- search, list_notes, list_tasks: trigger-worded descriptions that push
passing the active project's id and reserve project_id=0 for a
deliberate cross-project sweep.
- _INSTRUCTIONS: add a 'Reach for Scribe to RECALL, not just to record'
block — search before answering/starting, check for an existing ticket
before create_task, scope reads to the active project (which does not
stick on the server).
Paired with always-on rule #75 in the FabledSword-family rulebook.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Header wordmark Fabled -> Scribe; fable:calendar-changed event ->
scribe:calendar-changed; SettingsView CSS comment.
- Drop dead Project.auto_summary + summary_updated_at columns (migration
0063) -- the Ollama-era summarizer is gone; model + 2 frontend types +
projects test updated.
- Remove pivot vestiges: diagnostics _curator_busy()/curator_busy
heartbeat field, tz BRIEFING_DAY_START_HOUR/user_briefing_date dead
aliases, the ignored 'model' param on get_embedding (+ its test).
ruff src/ clean; CI is the gate. Part of scribe plan #599.
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>