_READ_ONLY_TOOLS fails closed, which is the right design — but the list had gone stale, so a read-only key could get_note and not get_snippet, both pure reads of the same table, and could not read a design system at all. That inverts the sensitivity ordering: the free-text records were reachable and the structured, low-sensitivity ones were not. `find_duplicate_snippets` sitting in the list was the tell — someone classified the report and missed the getters beside it. Adds the twelve reads that were missing: snippets, processes, the six design system tools, and list_repo_bindings. Each verified to mutate nothing rather than assumed — this is a security boundary, and a wrong entry does not cost what a missing one costs. record_pulled on four getters is telemetry about the read, not a change to what was read, and get_note already carried it inside the boundary. The list stays explicit. Deriving it from the name would be worse than staleness: it makes the boundary follow a naming convention, so any future get_* grants itself access. list_starter_role_groups is the live illustration — it reads a constant, but names create_design_system in its docstring, so a pattern-matcher flags it. So derive the CANDIDATES and keep the DECISION explicit: a new test asserts every read-shaped tool appears in _READ_ONLY_TOOLS or in a declared _DELIBERATELY_WRITE_SCOPED, and that neither set names a tool that no longer exists. Adding a getter now forces a classification at review time instead of denying it silently. The second set is empty and stays declared — otherwise a future get_or_create_* would be pushed into the allow-list to make the test pass, which is the wrong way to satisfy it. Third instance of the same shape, after #2476 and #2444: a hand-written enumeration that missed the members added after it was written. Refs #2496
Fabled Scribe
A self-hosted work system-of-record for software projects, built to be driven by Claude Code. Notes, tasks, issues, projects, milestones, rules, and stored processes — reachable from Claude via a built-in MCP endpoint and a bundled Claude Code plugin, with a clean web UI for humans. No in-app LLM; Claude is the sole assistant.
Features
Notes and tasks with a Markdown editor, sub-tasks, milestones, issues, and kanban project workspaces. Stored processes, an engineering rulebook system, and semantic search with proactive knowledge-injection into Claude's context. A knowledge graph, per-user/group sharing, and a built-in MCP server (/mcp) plus a bundled Claude Code plugin so Claude can record and recall your work directly.
Quick Start
Prerequisites: Docker and Docker Compose. No GPU or local model needed — Claude is the sole assistant, reached over MCP.
Download docker-compose.quickstart.yml from this repo, then:
# Optional but recommended — set a secret key
export SECRET_KEY=your-random-secret-here
docker compose -f docker-compose.quickstart.yml up -d
Open http://localhost:5000. The first user to register becomes admin. To connect Claude, create an API key under Settings → API Keys and install the Claude Code plugin — see API Keys & MCP.
Development: To build from source, see Development.
Documentation
| Doc | Contents |
|---|---|
| Architecture | Stack, design decisions, data models, key services |
| Configuration | Environment variables, Docker Compose, production setup, security |
| Features | Detailed feature breakdown and keyboard shortcuts |
| Development | Dev workflow, CI/CD, migrations, release process |
| API Keys & MCP | API key management and Fable MCP install guide |
| SSO / OAuth | OIDC setup for Authentik, Keycloak, and other providers |
| API Reference | All REST API endpoints |
License
This project is privately maintained.