feat(prior-art): edit-time record-sync nudge — the sync class (#2708)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / TypeScript typecheck (push) Successful in 24s
CI & Build / integration (push) Successful in 26s
CI & Build / Python tests (push) Successful in 56s
CI & Build / Build & push image (push) Successful in 43s

A snippet recorded AT the exact file being edited is not a reuse
suggestion — it IS the record of the file being changed. The write-path
hint now renders those as their own SYNC class: 'snippet #N records this
file — updating the record is part of the edit (update_snippet /
verify_snippet)'. Nearby and semantic hits stay the reuse menu.

The two classes dedup on separate per-session channels (exclude_ids vs
exclude_sync_ids, .ids vs .sync.ids in the hook), so a reuse hint shown
early in a session can no longer silence the record-sync nudge when the
recorded file itself is edited later. Sync surfacing is measured under
its own note_usage source (write_path_sync) — its pull-through rate is
the scoreboard for whether edit-time sync actually happens, per decision
#2707 (no forge connection; records stay current in the session that has
the context). Plugin 0.1.31.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 20:05:01 -04:00
co-authored by Claude Fable 5
parent 765635bbf2
commit 3162332a13
9 changed files with 290 additions and 69 deletions
+16 -7
View File
@@ -156,16 +156,25 @@ async def test_no_ids_short_circuits_without_a_query():
@pytest.mark.parametrize(
"marker, expected_source",
[("here", "write_path_place"), ("nearby", "write_path_place")],
"lookups, expected_source",
[
# A hit AT the exact file is the sync class (#2708); a hit from the
# directory query is the reuse-shaped place arm. Both are un-scored,
# and both must leave a usage trace under their own name.
([(1, "hit"), (2, "empty")], "write_path_sync"),
([(1, "empty"), (2, "hit")], "write_path_place"),
],
ids=["at-the-file", "nearby"],
)
async def test_write_path_place_arm_is_recorded(marker, expected_source):
"""The place arm carries no score, so it has no home in retrieval_logs — it
surfaced snippets while leaving no trace anywhere. That was the blocker
#2082 recorded against this task; this is the assertion that it's closed."""
async def test_unscored_location_arms_are_recorded(lookups, expected_source):
"""The location arms carry no score, so they have no home in retrieval_logs
— they surfaced snippets while leaving no trace anywhere. That was the
blocker #2082 recorded against this task; this is the assertion that it's
closed, per class."""
from scribe.services import plugin_context
here = [{"id": 42, "title": "helper", "user_id": 1, "note_type": "snippet"}]
responses = [(here, 1) if kind == "hit" else ([], 0) for _n, kind in lookups]
with (
patch.object(
plugin_context,
@@ -175,7 +184,7 @@ async def test_write_path_place_arm_is_recorded(marker, expected_source):
patch.object(
plugin_context.snippets_svc,
"list_snippets",
AsyncMock(side_effect=[(here, 1), ([], 0)]),
AsyncMock(side_effect=responses),
),
patch.object(
plugin_context, "semantic_search_notes", AsyncMock(return_value=[])