Rules consolidation: Scribe-first check, project-scoped rules, enter_project handshake #53

Merged
bvandeusen merged 3 commits from dev into main 2026-06-01 01:16:55 -04:00
Owner

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

  • 0058rulebooks.always_on boolean, default false. Seeds FabledSword family to true.
  • 0059rules.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

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)
bvandeusen added 3 commits 2026-06-01 01:16:42 -04:00
feat(rules): always_on rulebook flag + Scribe-first prompt
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 46s
CI & Build / Build & push image (push) Successful in 1m1s
658348f208
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>
feat(rules): project-scoped rules (S3)
CI & Build / Python lint (push) Successful in 2s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Python tests (push) Successful in 49s
CI & Build / Build & push image (push) Successful in 1m7s
43a860c3ac
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>
feat(rules): enter_project handshake (S4)
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Failing after 4s
CI & Build / Python tests (push) Successful in 49s
CI & Build / Build & push image (push) Has been skipped
c5469214e3
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>
bvandeusen merged commit b5870d4694 into main 2026-06-01 01:16:55 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/FabledScribe#53