feat(410): the Claude Code plugin becomes a thin adapter (#4031)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 56s
CI & Build / Python tests (push) Successful in 1m36s
CI & Build / Build & push image (push) Successful in 12s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 56s
CI & Build / Python tests (push) Successful in 1m36s
CI & Build / Build & push image (push) Successful in 12s
Step 4 of milestone 410 "One owner per piece of guidance". The plugin's static session context was a second copy of using-scribe and the server index. It now says only what Claude Code needs said: 8,705 -> 1,791 chars. scribe_static_context.md: - points at using-scribe for how to work with Scribe, and names the process skills - Claude Code specifics: keep one copy in Scribe rather than CLAUDE.md or auto-memory (leave auto-memory at its default); injected lines are retrieval, not the whole set; compact at clean seams; stored Processes arrive as scribe-proc-* skills with /scribe:sync; say so when the tools are unavailable - retired: the restated reflexes, "how the surfaces divide the work", and the "follow the surface that assumes least" precedence (decision #4027) Hook behaviour is unchanged. The SessionStart hook header says what the static tier now carries, and the unreachable-instance status points at the using-scribe skill instead of "the standing guidance above". README and manifest describe the plugin as the Claude Code adapter over the shared, client-neutral skills. Fixed along the way: the README said the SessionStart hook "injects your rules" and suggested disabling auto-memory, both contradicting the product since milestone 394. Tests: the session-start rules guards now pin the owner (using-scribe) and the index (_INSTRUCTIONS) rather than every surface; the Systems-reflex and snippet-trigger guards pin their owners; the reporting-reflex guard pins using-scribe. The loss guard and client-neutral guard stay green. Plugin version minted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -69,13 +69,19 @@ ABSENCE_CLAIMS = (
|
||||
"not evidence there is none",
|
||||
)
|
||||
|
||||
# Surfaces a session loads before substantive work. Hand-written because
|
||||
# "is this a session-start surface?" is an editorial fact, not a derivable one —
|
||||
# but each entry is asserted to EXIST, so a move or rename fails loudly here
|
||||
# instead of quietly dropping that surface from the check.
|
||||
# The surfaces that STATE the rules reflex. Hand-written because "who owns
|
||||
# this?" is an editorial fact, not a derivable one — but each entry is asserted
|
||||
# to EXIST, so a move or rename fails loudly here instead of quietly dropping
|
||||
# that surface from the check.
|
||||
#
|
||||
# Since milestone 410 (decision #4027) that is the owner and the index, not
|
||||
# every surface a session loads: `using-scribe` states the reflex in full and
|
||||
# the server's `_INSTRUCTIONS` gives it one line for every MCP client. The
|
||||
# Claude Code adapter's static context used to be a third copy; it now points
|
||||
# at the skill instead, and tests/test_guidance_ownership.py keeps the topic
|
||||
# from falling off.
|
||||
SESSION_START_SURFACES = (
|
||||
ROOT / "src" / "scribe" / "mcp" / "server.py",
|
||||
ROOT / "plugin" / "hooks" / "scribe_static_context.md",
|
||||
ROOT / "plugin" / "skills" / "using-scribe" / "SKILL.md",
|
||||
)
|
||||
|
||||
@@ -174,40 +180,42 @@ def test_instructions_fit_the_fold():
|
||||
)
|
||||
|
||||
|
||||
def test_floor_states_the_systems_reflex():
|
||||
"""Write-time tagging guidance must live on the surface that always arrives.
|
||||
def test_the_systems_reflex_is_stated_by_its_owner():
|
||||
"""Write-time tagging guidance must be stated as a reflex, not only per tool.
|
||||
|
||||
#2562's behavioral finding: with the guidance only in tool descriptions,
|
||||
sessions filed records untagged. The static context is the delivery floor,
|
||||
so the tag-as-you-write reflex has to be stated there.
|
||||
sessions filed records untagged. The tag-as-you-write reflex was pinned on
|
||||
the static context then; since milestone 410 its owner is using-scribe
|
||||
(decision #4027), with the in-band `systems_hint` as the half that fires on
|
||||
its own.
|
||||
"""
|
||||
floor = (ROOT / "plugin" / "hooks" / "scribe_static_context.md").read_text()
|
||||
for needle in ("system_ids", "create_system"):
|
||||
assert needle in floor, (
|
||||
f"plugin/hooks/scribe_static_context.md no longer mentions "
|
||||
f"{needle} — the Systems tagging reflex must be stated on the "
|
||||
f"floor, not only in tool descriptions (#2562)."
|
||||
owner = (ROOT / "plugin" / "skills" / "using-scribe" / "SKILL.md").read_text()
|
||||
for needle in ("system_ids", "create_system", "systems_hint"):
|
||||
assert needle in owner, (
|
||||
f"plugin/skills/using-scribe/SKILL.md no longer mentions "
|
||||
f"{needle} — the Systems tagging reflex must be stated by its "
|
||||
f"owner, not only in tool descriptions (#2562)."
|
||||
)
|
||||
|
||||
|
||||
def test_floor_names_the_snippet_recording_triggers():
|
||||
"""The floor must state the pattern-library recording model, by name.
|
||||
def test_the_snippet_recording_triggers_are_stated_by_their_owner():
|
||||
"""The pattern-library recording model must be stated, by name.
|
||||
|
||||
#2664's behavioral finding: recording guidance as a trailing clause of the
|
||||
reuse bullet converted zero times outside snippet-minded sessions. The
|
||||
2026-08-16 ruling (decision #2686) then replaced the reactive model
|
||||
entirely: every shape is recorded at FIRST build — no "will it recur?"
|
||||
judgment — and second-copy consolidation is only the backstop. The floor
|
||||
is the delivery surface for that reflex, so all three elements must stay
|
||||
stated: the tool, the first-build trigger, and the backstop.
|
||||
judgment — and second-copy consolidation is only the backstop. All three
|
||||
elements must stay stated: the tool, the first-build trigger, and the
|
||||
backstop. Since milestone 410 the owner is the reusing-code skill.
|
||||
"""
|
||||
floor = (ROOT / "plugin" / "hooks" / "scribe_static_context.md").read_text()
|
||||
owner = " ".join((ROOT / "plugin" / "skills" / "reusing-code" / "SKILL.md").read_text().split())
|
||||
for needle in ("create_snippet", "first build", "second copy"):
|
||||
assert needle in floor, (
|
||||
f"plugin/hooks/scribe_static_context.md no longer states the "
|
||||
assert needle in owner, (
|
||||
f"plugin/skills/reusing-code/SKILL.md no longer states the "
|
||||
f"snippet-recording model ({needle!r}) — record-every-shape-at-"
|
||||
f"first-build with second-copy consolidation as the backstop must "
|
||||
f"be stated on the floor (#2664, decision #2686)."
|
||||
f"be stated by its owner (#2664, decision #2686)."
|
||||
)
|
||||
|
||||
|
||||
@@ -287,19 +295,18 @@ def test_a_surface_claiming_rules_bind_also_names_what_does_not():
|
||||
# ── 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 lives on the two plugin
|
||||
# surfaces a session always reads; the in-band cue on update_task is the half
|
||||
# that reaches clients with no plugin at all. Since milestone 410 the server's
|
||||
# index carries a REPORT line too, pointing at `placement`.
|
||||
# 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
|
||||
# static context was a second plugin-side copy until milestone 410.
|
||||
|
||||
REPORT_REFLEX = "report back in a shape the operator can read"
|
||||
REPORT_SURFACES = (
|
||||
ROOT / "plugin" / "hooks" / "scribe_static_context.md",
|
||||
ROOT / "plugin" / "skills" / "using-scribe" / "SKILL.md",
|
||||
)
|
||||
|
||||
|
||||
def test_the_reporting_reflex_reaches_every_plugin_surface():
|
||||
def test_the_reporting_reflex_is_stated_in_using_scribe():
|
||||
missing = []
|
||||
for path in REPORT_SURFACES:
|
||||
text = " ".join(path.read_text().split()).lower()
|
||||
|
||||
Reference in New Issue
Block a user