diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index 2f5533e..c7740fc 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 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": "2026.09.14.1233", + "description": "Scribe system-of-record for Claude Code: MCP tools over your notes/tasks/projects/rules, a session-start push channel that surfaces your active-project context, process-skills (writing-plans, reporting-back, 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": "2026.09.14.1406", "author": { "name": "Bryan Van Deusen" }, diff --git a/plugin/README.md b/plugin/README.md index 68830e6..8007946 100644 --- a/plugin/README.md +++ b/plugin/README.md @@ -11,7 +11,8 @@ instance into a first-class Claude Code extension: file about to be written against your recorded snippets (what's kept at that path, and what resembles the code) and offers them before the helper is rewritten. Titles only, never blocks the edit. -- **Universal process-skills** — using-scribe, writing-plans, +- **Universal process-skills** — using-scribe, writing-plans, reporting-back + (reply to the operator in a shape that says where the work stands), systematic-debugging, verification, brainstorming, reusing-code (record and recall reusable code as snippets). Replaces superpowers. - **Your Scribe Processes as skills** — saved Processes are synced into local diff --git a/plugin/skills/reporting-back/SKILL.md b/plugin/skills/reporting-back/SKILL.md new file mode 100644 index 0000000..f7718e3 --- /dev/null +++ b/plugin/skills/reporting-back/SKILL.md @@ -0,0 +1,122 @@ +--- +name: reporting-back +description: Use when you are about to write the reply the operator will read — work finished, a task marked done, stopping on a blocker, asking them to decide or to do something, answering "where are we" / "what's next", or proposing an approach. Shapes the reply around where the work stands (which task, what changed, what needs them, what comes next) instead of the order you did things in. Triggers on reporting completion, handing off, asking a question, or summarising progress. +--- + +# Reporting back + +Your reply is where the operator finds out what happened. They were not there +while you worked: they don't hold the files you read, the names you used or +the order you did things in. A reply that follows *your* path is accurate and +still unreadable to them. Shape it around **where the work stands**. + +Pick the kind of reply first (the tables below), then fill its sections. The +sections are what lets the operator find things at a glance, so keep them even +when one is short — "**Needs you:** nothing" is an answer they were looking for. + +## Every reply + +- **Conclusion first.** The verdict, the result, or the question — before the + reasoning that supports it. +- **One topic per section.** Two things the operator raised get two sections. +- **Make priority visible.** Bold the few things that matter; let the rest be + plain. +- **End with the ask, in bold** — the one thing they need to decide or do. If + there is nothing, say so. +- **Plain words.** Use the operator's vocabulary, not the names you coined while + working. If a term has to appear, explain it once. +- **Place the work in Scribe.** Name the task, issue or milestone it belongs to, + by id *and* title (using-scribe: "Name the record, never just its number"). + +## Take the placement from the record + +A remembered milestone title or "next step" reads exactly like a real one when +it is wrong. So take placement from Scribe: + +- `update_task` and `create_task` return a **`placement`** block — the project, + the milestone, `position` (step N of M), `progress`, and `next` (the next open + step). Use those values as they came back. +- For a wider view, `get_milestone` (a plan and its steps) or `enter_project` + (the whole project). +- Work with no task behind it: say so plainly — "this wasn't tracked as a + task" — and offer to record it. An honest "untracked" is a placement too. + +## Reports — work happened + +| Kind | Sections | +|---|---| +| **Completion** | Where this sits · What now works · How / why · Needs you · Next | +| **Finding** (a problem you found and did not fix) | Symptom · Cause · Size of the fix · **Offer to fix it** | +| **Blocked / failed** | What stopped · What you tried · What you need from them | +| **Progress** (mid-work) | One or two lines: where things are, what's next, any blocker | +| **Where are we** | The milestone and its progress · Done · Open · Needs you · Next | + +## Asks — the operator needs to act or decide + +| Kind | Sections | +|---|---| +| **Decision** | The question first · 2–4 options, each with what it changes · recommendation first | +| **Clarification** | "My reading is X · the gap is Y · unless you say otherwise I'll do Z" | +| **Handoff** (only they can do it) | The action · why it needs them · what it unblocks · what you'll do after · any way to skip it | +| **Conflict** (what you're about to do clashes with a rule, a plan or an earlier decision) | What it says · what you were about to do · where they clash · A or B? | + +Before asking, check whether you can find the answer yourself — something that +can be read or looked up is a fact to check, not a question to send. + +## Answers — the operator asked something + +| Kind | Sections | +|---|---| +| **Explanation** | The answer first · then the evidence, pointing at what they could open to check it | +| **Evaluation** ("can we / should we") | Verdict · What exists · The gaps · Recommendation | + +## Proposals — shaping future work + +| Kind | Sections | +|---|---| +| **Options** | 2–3 approaches · the trade-off of each · one recommendation | +| **Plan** | Goal · Steps · Open questions — for review before starting (writing-plans) | +| **Review** | Findings ranked by how much they matter, one per item | + +## The completion report, in full + +The most common reply, and the one most often written in the order the work +happened. The shape: + +> **Where this sits:** milestone 12 "Move the backups offsite", step 3 of 5. +> Task #340 "Schedule the nightly sync" is done. +> +> **What now works** +> - The nightly sync runs at 02:00 and copies the photo library to the remote +> store. +> +> **How / why** +> - Used the scheduler the other jobs already use, so there is one place to +> look when a job doesn't run. +> - Verified by running it once by hand and checking the remote copy's size +> matches the source. +> +> **Needs you:** nothing. +> +> **Next:** #341 "Alert when a sync fails". Starting it unless you redirect. + +Notes on each section: + +- **Where this sits** — from `placement`. If the work isn't under a milestone, + the task alone is enough. +- **What now works** — outcomes the operator would notice: "You can now…", + "X no longer…". The files and steps behind them belong in the task's log. +- **How / why** — only the decisions worth knowing, plus **how it was + verified**. If something could not be verified, say what and why here rather + than letting it read as passed. +- **Needs you** — an action, an approval, a decision, or "nothing". If it's an + action, give the reason with it. +- **Next** — from `placement.next`, or say the milestone is finished. If you + found something you didn't fix, the offer to fix it goes here. + +## Before sending + +Read the reply as the operator will: someone who wasn't there, reading +quickly. Can they tell **what was done, whether anything needs them, and what +happens next** without asking a follow-up? If not, the sections are what's +missing — not more detail. diff --git a/tests/test_reporting_back_skill.py b/tests/test_reporting_back_skill.py new file mode 100644 index 0000000..7cdce13 --- /dev/null +++ b/tests/test_reporting_back_skill.py @@ -0,0 +1,55 @@ +"""The reporting-back skill keeps its shape (milestone 409 step 2). + +WHY THIS EXISTS + +The skill is what turns a reply written in the order the work happened into +one the operator can read: where the work sits, what changed, what needs +them, what is next. Its value is in its SECTIONS, and a later tidy-up that +folds them into prose would leave a skill that still loads and no longer +shapes anything. + +WHAT THIS PINS, AND WHAT IT DOES NOT + +Structure, never wording — the same reason test_create_tools_disambiguate +gives: a test that punishes rewriting gets deleted. It pins that the +completion report keeps its five sections, that placement is taken from the +record rather than recalled, and that the shipped shapes stay domain-neutral. +Whether the guidance is any good is milestone 409's last step, read against +real replies, not something a test can see. +""" +import pathlib +import re + +SKILL = pathlib.Path(__file__).resolve().parents[1] / "plugin/skills/reporting-back/SKILL.md" + + +def _text() -> str: + return " ".join(SKILL.read_text().split()) + + +def test_the_skill_names_itself_as_its_directory(): + front = re.search(r"^---\s*\nname:\s*(\S+)", SKILL.read_text()) + assert front and front.group(1) == "reporting-back" + + +def test_the_completion_report_keeps_its_sections(): + text = _text() + for section in ("Where this sits", "What now works", "How / why", "Needs you", "Next"): + assert section in text, f"the completion report lost its {section!r} section" + + +def test_placement_comes_from_the_record(): + """The failure this milestone started from: a placement written from memory + reads exactly like a real one when it is wrong.""" + text = _text().lower() + assert "placement" in text and "take the placement from the record" in text + + +def test_the_shipped_shapes_assume_no_particular_domain(): + """Scribe is domain-neutral: a home-infrastructure or writing project reads + these too. Software-specific evidence belongs in an operator's own + preferences, never in the product default.""" + text = _text() + dev_only = [w for w in (r"\bCI\b", r"\bcommit", r"\bpull request", r"file:line", r"\bpytest\b") + if re.search(w, text, re.IGNORECASE)] + assert not dev_only, f"software-only vocabulary in a product-wide shape: {dev_only}"