From 582a5a4f48bc0ff88f05bf05d0227ffd15830bc6 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 1 Oct 2026 08:46:58 -0400 Subject: [PATCH] feat(shapes): the practice is written where it is read, and the coverage line measures the slip (milestone 439 step 6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - reusing-code: "Before the turn ends — say what you built" — the four verdicts, the one classify_shapes(repo=…) call, and the component file as a shape. Description names the end-of-turn moment. - shape-accounting: the writer judges; audits are the check that it held. The write path SUGGESTS (no more hook instances); component file rows and whole-file canon described; scoped covers Svelte too. - _INSTRUCTIONS reuse line: "before the turn ends, say what you built (create_snippet the reusable, classify_shapes the rest)" — 1570/1600. - Coverage line: "written-shape check (7d): N turns checked, M asked, K left unjudged", from the Stop hook's recorded outcomes; silent until the question has been put. - test_guidance_ownership pins the new topic on reusing-code. - prior-art hook header no longer says it stamps instance rows. Plugin version minted. Co-Authored-By: Claude Opus 5.5 --- plugin/.claude-plugin/plugin.json | 2 +- plugin/hooks/scribe_prior_art.sh | 9 ++--- plugin/skills/reusing-code/SKILL.md | 28 ++++++++++++++- plugin/skills/shape-accounting/SKILL.md | 40 +++++++++++++--------- src/scribe/mcp/server.py | 3 +- src/scribe/services/coverage.py | 22 ++++++++++++ src/scribe/services/shape_check.py | 45 +++++++++++++++++++++++++ tests/test_guidance_ownership.py | 5 +++ tests/test_pattern_coverage.py | 13 +++++++ tests/test_services_shape_check.py | 8 +++++ 10 files changed, 153 insertions(+), 22 deletions(-) diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index 1874341..45eb2b3 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "scribe", "description": "Scribe for Claude Code: connects the scribe MCP server, adds the hooks that deliver live project state and relevant records at the right moment, ships the shared client-neutral Scribe skills (using-scribe, writing-plans, reporting-back, systematic-debugging, verification, brainstorming, reusing-code, shape-accounting), and syncs your saved Scribe Processes as skills (/scribe:sync).", - "version": "2026.10.01.1245", + "version": "2026.10.01.1246", "author": { "name": "Bryan Van Deusen" }, diff --git a/plugin/hooks/scribe_prior_art.sh b/plugin/hooks/scribe_prior_art.sh index c3a5715..5a741b9 100755 --- a/plugin/hooks/scribe_prior_art.sh +++ b/plugin/hooks/scribe_prior_art.sh @@ -17,8 +17,9 @@ # It is also the shape ledger's write-path feed (#2791): it names the # definitions being written (`shapes=`), and the server — only when the # session has PULLED a snippet this code references or resembles — records -# them as instance rows, classified_by=hook. Evidence, not judgment; the -# context line says what landed so a wrong stamp is corrected in the moment. +# it as a PROPOSAL on them ("looks like #N"), never a status (milestone 439). +# The agent's own end-of-turn judgment is the verdict; the same names go to +# the written-shapes ledger the Stop hook asks about. # # NEVER BLOCKS. It returns `additionalContext` with no `permissionDecision`, so # the write proceeds untouched and Claude sees the note beside the tool result. @@ -103,8 +104,8 @@ fi # changes the inside of a function rather than its signature — the definition # enclosing the edit, found by walking the target file upward from the edited # lines. The server decides whether evidence exists (the session pulled a -# snippet this code references or resembles) and stamps instance rows; with -# no pulled canon in play, nothing is recorded. Titles only still — this sends +# snippet this code references or resembles) and records it as a proposal; +# with no pulled canon in play, nothing is recorded. Titles only still — this sends # names, not bodies. # --------------------------------------------------------------------------- # diff --git a/plugin/skills/reusing-code/SKILL.md b/plugin/skills/reusing-code/SKILL.md index 5d4fe98..0aeab71 100644 --- a/plugin/skills/reusing-code/SKILL.md +++ b/plugin/skills/reusing-code/SKILL.md @@ -1,6 +1,6 @@ --- name: reusing-code -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. +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 before the turn that built it ends, say what it is — record the reusable as a snippet, classify the rest — 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", having just built the first instance of anything, or the end-of-turn list of shapes to judge. --- # Reusing code — the pattern library @@ -85,6 +85,32 @@ through recall/auto-inject; this skill is the active reflex around that. per reusable thing. If it already exists, `update_snippet` it instead of recording a second copy (the create gate will flag a near-duplicate anyway). +## Before the turn ends — say what you built + +You are the one who knows what the code you just wrote is. The shape ledger +records it from your answer, not from a guess made later, so give the answer +in the turn that built it — while it is still true. When a turn wrote +definitions nobody has judged, the plugin's Stop hook lists them once, with +whatever evidence the machinery holds ("looks like #N", "#M is canon in this +directory", "new"); other clients reach the same moment through +`list_shapes(project_id, path=…)`. For each one, decide: + +- **Something another part of the code should reuse** → record it with + `create_snippet` (the fields above). Its own row becomes the canon. +- **Built from a recorded snippet** → `instance` of it. +- **A deliberate departure from one** → `variant`, with the why. +- **A genuine one-off** → `exempt`, with a reason (`reason_code` such as + `one-off-handler`, `test-helper`, `scoped-css` or `pure-helper` indexes it). + +One call records them all: `classify_shapes(project_id, repo="", classifications=[{path, symbol, kind, status, snippet_id?, reason?}])`. +`repo` lets a verdict land on a shape the ledger has not synced yet — the one +you wrote a minute ago. A component file is a shape too (`kind: "file"`, named +by its stem): when a card, chip or dialog is the reusable thing, the file is +what you record. An unsure call is still yours to make — the reason you write +is what the next reader judges it by — and a verdict is permanent, so each +shape is asked about once. + ## A shared snippet is a suggestion, not a standard Scribe is multi-user, so a search can return snippets other people own. Those diff --git a/plugin/skills/shape-accounting/SKILL.md b/plugin/skills/shape-accounting/SKILL.md index 740ebe7..dff2eff 100644 --- a/plugin/skills/shape-accounting/SKILL.md +++ b/plugin/skills/shape-accounting/SKILL.md @@ -18,8 +18,8 @@ row carries a status: - `exempt` — judged genuinely one-off. **Reason required.** A recorded judgment, not silence — it stops the next pass re-litigating it. - `scoped` — one-off **by construction**, stamped by the coverage sync - (a Vue component's scoped `