From 2d58e74ec7441dfeb561910934c330c2daeb2cc9 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 16 Aug 2026 11:33:24 -0400 Subject: [PATCH] =?UTF-8?q?feat(reuse):=20recording=20model=20becomes=20th?= =?UTF-8?q?e=20pattern=20library=20=E2=80=94=20every=20shape=20at=20first?= =?UTF-8?q?=20build=20(#2687)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decision #2686: snippets are the project's pattern library, not a dedup net. The floor, the reusing-code skill, and the snippet tool docstrings now state the proactive model — record every shape the first time it is built, with no will-it-recur judgment, and start later instances from the recorded shape; second-copy consolidation stays as the backstop. The floor guard test pins all three elements (tool, first-build trigger, backstop) so the model cannot silently regress to the reactive wording. Co-Authored-By: Claude Fable 5 --- plugin/hooks/scribe_static_context.md | 25 +++++++++------ plugin/skills/reusing-code/SKILL.md | 41 +++++++++++++++--------- src/scribe/mcp/tools/snippets.py | 24 ++++++++++---- tests/test_instruction_surfaces_agree.py | 22 +++++++------ 4 files changed, 70 insertions(+), 42 deletions(-) diff --git a/plugin/hooks/scribe_static_context.md b/plugin/hooks/scribe_static_context.md index 82936d4..32ddf3d 100644 --- a/plugin/hooks/scribe_static_context.md +++ b/plugin/hooks/scribe_static_context.md @@ -52,16 +52,21 @@ for the operator's work, and as your own working memory across sessions. it. An untagged project record carries the `systems_hint` question instead, on creates, updates, and work-logs alike — treat it as the tagging question asked at the moment of work, not as noise to skip past. -- **Reuse before rebuilding — and record what you build** — before writing a - new helper/utility/component, search recorded **snippets** (reusable code - recorded once for recall) and reuse the prior art instead of re-solving it. - The recording half has NAMED TRIGGERS, not a vibe: the moment you extract a - shared component, hoist a helper into a common module, or notice you are - writing the second copy of anything, record it with `create_snippet` (name, - code, when-to-reach-for-it, location) in the same breath as the commit. - Work that "refactors X into a shared Y" is not finished until Y is recorded - — an unrecorded shared component is invisible to every later session, which - is how a codebase grows four `.btn-primary` definitions. +- **The pattern library: start from recorded shapes, and record every shape + at first build** — recorded **snippets** are the project's pattern library, + not a dedup net. Before building ANY shape — a button, an input field, a + modal, a route handler, a service class, a test scaffold, up through complex + subsystem patterns — search snippets and START from the recorded shape; a + deliberate departure is recorded as its own named variant, never left as + silent drift. And the FIRST time a shape is built, record it with + `create_snippet` (name, when-to-reach-for-it, location, code) in the same + breath — do not judge whether it "might recur": the builder of the first + instance can never know, and a missed record is invisible until it + resurfaces as an uninformed duplicate. A mature project's snippet corpus + should read as a map of every shape in it. The backstop still holds: + noticing the second copy of anything, or consolidating copies into a shared + X, means X gets recorded before that work is finished — which is how a + codebase is kept from growing four `.btn-primary` definitions. - Do **not** keep the operator's rules, plans, or project notes in local memory / CLAUDE.md in parallel with Scribe — Scribe holds the single copy. - **Compact at clean seams** — because you record as you go, a context diff --git a/plugin/skills/reusing-code/SKILL.md b/plugin/skills/reusing-code/SKILL.md index 8767e94..85ace2d 100644 --- a/plugin/skills/reusing-code/SKILL.md +++ b/plugin/skills/reusing-code/SKILL.md @@ -1,20 +1,24 @@ --- name: reusing-code -description: Use when you're about to write a helper, utility, hook, or reusable component — search recorded snippets FIRST so prior art is reused instead of re-solved. And the moment you build or notice something reusable, record it as a snippet so a later session finds it. Triggers on "write a util/helper", "I need a function that…", "let me add a component", or just having built something worth reusing. +description: Use when you're about to build ANY shape — a component, control, route handler, service class, helper, test scaffold — search recorded snippets FIRST and start from the recorded shape instead of re-solving it. And the FIRST time a shape is built, record it as a snippet so every later instance starts from it. Triggers on "write a util/helper", "I need a function that…", "let me add a component/button/field/route", or having just built the first instance of anything. --- -# Reusing code — recall before you rebuild +# Reusing code — the pattern library -Reusable code is worth writing once. Scribe stores **snippets** — a named, -reusable function or component recorded with its language, signature, canonical -location (repo · path · symbol), a one-line *"when to reach for it,"* and the -code itself — so prior art can surface *before* it's re-written as a one-off. +Snippets are the project's **pattern library**, not a dedup net. Each records a +named shape — with its language, signature, canonical location (repo · path · +symbol), a one-line *"when to reach for it,"* and the code — so every later +instance STARTS from the recorded shape: buttons start from the button shape, +fields from the field shape, and "special" is a deliberate, named exception +rather than drift. A mature project's snippet corpus reads as a map of every +shape in it, from the humblest control to the most complex subsystem pattern. Snippets are ordinary embedded notes, so a recorded one also surfaces on its own through recall/auto-inject; this skill is the active reflex around that. -## Before you write a new helper — search first +## Before you build any shape — search first -- About to write a utility, hook, formatter, adapter, or a reusable component? +- About to build a component, control, route handler, service class, utility, + hook, formatter, adapter, or test scaffold? **Search snippets before writing it.** `list_snippets(q="…")` (or a plain `search`) — a matching one may already exist, in this project or another. `list_snippets` searches every project by default; that's deliberate, since a @@ -38,10 +42,15 @@ through recall/auto-inject; this skill is the active reflex around that. duplicate — reuse it and drop yours — or it isn't, and the record needs the new location adding. Both are cheaper now than after the duplicate settles in. -## The moment you build something reusable — record it +## The first time a shape is built — record it -- Just wrote (or noticed) a helper, hook, pattern, or component worth repeating? - Record it with `create_snippet` while it's fresh: +- Just built the FIRST instance of anything with a shape — a component, a + field, a route, a service pattern, a scaffold? Record it with + `create_snippet` while it's fresh. Do **not** stop to judge whether it will + recur: the builder of the first instance can never know, and a missed record + is invisible until it resurfaces as an uninformed duplicate. Over-recording + is safe — dead weight shows up in the usage counters and can be pruned; + under-recording has no signal at all. The record is cheap — these fields: - **name** — what it's called, e.g. `useDebouncedRef`. - **code** — the implementation. - **when_to_use** — one sharp line on when to reach for it. This becomes part @@ -92,7 +101,9 @@ gate only hints at when it blocks a near-duplicate. ## Why this pays off -A one-off written a second time is the cost this avoids. Recording a snippet -once — with a location and a crisp "when to use" — means the next session is -offered the prior art instead of re-solving it. Search before writing; record -what's worth reusing. +A one-off written a second time is the cost this avoids — and at project +scale, the cost is an application whose buttons, fields, and services each +exist in four diverging shapes. Recording every shape once — with a location +and a crisp "when to use" — means every later session starts from the pattern +library instead of re-deriving it. Search before building; record every shape +at first build. diff --git a/src/scribe/mcp/tools/snippets.py b/src/scribe/mcp/tools/snippets.py index 7cd09ed..9d00510 100644 --- a/src/scribe/mcp/tools/snippets.py +++ b/src/scribe/mcp/tools/snippets.py @@ -23,7 +23,12 @@ async def list_snippets( q: str = "", tag: str = "", limit: int = 50, project_id: int = 0, repo: str = "", path: str = "", symbol: str = "", verification: str = "", ) -> dict: - """List recorded snippets (reusable functions/components). + """List recorded snippets — the project's pattern library. + + Search here BEFORE building any shape (a component, control, route + handler, service class, helper, scaffold): a recorded shape is the + starting point for every later instance, and building without checking is + how the same button ends up defined four diverging ways. Two ways to ask, usable together: by MEANING (`q` — "what do I need this code to do?") and by PLACE (`repo`/`path`/`symbol` — "what canonical helpers @@ -101,13 +106,18 @@ async def create_snippet( system_ids: list[int] | None = None, force: bool = False, ) -> dict: - """Record a reusable function/component so future sessions can RECALL it - instead of writing a fresh one-off. + """Record a shape in the project's pattern library, so every later + instance starts from it instead of re-deriving it. - Reach for this the moment you build (or notice) something reusable: a helper, - a hook, a component, a pattern worth repeating. Recording it once makes it - surface automatically when a similar problem comes up later. Before writing a - new utility, search first — a snippet may already exist. + Reach for this the FIRST time any shape is built — a component, a control, + a route handler, a service class, a helper, a test scaffold — not only + when something is judged "reusable": the builder of the first instance + can't know what will recur, and a missed record is invisible until it + resurfaces as an uninformed duplicate. Over-recording is safe (dead weight + shows in the usage counters and can be pruned); under-recording has no + signal. A deliberate departure from a recorded shape is recorded as its + own named variant, not left as drift. Before building, search first — the + shape may already be recorded. Args: name: Short name of the function/component, e.g. "useDebouncedRef". diff --git a/tests/test_instruction_surfaces_agree.py b/tests/test_instruction_surfaces_agree.py index b5e4017..89e1497 100644 --- a/tests/test_instruction_surfaces_agree.py +++ b/tests/test_instruction_surfaces_agree.py @@ -129,21 +129,23 @@ def test_floor_states_the_systems_reflex(): def test_floor_names_the_snippet_recording_triggers(): - """The recording half of reuse needs NAMED trigger moments on the floor. + """The floor must state the pattern-library recording model, by name. - #2664's behavioral finding: with recording guidance as a trailing clause of - the reuse bullet, zero snippets were ever recorded outside sessions already - thinking about snippets — extracting a shared component (Roundtable's - BaseModal) produced task prose and no record. The floor must name the - moments, not just the tool. + #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. """ floor = (ROOT / "plugin" / "hooks" / "scribe_static_context.md").read_text() - for needle in ("create_snippet", "second copy"): + for needle in ("create_snippet", "first build", "second copy"): assert needle in floor, ( f"plugin/hooks/scribe_static_context.md no longer states the " - f"snippet-recording trigger ({needle!r}) — the record-as-you-build " - f"reflex must be stated on the floor with its trigger moments " - f"(#2664)." + 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)." )