feat(500): the reply shapes become server product content - a compact core and per-kind slices, each tied to its moment (#5493)
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m12s
CI & Build / Python tests (push) Failing after 1m29s
CI & Build / Build & push image (push) Skipped
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 14s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m12s
CI & Build / Python tests (push) Failing after 1m29s
CI & Build / Build & push image (push) Skipped
services/reply_shapes.py is the single source of the default reply shapes: the core (every reply, ~1,800 chars against a 2,200 budget) and three slices - completion on work.finish, asks on reply.ask, plan on work.plan. Read through list_reply_shapes (MCP, read-only) and GET /api/retrieval/reply-shapes, one service behind both doors. Nothing delivers them yet; that is step 3. The skill still carries its copy until step 4 shrinks it to the long-form reference. The software-only vocabulary guard moves into tests/helpers.py (DEV_ONLY, dev_only_hits) rather than becoming a fourth copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+6
-13
@@ -10,7 +10,7 @@ import re
|
||||
import pytest
|
||||
|
||||
from scribe.services import moments
|
||||
from tests.helpers import FakeMCP
|
||||
from tests.helpers import FakeMCP, dev_only_hits
|
||||
|
||||
_NAME = re.compile(r"^[a-z]+\.[a-z]+$")
|
||||
|
||||
@@ -38,26 +38,18 @@ def test_the_skill_family_is_not_a_catalog_entry():
|
||||
assert not any(k.startswith(moments.SKILL_PREFIX) for k in moments.MOMENTS)
|
||||
|
||||
|
||||
# Software-only vocabulary, the same guard the completion query carries. The
|
||||
# moments are named for any work a person drives through an agent; the actions
|
||||
# particular to one kind of work belong in the mappings, not in the meaning.
|
||||
_DEV_ONLY = (r"\bCI\b", r"\bcommit", r"\bpull request", r"\bcode\b", r"\btest",
|
||||
r"\bgit\b", r"\brepo(s|sitor\w*)?\b", r"\bbranch", r"\bcompil", r"\bbuild\b")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("field", ["means", "reached_by"])
|
||||
def test_the_catalog_assumes_no_particular_domain(field):
|
||||
found = {
|
||||
m.name: hits
|
||||
for m in [*moments.MOMENTS.values(), moments.SKILL_FAMILY]
|
||||
if (hits := [w for w in _DEV_ONLY
|
||||
if re.search(w, getattr(m, field), re.IGNORECASE)])
|
||||
if (hits := dev_only_hits(getattr(m, field)))
|
||||
}
|
||||
assert not found, f"software-only vocabulary in `{field}`: {found}"
|
||||
|
||||
|
||||
def test_the_guard_can_fail():
|
||||
assert re.search(_DEV_ONLY[1], "after the commit lands", re.IGNORECASE)
|
||||
assert dev_only_hits("after the commit lands")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", list(moments.MOMENTS))
|
||||
@@ -116,11 +108,12 @@ def test_the_tools_are_registered_and_classified():
|
||||
mcp = FakeMCP()
|
||||
tool.register(mcp)
|
||||
assert mcp.names == [
|
||||
"list_moments", "map_action", "unmap_action",
|
||||
"list_moments", "list_reply_shapes", "map_action", "unmap_action",
|
||||
"rules_to_mount", "propose_rule_moments", "rule_moment_proposals",
|
||||
"judge_rule_moments", "rule_misfired",
|
||||
]
|
||||
assert {"list_moments", "rules_to_mount", "rule_moment_proposals"} <= _READ_ONLY_TOOLS
|
||||
assert {"list_moments", "list_reply_shapes",
|
||||
"rules_to_mount", "rule_moment_proposals"} <= _READ_ONLY_TOOLS
|
||||
# A confirm mounts a rule, so a read key must not reach it.
|
||||
assert {"map_action", "unmap_action",
|
||||
"propose_rule_moments", "judge_rule_moments", "rule_misfired"} <= _WRITE_TOOLS
|
||||
|
||||
Reference in New Issue
Block a user