feat(410): the server orients with a client-neutral index; the live context carries live state only (#4030)
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m28s
CI & Build / Build & push image (push) Successful in 27s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m28s
CI & Build / Build & push image (push) Successful in 27s
Step 3 of milestone 410 "One owner per piece of guidance".
_INSTRUCTIONS is rewritten as an index for every MCP client (1,597 of 2,000
chars): orient, rules, recall, record, plan, ids, reuse, UI, report. Each
line names its tool, and the block says every reflex is stated in full in
the using-scribe skill and in each tool description.
- names no client: CLAUDE.md, auto-memory and "the client injects ~2k
chars" are gone
- gains the two reflexes it lacked: records that cite each other go through
create_records with {{ref:N}}, and reports start from `placement`
The comment block above it now explains ownership (decision #4027) instead of
accumulating per-milestone trade history, and keeps the budget and its
reason (#2562).
build_session_context states only what the server knows about this session:
the active project and open work, its design system, an unbound-repo hint,
or that no project is bound. Removed: the "you are not holding the
operator's rules" section, the closing "Reflex: search Scribe" line, the
design-system usage sentence, and the plugin-specific header. using-scribe
owns all of that. The truncation note no longer restates the rules ask.
Tests: a pin that the live context carries no rules reflex; the cap test
drives truncation through the unbound-repo hint, since a bare session is now
one line; the budget test message describes the index.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+48
-99
@@ -7,111 +7,60 @@ from mcp.server.fastmcp import FastMCP
|
||||
from mcp.server.transport_security import TransportSecuritySettings
|
||||
from quart import Quart
|
||||
|
||||
## The delivery budget — read before editing this block
|
||||
## What this block is — read before editing it
|
||||
#
|
||||
# Claude Code injects only the FIRST ~2,048 CHARACTERS of an MCP server's
|
||||
# instructions into the system prompt; the rest is silently cut mid-word
|
||||
# (#2562 — the cut was observed live at exactly offset 2,048, and ~90% of the
|
||||
# previous 20k-char version of this block never reached any session). So this
|
||||
# block is deliberately a MAP, not a manual, and a test pins it under the
|
||||
# fold (test_instruction_surfaces_agree.py::test_instructions_fit_the_fold).
|
||||
# ONE OWNER PER PIECE OF GUIDANCE (decision #4027, milestone 410). Scribe's
|
||||
# guidance to agents lives where it can be delivered, and each topic is stated
|
||||
# in full exactly once:
|
||||
# - Tool docstrings: each tool's contract, delivered with its schema.
|
||||
# - In-band tool responses: behaviour prose cannot be trusted to trigger —
|
||||
# the duplicate gate, the guessed-id refusal, `systems_hint`,
|
||||
# `placement` and `report_back` — at the moment it applies.
|
||||
# - The bundled skills (Agent Skills, client-neutral): every reflex in full.
|
||||
# `using-scribe` owns the working reflexes; the process skills own arcs.
|
||||
# - Client adapters (the Claude Code plugin today): timing and that
|
||||
# client's own conventions, never a copy of the above.
|
||||
# tests/test_guidance_ownership.py holds the topic registry that enforces it.
|
||||
#
|
||||
# Where the detail lives instead — each surface has one job:
|
||||
# - Tool docstrings: the per-tool HOW. Delivered with the tool schema, at
|
||||
# reach-for time when the client defers tools. Guidance about one tool
|
||||
# belongs there, not here.
|
||||
# - Plugin static context (plugin/hooks/scribe_static_context.md): the
|
||||
# session-level reflexes (recall-first, record-as-you-go, tag-to-Systems,
|
||||
# compaction). Always delivered in full; needs no key and no network.
|
||||
# - Plugin skills: process arcs (planning, debugging, verification…).
|
||||
# Their listing line is the always-visible trigger; the body loads on
|
||||
# match. Stored Processes become skills via /scribe:sync.
|
||||
# - The server itself: behaviors prose can't be trusted to fire (the
|
||||
# duplicate gate, the untagged-record systems_hint) act in-band in tool
|
||||
# responses, at the moment they apply.
|
||||
# Grow one of those, not this block.
|
||||
# BUDGET: ~1980 of the client's ~2048-char cap (#2562). Everything below is
|
||||
# competing for the last ~68 characters, so an addition here is a trade, never
|
||||
# an append.
|
||||
# THIS BLOCK IS THE SERVER'S ORIENTATION, WRITTEN AS AN INDEX. It reaches
|
||||
# every MCP client, so it names no client, and it points at where each reflex
|
||||
# is stated rather than restating it. A new topic gets a line here only if it
|
||||
# is a session-start reflex; its full statement goes to its owner.
|
||||
#
|
||||
# Milestone 409 step 3 (reporting back to the operator in a readable shape)
|
||||
# took NO line here, deliberately: there is no room without trading out a
|
||||
# session-start reflex, and the moment it applies is when a task closes. So it
|
||||
# rides in-band instead — update_task returns `placement` and a one-line
|
||||
# `report_back` cue on done/cancelled, which every MCP client sees — with the
|
||||
# full shapes in the reporting-back skill and the static context.
|
||||
#
|
||||
# Milestone 317 (a note's own verify_with / expires_when, and the sweep over
|
||||
# them) was DECLINED a line, deliberately, by the operator — not overlooked.
|
||||
# The reasoning, so it is not re-litigated blind: this is a map, and its own
|
||||
# closing line says each tool's description carries the full contract. The
|
||||
# sweep is a curation act, not a session-start reflex like enter_project.
|
||||
# Spending the last of the budget on it would leave the
|
||||
# map unable to grow for something more central later.
|
||||
#
|
||||
# The accepted cost: an agent that never opens create_note's docstring never
|
||||
# learns the field exists. Guidance lives in the create_note / update_note
|
||||
# docstrings and the using-scribe skill instead.
|
||||
#
|
||||
# Milestone 333 step 3 (2026-09-04) bought the HOW bullet's second clause —
|
||||
# search(content_type="rule") before a consequential act — by TRADING OUT
|
||||
# "Processes are saved procedures (follow verbatim)" and "Deletes are
|
||||
# trash-recoverable". Recorded so the trade is not silently reversed:
|
||||
# - Both were already in test_instruction_surfaces_agree's DISPLACED_TOPICS
|
||||
# and already stated on a delivered surface, so nothing fell off: the
|
||||
# process reflex is in every scribe-proc-* skill listing (each says the
|
||||
# process governs and is followed verbatim), and trash recovery is in the
|
||||
# delete_*/list_trash/restore docstrings, which is where per-tool guidance
|
||||
# belongs by this block's own doctrine.
|
||||
# - What it bought is not per-tool guidance and has nowhere else to live at
|
||||
# session-start altitude. Rules were retrievable only by RESIDENCY: the
|
||||
# always-on preload put them in front of the agent, and nothing told a
|
||||
# session to go looking for one it had not been handed. That preload is
|
||||
# gone (milestone 394), which makes this line LOAD-BEARING rather than
|
||||
# supplementary: retrieval is now the only delivery, and retrieval fires
|
||||
# only if something asks. A session that waits to be handed a rule is
|
||||
# handed nothing. A tool-choice reflex asks least of all (#3476, #161).
|
||||
# - It also has to carry what absence MEANS. "No rule arrived" is now the
|
||||
# ordinary state rather than the exceptional one, and reading it as
|
||||
# "there is no rule" is the #3720 defect at session scale. Rule 119 makes
|
||||
# these surfaces the
|
||||
# specification, so the same sentence lands on all three session-start
|
||||
# surfaces, and test_instruction_surfaces_agree pins it.
|
||||
# BUDGET: at most 2,000 characters (test_instructions_fit_the_fold). Claude
|
||||
# Code injects only the first ~2,048 characters of a server's instructions and
|
||||
# cuts the rest mid-word (#2562, observed live), and other clients differ, so
|
||||
# nothing load-bearing may sit past the fold. The history of what was traded
|
||||
# for space before the ownership split (milestones 317, 333, 409) is in
|
||||
# decision #4027 and the notes it supersedes.
|
||||
_INSTRUCTIONS = """
|
||||
Scribe is the operator's self-hosted second brain and system of record — and
|
||||
yours: recall from it before acting, record as you go. Keep no parallel copy
|
||||
in local files (CLAUDE.md, auto-memory); Scribe holds the single copy.
|
||||
Scribe is the operator's system of record for their work, and yours: recall
|
||||
from it before acting, record in it as you go, and keep one copy here rather
|
||||
than in local memory files.
|
||||
|
||||
Hierarchy: Project -> Milestone -> Task/Note. The map, by purpose:
|
||||
- ORIENT: enter_project(id) at session start — rules, open tasks, recent
|
||||
notes, Systems, design system. `inception`: ask what the project
|
||||
inherits, then decide_project_inception.
|
||||
- DO: create_task. Fixed a problem? kind="issue" (symptom -> root cause ->
|
||||
fix), never a work-log line on an unrelated task. Log with add_task_log;
|
||||
keep status honest — in_progress on start, done on finish.
|
||||
- PLAN work with an arc: start_planning. The plan IS a milestone; each step is
|
||||
a child task, not a checkbox. No local plan .md files.
|
||||
- CAPTURE: create_note. RECALL: search first — prior art exists; pass the
|
||||
active project_id to stay in scope.
|
||||
- WHERE work happens: Systems. Tag records with system_ids as you write;
|
||||
create_system when the area is unmodelled.
|
||||
- HOW: rules bind; preferences guide. Nothing preloads — a rule arrives
|
||||
when your work matches it. Before a consequential act,
|
||||
search(content_type="rule"); silence means nothing matched, not none.
|
||||
- UI: the project's design system is binding — resolve_design_system /
|
||||
get_design_system_stylesheet before hand-writing a value.
|
||||
- REUSE: search snippets before writing a helper; record what you build with
|
||||
create_snippet; classify shapes against canon (classify_shapes) — a
|
||||
consumer map is rows, never prose.
|
||||
Every reflex below is stated in full in the using-scribe skill (if your
|
||||
client reads Agent Skills) and in each tool's description. The index:
|
||||
- ORIENT: enter_project(id) loads the project, open work, Systems and design
|
||||
system. An `inception` key: ask what it inherits, then
|
||||
decide_project_inception.
|
||||
- RULES: nothing preloads; a rule arrives when your work matches it. Before a
|
||||
consequential act, search(content_type="rule"). Silence means
|
||||
nothing matched, not none. Rules bind; preferences guide.
|
||||
- RECALL: search before acting, scoped with the active project_id.
|
||||
- RECORD: create_task; a fix is kind="issue". add_task_log as you go; status
|
||||
in_progress on start, done on finish. Tag system_ids as you write.
|
||||
- PLAN work with an arc: start_planning(steps=[...]). The plan is a milestone
|
||||
and each step a task.
|
||||
- IDS exist only once a create returns them. Records that cite each other go
|
||||
through create_records, writing {{ref:N}} for the Nth record.
|
||||
- REUSE: search snippets before building; create_snippet what you build.
|
||||
- UI: the project's design system binds; resolve_design_system before
|
||||
hand-writing a value.
|
||||
- REPORT back from the `placement` a task write returns: where the work sits,
|
||||
what changed, what needs the operator, what comes next.
|
||||
|
||||
A task is a note with status (*_note vs *_task tools).
|
||||
Creates are duplicate-gated: a near-match BLOCKS and returns the existing
|
||||
id — update it, don't force. shared:true records are another user's — a
|
||||
suggestion, not the operator's settled practice.
|
||||
|
||||
This is only a map — the client injects ~2k chars and cuts the rest. Each
|
||||
tool's description carries its full contract: read it when you load the
|
||||
tool, and trust it over habit.
|
||||
Creates are duplicate-gated: a near-match returns the existing id to update.
|
||||
shared:true records are another user's suggestion, not settled practice.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user