feat(plugin): a reply's sections are chosen, not filled (#4153)
CI & Build / Python lint (push) Successful in 4s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 49s
CI & Build / Python tests (push) Successful in 1m30s
CI & Build / Build & push image (push) Successful in 16s

Milestone 409 step 6 measured the scaffold on live sessions and found its
two halves disagreeing: adherence passed and the read test failed.
Completion replies carried every section the table asks for and were
still hard to read.

The cause was in the skill, not in compliance with it. It said to pick a
kind of reply "then fill its sections... keep them even when one is
short", which is an instruction to complete a form, and nothing anywhere
set a ceiling. A faithful reply and an unreadable one were the same
reply.

Four changes to the discipline around the scaffold. The categories and
their sections are untouched.

- Sections are what to consider including, not a form to complete. A
  section answering a standing question ("does anything need me?") is
  always answered, even with "nothing"; a section that explains earns its
  place only when it changes what the operator does. Otherwise it belongs
  in the record's log, where it is available and not in the way.
- Write the shortest reply that carries the answer, with named exceptions
  so this cannot be read as "always be terse".
- "Needs you" takes BOTH tests: theirs to decide, AND work is waiting on
  it. A question answerable by reading something or taking an available
  measurement is work not yet done, not a request — settle it, say which
  way you went, and leave them free to overrule.
- A decision already made gets acted on. Re-arguing a settled question
  reads as contradicting yourself rather than as being careful, and costs
  the operator the decision twice.

"Before sending" gains a second pass for what can go, since the existing
check asks what is MISSING, which a bloated reply passes.

Guards in tests/test_reply_discipline.py, three topics registered for
ownership. Every guard was falsified against the pre-change text before
committing (rule 167): all five fail on it and pass on the fix, and the
sixth deliberately passes both since it guards the scaffold against
collateral damage. No absence checks — the skill legitimately discusses
filling in order to warn against it, so asserting "fill" is absent would
false-alarm on the corrected text (snippet #3352).

Instance-agnostic per rule 115: the added text carries no record ids, no
software-specific terms and no verbatim quotes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
This commit is contained in:
2026-09-18 11:52:14 -04:00
co-authored by Claude Opus 5
parent 104c1d6f37
commit b5df9d6dca
4 changed files with 192 additions and 4 deletions
+1 -1
View File
@@ -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.09.18.0436",
"version": "2026.09.18.1551",
"author": {
"name": "Bryan Van Deusen"
},
+42 -3
View File
@@ -10,9 +10,25 @@ 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.
Pick the kind of reply first (the tables below). Its sections are **what to
consider including, not a form to complete.**
Two kinds of section, and they behave differently:
- **A section that answers a standing question** — *does anything need me? what
happens next?* — is always answered, even when the answer is nothing.
"**Needs you:** nothing" is what they were looking for.
- **A section that explains** — how it was done, why that way, what else you
noticed — earns its place only when it changes what the operator does or
decides. When it would not, leave it out: that detail belongs in the record's
log, where it is available and not in the way.
**Write the shortest reply that carries the answer.** To someone reading
quickly, length is not thoroughness — it is work handed back to them. A reply
that fills every heading faithfully and runs a full screen is worse than four
lines naming the two things that changed their position. Extra length has to be
earned: a comparison they asked for, options that need laying side by side, a
measurement whose numbers are the point.
## Every reply
@@ -33,6 +49,13 @@ when one is short — "**Needs you:** nothing" is an answer they were looking fo
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").
- **A decision already made gets acted on, and the reply says what you did with
it.** Once the operator has chosen, that is the input to the work, not a topic
to revisit. If something you have since learned genuinely overturns the
choice, say so once and plainly — name the new evidence and what it changes —
and otherwise let the decision stand. Laying out the trade-offs of a settled
question again reads as contradicting yourself rather than as being careful,
and it costs the operator the decision twice.
## Take the placement from the record
@@ -137,6 +160,15 @@ Notes on each section:
- **Needs you** — an action, an approval, a decision, or "nothing". If it's an
action, give the reason with it. An approval you are holding for also gets
its own **Approval requested** section, and this line points at it.
**Two tests, and it takes both: is this theirs to decide, and is work waiting
on it?** A choice that is genuinely theirs — a priority, a trade-off only they
can price, something they have to live with afterwards — belongs here. A
question you could settle by reading something, by taking a measurement you
already have access to, or by choosing the obvious default does not: that is
work not yet done, and sending it moves your uncertainty onto them. Settle it,
say which way you went and why, and leave them free to overrule you. This
section is for what blocks them, not for what you are unsure about.
- **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.
@@ -146,3 +178,10 @@ 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.
Then read it once more for **what can go**. A section filled because it was in
the table, reasoning supporting a conclusion nobody is going to dispute, a
finding already written to the record — none of it changes what the operator
does, so none of it belongs in the reply. Cutting is not hiding: the log holds
it, and the reply stays readable. A reply that has been cut twice is the one
they can act on.