refactor(mcp): _INSTRUCTIONS orients the workflow, not a rulebook (#4389)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Failing after 12s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m35s
CI & Build / Build & push image (push) Successful in 23s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Failing after 12s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 54s
CI & Build / Python tests (push) Successful in 1m35s
CI & Build / Build & push image (push) Successful in 23s
Spike #4389 read the spec, Claude's docs and a dozen servers: the field is for how the tools fit together, and the field runs ~600-1,600 characters. Ours sat at the 2,048 cap as a keyword index that also carried stance. - JUDGE, REPORT and MISSED leave the index. They fire mid-work, not at session start; using-scribe and reporting-back state them in full, and `placement`/`report_back` cue reporting in-band. No skill text changes. - The rest is rewritten as plain practices (1,503 chars) and keeps every session-start marker the ownership registry pins. - INSTRUCTIONS_BUDGET 2000 -> 1600; the three index markers are dropped from the registry; the miss-route index test now checks that the index keeps what_might_apply and stays off the route. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -25,7 +25,10 @@ WHAT IT PINS
|
||||
moments declares that in `shared_with`, with the reason beside it.
|
||||
3. THE INDEX NAMES THE SESSION-START REFLEXES. `_INSTRUCTIONS` is the one
|
||||
surface every MCP client receives, so each reflex it indexes keeps its
|
||||
`index` markers there — a one-line pointer, not a copy.
|
||||
`index` markers there — a one-line pointer, not a copy. Only session-start
|
||||
practices carry `index`: judging, reporting and the missed-rule route fire
|
||||
mid-work, and #4389 took them out of the index (the skills and the in-band
|
||||
responses carry them at the moment they apply).
|
||||
|
||||
WHAT IT CANNOT SEE
|
||||
|
||||
@@ -174,8 +177,7 @@ TOPICS: tuple[Topic, ...] = (
|
||||
# returned zero events. Machinery with no route to it.
|
||||
Topic("a missed rule is a trigger to fix before a floor to move", U,
|
||||
("retrieval_telemetry", "tune_retrieval", "retrieval_surfaces"),
|
||||
"take it to the record first and the dial second",
|
||||
index=("retrieval_telemetry",)),
|
||||
"take it to the record first and the dial second"),
|
||||
Topic("ask what already covers a moment before writing a record", U,
|
||||
("what_might_apply",), "ask what already covers that moment"),
|
||||
Topic("reference notes update in place; dev-logs don't", U, ("reference note",),
|
||||
@@ -193,7 +195,7 @@ TOPICS: tuple[Topic, ...] = (
|
||||
("create_snippet", "when_to_use", "first build", "second copy"),
|
||||
"prior art offered beside a write is not noise", index=("create_snippet",)),
|
||||
Topic("report back where the work stands", "skill:reporting-back", ("reporting-back", "placement"),
|
||||
"take the placement from the record", index=("placement",)),
|
||||
"take the placement from the record"),
|
||||
Topic("the operator's own reply shapes come first", "skill:reporting-back",
|
||||
("reply_preferences", 'content_type="rule"'),
|
||||
"the operator's own shapes come first"),
|
||||
@@ -225,8 +227,7 @@ TOPICS: tuple[Topic, ...] = (
|
||||
# that trimming one cannot quietly take the other with it.
|
||||
Topic("the agent judges the record, and judging is attended", U,
|
||||
("judge of record", "attended", "classify_shapes"),
|
||||
"a record that is wrong stays wrong until something rewrites it",
|
||||
index=("judge of record",)),
|
||||
"a record that is wrong stays wrong until something rewrites it"),
|
||||
Topic("a finding surfaced and not judged is a finding dropped",
|
||||
"skill:reporting-back", ("you are the judge", "hard to reverse"),
|
||||
"it reads as diligence and functions as a backlog"),
|
||||
|
||||
@@ -69,8 +69,11 @@ def _instructions_text() -> str:
|
||||
# Claude Code injects only the first ~2,048 characters of an MCP server's
|
||||
# instructions and silently cuts the rest mid-word (#2562: observed live —
|
||||
# the previous 20k-char version delivered ~10% of itself, and none of the
|
||||
# Systems tagging guidance ever reached a session). 2,000 leaves margin.
|
||||
INSTRUCTIONS_BUDGET = 2000
|
||||
# Systems tagging guidance ever reached a session). The budget is set below
|
||||
# the cap, not at it: the field runs ~600–1,600 characters (spike #4389), and
|
||||
# a block pressed against 2,048 is being used for depth that belongs in a
|
||||
# skill.
|
||||
INSTRUCTIONS_BUDGET = 1600
|
||||
|
||||
|
||||
def test_instructions_fit_the_fold():
|
||||
@@ -181,9 +184,10 @@ def test_the_strength_guard_can_fail():
|
||||
# ── Reporting back (milestone 409 step 3) ──────────────────────────────
|
||||
#
|
||||
# The reporting-back skill carries the shapes, but a skill only helps if it
|
||||
# fires. The reflex that sends a session to it is stated in using-scribe; the
|
||||
# server's index carries a REPORT line pointing at `placement`, and the in-band
|
||||
# cue on update_task is the half that fires on its own in every client. The
|
||||
# fires. The reflex that sends a session to it is stated in using-scribe, and
|
||||
# the in-band cue on update_task (`placement`, `report_back`) is the half that
|
||||
# fires on its own in every client. The server's index carried a REPORT line
|
||||
# until #4389: reporting is a mid-work stance, not a session-start practice. The
|
||||
# static context was a second plugin-side copy until milestone 410.
|
||||
|
||||
REPORT_REFLEX = "report back in a shape the operator can read"
|
||||
|
||||
@@ -139,20 +139,19 @@ def test_writing_a_new_record_asks_what_already_covers_the_moment():
|
||||
assert "no bar" in text
|
||||
|
||||
|
||||
def test_the_index_points_at_the_route_without_restating_it():
|
||||
def test_the_route_stays_off_the_index():
|
||||
"""`_INSTRUCTIONS` is the only surface every MCP client gets (decision #4027).
|
||||
|
||||
It indexes; using-scribe states. A client with no Agent Skills support still
|
||||
learns the ordering exists and which tool opens it.
|
||||
It carried a one-line MISSED pointer to this route until #4389, which found
|
||||
the index was being used as a rulebook: a missed rule is noticed mid-work,
|
||||
not at session start, and the spec asks server instructions for the
|
||||
workflow across tools, not stance. using-scribe states the route (the tests
|
||||
above pin it); the index keeps what_might_apply, the tool that opens it.
|
||||
|
||||
Whitespace-flattened before matching. The block is hard-wrapped to fit a
|
||||
2,000-character budget, so a phrase straddles a line break the moment
|
||||
anything before it changes length — #4103 shipped a guard that broke
|
||||
exactly that way, on text nobody had touched.
|
||||
Whitespace-flattened before matching: the block is hard-wrapped, so a
|
||||
phrase straddles a line break the moment anything before it changes length
|
||||
(#4103).
|
||||
"""
|
||||
text = " ".join(_instructions().split())
|
||||
assert "retrieval_telemetry" in text
|
||||
assert "not a floor to move" in text
|
||||
# The route itself must NOT be here — 2,000 characters is the whole budget
|
||||
# and Claude Code cuts the rest mid-word (#2562).
|
||||
assert "take it to the record first" not in text.lower()
|
||||
text = " ".join(_instructions().split()).lower()
|
||||
assert "what_might_apply" in text
|
||||
assert "take it to the record first" not in text
|
||||
|
||||
Reference in New Issue
Block a user