docs(notes): the surfaces say WHEN a note earns a check, and a guard keeps them saying it (#3168, milestone 317 step 5)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 10s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / integration (push) Successful in 43s
CI & Build / Python tests (push) Successful in 1m16s
CI & Build / Build & push image (push) Successful in 28s

The step that decides whether steps 1-4 were worth building. `verify_with` is
a free-text field on the highest-volume record kind in the product; described
only as "how to verify this note" it gets filled in on every note within a
week, and at that point the sweep returns the whole corpus and means nothing.
The signal is not "has a check" — it is "has a check AND almost nothing else
does".

Rule 119 puts this in the app's own instruction surfaces, never in a Scribe
rule. So:

- the using-scribe skill gains the reflex, next to "state updates in place;
  chronicles don't" — its other half, since supersession only fires once
  somebody has read a note and disagreed, which is the case where it was
  already believed;
- update_note's docstring now states the DEFAULT rather than only deferring
  to create_note for the test. Found by the new guard on its first run;
- plugin 0.1.48.

THE _INSTRUCTIONS BUDGET, decided rather than skipped. The payload is ~1980 of
the client's ~2048-char cap, so everything in it competes for the last ~68
characters. The operator declined a line for this milestone: the map's own
closing sentence says each tool's description carries the full contract, and
the sweep is a curation act rather than a session-start reflex like
enter_project or list_always_on_rules. That reasoning is now a comment beside
the constant, with the accepted cost named — an agent that never opens
create_note's docstring never learns the field exists — so the question is not
re-litigated blind.

The guard pins STRUCTURE, never wording, for the disambiguator's reason (a
test that punishes rewriting is a test that gets deleted): each write surface
must still draw the norm-vs-constraint distinction, say the empty case is
normal, and name where NOT to reach for it — plus that the skill carries the
one-question form, because the docstrings only reach a caller who already
opened the tool.

`_doc` moves to tests/helpers as `tool_doc`; it had been written twice.
This commit is contained in:
2026-08-29 00:10:43 -04:00
parent 1ec44071d2
commit 1d65e98ac2
7 changed files with 176 additions and 20 deletions
+1 -15
View File
@@ -29,6 +29,7 @@ parameters, with the "is this even the right tool" paragraph quietly gone.
import re
import pytest
from tests.helpers import tool_doc as _doc
# The create surfaces and where they live. `start_planning` is here because
# it is a create in everything but name — it is how a plan comes into being.
@@ -50,21 +51,6 @@ _ALTERNATIVES = [
]
def _doc(module: str, name: str) -> str:
"""The docstring with its whitespace flattened.
Flattened because a docstring is hard-wrapped: "design system" spans a
line break in at least one of these, and matching the raw text would
report it absent. The first draft of this check did exactly that, and
caught it on itself.
"""
import importlib
fn = getattr(importlib.import_module(module), name)
assert fn.__doc__, f"{name} has no docstring at all"
return re.sub(r"\s+", " ", fn.__doc__)
@pytest.mark.parametrize(("module", "name"), _SURFACES)
def test_a_create_surface_names_at_least_two_alternatives(module, name):
"""Reaching for the wrong tool must still put the right one in view."""