Implements plan-task #508. Three commits, four slices, all aimed at making Scribe the source of truth for engineering rules while preserving the existing CLAUDE.md / auto-memory stores.
What changed and why
Problem. Rules in Scribe were "pull-only" — Claude only saw them when it explicitly called get_project or list_rules. Memory files + CLAUDE.md, by contrast, are session-start auto-loaded, so they always won the "which rules did I read" contest. The MCP _INSTRUCTIONS also never directed Claude to land NEW rules in Scribe, so they accumulated in ~/.claude/.../memory/ and CLAUDE.md by default. Operator-confirmed direction: coexistence now, Scribe-first check, migrate later. No destructive consolidation.
Slices
S1 + S2 — always_on rulebook flag (commit 658348f)
Migration 0058: rulebooks.always_on boolean. FabledSword family seeded always_on=true at migrate-time.
New MCP tool list_always_on_rules() returns the union of all rules from always-on rulebooks for the user — a single round-trip that doesn't require an active-project context.
_INSTRUCTIONS updated: "At session start, call list_always_on_rules; treat the result as binding." Also adds creation-routing: rules belong in Scribe via create_rule, not in CLAUDE.md or memory 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 constraint enforces exactly-one of (topic_id, project_id), index on project_id.
New MCP tool create_project_rule(project_id, statement, title?, why?, how_to_apply?). No rulebook ceremony — bypasses the Rulebook → Topic → Rule chain when a rule only applies to one project.
get_applicable_rules adds a project_rules field alongside the existing rules; get_project, get_task (for plan-tasks), and start_planning all surface it.
Trash cascade: deleting a project soft-deletes its project-scoped rules along with the rest of the descendants.
REST: POST /api/projects/<id>/rules powers the frontend fast path.
Frontend: ProjectRulesTab.vue gains a "Project rules" section with an inline create form (title optional, statement required, why/how_to_apply optional), per-rule expand-on-click, and a Delete control.
get_rule/update_rule/delete_rule now route through a shared _fetch_owned_rule helper that handles both ownership paths (rulebook-via-topic OR project-via-project_id).
S4 — enter_project handshake (commit c546921)
New MCP tool enter_project(project_id) composes get_project + get_applicable_rules + get_project_milestone_summary + open-tasks + recent-notes into one round-trip. Intended to be called once at session start (or when the active project changes), in lieu of stitching those calls together separately.
_INSTRUCTIONS directs Claude to call it before project-scoped work begins.
No schema change; pure composition over existing services.
Migrations
0058 — rulebooks.always_on boolean, default false. Seeds FabledSword family to true.
0059 — rules.project_id added (FK CASCADE), rules.topic_id made nullable, CHECK ck_rule_topic_xor_project. Both run automatically on first boot of the new image. Down-migrations exist but the 0059 down requires you to migrate or delete project-scoped rules first (it re-tightens topic_id).
Verification
CI green on commit c546921 (typecheck / lint / Python tests / image build).
New tests cover: list_always_on_rules, update_rulebook(always_on=...) pass-through, create_project_rule field handling + title derivation, enter_project composed shape + 404 handling, get_applicable_rules returning project_rules, project-delete trash cascade now hitting 5 tables (was 4).
Existing tests updated where mocks needed an additional execute result (the project_rules query in get_applicable_rules) or where call counts changed (the project cascade in trash service).
What this does NOT do
No content migration. Rule-shaped entries in ~/.claude/.../memory/feedback_*.md and CLAUDE.md stay where they are; the prompt change reroutes NEW rules to Scribe, but existing entries are not migrated. Cleanup pass is a separate exercise.
No write-scope enforcement on the bearer token. Mutating tools still ignore the read/write scope on the api_keys row. That's M1 share-correctness work, tracked separately.
No share-aware read filtering on list_trash / restore / purge. Same M1 carve-out as before.
Deploy notes
Two migrations (0058, 0059) run automatically on container start.
After deploy, the next MCP reconnect picks up the new _INSTRUCTIONS; Claude should call list_always_on_rules and enter_project on session start. Eyeball the tool-call transcript on first use to confirm.
Test plan recommended: try a project, confirm enter_project returns the composed payload; create a project rule via the new tab; flip a rulebook's always_on from the UI; verify list_always_on_rules includes/excludes accordingly. After that's solid, cut the release tag.
Implements plan-task #508. Three commits, four slices, all aimed at making Scribe the source of truth for engineering rules while preserving the existing CLAUDE.md / auto-memory stores.
## What changed and why
**Problem.** Rules in Scribe were "pull-only" — Claude only saw them when it explicitly called `get_project` or `list_rules`. Memory files + CLAUDE.md, by contrast, are session-start auto-loaded, so they always won the "which rules did I read" contest. The MCP `_INSTRUCTIONS` also never directed Claude to land NEW rules in Scribe, so they accumulated in `~/.claude/.../memory/` and `CLAUDE.md` by default. Operator-confirmed direction: coexistence now, Scribe-first check, migrate later. No destructive consolidation.
## Slices
**S1 + S2 — `always_on` rulebook flag (commit `658348f`)**
- Migration 0058: `rulebooks.always_on` boolean. FabledSword family seeded `always_on=true` at migrate-time.
- New MCP tool `list_always_on_rules()` returns the union of all rules from always-on rulebooks for the user — a single round-trip that doesn't require an active-project context.
- `_INSTRUCTIONS` updated: "At session start, call `list_always_on_rules`; treat the result as binding." Also adds creation-routing: rules belong in Scribe via `create_rule`, not in CLAUDE.md or memory 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 constraint enforces exactly-one of (`topic_id`, `project_id`), index on `project_id`.
- New MCP tool `create_project_rule(project_id, statement, title?, why?, how_to_apply?)`. No rulebook ceremony — bypasses the Rulebook → Topic → Rule chain when a rule only applies to one project.
- `get_applicable_rules` adds a `project_rules` field alongside the existing `rules`; `get_project`, `get_task` (for plan-tasks), and `start_planning` all surface it.
- Trash cascade: deleting a project soft-deletes its project-scoped rules along with the rest of the descendants.
- REST: `POST /api/projects/<id>/rules` powers the frontend fast path.
- Frontend: `ProjectRulesTab.vue` gains a "Project rules" section with an inline create form (title optional, statement required, why/how_to_apply optional), per-rule expand-on-click, and a Delete control.
- `get_rule`/`update_rule`/`delete_rule` now route through a shared `_fetch_owned_rule` helper that handles both ownership paths (rulebook-via-topic OR project-via-project_id).
**S4 — `enter_project` handshake (commit `c546921`)**
- New MCP tool `enter_project(project_id)` composes `get_project` + `get_applicable_rules` + `get_project_milestone_summary` + open-tasks + recent-notes into one round-trip. Intended to be called once at session start (or when the active project changes), in lieu of stitching those calls together separately.
- `_INSTRUCTIONS` directs Claude to call it before project-scoped work begins.
- No schema change; pure composition over existing services.
## Migrations
- **0058** — `rulebooks.always_on` boolean, default false. Seeds FabledSword family to true.
- **0059** — `rules.project_id` added (FK CASCADE), `rules.topic_id` made nullable, CHECK `ck_rule_topic_xor_project`. Both run automatically on first boot of the new image. Down-migrations exist but the 0059 down requires you to migrate or delete project-scoped rules first (it re-tightens `topic_id`).
## Verification
- CI green on commit `c546921` (typecheck / lint / Python tests / image build).
- New tests cover: `list_always_on_rules`, `update_rulebook(always_on=...)` pass-through, `create_project_rule` field handling + title derivation, `enter_project` composed shape + 404 handling, `get_applicable_rules` returning `project_rules`, project-delete trash cascade now hitting 5 tables (was 4).
- Existing tests updated where mocks needed an additional execute result (the `project_rules` query in `get_applicable_rules`) or where call counts changed (the project cascade in trash service).
## What this does NOT do
- **No content migration.** Rule-shaped entries in `~/.claude/.../memory/feedback_*.md` and `CLAUDE.md` stay where they are; the prompt change reroutes NEW rules to Scribe, but existing entries are not migrated. Cleanup pass is a separate exercise.
- **No write-scope enforcement on the bearer token.** Mutating tools still ignore the read/write scope on the api_keys row. That's M1 share-correctness work, tracked separately.
- **No share-aware read filtering on `list_trash` / `restore` / `purge`.** Same M1 carve-out as before.
## Deploy notes
- Two migrations (0058, 0059) run automatically on container start.
- After deploy, the next MCP reconnect picks up the new `_INSTRUCTIONS`; Claude should call `list_always_on_rules` and `enter_project` on session start. Eyeball the tool-call transcript on first use to confirm.
- Test plan recommended: try a project, confirm `enter_project` returns the composed payload; create a project rule via the new tab; flip a rulebook's `always_on` from the UI; verify `list_always_on_rules` includes/excludes accordingly. After that's solid, cut the release tag.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Adds rulebooks.always_on (migration 0058) and a new list_always_on_rules
MCP tool so a session-start eager pull can fetch standing rules without
needing an active-project notion. Updates _INSTRUCTIONS so Claude calls
the new tool at session start and codifies engineering rules in Scribe
rather than CLAUDE.md / auto-memory.
Seeds FabledSword family rulebook to always_on=true on migrate, matching
its design role as the cross-project standards rulebook.
Frontend: badge in RulebookListPane for always-on rulebooks; toggle in
RulebookDetailPane header bound to a new toggleAlwaysOn store action.
This is S1+S2 of the rules-consolidation plan (Scribe task #508). S3
(project-scoped rules) and S4 (enter_project handshake) follow.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Rules can now belong to either a rulebook topic OR a single project,
enforced by a CHECK constraint (exactly-one of topic_id/project_id).
Adds the create_project_rule MCP tool + REST endpoint, surfaces
project-scoped rules in get_project/get_task/start_planning under a
new project_rules field, and adds a project Rules tab section with an
inline create form so the operator can author project rules from the
UI without rulebook ceremony.
- migration 0059: rules.project_id (FK projects ON DELETE CASCADE),
topic_id now nullable, CHECK ck_rule_topic_xor_project, index on
project_id
- model: Rule gains project_id; to_dict exposes it
- service: create_project_rule with project-ownership guard; list_rules
with project_id filter UNIONs subscription-derived + project-scoped;
get_applicable_rules adds a project_rules field; get_rule / update_rule
/ delete_rule fetch via a shared _fetch_owned_rule that handles both
rulebook and project ownership paths
- trash: project delete cascades to project-scoped rules
- MCP: create_project_rule tool registered; _INSTRUCTIONS mentions both
create_rule and create_project_rule paths
- REST: POST /api/projects/<id>/rules (statement required, title derived
if omitted)
- frontend: Rule type gains nullable topic_id + project_id; createProjectRule
client; ProjectRulesTab.vue gains a "Project rules" section with inline
create form and per-rule expand/delete
- tests: register count → 18; create_project_rule unit tests (required
fields, title derivation, explicit-title pass-through); applicable_rules
shape tests now include project_rules; trash cascade test updated to
expect 5 executions
S1+S2 (always_on flag + Scribe-first prompt) shipped in 658348f.
S4 (enter_project handshake) follows.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New enter_project(project_id) MCP tool composes get_project +
get_applicable_rules + get_project_milestone_summary + recent
open-tasks + recent notes into one round-trip, intended to be called
at session start (or whenever the active project changes) so Claude
has the full project context loaded before it starts mutating.
_INSTRUCTIONS now points Claude at enter_project for project-scoped
work, alongside the existing list_always_on_rules instruction. No
schema change; pure composition over existing services.
Closes the four-slice rules-consolidation plan (Scribe task #508):
S1+S2 (always_on flag + Scribe-first prompt, 658348f), S3 (project-
scoped rules, 43a860c), and now S4.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements plan-task #508. Three commits, four slices, all aimed at making Scribe the source of truth for engineering rules while preserving the existing CLAUDE.md / auto-memory stores.
What changed and why
Problem. Rules in Scribe were "pull-only" — Claude only saw them when it explicitly called
get_projectorlist_rules. Memory files + CLAUDE.md, by contrast, are session-start auto-loaded, so they always won the "which rules did I read" contest. The MCP_INSTRUCTIONSalso never directed Claude to land NEW rules in Scribe, so they accumulated in~/.claude/.../memory/andCLAUDE.mdby default. Operator-confirmed direction: coexistence now, Scribe-first check, migrate later. No destructive consolidation.Slices
S1 + S2 —
always_onrulebook flag (commit658348f)rulebooks.always_onboolean. FabledSword family seededalways_on=trueat migrate-time.list_always_on_rules()returns the union of all rules from always-on rulebooks for the user — a single round-trip that doesn't require an active-project context._INSTRUCTIONSupdated: "At session start, calllist_always_on_rules; treat the result as binding." Also adds creation-routing: rules belong in Scribe viacreate_rule, not in CLAUDE.md or memory feedback files; those stores are now scoped to user-facts and codebase onboarding respectively.RulebookListPane; toggle inRulebookDetailPaneheader.S3 — Project-scoped rules (commit
43a860c)rules.project_id(nullable FK, CASCADE),rules.topic_idmade nullable, CHECK constraint enforces exactly-one of (topic_id,project_id), index onproject_id.create_project_rule(project_id, statement, title?, why?, how_to_apply?). No rulebook ceremony — bypasses the Rulebook → Topic → Rule chain when a rule only applies to one project.get_applicable_rulesadds aproject_rulesfield alongside the existingrules;get_project,get_task(for plan-tasks), andstart_planningall surface it.POST /api/projects/<id>/rulespowers the frontend fast path.ProjectRulesTab.vuegains a "Project rules" section with an inline create form (title optional, statement required, why/how_to_apply optional), per-rule expand-on-click, and a Delete control.get_rule/update_rule/delete_rulenow route through a shared_fetch_owned_rulehelper that handles both ownership paths (rulebook-via-topic OR project-via-project_id).S4 —
enter_projecthandshake (commitc546921)enter_project(project_id)composesget_project+get_applicable_rules+get_project_milestone_summary+ open-tasks + recent-notes into one round-trip. Intended to be called once at session start (or when the active project changes), in lieu of stitching those calls together separately._INSTRUCTIONSdirects Claude to call it before project-scoped work begins.Migrations
rulebooks.always_onboolean, default false. Seeds FabledSword family to true.rules.project_idadded (FK CASCADE),rules.topic_idmade nullable, CHECKck_rule_topic_xor_project. Both run automatically on first boot of the new image. Down-migrations exist but the 0059 down requires you to migrate or delete project-scoped rules first (it re-tightenstopic_id).Verification
c546921(typecheck / lint / Python tests / image build).list_always_on_rules,update_rulebook(always_on=...)pass-through,create_project_rulefield handling + title derivation,enter_projectcomposed shape + 404 handling,get_applicable_rulesreturningproject_rules, project-delete trash cascade now hitting 5 tables (was 4).project_rulesquery inget_applicable_rules) or where call counts changed (the project cascade in trash service).What this does NOT do
~/.claude/.../memory/feedback_*.mdandCLAUDE.mdstay where they are; the prompt change reroutes NEW rules to Scribe, but existing entries are not migrated. Cleanup pass is a separate exercise.list_trash/restore/purge. Same M1 carve-out as before.Deploy notes
_INSTRUCTIONS; Claude should calllist_always_on_rulesandenter_projecton session start. Eyeball the tool-call transcript on first use to confirm.enter_projectreturns the composed payload; create a project rule via the new tab; flip a rulebook'salways_onfrom the UI; verifylist_always_on_rulesincludes/excludes accordingly. After that's solid, cut the release tag.🤖 Generated with Claude Code