feat(6243): move_path carries a moved path's snippets with its shapes
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 17s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / integration (push) Successful in 1m9s
CI & Build / Python tests (push) Successful in 2m12s
CI & Build / Build & push image (push) Successful in 31s

Snippet locations are keyed by path too, so a snippet recorded under a
moved directory reads `missing` at its next check. The tool is now
move_path(project_id, from_path, to_path). It moves the ledger rows as
before, and re-paths the project's snippet locations under from_path
through update_snippet, so each body and its indexed mirror stay one
record. A snippet the caller can only read is listed in
snippets_read_only. The tool refuses only when nothing at all is recorded
under from_path. The path test is the one snippets already uses
(_path_touches), and the plugin version is minted for the skill edit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 15:07:32 -04:00
co-authored by Claude Opus 5.5
parent 47b52197aa
commit 979efe6662
8 changed files with 114 additions and 36 deletions
+26 -3
View File
@@ -115,12 +115,35 @@ async def test_a_judgment_already_at_the_new_path_stands(seeded):
@pytest.mark.integration
async def test_overlapping_empty_and_unwritable_moves_are_refused(seeded):
async def test_overlapping_and_unwritable_moves_are_refused(seeded):
owner, other, pid = seeded["owner"], seeded["other"], seeded["pid"]
with pytest.raises(ValueError, match="overlap"):
await move_shapes(owner, pid, "src/old", "src/old/inner")
with pytest.raises(ValueError, match="no shapes under"):
await move_shapes(owner, pid, "src/gone", "src/new")
assert await move_shapes(owner, pid, "src/gone", "src/new") == {
"moved": 0, "replaced": 0, "kept": [],
}
with pytest.raises(ValueError, match="no write access"):
await move_shapes(other, pid, "src/old", "src/new")
assert ("src/old/app.py", "make_app") in await _ledger(pid)
@pytest.mark.integration
async def test_the_snippets_recorded_there_move_with_it(seeded):
from grimoire.services import snippets as snippets_svc
owner, pid = seeded["owner"], seeded["pid"]
note = await snippets_svc.create_snippet(
owner, name="move_helper", code="def helper():\n pass\n",
language="python", project_id=pid, locations=[
{"repo": "Widget", "path": "src/old/sub/util.py", "symbol": "helper"},
{"repo": "Widget", "path": "src/older/keep.py", "symbol": "untouched"},
],
)
result = await snippets_svc.move_locations(owner, pid, "src/old", "src/new")
assert result == {"moved": [int(note.id)], "read_only": []}
moved = await snippets_svc.get_snippet(owner, int(note.id))
assert [loc["path"] for loc in snippets_svc.snippet_fields(moved)["locations"]] == [
"src/new/sub/util.py", "src/older/keep.py",
]