CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 58s
CI & Build / Python tests (push) Successful in 1m53s
CI & Build / Build & push image (push) Successful in 32s
Fourteen generic moments of work (session.start, work.start … reply.ask) plus the skill.<name> family, each with what it means and the kinds of action that reach it, written for any kind of work rather than software alone. The catalog is code because every install needs the same mount points; which actions reach a moment is per-install data (step 2). require_moment refuses an unknown name with the catalog listed, so a typo cannot become a mount that never fires. list_moments (read-only) and GET /api/retrieval/moments hand out the same catalog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
135 lines
5.5 KiB
Python
135 lines
5.5 KiB
Python
"""The operator's half of the tuning surface (#4102).
|
|
|
|
WHY THIS EXISTS
|
|
|
|
The model moves these dials — that is the operator's decision for milestone
|
|
416 — so the browser's job is no longer "set the number". It is to show what
|
|
was set, by whom, and on what argument, and to let the operator disagree.
|
|
|
|
The load-bearing guard here is the last one. `/api/settings` is a generic
|
|
key-value endpoint that accepts any key at all, and every retrieval floor IS an
|
|
ordinary settings key. A floor written straight through it would land correctly
|
|
and record nothing — a tuning history with holes in it, which is worse than no
|
|
history because it reads as complete. So the generic endpoint routes those keys
|
|
through `set_dial`, and that routing is asserted rather than remembered.
|
|
"""
|
|
import inspect
|
|
|
|
import pytest
|
|
|
|
|
|
def test_retrieval_blueprint_registered():
|
|
from scribe.routes.retrieval import retrieval_bp
|
|
assert retrieval_bp.name == "retrieval"
|
|
assert retrieval_bp.url_prefix == "/api/retrieval"
|
|
|
|
|
|
def test_retrieval_blueprint_registered_in_app():
|
|
from scribe.app import create_app
|
|
app = create_app()
|
|
assert "retrieval" in app.blueprints
|
|
|
|
|
|
def test_every_endpoint_is_reachable_on_the_app():
|
|
"""Handlers existing is not the same as them being routed."""
|
|
from scribe.app import create_app
|
|
app = create_app()
|
|
rules = {
|
|
str(r.rule) for r in app.url_map.iter_rules()
|
|
if r.endpoint.startswith("retrieval.")
|
|
}
|
|
assert rules == {
|
|
"/api/retrieval/surfaces",
|
|
"/api/retrieval/surfaces/<surface>",
|
|
"/api/retrieval/tuning-history",
|
|
"/api/retrieval/moments",
|
|
}
|
|
|
|
|
|
def test_the_browser_and_the_agent_call_the_same_service():
|
|
"""Rule 33 parity. Two callers, one service — or the two surfaces drift and
|
|
the guard that a reason is required exists on only one of them."""
|
|
from scribe.mcp.tools import retrieval_tuning as tool
|
|
from scribe.routes import retrieval as routes
|
|
from scribe.services import retrieval_tuning as svc
|
|
|
|
assert routes.set_dial is svc.set_dial
|
|
assert tool.tuning_svc is svc
|
|
|
|
|
|
def test_the_route_does_not_take_the_actor_from_the_caller():
|
|
"""`actor` is the one field a reviewer leans on to answer "did I do this,
|
|
or did the session?". A payload-supplied actor would let a model claim to
|
|
be the operator, which turns the column into decoration."""
|
|
from scribe.routes import retrieval as routes
|
|
|
|
src = inspect.getsource(routes.tune_surface_route)
|
|
assert 'actor="human"' in src
|
|
assert 'data.get("actor"' not in src and 'data["actor"]' not in src
|
|
|
|
|
|
# ── the hole the generic settings endpoint would otherwise leave ────────────
|
|
|
|
def test_every_registry_key_is_recognised_as_a_dial():
|
|
"""Derived from the registry, both directions. A seventh surface added
|
|
without a row here is a floor that can be written silently again."""
|
|
from scribe.services.retrieval_surfaces import SURFACES, dial_for_key
|
|
|
|
for name, surface in SURFACES.items():
|
|
assert dial_for_key(surface.floor_key) == (name, "floor")
|
|
assert dial_for_key(surface.budget_key) == (name, "budget")
|
|
|
|
|
|
def test_an_ordinary_setting_is_not_mistaken_for_a_dial():
|
|
"""The interception must be narrow. A false positive here would send an
|
|
unrelated setting through a service that parses it as a float and rejects
|
|
the save."""
|
|
from scribe.services.retrieval_surfaces import dial_for_key
|
|
|
|
for key in ("smtp_password", "kb_autoinject_enabled", "theme",
|
|
"kb_planmatch_threshold", ""):
|
|
assert dial_for_key(key) is None, key
|
|
|
|
|
|
def test_the_settings_endpoint_routes_a_dial_through_the_recorder():
|
|
"""THE GUARD. Asserted on the source because the alternative is a full
|
|
request-context round trip for a branch whose whole content is "which
|
|
function gets called" — and because what must not regress is precisely
|
|
that this module reaches for `set_dial` at all."""
|
|
from scribe.routes import settings as routes
|
|
|
|
src = inspect.getsource(routes.update_settings_route)
|
|
assert "dial_for_key" in src, (
|
|
"the generic settings endpoint no longer recognises retrieval dials — "
|
|
"a floor written through it now moves with no event recorded, and the "
|
|
"tuning history will say nothing happened"
|
|
)
|
|
assert "set_dial" in src
|
|
assert 'actor="human"' in src
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_setting_a_dial_to_the_value_it_already_has_records_nothing():
|
|
"""The Settings form re-sends every field on every save. Without this, one
|
|
press of Save writes six rows saying the operator set six dials to the
|
|
numbers they were already on — and a history nobody can skim is one nobody
|
|
reads."""
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
from scribe.services import retrieval_tuning as rt
|
|
from tests.helpers import make_mock_session
|
|
|
|
session = make_mock_session()
|
|
with patch.object(rt, "async_session", MagicMock(return_value=session)), \
|
|
patch.object(rt, "set_setting", AsyncMock()) as setter, \
|
|
patch.object(rt, "floor_for", AsyncMock(return_value=0.72)), \
|
|
patch.object(rt, "budget_for", AsyncMock(return_value=3)):
|
|
out = await rt.set_dial(1, "prompt_rule", "floor", 0.72,
|
|
reason="re-saved the settings form untouched")
|
|
|
|
assert out["unchanged"] is True
|
|
session.add.assert_not_called()
|
|
# And the setting is left alone too: rewriting the same value would bump
|
|
# whatever timestamp the row carries for no reason.
|
|
setter.assert_not_called()
|