rename(547): the server is grimoire — package, image, DB defaults, backups
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m4s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 44s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m4s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 44s
Step 2 of the Scribe -> Grimoire rename. - src/scribe -> src/grimoire, and every import, patch target, module path, hypercorn entry point and `python -m grimoire.restore` with it; pyproject, uv.lock and the frontend package are named grimoire. - Image git.fabledsword.com/bvandeusen/fabledgrimoire; CI's Postgres service runs as grimoire / grimoire_test. - Compose files default the DB user, database and (prod) volume to grimoire, each overridable from .env, so an install created as Scribe keeps its data by setting POSTGRES_USER, POSTGRES_DB and PGDATA_VOLUME. - Backups are written as grimoire-<ts>.tar with format grimoire-backup-tar. Archives named scribe-* with the scribe-backup-tar manifest still restore and are still counted at the destination (folder and S3), with tests. - Exports, the MCP auth realm, the ASGI scope key and the persistent logger carry the new name. Kept on purpose: the secret-sealing key label stays "scribe.secrets.v1", because every stored secret was derived under it. Plugin identity (plugin name, hooks, env vars, .scribe marker, skills) is step 3. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,8 +28,8 @@ def _stored_settings(stored):
|
||||
in an integration job is worse: the test would pass or fail on whatever the
|
||||
instance happened to have stored.
|
||||
"""
|
||||
from scribe.services import plugin_context as pc
|
||||
from scribe.services import retrieval_surfaces as rs
|
||||
from grimoire.services import plugin_context as pc
|
||||
from grimoire.services import retrieval_surfaces as rs
|
||||
|
||||
def _side(uid, k, d=""):
|
||||
return stored.get(k, d)
|
||||
@@ -80,7 +80,7 @@ REAL_CODE = '''def debounce(fn, wait=0.25):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_disabled_returns_empty_and_touches_nothing():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
search, listing = AsyncMock(), AsyncMock()
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg(enabled=False))), \
|
||||
patch.object(pc.snippets_svc, "list_snippets", listing), \
|
||||
@@ -94,7 +94,7 @@ async def test_disabled_returns_empty_and_touches_nothing():
|
||||
@pytest.mark.asyncio
|
||||
async def test_no_path_returns_empty():
|
||||
"""Nothing to look up by place, and the semantic arm alone isn't this feature."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
search = AsyncMock()
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc, "semantic_search_notes", search):
|
||||
@@ -105,7 +105,7 @@ async def test_no_path_returns_empty():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_nothing_recorded_is_silent():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets", AsyncMock(return_value=([], 0))), \
|
||||
patch.object(pc, "semantic_search_notes", AsyncMock(return_value=[])), \
|
||||
@@ -122,7 +122,7 @@ async def test_snippet_at_this_path_becomes_the_sync_nudge():
|
||||
the record of the file being edited. It surfaces without a score,
|
||||
framed as "updating the record is part of this edit", and is reported in
|
||||
sync_note_ids so the hook feeds the sync dedup channel."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets",
|
||||
AsyncMock(return_value=([_snippet_item(12, "debounce — rate-limit a callback")], 1))), \
|
||||
@@ -145,7 +145,7 @@ async def test_reuse_dedup_never_suppresses_the_sync_nudge():
|
||||
and must not silence "you are editing the recorded
|
||||
file right now", the one claim that must fire at the moment of change. The
|
||||
sync class dedups only against its own channel."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
search = AsyncMock(return_value=[])
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets",
|
||||
@@ -165,7 +165,7 @@ async def test_sync_dedup_silences_a_sync_nudge_already_shown():
|
||||
"""Once the session has been told "snippet #12 records this file", every
|
||||
further edit of the same file stays quiet about it — on every arm: the
|
||||
directory query and the semantic arm would both re-surface it otherwise."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
async def _listing(uid, **kw):
|
||||
# Recorded at the exact file, so it matches the file AND dir queries.
|
||||
@@ -186,7 +186,7 @@ async def test_sync_dedup_silences_a_sync_nudge_already_shown():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_directory_is_only_consulted_when_the_file_leaves_room():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
calls = []
|
||||
|
||||
async def _listing(uid, **kw):
|
||||
@@ -208,7 +208,7 @@ async def test_directory_is_only_consulted_when_the_file_leaves_room():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_directory_hits_are_marked_nearby_not_here():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
async def _listing(uid, **kw):
|
||||
if kw["path"] == "src/lib":
|
||||
@@ -227,7 +227,7 @@ async def test_directory_hits_are_marked_nearby_not_here():
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_failing_location_lookup_does_not_sink_the_hint():
|
||||
"""The place arm is best-effort — a broken query must not cost the semantic one."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets", AsyncMock(side_effect=RuntimeError("boom"))), \
|
||||
patch.object(pc, "semantic_search_notes",
|
||||
@@ -242,7 +242,7 @@ async def test_a_failing_location_lookup_does_not_sink_the_hint():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_semantic_arm_is_snippet_only_and_browse_scoped():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
search = AsyncMock(return_value=[])
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets", AsyncMock(return_value=([], 0))), \
|
||||
@@ -263,7 +263,7 @@ async def test_semantic_arm_is_snippet_only_and_browse_scoped():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_margin_gate_applies_to_the_semantic_arm():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
hits = [(0.80, fake_note(id=11, title="near", user_id=1, note_type="snippet")), (0.74, fake_note(id=22, title="alsoNear", user_id=1, note_type="snippet")), (0.61, fake_note(id=33, title="far", user_id=1, note_type="snippet"))]
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg(top_k=5))), \
|
||||
patch.object(pc.snippets_svc, "list_snippets", AsyncMock(return_value=([], 0))), \
|
||||
@@ -278,7 +278,7 @@ async def test_margin_gate_applies_to_the_semantic_arm():
|
||||
@pytest.mark.asyncio
|
||||
async def test_no_code_means_no_semantic_arm():
|
||||
"""An Edit whose payload we couldn't read still gets the location answer."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
search = AsyncMock()
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets",
|
||||
@@ -293,7 +293,7 @@ async def test_no_code_means_no_semantic_arm():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_place_beats_meaning_and_the_cap_covers_both_arms():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
async def _listing(uid, **kw):
|
||||
if kw["path"] == "src/x.py":
|
||||
@@ -315,7 +315,7 @@ async def test_place_beats_meaning_and_the_cap_covers_both_arms():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_semantic_arm_only_asks_for_the_budget_the_place_arm_left():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
search = AsyncMock(return_value=[])
|
||||
|
||||
async def _listing(uid, **kw):
|
||||
@@ -348,7 +348,7 @@ async def test_session_dedup_marks_the_reuse_arms_rather_than_silencing_them():
|
||||
make the marker read as a complete account of what the session has met,
|
||||
when it would only cover half the lines.
|
||||
"""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
async def _listing(uid, **kw):
|
||||
# Recorded at a SIBLING file, so it reaches only the directory query.
|
||||
@@ -379,7 +379,7 @@ async def test_session_dedup_marks_the_reuse_arms_rather_than_silencing_them():
|
||||
@pytest.mark.asyncio
|
||||
async def test_another_users_snippet_is_attributed():
|
||||
"""An unattributed line reads as your own record."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets",
|
||||
AsyncMock(return_value=([_snippet_item(12, "theirs", user_id=42)], 1))), \
|
||||
@@ -393,7 +393,7 @@ async def test_another_users_snippet_is_attributed():
|
||||
@pytest.mark.asyncio
|
||||
async def test_telemetry_uses_its_own_source():
|
||||
"""Separate source is what lets this surface be tuned apart from auto_inject."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
rec = MagicMock()
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets", AsyncMock(return_value=([], 0))), \
|
||||
@@ -421,7 +421,7 @@ async def test_sync_surfacing_is_measured_under_its_own_usage_source():
|
||||
"""The sync class's pull-through rate is its scoreboard — whether
|
||||
sessions actually update the record when told they're editing it. Folding
|
||||
it into write_path_place would make that unmeasurable."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
async def _listing(uid, **kw):
|
||||
if kw["path"] == "src/x.py":
|
||||
@@ -445,7 +445,7 @@ async def test_sync_surfacing_is_measured_under_its_own_usage_source():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_config_has_its_own_switch_threshold_and_inherited_budget():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
stored = {pc.WRITEPATH_ENABLED_KEY: "false"}
|
||||
with _stored_settings(stored):
|
||||
cfg = await pc.get_writepath_config(1)
|
||||
@@ -469,7 +469,7 @@ async def test_config_has_its_own_switch_threshold_and_inherited_budget():
|
||||
async def test_writepath_threshold_is_operator_tunable_and_clamped():
|
||||
"""It's a product knob, so it has to be settable, and a typo in
|
||||
the settings box must not be able to open the gate to everything."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
async def _cfg_with(raw):
|
||||
stored = {pc.WRITEPATH_THRESHOLD_KEY: raw}
|
||||
@@ -493,7 +493,7 @@ async def test_the_rule_arm_has_its_own_tunable_bar():
|
||||
against note PROSE, and rules are short imperative technical
|
||||
English — a more homogeneous corpus whose noise floor sits higher.
|
||||
"""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
async def _cfg_with(raw):
|
||||
stored = {pc.RULEHINT_THRESHOLD_KEY: raw}
|
||||
@@ -513,7 +513,7 @@ async def test_the_two_write_path_bars_are_independent():
|
||||
"""The split, asserted. Setting one must not move the other — the failure
|
||||
that would silently undo this step is a config assembler that reads one key
|
||||
into both fields."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
stored = {pc.WRITEPATH_THRESHOLD_KEY: "0.90", pc.RULEHINT_THRESHOLD_KEY: "0.61"}
|
||||
with _stored_settings(stored):
|
||||
@@ -532,7 +532,7 @@ async def test_the_two_act_arms_read_independent_rule_bars():
|
||||
reads one key into both fields would silently undo that, and the symptom
|
||||
would be invisible: both arms would simply agree again.
|
||||
"""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
stored = {pc.RULEHINT_THRESHOLD_KEY: "0.75", pc.TOOLRULE_THRESHOLD_KEY: "0.61"}
|
||||
with _stored_settings(stored):
|
||||
@@ -551,7 +551,7 @@ async def test_a_garbage_command_bar_falls_back_to_its_own_default():
|
||||
that are kept separate, which is the harder failure to see because the arm
|
||||
keeps working.
|
||||
"""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
stored = {pc.TOOLRULE_THRESHOLD_KEY: "banana"}
|
||||
with _stored_settings(stored):
|
||||
@@ -569,7 +569,7 @@ def test_the_command_bar_defaults_below_the_write_path_bar():
|
||||
commands retrieve nothing. Tuning either value stays free; inverting the
|
||||
relationship would mute the command arm.
|
||||
"""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
assert pc.TOOLRULE_DEFAULT_THRESHOLD < pc.RULEHINT_DEFAULT_THRESHOLD
|
||||
|
||||
@@ -587,7 +587,7 @@ def test_the_rule_bar_defaults_above_the_code_bar():
|
||||
Pinned as an inequality so tuning the value stays free while inverting the
|
||||
relationship — which would silently drown the arm in noise — does not.
|
||||
"""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
assert pc.RULEHINT_DEFAULT_THRESHOLD > pc.WRITEPATH_DEFAULT_THRESHOLD
|
||||
|
||||
@@ -608,8 +608,8 @@ def test_the_rule_arm_asks_for_a_set_and_lets_the_band_narrow_it():
|
||||
return several, and that rules — which measured FLATTER than notes, not
|
||||
sharper — are narrowed harder than the notes menu is.
|
||||
"""
|
||||
from scribe.services import plugin_context as pc
|
||||
from scribe.services import retrieval_pipeline as rp
|
||||
from grimoire.services import plugin_context as pc
|
||||
from grimoire.services import retrieval_pipeline as rp
|
||||
|
||||
assert pc.RULEHINT_LIMIT > 1
|
||||
assert 0 < pc._RULEHINT_BAND < rp._NOTE_BAND
|
||||
@@ -623,7 +623,7 @@ async def test_trivial_payload_skips_the_semantic_arm_entirely():
|
||||
floor for "some code", not a resemblance. A payload too small to
|
||||
carry meaning must not reach the embedder at all, and must not cost a
|
||||
retrieval_logs row either."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
search, rec = AsyncMock(return_value=[]), MagicMock()
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets", AsyncMock(return_value=([], 0))), \
|
||||
@@ -639,7 +639,7 @@ async def test_trivial_payload_skips_the_semantic_arm_entirely():
|
||||
async def test_the_floor_counts_substance_not_indentation():
|
||||
"""Code is indentation-heavy, so raw length would let a deeply-nested
|
||||
one-liner through on padding alone."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
padded = " " * 400 + "return None" # long, but almost no substance
|
||||
search = AsyncMock(return_value=[])
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
@@ -655,7 +655,7 @@ async def test_the_floor_counts_substance_not_indentation():
|
||||
async def test_the_floor_does_not_block_the_place_arm():
|
||||
"""The floor is a statement about the semantic query, not about the edit. A
|
||||
snippet recorded at this exact path is prior art however small the change."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
search = AsyncMock(return_value=[])
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets",
|
||||
@@ -672,7 +672,7 @@ async def test_the_floor_does_not_block_the_place_arm():
|
||||
async def test_a_real_helper_clears_the_floor():
|
||||
"""The floor errs toward keeping recall — anything that plausibly IS a
|
||||
reusable helper has to get through, or the feature stops working."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
search = AsyncMock(return_value=[(0.80, fake_note(id=5, title="debounce — …", user_id=1, note_type="snippet"))])
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets", AsyncMock(return_value=([], 0))), \
|
||||
@@ -687,7 +687,7 @@ async def test_a_real_helper_clears_the_floor():
|
||||
def test_the_floor_stays_below_the_smallest_plausible_helper():
|
||||
"""A guard on the constant itself: raising it far enough to reject a real
|
||||
one-line helper would silently gut the arm rather than sharpen it."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
smallest_real_helper = 'def slug(t): return re.sub(r"[^a-z0-9]+", "-", t.lower()).strip("-")'
|
||||
substance = len("".join(smallest_real_helper.split()))
|
||||
assert pc.WRITEPATH_MIN_CODE_CHARS < substance
|
||||
@@ -702,7 +702,7 @@ def test_concept_query_prefers_the_name_and_docstring():
|
||||
title and the body — so a concept query out-scores the code itself (0.823 vs
|
||||
0.743 against a recorded snippet). The shape mirrors a snippet's own
|
||||
title."""
|
||||
from scribe.services.plugin_context import concept_query
|
||||
from grimoire.services.plugin_context import concept_query
|
||||
q = concept_query(
|
||||
'def collapse_into_clusters(edges):\n'
|
||||
' """Collapse similar-pairs into connected components."""\n'
|
||||
@@ -714,7 +714,7 @@ def test_concept_query_prefers_the_name_and_docstring():
|
||||
|
||||
def test_concept_query_reads_jsdoc_and_the_const_arrow_form():
|
||||
"""The dominant shape in this repo's frontend isn't `function foo()`."""
|
||||
from scribe.services.plugin_context import concept_query
|
||||
from grimoire.services.plugin_context import concept_query
|
||||
q = concept_query(
|
||||
"/**\n"
|
||||
" * Rate-limit a callback so it fires once after the last call.\n"
|
||||
@@ -728,7 +728,7 @@ def test_concept_query_reads_jsdoc_and_the_const_arrow_form():
|
||||
|
||||
def test_concept_query_does_not_let_a_shebang_become_the_description():
|
||||
"""`#!` matches the leading-comment pattern but says nothing about the code."""
|
||||
from scribe.services.plugin_context import concept_query
|
||||
from grimoire.services.plugin_context import concept_query
|
||||
q = concept_query(
|
||||
"#!/usr/bin/env bash\n"
|
||||
"# Resolve the repo remote and url-encode it for the plugin API.\n"
|
||||
@@ -745,7 +745,7 @@ def test_concept_query_falls_back_when_there_is_no_doc():
|
||||
a recorded snippet where the full code body scored 0.743 — same separation from the noise
|
||||
floor (0.113), but below the 0.68 bar, so preferring the bare name would turn
|
||||
a comfortable hit into a miss. Undocumented code keeps the raw payload."""
|
||||
from scribe.services.plugin_context import concept_query
|
||||
from grimoire.services.plugin_context import concept_query
|
||||
assert concept_query(
|
||||
"def collapse_into_clusters(edges):\n"
|
||||
" parent = {}\n"
|
||||
@@ -754,14 +754,14 @@ def test_concept_query_falls_back_when_there_is_no_doc():
|
||||
|
||||
|
||||
def test_concept_query_falls_back_on_a_doc_that_says_nothing():
|
||||
from scribe.services.plugin_context import concept_query
|
||||
from grimoire.services.plugin_context import concept_query
|
||||
assert concept_query('def f(x):\n """TODO"""\n return x\n') == ""
|
||||
|
||||
|
||||
def test_concept_query_falls_back_when_nothing_parses():
|
||||
"""A Vue SFC has neither a recognised declaration nor a doc block. Every
|
||||
unhandled language must degrade to querying by the raw payload."""
|
||||
from scribe.services.plugin_context import concept_query
|
||||
from grimoire.services.plugin_context import concept_query
|
||||
assert concept_query(
|
||||
'<script setup lang="ts">\n'
|
||||
"const props = defineProps<{ modelValue: string }>()\n"
|
||||
@@ -771,7 +771,7 @@ def test_concept_query_falls_back_when_nothing_parses():
|
||||
|
||||
def test_concept_query_accepts_a_doc_without_a_declaration():
|
||||
"""A module docstring is already the concept; it needs no signature."""
|
||||
from scribe.services.plugin_context import concept_query
|
||||
from grimoire.services.plugin_context import concept_query
|
||||
q = concept_query('"""Turn a raw git remote into the project it belongs to."""\nMAPPING = {}\n')
|
||||
assert q == "Turn a raw git remote into the project it belongs to."
|
||||
|
||||
@@ -779,7 +779,7 @@ def test_concept_query_accepts_a_doc_without_a_declaration():
|
||||
def test_concept_query_caps_what_it_collects():
|
||||
"""A 40-function Write must not become a wall of signatures, and a long module
|
||||
docstring must not drown out the declaration."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
many = '"""Utilities."""\n' + "".join(
|
||||
f"def helper_{i}(a, b):\n return a\n" for i in range(12)
|
||||
)
|
||||
@@ -792,7 +792,7 @@ def test_concept_query_caps_what_it_collects():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_semantic_arm_queries_the_concept_not_the_raw_payload():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
search = AsyncMock(return_value=[])
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets", AsyncMock(return_value=([], 0))), \
|
||||
@@ -809,7 +809,7 @@ async def test_semantic_arm_queries_the_concept_not_the_raw_payload():
|
||||
async def test_telemetry_records_the_query_actually_sent():
|
||||
"""retrieval_logs is what the threshold gets tuned from, so it has to hold the
|
||||
concept query — logging the raw code would make the scores uninterpretable."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
rec = MagicMock()
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets", AsyncMock(return_value=([], 0))), \
|
||||
@@ -827,7 +827,7 @@ async def test_the_floor_judges_the_raw_payload_not_the_concept():
|
||||
RAW payload; the rewrite happens after. A concept query is allowed to be
|
||||
shorter than the floor — that's the whole point, since the best queries are
|
||||
short — but a sub-floor payload must stay silent even when it has a docstring."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
|
||||
# Clears the floor, and its concept is deliberately SHORTER than the floor.
|
||||
verbose = (
|
||||
@@ -861,7 +861,7 @@ async def test_the_floor_judges_the_raw_payload_not_the_concept():
|
||||
# --- cross-language prior art is disclosed, not filtered ----------------------
|
||||
|
||||
def test_language_for_path_and_canonicalisation():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
assert pc._language_for_path("src/a/b.ts") == "typescript"
|
||||
assert pc._language_for_path("src/a/b.py") == "python"
|
||||
assert pc._language_for_path("ops/Dockerfile") == "dockerfile"
|
||||
@@ -876,7 +876,7 @@ def test_language_for_path_and_canonicalisation():
|
||||
|
||||
|
||||
def test_foreign_language_only_claims_a_mismatch_it_can_establish():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
assert pc._foreign_language({"language": "python"}, "typescript") == "python"
|
||||
assert pc._foreign_language({"language": "py"}, "python") == "" # same, aliased
|
||||
assert pc._foreign_language({"language": ""}, "typescript") == "" # theirs unknown
|
||||
@@ -888,7 +888,7 @@ def test_foreign_language_only_claims_a_mismatch_it_can_establish():
|
||||
async def test_a_cross_language_hit_is_labelled_and_explained():
|
||||
"""The fail state this closes: an unlabelled Python hit offered while writing
|
||||
TypeScript is either dismissed as irrelevant or pasted into the .ts file."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
note = fake_note(id=7, title="group_pairs — collapse related pairs into groups", user_id=1, note_type="snippet")
|
||||
note.data = {"language": "python"}
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
@@ -908,7 +908,7 @@ async def test_a_cross_language_hit_is_labelled_and_explained():
|
||||
async def test_a_same_language_hit_is_not_labelled_and_gets_no_preamble():
|
||||
"""The common case keeps a clean line; the explanation only appears when
|
||||
there is something on the menu it explains."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
note = fake_note(id=7, title="debounce — rate-limit a callback", user_id=1, note_type="snippet")
|
||||
note.data = {"language": "python"}
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
@@ -916,7 +916,7 @@ async def test_a_same_language_hit_is_not_labelled_and_gets_no_preamble():
|
||||
patch.object(pc, "semantic_search_notes", AsyncMock(return_value=[(0.72, note)])), \
|
||||
patch.object(pc, "record_retrieval", MagicMock()), \
|
||||
patch.object(pc, "owner_names_for", AsyncMock(return_value={})):
|
||||
out = await pc.build_write_path_hint(1, "src/scribe/services/x.py", code=REAL_CODE)
|
||||
out = await pc.build_write_path_hint(1, "src/grimoire/services/x.py", code=REAL_CODE)
|
||||
assert "[similar 0.72]" in out["context"]
|
||||
assert "·" not in out["context"]
|
||||
assert "shape of a solution" not in out["context"]
|
||||
@@ -925,7 +925,7 @@ async def test_a_same_language_hit_is_not_labelled_and_gets_no_preamble():
|
||||
@pytest.mark.asyncio
|
||||
async def test_an_unknown_target_extension_never_invents_a_mismatch():
|
||||
"""A wrong "· python" tag is worse than no tag at all."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
note = fake_note(id=7, title="helper — does a thing", user_id=1, note_type="snippet")
|
||||
note.data = {"language": "python"}
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
@@ -938,7 +938,7 @@ async def test_an_unknown_target_extension_never_invents_a_mismatch():
|
||||
|
||||
|
||||
def test_prior_art_line_keeps_language_and_attribution_together():
|
||||
from scribe.services.plugin_context import _prior_art_line
|
||||
from grimoire.services.plugin_context import _prior_art_line
|
||||
item = {"id": 12, "title": "group_pairs — collapse pairs"}
|
||||
assert _prior_art_line(item, "similar 0.72", None, "python") == \
|
||||
'> - #12 [similar 0.72 · python] "group_pairs — collapse pairs"'
|
||||
@@ -950,7 +950,7 @@ def test_prior_art_line_takes_a_search_hit_whose_snippet_is_its_excerpt():
|
||||
"""A search hit carries its matched passage as a STRING under `snippet` —
|
||||
the key a snippet record uses for its field dict. Reading it as the dict
|
||||
500'd the write-path route on any nameless hit."""
|
||||
from scribe.services.plugin_context import _prior_art_line
|
||||
from grimoire.services.plugin_context import _prior_art_line
|
||||
hit = {"id": 7, "title": "Paging contract", "snippet": "…the cursor is opaque…"}
|
||||
assert _prior_art_line(hit, "similar 0.61", None) == '> - #7 [similar 0.61] "Paging contract"'
|
||||
record = {"id": 8, "title": "long composed title", "snippet": {"name": "paginate"}}
|
||||
@@ -964,7 +964,7 @@ def test_route_reads_every_arg_the_hook_sends():
|
||||
and the hook is the only caller — a rename on either side is silent."""
|
||||
import inspect
|
||||
|
||||
from scribe.routes import plugin as routes
|
||||
from grimoire.routes import plugin as routes
|
||||
# The handler plus the query-scope helper it delegates repo/project_id
|
||||
# to — the contract is what the ROUTE MODULE reads, wherever it reads it.
|
||||
src = inspect.getsource(routes.write_path_prior_art) + inspect.getsource(
|
||||
@@ -1002,7 +1002,7 @@ def test_route_resolves_repo_to_a_project_not_to_a_location_filter():
|
||||
remote may only reach resolve_project."""
|
||||
import inspect
|
||||
|
||||
from scribe.routes import plugin as routes
|
||||
from grimoire.routes import plugin as routes
|
||||
src = inspect.getsource(routes.write_path_prior_art) + inspect.getsource(
|
||||
routes._project_scope
|
||||
)
|
||||
@@ -1284,7 +1284,7 @@ async def test_stamping_needs_named_shapes_and_a_recent_pull():
|
||||
"""Offered-but-ignored stamps nothing: without a PULLED event there is no
|
||||
evidence, and without the hook naming shapes there is nothing to stamp.
|
||||
Neither case may even read the pull stream."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
pulls = AsyncMock(return_value={})
|
||||
stamp = AsyncMock(return_value=[])
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
@@ -1317,7 +1317,7 @@ async def test_a_pulled_snippet_already_seen_is_evidence_not_menu():
|
||||
after the search. The part the stamp depends on: the pulled id stays in
|
||||
the query and its score reaches `resembles`.
|
||||
"""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
search = AsyncMock(return_value=[(0.91, fake_note(id=7, title="pulled", user_id=1, note_type="snippet")), (0.80, fake_note(id=8, title="fresh", user_id=1, note_type="snippet"))])
|
||||
stamp = AsyncMock(return_value=[{
|
||||
"path": "src/x.py", "symbol": "debounce", "kind": "sym",
|
||||
@@ -1366,7 +1366,7 @@ async def test_a_stamp_renders_even_when_the_hint_is_otherwise_silent():
|
||||
"""After dedup the common case is an empty hint; the stamp must still run
|
||||
and still be reported — silence about accounting is how hook rows would
|
||||
become invisible."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
stamped = [{"path": "web/b.css", "symbol": "btn-primary", "kind": "css",
|
||||
"snippet_id": 5, "reason": "hook: pulled #5; payload references `btn-primary`"}]
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
@@ -1388,7 +1388,7 @@ async def test_a_stamp_renders_even_when_the_hint_is_otherwise_silent():
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_failing_stamp_does_not_sink_the_hint():
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets",
|
||||
AsyncMock(return_value=([_snippet_item(12, "records me")], 1))), \
|
||||
@@ -1411,7 +1411,7 @@ def test_route_stamps_only_for_a_caller_allowed_to_write():
|
||||
shapes through only when the key is write-scoped (or it's a session)."""
|
||||
import inspect
|
||||
|
||||
from scribe.routes import plugin as routes
|
||||
from grimoire.routes import plugin as routes
|
||||
src = inspect.getsource(routes.write_path_prior_art)
|
||||
assert 'request.args.get("shapes"' in src
|
||||
assert '== "write"' in src
|
||||
@@ -1520,7 +1520,7 @@ async def test_the_write_time_derive_check_names_a_family_or_a_canon_in_band():
|
||||
family with no canon, or a canon recorded elsewhere — rendered at the
|
||||
write even when nothing else does; keyed so the session's exclude
|
||||
channel silences a family already named."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
found = [
|
||||
{"symbol": "log-empty", "kind": "css", "key": "name:css:log-empty",
|
||||
"family": {"group": "name:css:log-empty", "label": ".log-empty", "identical": False,
|
||||
@@ -1622,7 +1622,7 @@ async def test_the_write_time_divergence_check_is_named_in_band():
|
||||
"""The hook named a shape at a path whose directory a canon
|
||||
dominates, and the stamp didn't make it that canon's instance — the hint
|
||||
must say so at the write, even when nothing else renders."""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
div = [{"symbol": "confirmDanger", "kind": "sym", "canon_snippet_id": 2761,
|
||||
"instances": 20, "judged": 21}]
|
||||
check = AsyncMock(return_value=div)
|
||||
@@ -1711,7 +1711,7 @@ def _search_reporting(score, note):
|
||||
|
||||
|
||||
async def _write_path_row(rec, **kwargs):
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
with patch.object(pc, "get_writepath_config", AsyncMock(return_value=_cfg())), \
|
||||
patch.object(pc.snippets_svc, "list_snippets", AsyncMock(return_value=([], 0))), \
|
||||
patch.object(pc, "record_retrieval", rec), \
|
||||
@@ -1740,7 +1740,7 @@ async def test_a_record_this_arm_withheld_itself_is_not_a_near_miss():
|
||||
The filter is still there, and an arm that reported a score for it would
|
||||
be making a false claim about the bar.
|
||||
"""
|
||||
from scribe.services import plugin_context as pc
|
||||
from grimoire.services import plugin_context as pc
|
||||
rec = MagicMock()
|
||||
# Recorded at a sibling file → listed by PLACE in this same call, and
|
||||
# pulled this session → kept in the query as resemblance evidence.
|
||||
|
||||
Reference in New Issue
Block a user