CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m6s
CI & Build / Python tests (push) Successful in 1m50s
CI & Build / Build & push image (push) Successful in 43s
A System gains path_patterns: globs relative to the repo root (* within one directory, ** across any depth, a plain directory covering everything under it). One service validates them for every door, so the web UI and MCP refuse the same bad pattern with the same message. systems_for_paths resolves paths to every active System that covers them, which step 4 (#4757) uses to deliver an area's rulings when its files are touched. - schema: systems.path_patterns JSONB NOT NULL default [] (migration 0113) - service: normalize_path_patterns, path_matches, systems_for_paths - routes + MCP create_system/update_system accept it; [] clears - web UI: a Files field in the create and edit forms, patterns on the card - backup carries it through export and restore - using-scribe reflex 7: tagging work keeps a System's files current Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
337 lines
19 KiB
Markdown
337 lines
19 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. You hold none of the operator's rules: they arrive by retrieval when your work matches one, and what_might_apply is how you ask before a consequential act — it returns the wide net of candidates with no bar. Call enter_project when a repo/project is in scope. 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)
|
|
|
|
If the working directory maps to a Scribe project, call `enter_project(id)` —
|
|
it returns the project's goal, the milestones and open tasks worked on most
|
|
recently, its Systems and the titles of its own rules in one shot.
|
|
|
|
Binding a directory that is not a git repo, or starting a new project, has
|
|
its own steps: read [projects.md](projects.md).
|
|
|
|
Then **ask before you act**: before anything hard to reverse or outward-facing,
|
|
search the rules for what you are about to do. Reflex 2 below is why asking,
|
|
not loading, is how the operator's rules reach you.
|
|
|
|
## Scribe holds these functions — keep one copy
|
|
|
|
Scribe is the home for the operator's **rules, recall, and planning** — the
|
|
jobs your client's own local memory files would otherwise do. Route those jobs
|
|
to Scribe instead of also writing them locally: codify rules with
|
|
`create_rule` / `create_project_rule`, capture durable knowledge as Scribe
|
|
notes, and keep plans in Scribe milestones (via `start_planning`). One copy, in
|
|
Scribe; let any existing local memory shrink as Scribe takes over.
|
|
|
|
Two constraints on *how* that's achieved:
|
|
|
|
- **Steer behaviour; leave the client's own settings as they are.** Scribe works
|
|
alongside a client's built-in memory at its defaults. You replace memory's
|
|
functions by *doing the work in Scribe*, so there is no reason to ask the
|
|
operator to switch a built-in feature off — a setting they didn't knowingly
|
|
change is breakage they won't know to restore.
|
|
- **A Scribe-shaped hole is acceptable.** If Scribe is later removed, the
|
|
operator recovers context over time — that's fine. Local memory doesn't need
|
|
to be kept as a self-sufficient fallback.
|
|
|
|
## 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.
|
|
|
|
**Code tells you what a thing does, not what was wanted.** A limit, a
|
|
default or a fallback found by reading the work is a past session's choice
|
|
until a record says the operator made it. Before presenting one as a
|
|
safeguard or a known cost, read the `Rulings` on the System it belongs to;
|
|
when none speaks to it, call it unconfirmed rather than settled.
|
|
|
|
2. **Rules are binding, and they reach you by retrieval.** No call loads the
|
|
operator's rules and no standing set is handed to a session. A rule arrives
|
|
when what you are about to do resembles what it is about — a command, the
|
|
code you are writing, the operator's message — and on most turns none will.
|
|
That is the surface working: it is what lets a rulebook keep growing, since
|
|
a rule costs nothing in a session it has nothing to do with. A rule binds
|
|
just as hard for never having been handed to you.
|
|
|
|
So "no rule arrived" means "nothing matched", never "no rule exists" — an
|
|
empty session is not evidence of an empty rulebook. Retrieval fires when
|
|
something asks, so ask — and reach for the tool that fits the moment:
|
|
|
|
- **Before a consequential act**, and before handing work back because you
|
|
are unsure you may finish it: `what_might_apply("what you are about to
|
|
do")`. It returns up to fifty ranked candidates with NO bar. The arms that
|
|
push rules at you spend a budget of three and say nothing about what sat
|
|
just underneath — right for something firing before every command, wrong
|
|
for the one moment you actually want to be sure. Expect the tail to be
|
|
noise; you are reading for the one record you would have missed.
|
|
- **When you already suspect a particular rule**: reach for
|
|
`search(content_type="rule")` to read full statements, or `get_rule(id)`
|
|
when one is about to bite.
|
|
|
|
Either way, when a project is in scope pass its `project_id`: the answer is
|
|
then the global rules plus that project's own, never another project's. `enter_project(id)` lists the project's own
|
|
rules by title.
|
|
|
|
**`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. A
|
|
**lesson** is what is worth knowing — a better way to think about a
|
|
problem, a solution that transfers — and ignoring it costs time. All three
|
|
are worth reading and all three arrive beside your work; only the first is
|
|
a mistake to miss. An injected line names which in its opening words —
|
|
*"Standing rule that may apply…"* against *"Preference that may apply…"* —
|
|
a lesson arrives in the records menu marked `lesson`, under a header that
|
|
says outright that it binds nothing, and every payload carries `kind`.
|
|
|
|
**One question sorts them, and it is worth asking out loud: what happens if
|
|
someone doesn't do this?** *Something breaks, or a boundary is crossed* — a
|
|
rule, and the operator's to agree to. *It gets done a way they didn't want*
|
|
— a preference, recorded without a loop. *They lose time rediscovering it*
|
|
— a lesson, binding nobody. Ask it before proposing rather than settling it
|
|
silently, because it sometimes answers that what you are holding was never
|
|
a rule — and routing an observation is not losing it.
|
|
|
|
**A preference is yours to keep current.** 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
|
|
updating one mid-work is the normal case rather than an interruption of it.
|
|
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.
|
|
|
|
**A lesson grows each time it proves itself.** When one arrives and names the
|
|
situation you are actually in, `update_lesson` it with what you now know;
|
|
[writing-records.md](writing-records.md) says what is worth adding.
|
|
|
|
**Preferences shape how work is done, never what gets recorded.** They
|
|
govern your conduct — how you report, how carefully you pace, which form
|
|
you reach for. What ends up in Scribe is decided by what the record is:
|
|
a preference never makes a task into a note, downgrades a rule, or keeps
|
|
something out of the corpus that belongs there.
|
|
|
|
**A retrieved rule outranks a default habit.** Before a hard-to-reverse or
|
|
outward-facing act — changing shared state, publishing, deleting, sending
|
|
something outside the session — the operator's rules decide what to do, not
|
|
the generic conventions your client or your training suggest. When a rule
|
|
and a habit disagree, the rule wins; when no rule speaks to it, ask rather
|
|
than assume.
|
|
|
|
**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.
|
|
|
|
**Find the plan before you make one.** A project's existing milestones are
|
|
often its roadmap, and a milestone with no steps yet is still open work.
|
|
`search(content_type="milestone", project_id=...)` finds one by purpose. When
|
|
an active milestone covers the work, add steps to it rather than opening
|
|
another, and give any task you record for that work its `milestone_id`.
|
|
|
|
5. **Keep state honest.** Set a task `in_progress` when you start it, `done` the
|
|
moment it's complete; log progress as you go. Always log when you
|
|
**complete** a task and when you **hit or discover a problem**, so a change
|
|
of direction is on the record and not only the successes.
|
|
|
|
**Hand off before this session's context stops existing.** A compaction, a
|
|
`/clear`, the operator wrapping up for the day — each is the last moment the
|
|
reasoning behind the work lives anywhere but here. Log on the task you were
|
|
holding where it stands, what you tried and ruled out, and the next move:
|
|
write down what the next session needs, because it arrives with Scribe's
|
|
record and nothing else. Moving a task to `in_progress` or logging on it also
|
|
claims it for this session — that claim is what hands the work back to you
|
|
after a compaction, and it ends on its own when you stop, so there is nothing
|
|
to release by hand.
|
|
|
|
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.
|
|
|
|
Every read and write of a project record shows its `systems`. An untagged
|
|
one carries a `systems_hint` question instead — on creates, updates and
|
|
work-logs alike. Treat it as the tagging question asked at the moment of
|
|
work: tag the record, create the missing System, or deliberately leave it.
|
|
|
|
A System also names its files: `path_patterns`, globs from the repo root.
|
|
When the work you are tagging touched files its System's patterns don't
|
|
cover, and they plainly belong to that area, add them with `update_system`
|
|
— which files are which area is your call, as naming the area was.
|
|
|
|
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.
|
|
|
|
**An id exists only once a create call returns it.** Never write the id you
|
|
expect a record to get — every session and user draws from one sequence,
|
|
so the number goes to whoever creates next, and Scribe refuses a body that
|
|
cites an id not yet assigned. Records that must cite each other are created
|
|
together — `create_records` or `start_planning(steps=...)` — with
|
|
`{{ref:N}}` where the Nth record's id belongs.
|
|
|
|
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.
|
|
|
|
**What the operator decided an area must do is a ruling, and it lives on
|
|
that System** — a `Rulings` section in its description, written the turn
|
|
they decide. A quote in a work log reaches the next session only if a
|
|
search happens to match it; the description arrives with every record
|
|
filed there. [writing-records.md](writing-records.md) has the two tests and
|
|
the form.
|
|
|
|
10. **A note that asserts a fact about someone else's software can carry its
|
|
own check** (`verify_with`, `expires_when`), swept by
|
|
`notes_due_for_verification`. Most notes should not: read
|
|
[writing-records.md](writing-records.md) before filling either field.
|
|
|
|
11. **Report back in a shape the operator can read.** They were not there while
|
|
you worked, so organise the reply around **where the work stands**, not the
|
|
order you did things in: which task or milestone it belongs to, what now
|
|
works, what needs them, and what comes next. Take the placement from the
|
|
`placement` block that `create_task` / `update_task` return — the milestone,
|
|
step N of M, the next open step — rather than from memory. The
|
|
`reporting-back` skill holds the shape for each kind of reply: completions,
|
|
findings, decisions, handoffs, "where are we".
|
|
|
|
## 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.
|
|
|
|
## Read these when the moment comes
|
|
|
|
Each is part of this skill, kept in its own file because it matters at one
|
|
moment rather than on every turn:
|
|
|
|
- [projects.md](projects.md) — a directory with no project; starting a project
|
|
(`decide_project_inception` when `enter_project` returns `inception`).
|
|
- [writing-records.md](writing-records.md) — before writing a rule, preference
|
|
or lesson (where it goes, its `when_to_apply`, what already covers the
|
|
moment), when the operator decides how an area must behave (a ruling), and
|
|
before giving a note a check.
|
|
- [missed-retrieval.md](missed-retrieval.md) — a rule that missed the moment it
|
|
governed, or keeps arriving where it doesn't apply.
|
|
|
|
## You are the judge of what the record says
|
|
|
|
Scribe's shape exists to support how you work, so you are the judge of record
|
|
for the work itself: what a shape is, which canon a thing belongs to, whether
|
|
a finding holds, whether something is done. `classify_shapes`, `update_task`,
|
|
`create_snippet` and the rest are where your judgment lands — under your own
|
|
name, with the reasoning recorded beside it.
|
|
|
|
**Judging is attended, and that is the whole distinction.** You judge by
|
|
reading the evidence and writing down why. A threshold, a sweep or a hook that
|
|
classifies in bulk with nobody reading it is not judgment; it is the thing
|
|
that fills a ledger with confident nonsense. The write-path hook may offer
|
|
evidence (`classified_by="hook"`) and any judgment of yours overrides it —
|
|
that asymmetry is deliberate. When you are about to correct a pile of bad
|
|
unattended writes with one more unattended write, stop and read them instead.
|
|
|
|
**A finding you surface and do not judge is a finding dropped.** Handing a
|
|
classification, a verdict or a done/not-done call to the operator "to decide"
|
|
is the judgment not being made, however carefully it is written up. Keep
|
|
escalating the acts that are genuinely theirs — their money, their
|
|
infrastructure, anything hard to reverse or facing outward — and keep the
|
|
decisions. A hard call is still yours; an irreversible act is still theirs.
|
|
|
|
**A record that is wrong stays wrong until something rewrites it.** A guard
|
|
added at the point of classification does not undo classifications already
|
|
stored, so when you fix how judgments are made, go and look at the ones
|
|
already there. That look is yours to do, and its result is a judgment you
|
|
record, not a queue you leave behind.
|
|
|
|
## Building UI: the project's design system binds
|
|
|
|
`enter_project` names the project's `design_system` when it has one. Before
|
|
writing UI, read its guidance: `get_design_system(id)` returns it as
|
|
`resolved_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
|
|
|
|
Scribe also ships focused process-skills — writing-plans, reporting-back,
|
|
systematic debugging, verification, and brainstorming. Reach for the matching one when its
|
|
situation arises, the same way you reach for this skill.
|