Files
FabledScribe/plugin/skills/using-scribe/SKILL.md
T
bvandeusenandClaude Opus 5 8406871085
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m3s
CI & Build / Python tests (push) Successful in 1m27s
CI & Build / Build & push image (push) Successful in 41s
fix(plugin): the instruction surfaces still said every rule binds (#3849)
Step 3 shipped a line a session can receive — "Preference that may apply
here …" — into surfaces that told it, in the most authoritative voice it
has, that anything arriving in that shape is binding. That is the confusion
milestone 399 exists to prevent, arriving through the one channel a session
has least reason to doubt.

Silent in both directions, which is why it could not wait for step 6. A
session treating a preference as a rule refuses to proceed over something
the operator merely preferred; and it loses the whole reason preferences
exist, which is that they are brought up to date rather than obeyed.

Three surfaces, each to its own budget:

- SKILL.md gets the full account: kind decides force, the injected line names
  which in its opening words, and a preference is the one record a session
  keeps current itself (update_preference, with what taught the change).
- scribe_static_context.md gets six lines — enough to tell the kinds apart
  and to say a preference is yours to update.
- _INSTRUCTIONS gets four words. It is a MAP at 1978 of its 2000-char budget
  (#2562), and the detail belongs in the surfaces above and in the tool
  docstrings, which is what that budget exists to force.

Guarded so it cannot drift back: a surface that claims rules bind must name
the kind that does not. Pinned on the CLAIM rather than the word "bind",
because a bare substring also matches bind_repo, list_repo_bindings and
server.py's DNS-rebinding comment — a guard that would one day fail a skill
about repo binding is rule 167's named failure, raising a false alarm about
the very thing it protects. Falsified against all three surfaces losing the
mention.

Plugin version minted: the cache refreshes only on a version bump (#2209),
so a skill edit without one reaches no installed plugin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011cPyzNnegXHr5iRMzzy5KJ
2026-09-10 23:20:04 -04:00

288 lines
16 KiB
Markdown

---
name: using-scribe
description: Use at the START of every session, and before answering anything about the operator's work or starting any task — establishes the Scribe-first reflex. FIRST ACTION of a session: call list_always_on_rules() (and enter_project when a repo/project is in scope) to load the operator's binding rules. Then recall before acting, update over duplicate, plan in Scribe not in files.
---
# Using Scribe
Scribe is the operator's self-hosted system of record (notes, tasks, issues,
projects, milestones, systems) and rulebook, reachable
through the bundled `scribe` MCP server. Its value is mostly in what it
**already holds** — so make reading it a reflex, not something you wait to be
asked for.
## Do this first (every session)
**Pull the standing rules yourself — do not wait for them to be handed to you.**
At the start of a session, before substantive work, call
`list_always_on_rules()` to load the operator's always-on rules. If the working
repo maps to a Scribe project (you're in a known repo, or `list_repo_bindings`
shows a binding), call `enter_project(id)` instead/as-well — it returns the
project plus its applicable rules, open tasks, and recent notes in one shot.
Do this actively. A SessionStart hook *may* also inject a rule index, but treat
that as a bonus, not a precondition: it can be absent (e.g. when the instance is
unreachable, or the token didn't reach the hook), so the reliable path is this
explicit pull. Rules loaded this way are **binding** for the session.
## Scribe holds these functions — don't keep a second copy
This plugin makes Scribe the home for the operator's **rules, recall, and
planning** — the jobs Claude's native auto-memory would otherwise do. When the
plugin is present, route those jobs to Scribe and **do not also write them to
native memory**: codify rules with `create_rule` / `create_project_rule`,
capture durable knowledge as Scribe notes, and keep plans in Scribe milestones
(via `start_planning`) — not in `MEMORY.md` or `CLAUDE.md`. One copy, in Scribe; let any existing local
memory shrink as Scribe takes over. Don't maintain both stores in parallel.
Two constraints on *how* that's achieved:
- **Steer behavior; never flip a native switch.** The plugin must work with
native auto-memory at its default (ON). Never tell the operator to set
`autoMemoryEnabled:false` or otherwise disable a built-in function to make
Scribe "win" — a setting the operator may not know was changed (and wouldn't
know to restore) is exactly the hidden breakage to avoid. You replace memory's
functions by *doing the work in Scribe*, not by turning memory off.
- **A Scribe-shaped hole is acceptable.** If the plugin is later removed, the
operator recovers context over time — that's fine. You do **not** need to keep
native memory as a self-sufficient fallback. The only thing to avoid is
breakage caused by a settings change the operator didn't make knowingly.
## The reflex
1. **Recall before acting.** Before answering a question about the operator's
work, or starting a task, `search` Scribe (and `list_tasks` / `list_notes`)
for related prior work — an existing task, decision, or note — instead of
re-deriving it or opening a duplicate. When a project is in scope, pass its
`project_id` so results stay scoped.
2. **Standing rules are binding — and the ones you were handed are not all of
them.** Load the resident set via `list_always_on_rules()` at session start
(see "Do this first"). Pull a record's full statement with `get_rule(id)`
when it's about to bite. When a project is in scope, `enter_project(id)`
also returns its applicable rules.
**`kind` says how much force a record carries, and it is never something to
infer.** A **rule** must be followed: ignoring it breaks something or
crosses a boundary. A **preference** records how the operator wants work
done, and ignoring it costs consistency rather than correctness. Both are
worth following and both arrive the same way; only one is a mistake to
miss. An injected line names which in its opening words — *"Standing rule
that may apply…"* against *"Preference that may apply…"* — and every
payload carries `kind` outright.
**A preference is the one record you keep current yourself.** When the
operator corrects you, or the preference on file no longer matches how they
actually want something done, `update_preference` — that is expected, not a
liberty, and it wants the task or note that taught the change. Say in the
same turn that you did it, so they can disagree while it is in front of
them. A rule waits for the operator instead: `create_rule` proposes and
asks. If what you learned is that something MUST be done a certain way,
that is a rule to propose, not a preference to harden in place.
Rules come in two tiers. **Always-on** rules are delivered — they arrive
whether or not you ask. **Conditional** rules are RETRIEVED, and one binds
just as hard for never having been handed to you. So before a consequential
act, `search(content_type="rule")` on what you are about to do. An empty
loaded set is not evidence that no rule applies; it is only evidence that
none was pushed, and those are different claims.
The tier split exists because delivery does not scale: every resident rule
costs tokens in every session forever, so a rulebook that grows past a few
dozen either stops growing or stops fitting. Retrieval is what lets the
rulebook keep growing — but retrieval only fires if something asks.
**Ask hardest where you feel most certain.** Rules about which TOOL to reach
for — use the forge's MCP client rather than curling its API, don't stand up
a local stack, don't run the suite CI owns — govern moves that feel like
mechanics rather than decisions. A reflex raises no doubt, so it generates
no query, so the rule that would have stopped it is never retrieved. That is
the failure this instruction exists to prevent, and confidence is its only
warning sign.
3. **Update over duplicate.** When recording, prefer updating an existing
note/rule/task over creating a new one. Search first; revise what's there.
4. **When you plan, plan in Scribe.** Work with an *arc* — several steps toward
one goal — gets a plan, and a plan is a milestone: `start_planning(project_id,
title)` creates one whose `body` holds the design, each step is its own task
under it (`create_task(milestone_id=...)`), progress goes in work-logs
(`add_task_log`). Work without an arc (a fix, a one-file change, a question)
is just a task — don't wrap it in a milestone. Either way, do not write
plans/specs to local `.md` files. See the **writing-plans** skill.
5. **Keep state honest.** Set a task `in_progress` when you start it, `done` the
moment it's complete; log progress as you go.
6. **Fixes are issues, not work-logs.** When you fix a problem — even one solved
in passing — record it as its own issue (`create_task(kind="issue")`) with
symptom → root cause → fix, optionally linked to the task it arose from
(`arose_from_id`) and the subsystem it touches (`system_ids`). Don't bury a
fix as a work-log line on whatever task happened to be open.
7. **Tag records to Systems.** `enter_project` lists the project's Systems —
its named subsystems/areas. When you create or meaningfully update a record,
ask which areas it is *about* and pass `system_ids`. The test: would someone
investigating that subsystem want this record in the pile
`list_system_records` returns? If the area has no System yet, create one
(`create_system`: name + a one-paragraph charter) — an area that plainly
exists deserves naming the moment two records would share it; don't wait to
be asked. Cross-cutting records — audits, sweeps, reviews — take *several*
tags and are the prime discovery moment: a pass that walks the subsystems
has just enumerated the vocabulary, so mint the Systems it names as it
names them. Create liberally — `create_system` is duplicate-gated, and that
gate (plus reviewing the existing list) is the guardrail against sprawl,
not restraint. Only a record genuinely about no particular area goes
untagged.
8. **Name the record, never just its number.** Whenever you refer to a Scribe
record — in a message to the operator, a commit message, a task body, a
work-log — write the id *and* its title: `#3244 "the staleness signal"`,
`milestone 323 "rule versioning"`. Not `#3244`.
You have the record open; the operator does not. A bare id reads as
complete to you and as homework to them — they have to look it up to know
what their own conversation is about, or guess. Scribe's own duplicate gate
already writes `id 412: "debounce helper"` for exactly this reason; match
it everywhere else.
The first mention in a message carries the title; later mentions of the
same record can use the bare id. If you don't know the title, look it up
before citing the number — an id you can't name is one you haven't checked.
This matters most in the places read later by someone with even less
context than the operator has now: commit messages, task bodies, and any
record that cites another.
9. **State updates in place; chronicles don't.** A dev-log records what
*happened* — write it once, never rewrite it. A durable finding (how a
subsystem works, a measured number) lives in that System's **reference
note** ("«System» — reference"), which you UPDATE as facts change — safe,
because every meaningful edit is snapshotted and the version history is the
changelog. The dev-log then `[[links]]` the reference note instead of
restating state. When a new record outright *corrects* an older one (a
re-measurement, a reversed decision), pass the old id in `supersedes` so the
stale record is demoted and labelled rather than left competing.
10. **A few notes assert a FACT, and those can carry their own check.**
Supersession only fires once somebody has read a note and disagreed — which
is the case where it was already believed. A note asserting something about
*someone else's* software — what a service does on a duplicate upload, how a
forge numbers its CI runs, what an updater compares — can instead carry
`verify_with` (how to check it) and `expires_when` (the STATE that ends it:
"when the forge numbers runs per workflow", never "in six months").
`notes_due_for_verification` lists them least-recently-confirmed first, with
never-checked at the top; `mark_note_verified` records what you found, and
`still_true=False` deliberately writes nothing — a note whose check failed
is wrong rather than in a state worth recording, so it keeps its place.
**The test is one question: could this note become false without anyone
editing it?** If no, leave both fields empty. That is the normal case, and
an empty `verify_with` is the positive marker for "this is a decision, there
is nothing to go and check" — not an unfinished record. The sweep is only
worth reading while almost nothing is on it, so a check added out of
tidiness costs the whole surface, not just that note.
**The sharper form of the same test: is the thing this note describes yours
to change?** If yes it is a decision — editing your own software is how it
changes, and you will know you did it. Measured against a real corpus, every
note that earned a check was about somebody ELSE's software: a signing
service, a forge, a hub, an SDK, a model, a dependency set.
**Three that look like candidates and are not:**
- **Resume pointers and "current state" notes.** They go stale fastest of
anything, which is exactly why they tempt — but the cure is to update or
delete them, not to schedule a check. A sweep full of pointers is a sweep
nobody reads.
- **Measurements of your own system.** They go false because you changed
something, and you knew. A measurement earns a check only when what it
measures is outside your control.
- **A decision that RESTS on somebody else's behaviour.** The decision is
still a decision. Put the check on the note asserting the fact, and link
the decision to it.
Not for tasks — a task's decay is its status, and a done issue records what
happened rather than asserting something that can go false. Not for snippets
either: `verify_snippet` compares the recorded location and code against the
repo, which is richer and already wired to drift detection.
## Stay inside the active project's scope
Once a project is in scope — you called `enter_project`, or the working repo is
bound — confine the session to it:
- **Pass that `project_id` to every read** (`search`, `list_tasks`,
`list_notes`). An unscoped read bleeds every other project's work into your
context.
- **Only reference or offer work on the in-scope project.** Don't surface,
suggest, or start work on other projects unless the operator explicitly widens
scope.
- If something clearly belongs to a *different* project, say so and **ask before
switching** — never silently operate cross-project.
## Starting a project: decide what it inherits
A project's inheritance is a **decision, not a default**. Before
`create_project`, ask the operator the four inception questions and pass the
answers — never create a project bare by default:
- which **always-on rulebooks** it should NOT inherit (`list_rulebooks` shows
which are always_on; default: inherit them all) →
`exclude_always_on_rulebooks=[...]`
- which other rulebooks to **subscribe**`subscribe_rulebooks=[...]`
- which **design system** its UI is built from (`list_design_systems`; or
none) → `design_system_id=<id | -1>`
- whether to **seed the standard starter Systems** so records can be tagged
from day one → `seed_systems=true|false`
If `enter_project` returns an `inception` key, the project was never decided
(it inherits its defaults silently): raise that ask once, with the defaults it
carries, then `decide_project_inception(project_id, …)`. Existing projects
were stamped "legacy" (inherit-all) and do not ask; any project can be
re-decided. The rules/design-system/Systems tools still work one at a time —
inception is the moment they are decided together, and the record of why.
## Where a new rule goes
When codifying a rule, pick its home by **who it should bind** — and keep
shared homes general:
- **Always-on rulebook** (`create_rule` in an `always_on` rulebook) — universal
norms that bind *every* project. Cross-project standards only.
- **Subscribed rulebook** (`create_rule` + `subscribe_project_to_rulebook`) — a
reusable, *themed* module of general rules that binds only projects that opt
in (e.g. a review checklist → every service). Themed, but project-agnostic.
- **Project rule** (`create_project_rule`) — anything specific to one project
(its files, paths, quirks).
Both rulebook tiers are shared, so their rules stay general; they differ in
**reach** (all vs opt-in), not generality. Names one project's specifics →
project rule; a standard a category shares → subscribed rulebook; a universal
norm → always-on rulebook. Never put project-specific detail in a shared
rulebook — it leaks to every other project that gets it.
**First ask whether it's a rule at all.** A rule is prose you have to remember
and apply; Scribe's other entities are structure a tool can resolve and check.
Visual standards belong in a **design system**, not a rulebook — a token can be
inherited, resolved per mode, rendered to a stylesheet and diffed against code,
and none of that survives being written as a rule. A repeatable procedure is a
**process**; reusable code is a **snippet**. Reach for a rule when the thing
really is a standing instruction about how to work.
## Building UI: the project's design system binds
`enter_project` returns a `design_system` when the project has one, with the
guidance **chain-merged** — the house style it inherits plus its own departures
from it. Treat it the way you treat a rule.
Before writing a colour, size, radius, weight or duration by hand, reach for a
token: `resolve_design_system(id)` for the values, or
`get_design_system_stylesheet(id)` for the rendered sheet. A literal is a value
stated outside the system, so it can never follow a palette change — and
nothing will tell you it drifted.
## Other Scribe process-skills
This plugin also ships focused process-skills — writing-plans, systematic
debugging, verification, and brainstorming. Reach for the matching one when its
situation arises, the same way you reach for this skill.