Claude's behavior is governed by two markdown files that Code Raven can currently only read. This turns each ## section of those files into an addressable segment in a library, so segments can be enabled, disabled, reordered, and shared across projects from inside the app. Disabling a segment removes its text from the file entirely rather than commenting it out, because CLAUDE.md is injected verbatim and a commented block still costs context.
The problem
Rules cannot be selectively retired. The "Document Output Format" segment — the one producing both a .md and an .html copy of every plan plus the Documents block — applies to every session in every project, always. Turning it off means cutting the text out and keeping it somewhere.
Shared rules drift silently. CodeRaven and VentureRaven carry the same four ## sections and have already diverged. VentureRaven's H1 still reads # Code Raven - Project Instructions, and its "Plan File Management" segment is missing the "Link both files at the end of the reply" bullet and the mobile-workbench paragraph. Same failure shape as the cr-*/vr-* command divergence already on record.
Approach
Segments, library, targets
A segment is one ## section, stored in a library independent of any file. A target is a file composed into — .global for ~/.claude/CLAUDE.md, or .project(id). An assignment binds a segment to a target with an order and an enabled flag. One segment assigned to many targets is what makes a rule shared.
Managed region, by default
Code Raven owns a fenced block and nothing else. Text above or below the markers is never read, moved, or touched. The marker format mirrors DossierClaudeCodeSync:19-20; the integrity stamp reuses the CommandInstaller format at CommandInstaller.swift:65-67.
<!-- BEGIN CodeRaven Instructions — managed by Code Raven; edits inside are overwritten -->
<!-- CodeRaven v1.102.x sha256:a1b2c3d4e5f6 -->
...composed segments...
<!-- END CodeRaven Instructions -->
Full-file mode, opt-in and destructive
Code Raven owns the whole file. Reaching this mode requires a typed confirmation and a snapshot, and the UI labels it CAUTION / IRREVERSIBLE while stating that hand edits made outside the app are lost on the next compose. It exists because a fully-generated file is the cleaner model once the library holds everything, and getting there should be possible deliberately.
Drift is detected, never overwritten blind
Every write stores the sha256 of the region it wrote. Before the next write the app re-hashes what is on disk. On mismatch it refuses to write and offers two explicit choices: adopt the on-disk text back into the library, or overwrite it. This is the discipline CommandInstaller already applies to slash commands via CommandFileStatus.userModified. Drift here is expected: Claude itself edits CLAUDE.md through /init, the update-memory skill, and this project's own mirror-config rule.
Snapshots are the only undo
~/.claude is not a git repository. Before every write the current file is copied to Application Support/Code Raven/Instructions/snapshots/{target}/{ISO8601}.md, keeping the last 20, restorable from the section UI.
Getting ahead of drift, in three layers
Detecting drift after the fact is the backstop. Three layers sit in front of it.
A pinned instruction segment. The first segment in every managed region is Code Raven Managed Instructions, written by the app and non-disableable. It states that the block is composed from a library, that edits inside it are overwritten, that content outside the markers is hand-owned, and that a change to a managed rule goes through the bridge.
A sanctioned path. An instruction that only says do not fails, because Claude still has to add a rule when asked. .claude/instruction-changes/*.json follows the established bridge convention, so do not edit the region has an answer to then how do I add a rule?
A hook that enforces it. HookInstaller already installs a PreToolUse hook, and approve-bridge-writes.py already singles out CLAUDE.md as a path it deliberately leaves on the normal permission path. A sibling hook matches Edit|Write|NotebookEdit and, when the edit would touch a managed region, exits 0 and emits a deny.
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "This block is composed by Code Raven. Write the change to .claude/instruction-changes/ instead."
},
"systemMessage": "Managed instruction region — change routed through the bridge."
}Exit 0 with JSON rather than exit 2, because exit 2 blocks deterministically and discards the reason, leaving Claude blocked without knowing the bridge exists.
One field name has to be checked against reality. A documentation pass reported the path arrives as tool_input.path. The hook already running in this repo reads tool_input.get("file_path", "") and works. The installed hook is ground truth; the new one reads both keys and prefers whichever is populated.
What this does not cover. The hook sees Claude Code tool calls and nothing else, so an external editor, a shell redirect, or the LAN sync still reach the file. /init regenerates CLAUDE.md wholesale and would be blocked rather than redirected. Instruction text is persuasion and lowers the rate without driving it to zero. Every one of those paths lands on the Phase 1 sha check, which stays the design's floor.
Shared segments and token substitution
A shared segment resolves {{PROJECT_NAME}}, {{PROJECT_PATH}}, and {{COMMAND_PREFIX}} at compose time from the target project. Unresolved tokens are left verbatim and flagged in the preview rather than silently blanked. The CodeRaven/VentureRaven diff shows project name and command prefix are the only real differences, so this is enough to make those four sections genuinely shared.
Decisions made for you
Disabling removes the text, rather than commenting it out
CLAUDE.md is injected verbatim including HTML comments, so a commented-out segment still reaches the model and still costs context. Reversibility comes from the library holding the body, not from the file retaining it.
Managed region is the default; full-file ownership is gated
Full generation is the cleaner data model but destroys any edit made outside the app, and ~/.claude has no version control to recover from. It ships as an explicitly-confirmed mode rather than the default.
The never-write-CLAUDE.md rule is narrowed, not discarded
DossierClaudeCodeSync.swift:5-10 and the developer-profile blueprint both record that the app writes to a dedicated file and never CLAUDE.md. That rule protected against automatic, generated content landing in a hand-maintained file. It is rewritten to scope it to automatic writes: the Instruction Manager writes only on explicit user action, only inside markers, only after a snapshot.
One section parser, not two
DossierHarvestService.sections(of:source:) already splits CLAUDE.md on ##. Rather than writing a second parser, the lossless version is extracted and the harvest is refactored to call it.
Composition is deterministic, with no AI call
Matching DossierProjectionBuilder.buildPackage. What the preview shows is exactly what lands on disk.
The work
Phase 1 — Model, store, parser, composer (no UI)
- Add
Models/InstructionSegment.swiftwithInstructionSegment,InstructionTarget,InstructionAssignment, andInstructionWriteMode, using defensive Codable withdecodeIfPresent ?? defaulton every property - Add
Services/InstructionSegmentParser.swift— lossless split of a markdown document into preamble plus##sections, preserving exact body text and line ranges - Refactor
DossierHarvestService.sections(of:source:)to delegate toInstructionSegmentParserso only one section parser exists - Extract the version+sha256 stamp helpers from
CommandInstallerintoServices/ManagedFileStamp.swiftand updateCommandInstallerto use it - Add
Services/InstructionLibraryStore.swiftas a@MainActorObservableObjectsingleton persisting toApplication Support/Code Raven/Instructions/library.json - Add
Services/InstructionFileComposer.swiftwithcompose(target:),write(target:),unmanage(target:), and region detection returning a.pristine/.externallyModified/.unmanagedstate - Add
Services/InstructionSnapshotStore.swiftwriting a pre-write copy per target and pruning to the last 20 - Verify Phase 1 with a standalone
swiftcharness covering round-trip parse, region replace, drift detection, and snapshot pruning
Phase 2 — Import and adoption
- Add an import scan that parses the global and every project
CLAUDE.mdinto candidate segments without writing anything - Add an adoption review sheet listing candidates per file with per-segment include toggles and a full preview of the resulting file
- On confirmed adoption, populate the library, write assignments, take a snapshot, and convert the file to managed-region form
- Detect and merge duplicate segments across files by normalized title and body so a shared rule adopts as one segment, not three
Phase 3 — Getting ahead of drift
- Confirm the
PreToolUsestdin field for the target path by testing a live hook, since the documentedtool_input.pathconflicts with the workingtool_input.file_pathinapprove-bridge-writes.py - Confirm whether
permissionDecisionReasonreaches the model on a deny, and fall back to relying on the pinned instruction for redirection if it does not - Add a
Code Raven Managed Instructionssegment that the composer pins first in every managed region and refuses to disable or reorder - Add
Services/InstructionChangeBridgeImporter.swiftreading.claude/instruction-changes/*.json, following the existing bridge contract including a version field,processed/archival,failed-{name}.jsonon rejection, and an ISO8601 date strategy on the decoder - Surface imported change proposals in the Instructions section as pending review rather than applying them
- Add
guard-managed-instructions.pymatchingEdit|Write|NotebookEdit, exiting 0 and emittingpermissionDecision: denywith bothpermissionDecisionReasonandsystemMessage, never exit 2 - Install the hook through
HookInstalleralongside the existing two, reusing its stamp-and-merge path sosettings.jsonis merged rather than rewritten - Add a settings toggle for the hook, defaulting to on, so it can be switched off without uninstalling the feature
- Verify the hook by attempting an edit inside the region, an edit outside it, and a bridge write, confirming deny, allow, and allow respectively
Phase 4 — Instructions section UI
- Add
case instructionstoAppMainView.MainContentViewand a sidebar button modeled on the Developer Profile block, with a drift dot when any target is externally modified - Add
Views/Instructions/InstructionManagerView.swift— target picker, segment list, detail pane - Add segment list with enable toggles, drag reordering, shared badge, and assigned-target count
- Add a segment detail editor with title, body, notes, tags, and shared flag
- Add a live composed preview with a diff against what is currently on disk
- Add the drift resolution UI offering adopt-from-disk or overwrite, with the diff shown before either
- Add snapshot history with restore
- Add the full-file mode switch behind a typed confirmation labeled CAUTION / IRREVERSIBLE, stating that hand edits outside the app will be lost
- Add an
Unmanageaction that dissolves the markers and leaves plain hand-owned markdown
Phase 5 — Shared segments across projects
- Add token substitution at compose time for
{{PROJECT_NAME}},{{PROJECT_PATH}}, and{{COMMAND_PREFIX}}, flagging unresolved tokens in the preview - Add a per-project command prefix field with a derived default, surfaced in the target picker
- Add multi-target assignment UI so one segment can be applied to several projects at once
- Add an apply-to-all action that previews every affected file before writing any of them
Phase 6 — Surfacing and documentation
- Extend
ClaudeSetupSnapshotwith per-target instruction management state and show it inClaudeSetupSheet - Narrow the never-write-CLAUDE.md rule in the
DossierClaudeCodeSyncheader comment and in.claude/context/blueprints/developer-profile.mdso it reads as scoped to automatic writes - Add
.claude/context/blueprints/instruction-manager.mdand register it inblueprints/index.md - Verify end to end by hand: adopt the CodeRaven and VentureRaven files, mark the four shared sections shared, apply to both, and confirm the VentureRaven H1 and missing bullets are repaired
Risks & edge cases
Claude edits the managed region mid-session
Phase 3 addresses this with a pinned instruction, a bridge path, and a PreToolUse deny, which together should make it uncommon. None of the three is airtight: the hook sees only Claude Code tool calls, the instruction is persuasion, and /init would be blocked rather than redirected. The sha check stays as the floor.
The hook is a new way for the environment to feel broken
A denied write during /init reads as a failure rather than a redirect. The settings toggle exists for that reason, defaulting to on but switchable without uninstalling the feature.
The deny reason may never reach the model
There are open reports of permissionDecisionReason being discarded on a PreToolUse deny. If that holds, Claude sees a bare refusal rather than a pointer to the bridge, and the pinned instruction is the only remaining signal. Bash cannot be matched by file path at all, so a shell redirect into CLAUDE.md bypasses the hook entirely.
~/.claude is LAN-synced
LANSyncService.swift:298 syncs the whole folder between the iMac and the MacBook Pro. Writes from both machines are a genuine conflict. This plan detects it by sha mismatch and refuses; it does not merge.
Substituted text is not literally the library text
For shared segments the preview is the only accurate statement of what lands on disk. Reading the library body alone will mislead.
Full-file mode is genuinely destructive
Snapshots are the entire recovery story, and they live in Application Support, so clearing app data loses them.
Adoption is a one-way door in practice
Nothing technically prevents reverting, but once CLAUDE.md carries markers every later hand edit has to reckon with them. The Unmanage action exists so the exit is real.
Out of scope
settings.json,commands/,agents/, and hooks —CommandInstallerandHookInstalleralready own those surfacesAGENTS.md— same parsing model, deferred untilCLAUDE.mdis proven- Detecting contradictions between two enabled segments
- Named presets or profiles that toggle a set of segments together
- Two-machine merge of conflicting LAN-synced writes
- Any AI call — composition stays deterministic
Final review
What this does well. It solves the stated problem without inventing a new mechanism. The fenced region, the sha stamp, the drift states, the JSON store, and the deterministic composer are all patterns already running in this codebase, so the new code is mostly assembly. Making "off" mean the text actually leaves the file is the decision that gives the feature real value rather than cosmetic value. The shared-segment work is justified by a drift bug that already exists.
Where it is thin. Phase 3 moves drift from detection to prevention, which is the right direction, but it adds a hook that can deny a write and therefore a new way for the environment to feel broken. Three-way merge is still absent, so a simultaneous LAN-synced edit from the other machine remains a refusal rather than a resolution.
Adjustments worth considering. Phase 4 could ship before Phase 3 as a headless "repair the fork drift" action, delivering the concrete VentureRaven fix sooner. Cutting full-file mode entirely would remove the only irreversible path; it is kept because it was explicitly requested, gated behind a typed confirmation.
High on the data model, the reuse map, and the write mechanics, since those follow existing code closely, and raised from 82 by Phase 3 turning the weakest joint from after-the-fact detection into prevention with a sanctioned path. The remaining deduction is for hook ergonomics, which cannot be judged before it runs, and for the LAN-sync conflict case, which is detected but not solved.