gabriel / muse public
test_core_query_engine.py python
577 lines 22.6 KB
Raw
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9 docs: expand cache plan with all seven testing tiers and do… Sonnet 4.6 131 days ago
1 """Tests for the generic query engine in muse/core/query_engine.py.
2
3 Also contains regression tests that prove the two dead walkers in
4 ``muse.plugins.code._query`` (``walk_commits`` and ``walk_commits_range``) are
5 fully covered by the live walkers (``walk_commits_bfs`` and
6 ``store.walk_commits_between``) before those dead functions are deleted.
7 """
8
9 import datetime
10 import pathlib
11 import tempfile
12
13 import pytest
14
15 from muse.core.query_engine import QueryMatch, format_matches, walk_history
16 from muse.core.snapshot import compute_commit_id, compute_snapshot_id
17 from muse.core.store import CommitRecord, write_commit, walk_commits_between
18 from muse.plugins.code._query import walk_commits_bfs
19 from muse.core._types import Manifest
20
21
22 # ---------------------------------------------------------------------------
23 # Helpers
24 # ---------------------------------------------------------------------------
25
26
27 def _make_repo(tmp_path: pathlib.Path) -> pathlib.Path:
28 """Set up a minimal .muse/ structure for query_engine tests."""
29 muse = tmp_path / ".muse"
30 muse.mkdir()
31 (muse / "repo.json").write_text('{"repo_id":"test-repo"}')
32 (muse / "HEAD").write_text("ref: refs/heads/main")
33 (muse / "commits").mkdir()
34 (muse / "snapshots").mkdir()
35 (muse / "refs" / "heads").mkdir(parents=True)
36 return tmp_path
37
38
39 def _write_commit(root: pathlib.Path, label: str, parent_id: str | None = None) -> CommitRecord:
40 """Write a content-addressed CommitRecord. *label* is used only in the message."""
41 snap_id = compute_snapshot_id({})
42 committed_at = datetime.datetime.now(datetime.timezone.utc)
43 parent_ids = [parent_id] if parent_id else []
44 commit_id = compute_commit_id(
45 repo_id="test-repo",
46 parent_ids=parent_ids,
47 snapshot_id=snap_id,
48 message=f"commit {label}",
49 committed_at_iso=committed_at.isoformat(),
50 author="test-author",)
51 record = CommitRecord(
52 commit_id=commit_id,
53 repo_id="test-repo",
54 created_on_branch="main",
55 snapshot_id=snap_id,
56 message=f"commit {label}",
57 committed_at=committed_at,
58 parent_commit_id=parent_id,
59 author="test-author",
60 )
61 write_commit(root, record)
62 return record
63
64
65 # ---------------------------------------------------------------------------
66 # walk_history
67 # ---------------------------------------------------------------------------
68
69
70 class TestWalkHistory:
71 def test_empty_branch_returns_empty(self) -> None:
72 with tempfile.TemporaryDirectory() as tmp:
73 root = _make_repo(pathlib.Path(tmp))
74 results = walk_history(root, "main", lambda c, m, r: [])
75 assert results == []
76
77 def test_single_commit_visited(self) -> None:
78 with tempfile.TemporaryDirectory() as tmp:
79 root = _make_repo(pathlib.Path(tmp))
80 c = _write_commit(root, "aaa111")
81 (root / ".muse" / "refs" / "heads" / "main").write_text(c.commit_id)
82
83 visited: list[str] = []
84
85 def evaluator(commit: CommitRecord, manifest: Manifest, r: pathlib.Path) -> list[QueryMatch]:
86 visited.append(commit.commit_id)
87 return []
88
89 walk_history(root, "main", evaluator, load_manifest=False)
90 assert visited == [c.commit_id]
91
92 def test_chain_walked_newest_first(self) -> None:
93 with tempfile.TemporaryDirectory() as tmp:
94 root = _make_repo(pathlib.Path(tmp))
95 c_aaa = _write_commit(root, "aaa111")
96 c_bbb = _write_commit(root, "bbb222", parent_id=c_aaa.commit_id)
97 (root / ".muse" / "refs" / "heads" / "main").write_text(c_bbb.commit_id)
98
99 visited: list[str] = []
100
101 def evaluator(commit: CommitRecord, manifest: Manifest, r: pathlib.Path) -> list[QueryMatch]:
102 visited.append(commit.commit_id)
103 return []
104
105 walk_history(root, "main", evaluator, load_manifest=False)
106 assert visited == [c_bbb.commit_id, c_aaa.commit_id]
107
108 def test_matches_collected(self) -> None:
109 with tempfile.TemporaryDirectory() as tmp:
110 root = _make_repo(pathlib.Path(tmp))
111 c = _write_commit(root, "ccc333")
112 (root / ".muse" / "refs" / "heads" / "main").write_text(c.commit_id)
113
114 def evaluator(commit: CommitRecord, manifest: Manifest, r: pathlib.Path) -> list[QueryMatch]:
115 return [QueryMatch(
116 commit_id=commit.commit_id,
117 author=commit.author,
118 committed_at=commit.committed_at.isoformat(),
119 branch=commit.created_on_branch,
120 detail="test match",
121 extra={},
122 )]
123
124 results = walk_history(root, "main", evaluator, load_manifest=False)
125 assert len(results) == 1
126 assert results[0]["detail"] == "test match"
127
128 def test_max_commits_limits_walk(self) -> None:
129 with tempfile.TemporaryDirectory() as tmp:
130 root = _make_repo(pathlib.Path(tmp))
131 records: list[CommitRecord] = []
132 for i in range(10):
133 parent_id = records[i - 1].commit_id if i > 0 else None
134 records.append(_write_commit(root, f"commit{i:03d}", parent_id=parent_id))
135 (root / ".muse" / "refs" / "heads" / "main").write_text(records[-1].commit_id)
136
137 visited: list[str] = []
138
139 def evaluator(commit: CommitRecord, manifest: Manifest, r: pathlib.Path) -> list[QueryMatch]:
140 visited.append(commit.commit_id)
141 return []
142
143 walk_history(root, "main", evaluator, max_commits=3, load_manifest=False)
144 assert len(visited) == 3
145
146 def test_head_commit_id_override(self) -> None:
147 with tempfile.TemporaryDirectory() as tmp:
148 root = _make_repo(pathlib.Path(tmp))
149 c_aaa = _write_commit(root, "aaa111")
150 c_bbb = _write_commit(root, "bbb222", parent_id=c_aaa.commit_id)
151 # HEAD points to bbb222 but we override to aaa111.
152 (root / ".muse" / "refs" / "heads" / "main").write_text(c_bbb.commit_id)
153
154 visited: list[str] = []
155
156 def evaluator(commit: CommitRecord, manifest: Manifest, r: pathlib.Path) -> list[QueryMatch]:
157 visited.append(commit.commit_id)
158 return []
159
160 walk_history(root, "main", evaluator, head_commit_id=c_aaa.commit_id, load_manifest=False)
161 assert visited == [c_aaa.commit_id]
162
163
164 # ---------------------------------------------------------------------------
165 # format_matches
166 # ---------------------------------------------------------------------------
167
168
169 class TestFormatMatches:
170 def test_empty_returns_no_matches(self) -> None:
171 assert "No matches" in format_matches([])
172
173 def test_single_match_formatted(self) -> None:
174 m = QueryMatch(
175 commit_id="a" * 64,
176 author="gabriel",
177 committed_at="2026-03-18T12:00:00+00:00",
178 created_on_branch="main",
179 detail="my_function (added)",
180 extra={},
181 )
182 out = format_matches([m])
183 assert ("a" * 64)[:8] in out
184 assert "gabriel" in out
185 assert "my_function (added)" in out
186
187 def test_agent_id_shown_when_present(self) -> None:
188 m = QueryMatch(
189 commit_id="a" * 64,
190 author="bot",
191 committed_at="2026-03-18T12:00:00+00:00",
192 created_on_branch="main",
193 detail="something",
194 extra={},
195 agent_id="claude-v4",
196 )
197 out = format_matches([m])
198 assert "claude-v4" in out
199
200 def test_max_results_truncation_message_updated(self) -> None:
201 """format_matches uses '--limit' in the truncation hint (not '--max')."""
202 matches = [
203 QueryMatch(
204 commit_id=f"commit{i:04d}",
205 author="x",
206 committed_at="2026-01-01T00:00:00+00:00",
207 created_on_branch="main",
208 detail=f"match {i}",
209 extra={},
210 )
211 for i in range(10)
212 ]
213 out = format_matches(matches, max_results=5)
214 assert "--limit" in out
215
216 def test_max_results_capped(self) -> None:
217 matches = [
218 QueryMatch(
219 commit_id=f"commit{i:04d}",
220 author="x",
221 committed_at="2026-01-01T00:00:00+00:00",
222 created_on_branch="main",
223 detail=f"match {i}",
224 extra={},
225 )
226 for i in range(100)
227 ]
228 out = format_matches(matches, max_results=5)
229 assert "95 more" in out
230
231
232 # ---------------------------------------------------------------------------
233 # Regression tests: dead walkers covered by live walkers
234 #
235 # These tests prove that walk_commits_bfs and store.walk_commits_between
236 # fully cover the use-cases of the dead walk_commits and walk_commits_range
237 # before those functions are deleted. If these tests pass, deletion is safe.
238 # ---------------------------------------------------------------------------
239
240
241 def _make_repo_for_walker(tmp_path: pathlib.Path) -> pathlib.Path:
242 muse = tmp_path / ".muse"
243 muse.mkdir()
244 (muse / "repo.json").write_text('{"repo_id":"walker-test"}')
245 (muse / "HEAD").write_text("main")
246 (muse / "commits").mkdir()
247 (muse / "snapshots").mkdir()
248 (muse / "refs" / "heads").mkdir(parents=True)
249 return tmp_path
250
251
252 def _commit(
253 root: pathlib.Path,
254 label: str,
255 parent: str | None = None,
256 parent2: str | None = None,
257 ) -> CommitRecord:
258 """Write a content-addressed CommitRecord. *label* is used only in the message."""
259 snap_id = compute_snapshot_id({})
260 committed_at = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
261 parent_ids = [p for p in [parent, parent2] if p is not None]
262 commit_id = compute_commit_id(
263 repo_id="walker-test",
264 parent_ids=parent_ids,
265 snapshot_id=snap_id,
266 message=f"msg {label}",
267 committed_at_iso=committed_at.isoformat(),
268 author="tester",)
269 rec = CommitRecord(
270 commit_id=commit_id,
271 repo_id="walker-test",
272 created_on_branch="main",
273 snapshot_id=snap_id,
274 message=f"msg {label}",
275 committed_at=committed_at,
276 parent_commit_id=parent,
277 parent2_commit_id=parent2,
278 author="tester",
279 )
280 write_commit(root, rec)
281 return rec
282
283
284 class TestWalkHistoryFollowMerges:
285 """Belt-and-suspenders tests for walk_history(follow_merges=True/False)."""
286
287 def test_follow_merges_false_skips_parent2(
288 self, tmp_path: pathlib.Path
289 ) -> None:
290 """follow_merges=False (default) stays on the main parent chain only."""
291 root = _make_repo_for_walker(tmp_path)
292 c_main1 = _commit(root, "main1")
293 c_feat1 = _commit(root, "feat1", parent=c_main1.commit_id)
294 c_merge = _commit(root, "merge_c", parent=c_main1.commit_id, parent2=c_feat1.commit_id)
295 (root / ".muse" / "refs" / "heads" / "main").write_text(c_merge.commit_id)
296
297 visited: list[str] = []
298
299 def ev(c: CommitRecord, m: Manifest, r: pathlib.Path) -> list[QueryMatch]:
300 visited.append(c.commit_id)
301 return []
302
303 walk_history(root, "main", ev, follow_merges=False, load_manifest=False)
304 assert c_feat1.commit_id not in visited
305 assert c_merge.commit_id in visited
306 assert c_main1.commit_id in visited
307
308 def test_follow_merges_true_visits_parent2(
309 self, tmp_path: pathlib.Path
310 ) -> None:
311 """follow_merges=True visits both parents of a merge commit."""
312 root = _make_repo_for_walker(tmp_path)
313 c_base = _commit(root, "base")
314 c_feature = _commit(root, "feature", parent=c_base.commit_id)
315 c_merge = _commit(root, "merge_c", parent=c_base.commit_id, parent2=c_feature.commit_id)
316 (root / ".muse" / "refs" / "heads" / "main").write_text(c_merge.commit_id)
317
318 visited: list[str] = []
319
320 def ev(c: CommitRecord, m: Manifest, r: pathlib.Path) -> list[QueryMatch]:
321 visited.append(c.commit_id)
322 return []
323
324 walk_history(root, "main", ev, follow_merges=True, load_manifest=False)
325 assert set(visited) == {c_merge.commit_id, c_base.commit_id, c_feature.commit_id}
326
327 def test_follow_merges_true_linear_chain(
328 self, tmp_path: pathlib.Path
329 ) -> None:
330 """follow_merges=True on a linear chain behaves identically to False."""
331 root = _make_repo_for_walker(tmp_path)
332 c_a = _commit(root, "a")
333 c_b = _commit(root, "b", parent=c_a.commit_id)
334 c_c = _commit(root, "c", parent=c_b.commit_id)
335 (root / ".muse" / "refs" / "heads" / "main").write_text(c_c.commit_id)
336
337 visited_ff: list[str] = []
338 visited_ft: list[str] = []
339
340 def ev_ff(c: CommitRecord, m: Manifest, r: pathlib.Path) -> list[QueryMatch]:
341 visited_ff.append(c.commit_id)
342 return []
343
344 def ev_ft(c: CommitRecord, m: Manifest, r: pathlib.Path) -> list[QueryMatch]:
345 visited_ft.append(c.commit_id)
346 return []
347
348 walk_history(root, "main", ev_ff, follow_merges=False, load_manifest=False)
349 walk_history(root, "main", ev_ft, follow_merges=True, load_manifest=False)
350 assert set(visited_ff) == set(visited_ft) == {c_a.commit_id, c_b.commit_id, c_c.commit_id}
351
352 def test_follow_merges_since_filter_applies(
353 self, tmp_path: pathlib.Path
354 ) -> None:
355 """since filter still prunes commits even with follow_merges=True."""
356 root = _make_repo_for_walker(tmp_path)
357 # Pin explicit timestamps so since filter is deterministic.
358 t_old = datetime.datetime(2020, 1, 1, tzinfo=datetime.timezone.utc)
359 t_new = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc)
360
361 snap_id = compute_snapshot_id({})
362 cid_old = compute_commit_id(
363 repo_id="walker-test",
364 parent_ids=[],
365 snapshot_id=snap_id,
366 message="old",
367 committed_at_iso=t_old.isoformat(),
368 author="tester",)
369 rec_old = CommitRecord(
370 commit_id=cid_old,
371 repo_id="walker-test",
372 created_on_branch="main",
373 snapshot_id=snap_id,
374 message="old",
375 committed_at=t_old,
376 author="tester",
377 )
378 write_commit(root, rec_old)
379
380 cid_new = compute_commit_id(
381 repo_id="walker-test",
382 parent_ids=[cid_old],
383 snapshot_id=snap_id,
384 message="new",
385 committed_at_iso=t_new.isoformat(),
386 author="tester",)
387 rec_new = CommitRecord(
388 commit_id=cid_new,
389 repo_id="walker-test",
390 created_on_branch="main",
391 snapshot_id=snap_id,
392 message="new",
393 committed_at=t_new,
394 parent_commit_id=cid_old,
395 author="tester",
396 )
397 write_commit(root, rec_new)
398 (root / ".muse" / "refs" / "heads" / "main").write_text(cid_new)
399
400 visited: list[str] = []
401
402 def ev(c: CommitRecord, m: Manifest, r: pathlib.Path) -> list[QueryMatch]:
403 visited.append(c.commit_id)
404 return []
405
406 since = datetime.datetime(2025, 1, 1, tzinfo=datetime.timezone.utc)
407 walk_history(root, "main", ev, follow_merges=True, since=since, load_manifest=False)
408 assert cid_new in visited
409 assert cid_old not in visited
410
411
412 def test_follow_merges_true_diamond_dag_no_duplicates(
413 self, tmp_path: pathlib.Path
414 ) -> None:
415 """BFS never visits the same commit twice (diamond DAG case)."""
416 root = _make_repo_for_walker(tmp_path)
417 # Diamond: base ← left ← merge, base ← right ← merge
418 c_base = _commit(root, "base")
419 c_left = _commit(root, "left", parent=c_base.commit_id)
420 c_right = _commit(root, "right", parent=c_base.commit_id)
421 c_merge = _commit(root, "merge_c", parent=c_left.commit_id, parent2=c_right.commit_id)
422 (root / ".muse" / "refs" / "heads" / "main").write_text(c_merge.commit_id)
423
424 visited: list[str] = []
425
426 def ev(c: CommitRecord, m: Manifest, r: pathlib.Path) -> list[QueryMatch]:
427 visited.append(c.commit_id)
428 return []
429
430 walk_history(root, "main", ev, follow_merges=True, load_manifest=False)
431 # Each commit visited exactly once.
432 assert len(visited) == len(set(visited))
433 assert set(visited) == {c_base.commit_id, c_left.commit_id, c_right.commit_id, c_merge.commit_id}
434
435 def test_follow_merges_max_commits_respected(
436 self, tmp_path: pathlib.Path
437 ) -> None:
438 """max_commits caps BFS walk even with follow_merges=True."""
439 root = _make_repo_for_walker(tmp_path)
440 c1 = _commit(root, "c1")
441 c2 = _commit(root, "c2", parent=c1.commit_id)
442 c3 = _commit(root, "c3", parent=c2.commit_id)
443 c4 = _commit(root, "c4", parent=c3.commit_id)
444 (root / ".muse" / "refs" / "heads" / "main").write_text(c4.commit_id)
445
446 visited: list[str] = []
447
448 def ev(c: CommitRecord, m: Manifest, r: pathlib.Path) -> list[QueryMatch]:
449 visited.append(c.commit_id)
450 return []
451
452 walk_history(root, "main", ev, follow_merges=True, max_commits=2, load_manifest=False)
453 assert len(visited) == 2
454
455 def test_follow_merges_evaluator_sees_match(
456 self, tmp_path: pathlib.Path
457 ) -> None:
458 """Matches from parent2 commits are included in results."""
459 root = _make_repo_for_walker(tmp_path)
460 c_base = _commit(root, "base")
461 c_feature = _commit(root, "feature", parent=c_base.commit_id)
462 c_merge = _commit(root, "merge_c", parent=c_base.commit_id, parent2=c_feature.commit_id)
463 (root / ".muse" / "refs" / "heads" / "main").write_text(c_merge.commit_id)
464
465 def ev(c: CommitRecord, m: Manifest, r: pathlib.Path) -> list[QueryMatch]:
466 if c.commit_id == c_feature.commit_id:
467 return [QueryMatch(
468 commit_id=c.commit_id,
469 author=c.author,
470 committed_at=c.committed_at.isoformat(),
471 branch=c.created_on_branch,
472 detail="feature found",
473 extra={},
474 )]
475 return []
476
477 results = walk_history(root, "main", ev, follow_merges=True, load_manifest=False)
478 assert len(results) == 1
479 assert results[0]["detail"] == "feature found"
480
481 def test_follow_merges_false_misses_parent2_commit(
482 self, tmp_path: pathlib.Path
483 ) -> None:
484 """With follow_merges=False, parent2 commits are never evaluated."""
485 root = _make_repo_for_walker(tmp_path)
486 c_base = _commit(root, "base")
487 c_feature = _commit(root, "feature", parent=c_base.commit_id)
488 c_merge = _commit(root, "merge_c", parent=c_base.commit_id, parent2=c_feature.commit_id)
489 (root / ".muse" / "refs" / "heads" / "main").write_text(c_merge.commit_id)
490
491 def ev(c: CommitRecord, m: Manifest, r: pathlib.Path) -> list[QueryMatch]:
492 if c.commit_id == c_feature.commit_id:
493 return [QueryMatch(
494 commit_id=c.commit_id,
495 author=c.author,
496 committed_at=c.committed_at.isoformat(),
497 branch=c.created_on_branch,
498 detail="feature found",
499 extra={},
500 )]
501 return []
502
503 results = walk_history(root, "main", ev, follow_merges=False, load_manifest=False)
504 assert results == [] # feature commit is never visited
505
506
507 class TestLiveWalkersContracts:
508 """Regression: walk_commits_bfs and walk_commits_between cover deleted walkers.
509
510 These tests lock down the contracts of the surviving walkers, proving
511 the deleted walk_commits and walk_commits_range are fully superseded.
512 """
513
514 def test_walk_commits_bfs_linear_chain(self, tmp_path: pathlib.Path) -> None:
515 """walk_commits_bfs on a linear chain returns all commits, newest first."""
516 root = _make_repo_for_walker(tmp_path)
517 c_aaa = _commit(root, "aaa")
518 c_bbb = _commit(root, "bbb", parent=c_aaa.commit_id)
519 c_ccc = _commit(root, "ccc", parent=c_bbb.commit_id)
520
521 live_commits, truncated = walk_commits_bfs(root, c_ccc.commit_id)
522 live_ids = [c.commit_id for c in live_commits]
523
524 assert truncated is False
525 assert set(live_ids) == {c_aaa.commit_id, c_bbb.commit_id, c_ccc.commit_id}
526
527 def test_walk_commits_bfs_follows_parent2(self, tmp_path: pathlib.Path) -> None:
528 """walk_commits_bfs reaches parent2 branches — supersedes dead linear walker."""
529 root = _make_repo_for_walker(tmp_path)
530 c_base = _commit(root, "base")
531 c_feature = _commit(root, "feature", parent=c_base.commit_id)
532 c_merge = _commit(root, "merge_commit", parent=c_base.commit_id, parent2=c_feature.commit_id)
533
534 live_commits, _ = walk_commits_bfs(root, c_merge.commit_id)
535 live_ids = set(c.commit_id for c in live_commits)
536
537 assert c_feature.commit_id in live_ids
538 assert c_base.commit_id in live_ids
539 assert c_merge.commit_id in live_ids
540
541 def test_walk_commits_between_range(self, tmp_path: pathlib.Path) -> None:
542 """walk_commits_between excludes from_commit_id — supersedes walk_commits_range."""
543 root = _make_repo_for_walker(tmp_path)
544 c1 = _commit(root, "c1")
545 c2 = _commit(root, "c2", parent=c1.commit_id)
546 c3 = _commit(root, "c3", parent=c2.commit_id)
547 c4 = _commit(root, "c4", parent=c3.commit_id)
548
549 result = walk_commits_between(root, to_commit_id=c4.commit_id, from_commit_id=c1.commit_id)
550 ids = [c.commit_id for c in result]
551
552 assert ids == [c4.commit_id, c3.commit_id, c2.commit_id]
553 assert c1.commit_id not in ids
554
555 def test_walk_commits_between_none_from(self, tmp_path: pathlib.Path) -> None:
556 """walk_commits_between with from_commit_id=None returns entire chain."""
557 root = _make_repo_for_walker(tmp_path)
558 c_x1 = _commit(root, "x1")
559 c_x2 = _commit(root, "x2", parent=c_x1.commit_id)
560
561 ids = [c.commit_id for c in walk_commits_between(root, c_x2.commit_id, None)]
562 assert ids == [c_x2.commit_id, c_x1.commit_id]
563
564 def test_walk_commits_bfs_stop_at_excludes_boundary(
565 self, tmp_path: pathlib.Path
566 ) -> None:
567 """walk_commits_bfs stop_at_commit_id excludes the boundary — same contract as walk_commits_between."""
568 root = _make_repo_for_walker(tmp_path)
569 c_p1 = _commit(root, "p1")
570 c_p2 = _commit(root, "p2", parent=c_p1.commit_id)
571 c_p3 = _commit(root, "p3", parent=c_p2.commit_id)
572
573 bfs_commits, _ = walk_commits_bfs(root, c_p3.commit_id, stop_at_commit_id=c_p1.commit_id)
574 bfs_ids = [c.commit_id for c in bfs_commits]
575
576 assert c_p1.commit_id not in bfs_ids
577 assert set(bfs_ids) == {c_p3.commit_id, c_p2.commit_id}
File History 3 commits
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9 docs: expand cache plan with all seven testing tiers and do… Sonnet 4.6 131 days ago
sha256:7f9e2ef5286aedad9c1e6011b4c46ca27f39dbdad6e3409357e36b26e46b3b7c docs: docstring sprint for-each-ref→hotspots — idiomatic ru… Sonnet 4.6 patch 137 days ago
sha256:a09b1b4f6838754495547f200aa0ce88e2f56ffc5b20b900f6f0cff2c3cdede9 fix(cursorignore): remove git-ism (.git/worktrees) Human minor 140 days ago