CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 1m1s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 36s
Step 1 of the Scribe -> Grimoire rename: every capitalised "Scribe" in prose
becomes "Grimoire" ("Fabled Scribe" -> "Fabled Grimoire") across the UI,
the PWA manifest and page title, the MCP instructions and tool docstrings,
hook and skill text, docs and README, with the tests that assert on that
prose moving with it. Old migrations keep their wording.
Identifiers are untouched here: the package, plugin name, MCP server key,
hook file names, env vars, the .scribe marker and FabledScribe repo/image
names move in steps 2 and 3, along with prose that quotes them.
The sweep matched "Scribe" only as a whole word, so describe/subscribe and
friends are untouched. The longer name took the MCP instructions one
character over their 1,600 budget; two phrases are shortened to fit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
95 lines
4.0 KiB
Python
95 lines
4.0 KiB
Python
"""The reporting-back skill keeps its shape.
|
|
|
|
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 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_a_request_for_approval_has_its_own_named_section():
|
|
"""A go-ahead request inside a completion list, as "Blocked by the
|
|
permission check", reads as a fault rather than a question waiting on the
|
|
operator. The heading is what they scan for, so it is pinned by name."""
|
|
from scribe.services import reply_shapes
|
|
|
|
assert "Approval requested" in _text()
|
|
# The kinds live in the delivered shapes: the asks shape
|
|
# carries the Approval kind, and the core sends a held action there.
|
|
assert "**Approval**" in reply_shapes.SHAPES["asks"].text
|
|
assert "Approval requested" in reply_shapes.core().text
|
|
|
|
|
|
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():
|
|
"""Grimoire 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}"
|
|
|
|
|
|
def test_the_skill_points_at_the_delivered_shapes_instead_of_restating_them():
|
|
"""The shapes are server product, delivered at their
|
|
moments. The skill says where they come from and holds the reasoning."""
|
|
text = _text()
|
|
assert "list_reply_shapes" in text and "Reply shape · Every reply" in text
|
|
|
|
|
|
def test_the_worked_completion_report_agrees_with_the_delivered_completion_shape():
|
|
"""The two copies that could drift: the worked example here, and the
|
|
completion shape the server sends when a task closes. Same sections."""
|
|
from scribe.services import reply_shapes
|
|
|
|
shape = reply_shapes.SHAPES["completion"].text
|
|
for section in ("Where this sits", "What now works", "How / why", "Needs you", "Next"):
|
|
assert f"**{section}**" in shape, f"the completion shape lost {section!r}"
|
|
assert f"**{section}" in _text(), f"the worked example lost {section!r}"
|
|
|
|
|
|
def test_the_core_names_the_header_the_skill_quotes():
|
|
"""The skill tells the reader what the delivered core looks like; if the
|
|
header changes, the pointer would describe something that never arrives."""
|
|
from scribe.services import reply_shapes
|
|
|
|
assert reply_shapes.render(reply_shapes.core(), full=True).startswith("Reply shape · Every reply")
|