gabriel / musehub public
test_phase5_gravity_derived.py python
634 lines 23.6 KB
Raw
sha256:bb2baaabdd19320bde50cb69d447fd1c1e571467df729be1a23b5e93064b046a feat(intel): standardize headers, gauge icon, velocity card… Sonnet 4.6 minor ⚠ breaking 144 days ago
1 """TDD spec — GravityProvider rewrite: SQL-derived gravity, no muse CLI.
2
3 GravityProvider must compute gravity scores directly from the blast columns
4 already written by intel.code, rather than calling `muse code gravity`.
5
6 Formula (mirrors muse/muse/cli/commands/gravity.py exactly):
7
8 total = count of tracked-kind symbols for this repo
9 denom = max(1, total - 1) # exclude self, guard /0
10 gravity_pct = round(blast / denom * 100, 1)
11
12 Column mapping:
13 gravity_direct_dependents ← blast_direct
14 gravity_transitive_dependents ← blast (blast_direct + blast_cross)
15 gravity_pct ← round(blast / max(1, total - 1) * 100, 1)
16 gravity_max_depth — not derivable from blast; left NULL
17 gravity_depth_distribution — not derivable from blast; left NULL
18
19 Tracked kinds (denominator scope, matching gravity.py _TRACKED_KINDS):
20 function, async_function, method, async_method, class
21
22 Layers:
23 1. No subprocess — compute() never spawns a process
24 2. Formula — gravity_pct matches gravity.py rounding + denominator
25 3. Mapping — gravity_direct_dependents = blast_direct;
26 gravity_transitive_dependents = blast
27 4. Denominator — untracked kinds (import, None) excluded from total
28 5. Edge: single — denom = max(1, 1-1) = 1, no /0
29 6. Writes only — rows that exist get updated; no new rows inserted
30 7. Preserve — churn/blast columns untouched after compute()
31 8. Idempotent — run twice yields identical rows, no duplicates
32 9. Empty — no blast data → returns []
33 10. Null max_depth — gravity_max_depth stays NULL (not derivable)
34 """
35 from __future__ import annotations
36
37 import pytest
38 import pytest_asyncio
39 from sqlalchemy.dialects.postgresql import insert as pg_insert
40 from sqlalchemy.ext.asyncio import AsyncSession
41 from sqlalchemy import select, func
42
43 from musehub.db import musehub_models as db
44 from tests.factories import create_repo
45
46
47 _TRACKED_KINDS = ("function", "async_function", "method", "async_method", "class")
48
49
50 # ---------------------------------------------------------------------------
51 # Helpers
52 # ---------------------------------------------------------------------------
53
54 async def _seed_symbols(
55 session: AsyncSession,
56 repo_id: str,
57 symbols: list[dict],
58 ) -> None:
59 """Insert musehub_symbol_intel rows with blast + kind data."""
60 for s in symbols:
61 stmt = (
62 pg_insert(db.MusehubSymbolIntel)
63 .values(repo_id=repo_id, **s)
64 .on_conflict_do_update(
65 index_elements=["repo_id", "address"],
66 set_={k: v for k, v in s.items() if k != "address"},
67 )
68 )
69 await session.execute(stmt)
70 await session.flush()
71
72
73 async def _get_row(session: AsyncSession, repo_id: str, address: str) -> db.MusehubSymbolIntel | None:
74 result = await session.execute(
75 select(db.MusehubSymbolIntel).where(
76 db.MusehubSymbolIntel.repo_id == repo_id,
77 db.MusehubSymbolIntel.address == address,
78 )
79 )
80 return result.scalar_one_or_none()
81
82
83 def _gravity_pct(blast: int, total: int) -> float:
84 """Reference implementation — mirrors gravity.py exactly."""
85 denom = max(1, total - 1)
86 return round(blast / denom * 100, 1)
87
88
89 # ---------------------------------------------------------------------------
90 # Layer 1 — No subprocess
91 # ---------------------------------------------------------------------------
92
93 class TestNoSubprocess:
94
95 @pytest.mark.asyncio
96 async def test_P5_01_compute_never_spawns_subprocess(
97 self, db_session: AsyncSession
98 ) -> None:
99 """GravityProvider must not call muse CLI — pure SQL derivation."""
100 import asyncio
101 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
102
103 repo = await create_repo(db_session)
104 await _seed_symbols(db_session, repo.repo_id, [
105 {"address": "a.py::fn", "symbol_kind": "function",
106 "blast": 5, "blast_direct": 2, "blast_cross": 3},
107 ])
108
109 spawned: list[tuple] = []
110 original = asyncio.create_subprocess_exec
111
112 async def _spy(*args, **kwargs):
113 spawned.append(args)
114 return await original(*args, **kwargs)
115
116 import unittest.mock as mock
117 with mock.patch("asyncio.create_subprocess_exec", side_effect=_spy):
118 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
119 db_session, repo.repo_id, "ref",
120 {"owner": repo.owner, "slug": repo.slug},
121 )
122
123 assert spawned == [], (
124 f"GravityProvider spawned {len(spawned)} subprocess(es); expected 0. "
125 "Gravity must be derived from blast columns, not from muse CLI."
126 )
127
128
129 # ---------------------------------------------------------------------------
130 # Layer 2 — Formula: gravity_pct matches gravity.py exactly
131 # ---------------------------------------------------------------------------
132
133 class TestFormula:
134
135 @pytest.mark.asyncio
136 async def test_P5_02_gravity_pct_matches_formula(
137 self, db_session: AsyncSession
138 ) -> None:
139 """gravity_pct = round(blast / max(1, total-1) * 100, 1)."""
140 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
141
142 repo = await create_repo(db_session)
143 # 10 tracked symbols; symbol A has blast=9
144 symbols = [
145 {"address": f"a.py::fn{i}", "symbol_kind": "function",
146 "blast": 1, "blast_direct": 1, "blast_cross": 0}
147 for i in range(9)
148 ]
149 symbols.append(
150 {"address": "a.py::target", "symbol_kind": "function",
151 "blast": 9, "blast_direct": 3, "blast_cross": 6}
152 )
153 await _seed_symbols(db_session, repo.repo_id, symbols)
154
155 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
156 db_session, repo.repo_id, "ref", {},
157 )
158
159 row = await _get_row(db_session, repo.repo_id, "a.py::target")
160 assert row is not None
161 expected = _gravity_pct(blast=9, total=10) # round(9/9*100, 1) = 100.0
162 assert row.gravity_pct == pytest.approx(expected), (
163 f"gravity_pct={row.gravity_pct}, expected {expected}"
164 )
165
166 @pytest.mark.asyncio
167 async def test_P5_03_gravity_pct_fractional_rounding(
168 self, db_session: AsyncSession
169 ) -> None:
170 """Rounding to 1 decimal place matches Python round()."""
171 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
172
173 repo = await create_repo(db_session)
174 # 9 symbols total; target has blast=4 → 4/8*100 = 50.0
175 symbols = [
176 {"address": f"a.py::fn{i}", "symbol_kind": "method",
177 "blast": 0, "blast_direct": 0, "blast_cross": 0}
178 for i in range(8)
179 ]
180 symbols.append(
181 {"address": "a.py::target", "symbol_kind": "method",
182 "blast": 4, "blast_direct": 1, "blast_cross": 3}
183 )
184 await _seed_symbols(db_session, repo.repo_id, symbols)
185
186 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
187 db_session, repo.repo_id, "ref", {},
188 )
189
190 row = await _get_row(db_session, repo.repo_id, "a.py::target")
191 expected = _gravity_pct(blast=4, total=9) # round(4/8*100, 1) = 50.0
192 assert row.gravity_pct == pytest.approx(expected)
193
194 @pytest.mark.asyncio
195 async def test_P5_04_all_symbols_get_gravity_pct(
196 self, db_session: AsyncSession
197 ) -> None:
198 """Every tracked-kind row gets a gravity_pct, including blast=0 rows."""
199 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
200
201 repo = await create_repo(db_session)
202 await _seed_symbols(db_session, repo.repo_id, [
203 {"address": "a.py::fn_a", "symbol_kind": "function",
204 "blast": 3, "blast_direct": 1, "blast_cross": 2},
205 {"address": "a.py::fn_b", "symbol_kind": "function",
206 "blast": 0, "blast_direct": 0, "blast_cross": 0},
207 ])
208
209 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
210 db_session, repo.repo_id, "ref", {},
211 )
212
213 for addr in ("a.py::fn_a", "a.py::fn_b"):
214 row = await _get_row(db_session, repo.repo_id, addr)
215 assert row is not None
216 assert row.gravity_pct is not None, f"{addr} missing gravity_pct"
217
218
219 # ---------------------------------------------------------------------------
220 # Layer 3 — Column mapping
221 # ---------------------------------------------------------------------------
222
223 class TestColumnMapping:
224
225 @pytest.mark.asyncio
226 async def test_P5_05_gravity_direct_dependents_equals_blast_direct(
227 self, db_session: AsyncSession
228 ) -> None:
229 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
230
231 repo = await create_repo(db_session)
232 await _seed_symbols(db_session, repo.repo_id, [
233 {"address": "a.py::fn", "symbol_kind": "function",
234 "blast": 7, "blast_direct": 3, "blast_cross": 4},
235 ])
236
237 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
238 db_session, repo.repo_id, "ref", {},
239 )
240
241 row = await _get_row(db_session, repo.repo_id, "a.py::fn")
242 assert row.gravity_direct_dependents == 3
243
244 @pytest.mark.asyncio
245 async def test_P5_06_gravity_transitive_dependents_equals_blast(
246 self, db_session: AsyncSession
247 ) -> None:
248 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
249
250 repo = await create_repo(db_session)
251 await _seed_symbols(db_session, repo.repo_id, [
252 {"address": "a.py::fn", "symbol_kind": "function",
253 "blast": 7, "blast_direct": 3, "blast_cross": 4},
254 ])
255
256 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
257 db_session, repo.repo_id, "ref", {},
258 )
259
260 row = await _get_row(db_session, repo.repo_id, "a.py::fn")
261 assert row.gravity_transitive_dependents == 7 # blast = blast_direct + blast_cross
262
263
264 # ---------------------------------------------------------------------------
265 # Layer 4 — Denominator scope: only tracked kinds
266 # ---------------------------------------------------------------------------
267
268 class TestDenominator:
269
270 @pytest.mark.asyncio
271 async def test_P5_07_import_kind_excluded_from_denominator(
272 self, db_session: AsyncSession
273 ) -> None:
274 """import-kind rows don't count toward total_prod_symbols."""
275 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
276
277 repo = await create_repo(db_session)
278 # 2 tracked + 5 import = 2 tracked total for denominator
279 await _seed_symbols(db_session, repo.repo_id, [
280 {"address": "a.py::fn_a", "symbol_kind": "function",
281 "blast": 1, "blast_direct": 1, "blast_cross": 0},
282 {"address": "a.py::fn_b", "symbol_kind": "function",
283 "blast": 1, "blast_direct": 1, "blast_cross": 0},
284 ] + [
285 {"address": f"a.py::import_{i}", "symbol_kind": "import",
286 "blast": 0, "blast_direct": 0, "blast_cross": 0}
287 for i in range(5)
288 ])
289
290 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
291 db_session, repo.repo_id, "ref", {},
292 )
293
294 row = await _get_row(db_session, repo.repo_id, "a.py::fn_a")
295 # total tracked = 2, denom = max(1, 2-1) = 1
296 expected = _gravity_pct(blast=1, total=2)
297 assert row.gravity_pct == pytest.approx(expected)
298
299 @pytest.mark.asyncio
300 async def test_P5_08_null_kind_excluded_from_denominator(
301 self, db_session: AsyncSession
302 ) -> None:
303 """Rows with symbol_kind=NULL don't count toward total."""
304 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
305
306 repo = await create_repo(db_session)
307 await _seed_symbols(db_session, repo.repo_id, [
308 {"address": "a.py::fn", "symbol_kind": "function",
309 "blast": 1, "blast_direct": 1, "blast_cross": 0},
310 {"address": "a.py::unknown", "symbol_kind": None,
311 "blast": 0, "blast_direct": 0, "blast_cross": 0},
312 ])
313
314 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
315 db_session, repo.repo_id, "ref", {},
316 )
317
318 row = await _get_row(db_session, repo.repo_id, "a.py::fn")
319 # total tracked = 1, denom = max(1, 1-1) = 1
320 expected = _gravity_pct(blast=1, total=1)
321 assert row.gravity_pct == pytest.approx(expected)
322
323 @pytest.mark.asyncio
324 async def test_P5_09_all_tracked_kinds_count_in_denominator(
325 self, db_session: AsyncSession
326 ) -> None:
327 """All 5 tracked kinds contribute to the denominator."""
328 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
329
330 repo = await create_repo(db_session)
331 symbols = [
332 {"address": f"a.py::{kind}_sym", "symbol_kind": kind,
333 "blast": 0, "blast_direct": 0, "blast_cross": 0}
334 for kind in _TRACKED_KINDS
335 ]
336 symbols.append(
337 {"address": "a.py::target", "symbol_kind": "function",
338 "blast": 5, "blast_direct": 2, "blast_cross": 3}
339 )
340 await _seed_symbols(db_session, repo.repo_id, symbols)
341
342 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
343 db_session, repo.repo_id, "ref", {},
344 )
345
346 row = await _get_row(db_session, repo.repo_id, "a.py::target")
347 # 6 tracked symbols total (5 kinds + target itself)
348 expected = _gravity_pct(blast=5, total=6)
349 assert row.gravity_pct == pytest.approx(expected)
350
351
352 # ---------------------------------------------------------------------------
353 # Layer 5 — Edge: single symbol
354 # ---------------------------------------------------------------------------
355
356 class TestEdgeCases:
357
358 @pytest.mark.asyncio
359 async def test_P5_10_single_symbol_denom_is_one(
360 self, db_session: AsyncSession
361 ) -> None:
362 """Single symbol: denom = max(1, 1-1) = 1, no ZeroDivisionError."""
363 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
364
365 repo = await create_repo(db_session)
366 await _seed_symbols(db_session, repo.repo_id, [
367 {"address": "a.py::only", "symbol_kind": "function",
368 "blast": 0, "blast_direct": 0, "blast_cross": 0},
369 ])
370
371 results = await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
372 db_session, repo.repo_id, "ref", {},
373 )
374
375 row = await _get_row(db_session, repo.repo_id, "a.py::only")
376 assert row.gravity_pct == pytest.approx(0.0)
377 assert results != []
378
379 @pytest.mark.asyncio
380 async def test_P5_11_zero_blast_yields_zero_pct(
381 self, db_session: AsyncSession
382 ) -> None:
383 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
384
385 repo = await create_repo(db_session)
386 await _seed_symbols(db_session, repo.repo_id, [
387 {"address": "a.py::leaf", "symbol_kind": "function",
388 "blast": 0, "blast_direct": 0, "blast_cross": 0},
389 {"address": "b.py::other", "symbol_kind": "function",
390 "blast": 2, "blast_direct": 1, "blast_cross": 1},
391 ])
392
393 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
394 db_session, repo.repo_id, "ref", {},
395 )
396
397 row = await _get_row(db_session, repo.repo_id, "a.py::leaf")
398 assert row.gravity_pct == pytest.approx(0.0)
399
400
401 # ---------------------------------------------------------------------------
402 # Layer 6 — Writes only existing rows, no new inserts
403 # ---------------------------------------------------------------------------
404
405 class TestWriteBehavior:
406
407 @pytest.mark.asyncio
408 async def test_P5_12_row_count_unchanged_after_compute(
409 self, db_session: AsyncSession
410 ) -> None:
411 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
412
413 repo = await create_repo(db_session)
414 await _seed_symbols(db_session, repo.repo_id, [
415 {"address": "a.py::fn_a", "symbol_kind": "function",
416 "blast": 3, "blast_direct": 1, "blast_cross": 2},
417 {"address": "a.py::fn_b", "symbol_kind": "function",
418 "blast": 1, "blast_direct": 1, "blast_cross": 0},
419 ])
420
421 before = (await db_session.execute(
422 select(func.count()).where(
423 db.MusehubSymbolIntel.repo_id == repo.repo_id
424 )
425 )).scalar_one()
426
427 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
428 db_session, repo.repo_id, "ref", {},
429 )
430
431 after = (await db_session.execute(
432 select(func.count()).where(
433 db.MusehubSymbolIntel.repo_id == repo.repo_id
434 )
435 )).scalar_one()
436
437 assert after == before, (
438 f"Row count changed: {before} → {after}. "
439 "GravityProvider must update existing rows, not insert new ones."
440 )
441
442 @pytest.mark.asyncio
443 async def test_P5_13_returns_intel_results_tuple(
444 self, db_session: AsyncSession
445 ) -> None:
446 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
447
448 repo = await create_repo(db_session)
449 await _seed_symbols(db_session, repo.repo_id, [
450 {"address": "a.py::fn", "symbol_kind": "function",
451 "blast": 1, "blast_direct": 1, "blast_cross": 0},
452 ])
453
454 results = await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
455 db_session, repo.repo_id, "ref", {},
456 )
457
458 assert isinstance(results, list) and len(results) > 0
459 job_type, data = results[0]
460 assert job_type == "intel.code.gravity"
461 assert "count" in data
462 assert data["count"] >= 1
463
464
465 # ---------------------------------------------------------------------------
466 # Layer 7 — Preserve non-gravity columns
467 # ---------------------------------------------------------------------------
468
469 class TestPreserve:
470
471 @pytest.mark.asyncio
472 async def test_P5_14_churn_preserved_after_compute(
473 self, db_session: AsyncSession
474 ) -> None:
475 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
476
477 repo = await create_repo(db_session)
478 await _seed_symbols(db_session, repo.repo_id, [
479 {"address": "a.py::fn", "symbol_kind": "function",
480 "blast": 5, "blast_direct": 2, "blast_cross": 3,
481 "churn": 42, "churn_30d": 7},
482 ])
483
484 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
485 db_session, repo.repo_id, "ref", {},
486 )
487
488 row = await _get_row(db_session, repo.repo_id, "a.py::fn")
489 assert row.churn == 42, "churn must not be overwritten by gravity compute"
490 assert row.churn_30d == 7, "churn_30d must not be overwritten"
491
492 @pytest.mark.asyncio
493 async def test_P5_15_blast_columns_preserved_after_compute(
494 self, db_session: AsyncSession
495 ) -> None:
496 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
497
498 repo = await create_repo(db_session)
499 await _seed_symbols(db_session, repo.repo_id, [
500 {"address": "a.py::fn", "symbol_kind": "function",
501 "blast": 5, "blast_direct": 2, "blast_cross": 3},
502 ])
503
504 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
505 db_session, repo.repo_id, "ref", {},
506 )
507
508 row = await _get_row(db_session, repo.repo_id, "a.py::fn")
509 assert row.blast == 5
510 assert row.blast_direct == 2
511 assert row.blast_cross == 3
512
513
514 # ---------------------------------------------------------------------------
515 # Layer 8 — Idempotent
516 # ---------------------------------------------------------------------------
517
518 class TestIdempotent:
519
520 @pytest.mark.asyncio
521 async def test_P5_16_second_run_yields_same_pct(
522 self, db_session: AsyncSession
523 ) -> None:
524 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
525
526 repo = await create_repo(db_session)
527 await _seed_symbols(db_session, repo.repo_id, [
528 {"address": "a.py::fn", "symbol_kind": "function",
529 "blast": 3, "blast_direct": 1, "blast_cross": 2},
530 ])
531
532 for _ in range(3):
533 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
534 db_session, repo.repo_id, "ref", {},
535 )
536
537 row = await _get_row(db_session, repo.repo_id, "a.py::fn")
538 expected = _gravity_pct(blast=3, total=1)
539 assert row.gravity_pct == pytest.approx(expected)
540
541 count = (await db_session.execute(
542 select(func.count()).where(
543 db.MusehubSymbolIntel.repo_id == repo.repo_id
544 )
545 )).scalar_one()
546 assert count == 1
547
548
549 # ---------------------------------------------------------------------------
550 # Layer 9 — Empty: no tracked rows → returns []
551 # ---------------------------------------------------------------------------
552
553 class TestEmpty:
554
555 @pytest.mark.asyncio
556 async def test_P5_17_no_rows_returns_empty(
557 self, db_session: AsyncSession
558 ) -> None:
559 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
560
561 repo = await create_repo(db_session)
562
563 results = await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
564 db_session, repo.repo_id, "ref", {},
565 )
566
567 assert results == []
568
569 @pytest.mark.asyncio
570 async def test_P5_18_only_import_kind_rows_returns_empty(
571 self, db_session: AsyncSession
572 ) -> None:
573 """No tracked-kind rows means no gravity to compute."""
574 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
575
576 repo = await create_repo(db_session)
577 await _seed_symbols(db_session, repo.repo_id, [
578 {"address": f"a.py::import_{i}", "symbol_kind": "import",
579 "blast": 0, "blast_direct": 0, "blast_cross": 0}
580 for i in range(3)
581 ])
582
583 results = await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
584 db_session, repo.repo_id, "ref", {},
585 )
586
587 assert results == []
588
589
590 # ---------------------------------------------------------------------------
591 # Layer 10 — max_depth and depth_distribution not derivable
592 # ---------------------------------------------------------------------------
593
594 class TestNotDerivable:
595
596 @pytest.mark.asyncio
597 async def test_P5_19_gravity_max_depth_stays_null(
598 self, db_session: AsyncSession
599 ) -> None:
600 """gravity_max_depth is not derivable from blast columns."""
601 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
602
603 repo = await create_repo(db_session)
604 await _seed_symbols(db_session, repo.repo_id, [
605 {"address": "a.py::fn", "symbol_kind": "function",
606 "blast": 5, "blast_direct": 2, "blast_cross": 3},
607 ])
608
609 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
610 db_session, repo.repo_id, "ref", {},
611 )
612
613 row = await _get_row(db_session, repo.repo_id, "a.py::fn")
614 assert row.gravity_max_depth is None
615
616 @pytest.mark.asyncio
617 async def test_P5_20_gravity_depth_distribution_stays_null(
618 self, db_session: AsyncSession
619 ) -> None:
620 """gravity_depth_distribution is not derivable from blast columns."""
621 from musehub.services.musehub_intel_providers import _PROVIDER_REGISTRY
622
623 repo = await create_repo(db_session)
624 await _seed_symbols(db_session, repo.repo_id, [
625 {"address": "a.py::fn", "symbol_kind": "function",
626 "blast": 5, "blast_direct": 2, "blast_cross": 3},
627 ])
628
629 await _PROVIDER_REGISTRY["intel.code.gravity"].compute(
630 db_session, repo.repo_id, "ref", {},
631 )
632
633 row = await _get_row(db_session, repo.repo_id, "a.py::fn")
634 assert row.gravity_depth_distribution is None
File History 1 commit
sha256:bb2baaabdd19320bde50cb69d447fd1c1e571467df729be1a23b5e93064b046a feat(intel): standardize headers, gauge icon, velocity card… Sonnet 4.6 minor ⚠ 144 days ago