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

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:
2026-09-14 12:41:08 -04:00
co-authored by Claude Opus 5
parent 0a29252f9b
commit 76bfd92c21
4 changed files with 80 additions and 145 deletions
+15 -35
View File
@@ -2160,30 +2160,18 @@ async def build_session_context(
on each write so the server could say whether the resident rules had
moved; nothing is resident now, so nothing can have moved, and a rule is
re-retrieved at the moment it applies rather than held and aged.
`context` is markdown ready to drop into `additionalContext`; it is capped
at _MAX_CHARS with an explicit truncation note so the hook can pass it
through verbatim.
"""
lines: list[str] = [
"# Scribe — standing session context (auto-injected by the Scribe plugin)",
"",
"You are working with Scribe, the operator's self-hosted second brain.",
"",
"## You are not holding the operator's rules",
"",
"No rule has been loaded into this session, and that is deliberate. "
"Rules arrive when something you are about to do makes one relevant — "
"a command you are about to run, code you are writing, or what the "
"operator just asked for. On most turns none will, and that is the "
"surface working rather than failing.",
"",
"**\"No rule arrived\" means \"nothing matched\" — never \"there is no "
"rule.\"** Before a consequential act, one that is hard to reverse or "
"outward-facing, `search(content_type=\"rule\")` is how you ask. "
"Retrieval runs on its own and is a convenience; asking is what you do "
"when it matters and nothing has spoken.",
]
`context` is markdown an adapter can drop into its session verbatim; it
is capped at _MAX_CHARS with an explicit truncation note.
LIVE STATE ONLY (decision #4027, milestone 410). This used to open with the
rules reflex and close with a recall reflex — a fifth copy of guidance the
using-scribe skill owns, arriving in every session beside the other four.
It now says only what the server alone knows about THIS session: the
active project, its open work, its design system, or that the working repo
is unbound. How to work with Scribe is the skill's to say, and it says it
once.
"""
lines: list[str] = ["# Scribe — live session state"]
project_dict: dict | None = None
if project_id:
@@ -2222,9 +2210,7 @@ async def build_session_context(
f"(id {design['id']}){inherits}",
f"{design['token_count']} tokens"
+ (f" across {groups}" if groups else "")
+ ". This project's UI is built from these, not from "
"literals — reach for a token before writing a colour, "
"size, radius or duration by hand.",
+ ".",
f"Values: `resolve_design_system({design['id']})` · "
f"stylesheet: `get_design_system_stylesheet({design['id']})` "
f"· the prose (aesthetic, voice, where the accent may "
@@ -2241,18 +2227,12 @@ async def build_session_context(
"(call `list_projects` to find the id) and future sessions here will "
"auto-load that project's context.",
]
lines += [
"",
"Reflex: search Scribe (search / list_tasks / list_notes, scoped to the "
"active project) before answering or starting work; prefer UPDATING an "
"existing note/rule over creating a new one.",
]
else:
lines += ["", "No Scribe project is bound to this working directory."]
context = "\n".join(line for line in lines if line is not None)
if len(context) > _MAX_CHARS:
context = context[:_MAX_CHARS].rstrip() + \
"\n\n…(truncated — ask with search(content_type=\"rule\"))"
context = context[:_MAX_CHARS].rstrip() + "\n\n…(truncated)"
return {
"context": context,