Cross-language prior-art labelling + close the surfaced→pulled loop on get_task #87
@@ -27,6 +27,7 @@ from scribe.services import rulebooks as rulebooks_svc
|
|||||||
from scribe.services import systems as systems_svc
|
from scribe.services import systems as systems_svc
|
||||||
from scribe.services import task_logs as task_logs_svc
|
from scribe.services import task_logs as task_logs_svc
|
||||||
from scribe.services import trash as trash_svc
|
from scribe.services import trash as trash_svc
|
||||||
|
from scribe.services.note_usage import record_pulled
|
||||||
|
|
||||||
|
|
||||||
async def list_tasks(
|
async def list_tasks(
|
||||||
@@ -99,6 +100,14 @@ async def get_task(task_id: int) -> dict:
|
|||||||
data["suppressed_rules"] = applicable.get("suppressed_rules", [])
|
data["suppressed_rules"] = applicable.get("suppressed_rules", [])
|
||||||
data["suppressed_topics"] = applicable.get("suppressed_topics", [])
|
data["suppressed_topics"] = applicable.get("suppressed_topics", [])
|
||||||
data.update(await access_svc.describe_provenance(uid, note))
|
data.update(await access_svc.describe_provenance(uid, note))
|
||||||
|
# Same reasoning as get_note's record_pulled, and this is the tool where it
|
||||||
|
# matters MOST: auto-inject ranks kind-blind over a corpus that is
|
||||||
|
# overwhelmingly tasks and issues, so tasks dominate what it surfaces. Without
|
||||||
|
# this the surfaced→pulled loop was open exactly where the volume is — every
|
||||||
|
# surfaced task counted as never-pulled because the tool that opens one didn't
|
||||||
|
# say so, driving auto-inject's measured pull-through toward zero for its own
|
||||||
|
# dominant kind. #1038 and #2085 are explicitly gated on that number (#2245).
|
||||||
|
record_pulled(user_id=uid, note_id=int(note.id), source="mcp_get_task")
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -186,3 +186,37 @@ async def test_auto_inject_records_what_survived_the_margin_gate():
|
|||||||
|
|
||||||
assert surfaced.call_args.kwargs["note_ids"] == [1]
|
assert surfaced.call_args.kwargs["note_ids"] == [1]
|
||||||
assert surfaced.call_args.kwargs["source"] == "auto_inject"
|
assert surfaced.call_args.kwargs["source"] == "auto_inject"
|
||||||
|
|
||||||
|
|
||||||
|
def test_every_getter_that_can_be_surfaced_also_records_a_pull():
|
||||||
|
"""Rule #33 contract check — and the one that would have caught #2245.
|
||||||
|
|
||||||
|
`surfaced` and `pulled` only mean something as a PAIR: the rate between them
|
||||||
|
is what #1038 and #2085 gate on. That pair is only closed if the tool which
|
||||||
|
OPENS a record reports it. `get_note` did, `get_snippet` did, `get_task` did
|
||||||
|
NOT — and auto-inject ranks kind-blind over a corpus that is overwhelmingly
|
||||||
|
tasks and issues, so the gap sat exactly where the volume is: every surfaced
|
||||||
|
task counted as never-pulled, dragging measured pull-through toward zero for
|
||||||
|
the menu's own dominant kind.
|
||||||
|
|
||||||
|
Asserted by source inspection rather than by calling the tools, because the
|
||||||
|
failure is a MISSING call — which no behavioural test of the tool's return
|
||||||
|
value can see.
|
||||||
|
"""
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from scribe.mcp.tools import notes as notes_tools
|
||||||
|
from scribe.mcp.tools import snippets as snippet_tools
|
||||||
|
from scribe.mcp.tools import tasks as task_tools
|
||||||
|
|
||||||
|
getters = (
|
||||||
|
(notes_tools, "get_note"),
|
||||||
|
(task_tools, "get_task"),
|
||||||
|
(snippet_tools, "get_snippet"),
|
||||||
|
)
|
||||||
|
for module, name in getters:
|
||||||
|
src = inspect.getsource(getattr(module, name))
|
||||||
|
assert "record_pulled(" in src, (
|
||||||
|
f"{name} can be surfaced in an auto-inject menu but records no pull — "
|
||||||
|
"its pull-through rate will read as zero regardless of real usage"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user