From 9f1a52a03573956847a35efee9423ff0b3ae52b5 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 19 Aug 2026 21:16:04 -0400 Subject: [PATCH] =?UTF-8?q?feat(ledger):=20audits=20write=20rows,=20not=20?= =?UTF-8?q?prose=20=E2=80=94=20instruction=20surfaces=20carry=20the=20clas?= =?UTF-8?q?sification=20duty=20(#2790,=20milestone=20294=20step=204)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit verify_snippet docstring: consumers enumerated while checking are classify_shapes instance rows; detail keeps the WHY and what changed, nothing that belongs in a row. The MCP _INSTRUCTIONS REUSE bullet gains "classify shapes against canon — a consumer map is rows, never prose" (budget rebalanced to 1,998/2,000: the rules bullet lost its historical push-optimisation clause, the task-note line tightened). The reusing-code skill gains the full contract: instance/variant/exempt with required reasons, list_shapes(status=unclassified) as the standing todo, and the derive-one-first rule for repeating shapes with no canon. Plugin 0.1.32. The operator-side halves of this step live outside the repo: the Drift Audit process gained step 7 (classify what the walk proved) and the DRY Pass s9 now sends consumer maps to the ledger with the note keeping only the narrative. The Forge P7 backfill payload is enumerated and parked on task 2790 — it fires after the next deploy + coverage refresh. Co-Authored-By: Claude Fable 5 --- plugin/.claude-plugin/plugin.json | 2 +- plugin/skills/reusing-code/SKILL.md | 15 +++++++++++++++ src/scribe/mcp/server.py | 10 +++++----- src/scribe/mcp/tools/snippets.py | 7 +++++++ 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index 6fd872a..fb5dc06 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "scribe", "description": "Scribe system-of-record for Claude Code: MCP tools over your notes/tasks/projects/rules, a session-start push channel that surfaces your always-on rules + active-project context, process-skills (writing-plans, systematic-debugging, verification, brainstorming, reusing-code), and your saved Scribe Processes auto-surfaced as skills (/scribe:sync). Replaces superpowers + file-memory with one app-backed plugin.", - "version": "0.1.31", + "version": "0.1.32", "author": { "name": "Bryan Van Deusen" }, "mcpServers": { "scribe": { diff --git a/plugin/skills/reusing-code/SKILL.md b/plugin/skills/reusing-code/SKILL.md index 1ae1c23..bfc9921 100644 --- a/plugin/skills/reusing-code/SKILL.md +++ b/plugin/skills/reusing-code/SKILL.md @@ -107,6 +107,21 @@ The result is a single entry that shows every place the thing is used — which exactly the signal that it was worth consolidating. This is the cure the create gate only hints at when it blocks a near-duplicate. +## Consumer maps are rows, never prose + +Every enumerated relationship between code and canon belongs in the shape +ledger, not in a sentence. When you establish that call sites route through a +canonical helper — during an audit, a verify pass, or a consolidation — +record each consuming definition with +`classify_shapes(project_id, [{path, symbol, status: "instance", snippet_id}])`. +A deliberate departure is a `"variant"` (reason required — the why IS the +record); a judged one-off is `"exempt"` (reason required). Prose in a +verification detail cannot be sorted, queried, or diffed; rows are what make +"what uses this?" answerable forever. `list_shapes(project_id, +status="unclassified")` is the standing todo — and N same-shaped occurrences +matching no canon means derive one first (consolidate, `create_snippet`, +then classify the rest against it), never N loose classifications. + ## Why this pays off A one-off written a second time is the cost this avoids — and at project diff --git a/src/scribe/mcp/server.py b/src/scribe/mcp/server.py index bd19447..f0ee82c 100644 --- a/src/scribe/mcp/server.py +++ b/src/scribe/mcp/server.py @@ -49,15 +49,15 @@ Hierarchy: Project -> Milestone -> Task/Note. The map, by purpose: - WHERE work happens: Systems. Tag records with system_ids as you write; create_system when the area is unmodelled. - HOW to work: rules are pull-only and binding — call list_always_on_rules() - yourself at session start; a push that also delivered them was an - optimisation, not the bridge. + yourself at session start. - UI: the project's design system is binding — resolve_design_system / get_design_system_stylesheet before hand-writing a value. - REUSE: search snippets before writing a helper; record what you build with - create_snippet. Saved procedures are Processes (follow verbatim). Deletes - are trash-recoverable. + create_snippet; classify shapes against canon (classify_shapes) — a + consumer map is rows, never prose. Saved procedures are Processes (follow + verbatim). Deletes are trash-recoverable. -A task is a note with status; *_note tools for notes, *_task for tasks. +A task is a note with status (*_note vs *_task tools). Creates are duplicate-gated: a near-match BLOCKS and returns the existing id — update it, don't force. shared:true records are another user's — a suggestion, not the operator's settled practice. diff --git a/src/scribe/mcp/tools/snippets.py b/src/scribe/mcp/tools/snippets.py index 1cc2871..748438e 100644 --- a/src/scribe/mcp/tools/snippets.py +++ b/src/scribe/mcp/tools/snippets.py @@ -352,6 +352,13 @@ async def verify_snippet( "moved to services/knowledge.py"). It's what makes the record fixable later by someone who wasn't here, so write it for them, not as a status echo. + CONSUMERS you enumerate while checking ("all N call sites still route + through it") are ledger rows, not detail prose: classify each consuming + definition as an `instance` of this snippet with classify_shapes — prose + cannot be sorted, queried, or diffed (note 2786's lesson), and the rows + are what make "what uses this?" answerable forever. `detail` keeps the + WHY and what changed, nothing that belongs in a row. + A verdict expires automatically if the snippet is edited afterwards: it is stamped with a hash of the code it was checked against, so it can never go on vouching for code nobody checked. Re-verify after fixing a record.