personal memory agent
0

Configure Feed

Select the types of activity you want to include in your feed.

solstone / tests / test_index_edges.py
38 kB 1217 lines
1# SPDX-License-Identifier: AGPL-3.0-only 2# Copyright (c) 2026 sol pbc 3 4"""Tests for the derived edge index layer.""" 5 6from __future__ import annotations 7 8import json 9import logging 10import os 11import shutil 12import sqlite3 13from pathlib import Path 14from typing import Any 15 16import pytest 17 18from solstone.think import edge_sources 19from solstone.think.activities import ( 20 _relation_label, 21 extract_activity_edges, 22) 23from solstone.think.edge_sources import EdgeContext, get_edge_source 24from solstone.think.formatters import discover_files 25from solstone.think.indexer.edges import ( 26 EDGES_SCHEMA_PATH, 27 _extract_file_edges, 28 discover_edge_files, 29 insert_edges, 30 rebuild_edges, 31) 32from solstone.think.indexer.journal import get_journal_index, index_file, scan_journal 33from tests._sqlite_assertions import ( 34 CHUNK_COLUMNS, 35 EDGE_COLUMNS, 36 FILE_COLUMNS, 37 edges_content_hash, 38 table_content_hash, 39) 40 41EDGE_FIXTURE = Path(__file__).resolve().parent / "fixtures" / "edges_journal" 42 43 44@pytest.fixture 45def edges_journal(tmp_path, monkeypatch): 46 journal = tmp_path / "edges_journal" 47 shutil.copytree(EDGE_FIXTURE, journal) 48 monkeypatch.setenv("SOLSTONE_JOURNAL", str(journal.resolve())) 49 return journal 50 51 52def synthetic_edge_extractor(entries: list[dict], ctx: EdgeContext) -> list[dict]: 53 return [ 54 { 55 "src": "edge_ada", 56 "dst": "edge_byron", 57 "kind": "attended-with", 58 "src_name": None, 59 "dst_name": None, 60 "day": ctx.day or None, 61 "facet": ctx.facet, 62 "source": "participation", 63 "path": ctx.path, 64 "anchor": "synthetic", 65 "label": "Synthetic edge-only file", 66 "ts": 0, 67 "weight": 1, 68 } 69 ] 70 71 72def bad_kind_edge_extractor(entries: list[dict], ctx: EdgeContext) -> list[dict]: 73 return [ 74 { 75 "src": "edge_ada", 76 "dst": "edge_byron", 77 "kind": "bad-kind", 78 "source": "participation", 79 "path": ctx.path, 80 "weight": 1, 81 } 82 ] 83 84 85def day_rooted_resolving_edge_extractor( 86 entries: list[dict], 87 ctx: EdgeContext, 88) -> list[dict]: 89 assert entries == [{"ok": True}] 90 src = ctx.resolve("Ada Edge") 91 assert src == "edge_ada" 92 ctx.drop() 93 return [ 94 { 95 "src": src, 96 "dst": "edge_byron", 97 "kind": "attended-with", 98 "source": "participation", 99 "path": ctx.path, 100 "anchor": "day-rooted", 101 "label": "", 102 "day": ctx.day, 103 "facet": ctx.facet, 104 "ts": 0, 105 "weight": 1, 106 } 107 ] 108 109 110def _conn(journal: Path) -> sqlite3.Connection: 111 conn, _ = get_journal_index(str(journal)) 112 conn.row_factory = sqlite3.Row 113 return conn 114 115 116def _edge_rows(conn: sqlite3.Connection, where: str = "") -> list[dict[str, Any]]: 117 sql = f"SELECT {', '.join(EDGE_COLUMNS)} FROM edges" 118 if where: 119 sql += f" WHERE {where}" 120 sql += " ORDER BY source, src, dst, anchor" 121 return [dict(row) for row in conn.execute(sql)] 122 123 124def _source_counts(conn: sqlite3.Connection) -> dict[str, int]: 125 return { 126 source: count 127 for source, count in conn.execute( 128 "SELECT source, count(*) FROM edges GROUP BY source" 129 ) 130 } 131 132 133def _write_jsonl(path: Path, rows: list[dict[str, Any]]) -> None: 134 path.write_text( 135 "".join(json.dumps(row, separators=(",", ":")) + "\n" for row in rows), 136 encoding="utf-8", 137 ) 138 139 140def _write_json(path: Path, payload: dict[str, Any]) -> None: 141 path.write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8") 142 143 144def _read_jsonl(path: Path) -> list[dict[str, Any]]: 145 return [ 146 json.loads(line) 147 for line in path.read_text(encoding="utf-8").splitlines() 148 if line.strip() 149 ] 150 151 152def _bump_mtime(path: Path) -> None: 153 bumped = path.stat().st_mtime + 2 154 os.utime(path, (bumped, bumped)) 155 156 157def _edge_ctx(rel: str, *, facet: str = "edges-story") -> EdgeContext: 158 return EdgeContext( 159 path=rel, 160 day="20260430", 161 facet=facet, 162 resolve=lambda _name: None, 163 drop=lambda: None, 164 ) 165 166 167def _valid_insert_edge_row(day: Any, *, path: str = "synthetic") -> dict[str, Any]: 168 return { 169 "src": "edge_ada", 170 "dst": "edge_byron", 171 "kind": "attended-with", 172 "src_name": None, 173 "dst_name": None, 174 "day": day, 175 "facet": "edges-activity", 176 "source": "participation", 177 "path": path, 178 "anchor": "ok", 179 "label": "valid row", 180 "ts": 0, 181 "weight": 1, 182 } 183 184 185def test_relation_label_formats_documented_forms(): 186 assert _relation_label("Works together", None) == "Works together" 187 assert _relation_label("", "quoted text") == '"quoted text"' 188 assert ( 189 _relation_label("Works together", "quoted text") 190 == 'Works together — "quoted text"' 191 ) 192 assert _relation_label(" ", None) == "" 193 194 195def test_activity_story_generalization_preserves_commitment_and_closure_rows_byte_exact( 196 edges_journal, 197): 198 rel = "facets/edges-story/activities/20260430.jsonl" 199 rows = extract_activity_edges(_read_jsonl(edges_journal / rel), _edge_ctx(rel)) 200 201 story_rows = [row for row in rows if row["source"] in {"commitment", "closure"}] 202 assert story_rows == [ 203 { 204 "src": "edge_mina", 205 "dst": "edge_ravi", 206 "kind": "committed-to", 207 "src_name": None, 208 "dst_name": None, 209 "day": "20260430", 210 "facet": "edges-story", 211 "source": "commitment", 212 "path": rel, 213 "anchor": "story-commitments-1", 214 "label": "Send the proposal", 215 "ts": 1777554000000, 216 "weight": 1, 217 }, 218 { 219 "src": "edge_tessa", 220 "dst": "edge_mina", 221 "kind": "committed-to", 222 "src_name": None, 223 "dst_name": None, 224 "day": "20260430", 225 "facet": "edges-story", 226 "source": "closure", 227 "path": rel, 228 "anchor": "story-commitments-1", 229 "label": "Confirm the handoff", 230 "ts": 1777554000000, 231 "weight": 1, 232 }, 233 ] 234 235 236def test_activity_relations_and_decisions_emit_expected_rows(edges_journal): 237 rel = "facets/edges-story/activities/20260430.jsonl" 238 record = { 239 "id": "story-relations-1", 240 "title": "Relation session", 241 "created_at": 1777555000000, 242 "relations": [ 243 { 244 "from": "Mina Edge", 245 "to": "Ravi Edge", 246 "from_entity_id": "edge_mina", 247 "to_entity_id": "edge_ravi", 248 "kind": "works-with", 249 "note": "Runs planning together", 250 "quote": "Let's pair on this", 251 }, 252 { 253 "from": "Tessa Edge", 254 "to": "Tessa Edge", 255 "from_entity_id": "edge_tessa", 256 "to_entity_id": "edge_tessa", 257 "kind": "knows", 258 "note": "self", 259 "quote": None, 260 }, 261 ], 262 "decisions": [ 263 { 264 "owner": "Mina Edge", 265 "counterparty": "Ravi Edge", 266 "owner_entity_id": "edge_mina", 267 "counterparty_entity_id": "edge_ravi", 268 "action": "Use the stable plan", 269 }, 270 { 271 "owner": "Mina Edge", 272 "counterparty": "Mina Edge", 273 "owner_entity_id": "edge_mina", 274 "counterparty_entity_id": "edge_mina", 275 "action": "Skip self", 276 }, 277 ], 278 } 279 280 rows = extract_activity_edges([record], _edge_ctx(rel)) 281 282 assert rows == [ 283 { 284 "src": "edge_mina", 285 "dst": "edge_ravi", 286 "kind": "decided-with", 287 "src_name": None, 288 "dst_name": None, 289 "day": "20260430", 290 "facet": "edges-story", 291 "source": "decision", 292 "path": rel, 293 "anchor": "story-relations-1", 294 "label": "Use the stable plan", 295 "ts": 1777555000000, 296 "weight": 1, 297 }, 298 { 299 "src": "edge_mina", 300 "dst": "edge_ravi", 301 "kind": "works-with", 302 "src_name": "Mina Edge", 303 "dst_name": "Ravi Edge", 304 "day": "20260430", 305 "facet": "edges-story", 306 "source": "relation", 307 "path": rel, 308 "anchor": "story-relations-1", 309 "label": 'Runs planning together — "Let\'s pair on this"', 310 "ts": 1777555000000, 311 "weight": 1, 312 }, 313 ] 314 315 316def test_activity_unknown_relation_kind_raises_at_insert(edges_journal): 317 rel = "facets/edges-story/activities/20260430.jsonl" 318 rows = extract_activity_edges( 319 [ 320 { 321 "id": "story-bad-relation-kind", 322 "created_at": 1777555000000, 323 "relations": [ 324 { 325 "from": "Mina Edge", 326 "to": "Ravi Edge", 327 "from_entity_id": "edge_mina", 328 "to_entity_id": "edge_ravi", 329 "kind": "unknown-relation-kind", 330 "note": "Bad kind", 331 "quote": None, 332 } 333 ], 334 } 335 ], 336 _edge_ctx(rel), 337 ) 338 339 conn = _conn(edges_journal) 340 with pytest.raises(ValueError, match="Unknown edge kind"): 341 insert_edges(conn, rows) 342 conn.close() 343 344 345def test_day_rooted_context_resolves_journal_entities_and_drop_hook_counts( 346 edges_journal, 347 monkeypatch, 348): 349 monkeypatch.setitem( 350 edge_sources.EDGE_SOURCES, 351 "*/*/*/synthetic.jsonl", 352 ("tests.test_index_edges", "day_rooted_resolving_edge_extractor"), 353 ) 354 rel = "20260430/default/090000_300/synthetic.jsonl" 355 path = edges_journal / "chronicle" / rel 356 path.parent.mkdir(parents=True, exist_ok=True) 357 _write_jsonl(path, [{"ok": True}]) 358 359 conn = _conn(edges_journal) 360 result = _extract_file_edges(conn, rel, str(path), {}) 361 362 assert result.rows_inserted == 1 363 assert result.drops == 1 364 assert not result.failed 365 assert ( 366 conn.execute("SELECT src FROM edges WHERE path=?", (rel,)).fetchone()[0] 367 == "edge_ada" 368 ) 369 conn.close() 370 371 372def test_discover_edge_files_keeps_structural_sources_with_chronicle_root( 373 edges_journal, 374): 375 files = discover_edge_files(str(edges_journal)) 376 377 assert "facets/edges-activity/activities/20260430.jsonl" in files 378 assert "facets/edges-story/activities/20260430.jsonl" in files 379 assert "facets/edges-copresence/entities/20260430.jsonl" in files 380 assert "facets/edges-events/events/20260430.jsonl" in files 381 assert "20260430/default/090000_300/screen.jsonl" in files 382 assert "20260430/default/090000_300/talents/documents.json" in files 383 384 385def test_scan_indexes_edges_and_second_scan_is_zero_delta(edges_journal): 386 assert scan_journal(str(edges_journal), full=True).changed is True 387 388 conn = _conn(edges_journal) 389 schema_version = conn.execute( 390 "SELECT mtime FROM edge_files WHERE path=?", (EDGES_SCHEMA_PATH,) 391 ).fetchone()[0] 392 assert schema_version == 1 393 assert conn.execute("SELECT count(*) FROM edges").fetchone()[0] == 24 394 assert _source_counts(conn) == { 395 "calendar": 3, 396 "co-presence": 2, 397 "closure": 1, 398 "commitment": 1, 399 "decision": 1, 400 "document": 3, 401 "event-legacy": 3, 402 "messaging": 4, 403 "observation": 1, 404 "participation": 3, 405 "relation": 2, 406 } 407 408 participation = _edge_rows(conn, "source='participation'") 409 assert len(participation) == 3 410 assert {row["anchor"] for row in participation} == {"activity-attendees-1"} 411 assert all( 412 row["src_name"] is None and row["dst_name"] is None for row in participation 413 ) 414 415 commitment = _edge_rows(conn, "source='commitment'") 416 assert commitment == [ 417 { 418 "src": "edge_mina", 419 "dst": "edge_ravi", 420 "kind": "committed-to", 421 "directed": 1, 422 "src_name": None, 423 "dst_name": None, 424 "day": "20260430", 425 "facet": "edges-story", 426 "source": "commitment", 427 "path": "facets/edges-story/activities/20260430.jsonl", 428 "anchor": "story-commitments-1", 429 "label": "Send the proposal", 430 "ts": 1777554000000, 431 "weight": 1, 432 } 433 ] 434 closure = _edge_rows(conn, "source='closure'") 435 assert closure[0]["src"] == "edge_tessa" 436 assert closure[0]["dst"] == "edge_mina" 437 assert closure[0]["directed"] == 1 438 assert closure[0]["src_name"] is None 439 assert closure[0]["dst_name"] is None 440 441 relation = {row["kind"]: row for row in _edge_rows(conn, "source='relation'")} 442 assert set(relation) == {"reports-to", "works-with"} 443 assert relation["works-with"]["label"] == ( 444 'Runs planning together — "Let\'s pair on this"' 445 ) 446 assert relation["works-with"]["directed"] == 0 447 assert relation["reports-to"]["directed"] == 1 448 decision = _edge_rows(conn, "source='decision'") 449 assert len(decision) == 1 450 assert decision[0]["kind"] == "decided-with" 451 assert decision[0]["label"] == "Use the stable plan together" 452 453 copresence = { 454 (row["src"], row["dst"]): row 455 for row in _edge_rows(conn, "source='co-presence'") 456 } 457 assert copresence[("edge_alice", "edge_bob")]["weight"] == 3 458 assert ( 459 copresence[("edge_alice", "edge_bob")]["anchor"] 460 == "20260430/default/090000_300" 461 ) 462 assert copresence[("edge_alice", "edge_bob")]["src_name"] == "Alice Edge" 463 assert copresence[("edge_alice", "edge_bob")]["dst_name"] == "Bob Edge" 464 assert copresence[("edge_alice", "edge_cora")]["weight"] == 1 465 assert copresence[("edge_alice", "edge_cora")]["src_name"] == "Alice Edge" 466 467 events = _edge_rows(conn, "source='event-legacy'") 468 assert len(events) == 3 469 assert all(row["src_name"] and row["dst_name"] for row in events) 470 assert {row["label"] for row in events} == {"Edge Legacy Meetup"} 471 assert all(row["ts"] > 0 for row in events) 472 473 assert conn.execute( 474 "SELECT 1 FROM edges WHERE src='edge_alice' OR dst='edge_alice' LIMIT 1" 475 ).fetchone() 476 477 first_hash = edges_content_hash(conn) 478 conn.close() 479 480 assert scan_journal(str(edges_journal), full=True).changed is False 481 conn = _conn(edges_journal) 482 assert edges_content_hash(conn) == first_hash 483 conn.close() 484 485 486def test_touching_one_edge_file_replaces_only_that_path(edges_journal): 487 scan_journal(str(edges_journal), full=True) 488 conn = _conn(edges_journal) 489 before = _source_counts(conn) 490 conn.close() 491 492 path = edges_journal / "facets" / "edges-activity" / "activities" / "20260430.jsonl" 493 records = _read_jsonl(path) 494 records[0]["participation"][4]["role"] = "attendee" 495 _write_jsonl(path, records) 496 _bump_mtime(path) 497 498 assert scan_journal(str(edges_journal), full=True).changed is True 499 conn = _conn(edges_journal) 500 after = _source_counts(conn) 501 assert after["participation"] == 6 502 assert {k: v for k, v in after.items() if k != "participation"} == { 503 k: v for k, v in before.items() if k != "participation" 504 } 505 assert conn.execute( 506 "SELECT 1 FROM edge_files WHERE path=?", 507 ("facets/edges-activity/activities/20260430.jsonl",), 508 ).fetchone() 509 conn.close() 510 511 512def test_deleted_edge_source_removes_rows_and_ledger(edges_journal): 513 scan_journal(str(edges_journal), full=True) 514 path = edges_journal / "facets" / "edges-events" / "events" / "20260430.jsonl" 515 path.unlink() 516 517 assert scan_journal(str(edges_journal), full=True).changed is True 518 conn = _conn(edges_journal) 519 assert ( 520 conn.execute( 521 "SELECT count(*) FROM edges WHERE source='event-legacy'" 522 ).fetchone()[0] 523 == 0 524 ) 525 assert ( 526 conn.execute( 527 "SELECT 1 FROM edge_files WHERE path=?", 528 ("facets/edges-events/events/20260430.jsonl",), 529 ).fetchone() 530 is None 531 ) 532 conn.close() 533 534 535def test_observation_relations_emit_edges_and_non_relations_still_index( 536 edges_journal, 537): 538 rel = "facets/edges-observations/entities/edge_mina/observations.jsonl" 539 extractor = get_edge_source("facets/work/entities/acme/observations.jsonl") 540 assert extractor is not None 541 assert extractor.__name__ == "extract_observation_edges" 542 543 conn = _conn(edges_journal) 544 result = _extract_file_edges(conn, rel, str(edges_journal / rel), {}) 545 assert result.rows_inserted == 1 546 assert result.drops == 1 547 row = conn.execute( 548 """ 549 SELECT src, dst, kind, day, facet, source, anchor, label 550 FROM edges 551 WHERE path=? 552 """, 553 (rel,), 554 ).fetchone() 555 assert dict(row) == { 556 "src": "edge_mina", 557 "dst": "edge_ravi", 558 "kind": "works-with", 559 "day": "20260430", 560 "facet": "edges-observations", 561 "source": "observation", 562 "anchor": "1777556000000", 563 "label": "Plans edge enrichment together", 564 } 565 conn.close() 566 567 assert index_file(str(edges_journal), rel) is True 568 569 conn = _conn(edges_journal) 570 assert ( 571 conn.execute("SELECT count(*) FROM chunks WHERE path=?", (rel,)).fetchone()[0] 572 > 0 573 ) 574 assert ( 575 conn.execute("SELECT count(*) FROM edges WHERE path=?", (rel,)).fetchone()[0] 576 == 1 577 ) 578 assert ( 579 conn.execute( 580 "SELECT count(*) FROM chunks WHERE path=? AND chunks MATCH ?", 581 (rel, '"non relation observation"'), 582 ).fetchone()[0] 583 > 0 584 ) 585 conn.close() 586 587 588def test_screen_sources_emit_messaging_calendar_and_event_day(edges_journal): 589 rel = "20260430/default/090000_300/screen.jsonl" 590 named_rel = "20260430/default/090000_300/left_screen.jsonl" 591 592 assert index_file(str(edges_journal), rel) is True 593 assert index_file(str(edges_journal), named_rel) is True 594 595 conn = _conn(edges_journal) 596 screen_rows = _edge_rows(conn, f"path='{rel}'") 597 named_rows = _edge_rows(conn, f"path='{named_rel}'") 598 conn.close() 599 600 messaging = [row for row in screen_rows if row["source"] == "messaging"] 601 calendar = [row for row in screen_rows if row["source"] == "calendar"] 602 assert len(messaging) == 3 603 assert {row["kind"] for row in messaging} == {"messaged-with"} 604 assert {row["label"] for row in messaging} == {"Edge Planning"} 605 assert {row["weight"] for row in messaging} == {2} 606 607 assert len(calendar) == 3 608 assert {row["kind"] for row in calendar} == {"scheduled-with"} 609 assert {row["day"] for row in calendar} == {"20260501"} 610 assert {row["label"] for row in calendar} == {"Future Edge Review"} 611 612 assert len(named_rows) == 1 613 assert named_rows[0]["kind"] == "messaged-with" 614 assert named_rows[0]["source"] == "messaging" 615 assert named_rows[0]["label"] == "Doc Review" 616 assert named_rows[0]["weight"] == 2 617 618 619def test_pretty_documents_json_edges_via_index_file_and_scan_journal(edges_journal): 620 rel = "20260430/default/090000_300/talents/documents.json" 621 622 conn = _conn(edges_journal) 623 result = _extract_file_edges(conn, rel, str(edges_journal / "chronicle" / rel), {}) 624 assert result.rows_inserted == 3 625 assert result.drops == 1 626 conn.close() 627 628 assert index_file(str(edges_journal), rel) is True 629 conn = _conn(edges_journal) 630 assert ( 631 conn.execute( 632 "SELECT count(*) FROM edges WHERE path=? AND source='document'", 633 (rel,), 634 ).fetchone()[0] 635 == 3 636 ) 637 assert ( 638 conn.execute("SELECT count(*) FROM chunks WHERE path=?", (rel,)).fetchone()[0] 639 > 0 640 ) 641 conn.close() 642 643 assert scan_journal(str(edges_journal), full=True).changed is True 644 conn = _conn(edges_journal) 645 assert ( 646 conn.execute( 647 "SELECT count(*) FROM edges WHERE path=? AND source='document'", 648 (rel,), 649 ).fetchone()[0] 650 == 3 651 ) 652 conn.close() 653 654 655def _replace_new_source_fixture(shape: str, path: Path) -> None: 656 if shape == "observations": 657 _write_jsonl( 658 path, 659 [ 660 { 661 "content": "Mina works with Ravi after replacement.", 662 "observed_at": 1777557000000, 663 "source_day": "20260430", 664 "relation": { 665 "kind": "works-with", 666 "target_entity_id": "edge_ravi", 667 "target_name": "Ravi Edge", 668 "note": "Replacement relation one", 669 }, 670 }, 671 { 672 "content": "Mina knows Tessa after replacement.", 673 "observed_at": 1777557100000, 674 "source_day": "20260430", 675 "relation": { 676 "kind": "knows", 677 "target_entity_id": "edge_tessa", 678 "target_name": "Tessa Edge", 679 "note": "Replacement relation two", 680 }, 681 }, 682 ], 683 ) 684 return 685 686 if shape == "screen": 687 _write_jsonl( 688 path, 689 [ 690 {"raw": "screen.png", "model": "fixture"}, 691 { 692 "timestamp": 0, 693 "content": { 694 "messaging": { 695 "view": "conversation", 696 "app": "Signal", 697 "thread": "Replacement Thread", 698 "messages": [ 699 { 700 "sender": "Alice Edge", 701 "timestamp": "2026-04-30T09:10:00Z", 702 "subject": "", 703 "text": "Replacement message", 704 }, 705 { 706 "sender": "Bob Edge", 707 "timestamp": "2026-04-30T09:10:30Z", 708 "subject": "", 709 "text": "Replacement reply", 710 }, 711 ], 712 } 713 }, 714 }, 715 ], 716 ) 717 return 718 719 if shape == "named_screen": 720 _write_jsonl( 721 path, 722 [ 723 {"raw": "left_screen.png", "model": "fixture"}, 724 { 725 "timestamp": 0, 726 "content": { 727 "calendar": { 728 "view": "week", 729 "app": "Calendar", 730 "events": [ 731 { 732 "title": "Replacement Calendar", 733 "start": "20260430T093000", 734 "end": "20260430T100000", 735 "calendar": "Work", 736 "guests": [ 737 "Mina Edge", 738 "Ravi Edge", 739 "Tessa Edge", 740 ], 741 } 742 ], 743 } 744 }, 745 }, 746 ], 747 ) 748 return 749 750 if shape == "documents": 751 _write_json( 752 path, 753 { 754 "overview": "Replacement document.", 755 "parties": [ 756 {"name": "Mina Edge", "role": "author"}, 757 {"name": "Ravi Edge", "role": "reviewer"}, 758 ], 759 "key_provisions": [], 760 "assets": [], 761 "conditions": [], 762 "important_dates": [], 763 "summary": "Replacement parties.", 764 }, 765 ) 766 return 767 768 raise AssertionError(f"unknown source fixture shape: {shape}") 769 770 771@pytest.mark.parametrize( 772 ("shape", "rel", "initial_rows", "replacement_rows"), 773 [ 774 ( 775 "observations", 776 "facets/edges-observations/entities/edge_mina/observations.jsonl", 777 1, 778 2, 779 ), 780 ("screen", "20260430/default/090000_300/screen.jsonl", 6, 1), 781 ("named_screen", "20260430/default/090000_300/left_screen.jsonl", 1, 3), 782 ( 783 "documents", 784 "20260430/default/090000_300/talents/documents.json", 785 3, 786 1, 787 ), 788 ], 789) 790def test_new_source_shapes_support_index_file_replacement_and_deletion( 791 edges_journal, 792 shape, 793 rel, 794 initial_rows, 795 replacement_rows, 796): 797 assert index_file(str(edges_journal), rel) is True 798 conn = _conn(edges_journal) 799 assert ( 800 conn.execute("SELECT count(*) FROM edges WHERE path=?", (rel,)).fetchone()[0] 801 == initial_rows 802 ) 803 conn.close() 804 805 path = edges_journal / ("chronicle" if rel.startswith("202") else "") / rel 806 _replace_new_source_fixture(shape, path) 807 _bump_mtime(path) 808 809 assert scan_journal(str(edges_journal), full=True).changed is True 810 conn = _conn(edges_journal) 811 assert ( 812 conn.execute("SELECT count(*) FROM edges WHERE path=?", (rel,)).fetchone()[0] 813 == replacement_rows 814 ) 815 conn.close() 816 817 path.unlink() 818 assert scan_journal(str(edges_journal), full=True).changed is True 819 conn = _conn(edges_journal) 820 assert ( 821 conn.execute("SELECT count(*) FROM edges WHERE path=?", (rel,)).fetchone()[0] 822 == 0 823 ) 824 assert ( 825 conn.execute("SELECT 1 FROM edge_files WHERE path=?", (rel,)).fetchone() is None 826 ) 827 conn.close() 828 829 830def test_malformed_new_source_fails_without_suppressing_sibling( 831 edges_journal, 832 caplog, 833): 834 bad_rel = "20260430/default/090000_300/talents/documents.json" 835 bad_path = edges_journal / "chronicle" / bad_rel 836 bad_path.write_text("{\n", encoding="utf-8") 837 838 caplog.set_level(logging.ERROR, logger="solstone.think.indexer.edges") 839 conn = _conn(edges_journal) 840 result = _extract_file_edges(conn, bad_rel, str(bad_path), {}) 841 assert result.failed 842 assert result.rows_inserted == 0 843 conn.close() 844 assert f"Skipping edge extraction for {bad_rel}" in caplog.text 845 846 caplog.clear() 847 caplog.set_level(logging.ERROR, logger="solstone.think.indexer.edges") 848 assert scan_journal(str(edges_journal), full=True).changed is True 849 assert f"Skipping edge extraction for {bad_rel}" in caplog.text 850 851 conn = _conn(edges_journal) 852 assert ( 853 conn.execute("SELECT count(*) FROM edges WHERE path=?", (bad_rel,)).fetchone()[ 854 0 855 ] 856 == 0 857 ) 858 assert ( 859 conn.execute( 860 "SELECT count(*) FROM edges WHERE path=?", 861 ("20260430/default/090000_300/screen.jsonl",), 862 ).fetchone()[0] 863 == 6 864 ) 865 assert ( 866 conn.execute("SELECT 1 FROM edge_files WHERE path=?", (bad_rel,)).fetchone() 867 is not None 868 ) 869 conn.close() 870 871 872def test_invalid_segment_dir_skips_at_warning_not_error(edges_journal, caplog): 873 rel = "20260430/default/234567_300/screen.jsonl" 874 path = edges_journal / "chronicle" / rel 875 path.parent.mkdir(parents=True, exist_ok=True) 876 _write_jsonl(path, [{"ok": True}]) 877 878 caplog.set_level(logging.WARNING, logger="solstone.think.indexer.edges") 879 conn = _conn(edges_journal) 880 result = _extract_file_edges(conn, rel, str(path), {}) 881 conn.close() 882 883 assert result.invalid_segment is True 884 assert result.failed is False 885 assert result.rows_inserted == 0 886 assert f"Skipping edge extraction for {rel}" in caplog.text 887 assert not [record for record in caplog.records if record.levelno >= logging.ERROR] 888 889 890def test_index_file_supports_edge_only_file(edges_journal, monkeypatch): 891 monkeypatch.setitem( 892 edge_sources.EDGE_SOURCES, 893 "edge-only/*.jsonl", 894 ("tests.test_index_edges", "synthetic_edge_extractor"), 895 ) 896 rel = "edge-only/sample.jsonl" 897 path = edges_journal / rel 898 path.parent.mkdir(parents=True) 899 _write_jsonl(path, [{"ok": True}]) 900 901 assert index_file(str(edges_journal), rel) is True 902 903 conn = _conn(edges_journal) 904 assert ( 905 conn.execute("SELECT count(*) FROM edges WHERE path=?", (rel,)).fetchone()[0] 906 == 1 907 ) 908 assert ( 909 conn.execute("SELECT count(*) FROM chunks WHERE path=?", (rel,)).fetchone()[0] 910 == 0 911 ) 912 assert ( 913 conn.execute("SELECT count(*) FROM files WHERE path=?", (rel,)).fetchone()[0] 914 == 0 915 ) 916 assert ( 917 conn.execute("SELECT count(*) FROM edge_files WHERE path=?", (rel,)).fetchone()[ 918 0 919 ] 920 == 1 921 ) 922 conn.close() 923 924 925def test_edge_driver_catches_file_boundary_failures( 926 edges_journal, 927 monkeypatch, 928 caplog, 929): 930 monkeypatch.setitem( 931 edge_sources.EDGE_SOURCES, 932 "bad-edge/*.jsonl", 933 ("tests.test_index_edges", "bad_kind_edge_extractor"), 934 ) 935 rel = "bad-edge/source.jsonl" 936 path = edges_journal / rel 937 path.parent.mkdir(parents=True) 938 _write_jsonl(path, [{"ok": True}]) 939 940 caplog.set_level(logging.ERROR, logger="solstone.think.indexer.edges") 941 assert index_file(str(edges_journal), rel) is True 942 943 conn = _conn(edges_journal) 944 assert ( 945 conn.execute("SELECT count(*) FROM edges WHERE path=?", (rel,)).fetchone()[0] 946 == 0 947 ) 948 assert ( 949 conn.execute("SELECT count(*) FROM edge_files WHERE path=?", (rel,)).fetchone()[ 950 0 951 ] 952 == 1 953 ) 954 conn.close() 955 assert f"Skipping edge extraction for {rel}" in caplog.text 956 957 958def test_rescan_file_indexes_activity_edges(edges_journal): 959 rel = "facets/edges-activity/activities/20260430.jsonl" 960 assert index_file(str(edges_journal), rel) is True 961 962 conn = _conn(edges_journal) 963 assert ( 964 conn.execute( 965 "SELECT count(*) FROM edges WHERE path=? AND source='participation'", 966 (rel,), 967 ).fetchone()[0] 968 == 3 969 ) 970 assert ( 971 conn.execute("SELECT count(*) FROM chunks WHERE path=?", (rel,)).fetchone()[0] 972 > 0 973 ) 974 conn.close() 975 976 977def test_non_date_activity_edge_source_fails_without_poisoning_sibling( 978 edges_journal, 979 caplog, 980): 981 bad_rel = "facets/edges-story/activities/notaday.jsonl" 982 bad_path = edges_journal / bad_rel 983 _write_jsonl( 984 bad_path, 985 [ 986 { 987 "id": "invalid-day-activity", 988 "title": "Invalid day source", 989 "created_at": 1777555000000, 990 "participation": [ 991 {"entity_id": "edge_ada", "role": "attendee"}, 992 {"entity_id": "edge_byron", "role": "attendee"}, 993 ], 994 } 995 ], 996 ) 997 998 caplog.set_level(logging.ERROR, logger="solstone.think.indexer.edges") 999 conn = _conn(edges_journal) 1000 result = _extract_file_edges(conn, bad_rel, str(bad_path), {}) 1001 1002 assert result.failed 1003 assert result.rows_inserted == 0 1004 assert result.drops == 0 1005 assert ( 1006 conn.execute("SELECT count(*) FROM edges WHERE path=?", (bad_rel,)).fetchone()[ 1007 0 1008 ] 1009 == 0 1010 ) 1011 conn.close() 1012 assert f"Skipping edge extraction for {bad_rel}" in caplog.text 1013 1014 caplog.clear() 1015 assert scan_journal(str(edges_journal), full=True).changed is True 1016 1017 conn = _conn(edges_journal) 1018 assert ( 1019 conn.execute("SELECT count(*) FROM edges WHERE path=?", (bad_rel,)).fetchone()[ 1020 0 1021 ] 1022 == 0 1023 ) 1024 assert ( 1025 conn.execute( 1026 "SELECT count(*) FROM edges WHERE path=?", 1027 ("facets/edges-story/activities/20260430.jsonl",), 1028 ).fetchone()[0] 1029 > 0 1030 ) 1031 conn.close() 1032 assert f"Skipping edge extraction for {bad_rel}" in caplog.text 1033 1034 1035def test_rebuild_edges_is_idempotent_and_preserves_chunks_files(edges_journal): 1036 scan_journal(str(edges_journal), full=True) 1037 conn = _conn(edges_journal) 1038 chunks_hash = table_content_hash(conn, "chunks", CHUNK_COLUMNS) 1039 files_hash = table_content_hash(conn, "files", FILE_COLUMNS) 1040 assert ( 1041 conn.execute( 1042 "SELECT count(*) FROM chunks WHERE path=?", 1043 ("20260430/default/090000_300/talents/documents.json",), 1044 ).fetchone()[0] 1045 > 0 1046 ) 1047 conn.close() 1048 1049 first = rebuild_edges(str(edges_journal)) 1050 conn = _conn(edges_journal) 1051 first_edges_hash = edges_content_hash(conn) 1052 assert table_content_hash(conn, "chunks", CHUNK_COLUMNS) == chunks_hash 1053 assert table_content_hash(conn, "files", FILE_COLUMNS) == files_hash 1054 conn.close() 1055 1056 second = rebuild_edges(str(edges_journal)) 1057 conn = _conn(edges_journal) 1058 assert edges_content_hash(conn) == first_edges_hash 1059 assert table_content_hash(conn, "chunks", CHUNK_COLUMNS) == chunks_hash 1060 assert table_content_hash(conn, "files", FILE_COLUMNS) == files_hash 1061 conn.close() 1062 1063 assert first["rows"] == 24 1064 assert second["rows"] == 24 1065 assert first["drops"] == 3 1066 assert second["drops"] == 3 1067 1068 1069def test_extract_file_edges_counts_only_resolution_drops(edges_journal): 1070 conn = _conn(edges_journal) 1071 rel = "facets/edges-copresence/entities/20260430.jsonl" 1072 result = _extract_file_edges(conn, rel, str(edges_journal / rel), {}) 1073 assert result.rows_inserted == 2 1074 assert result.drops == 1 1075 assert not result.failed 1076 conn.close() 1077 1078 1079def test_insert_bad_kind_raises_directly(edges_journal): 1080 conn = _conn(edges_journal) 1081 with pytest.raises(ValueError, match="Unknown edge kind"): 1082 insert_edges( 1083 conn, 1084 [ 1085 { 1086 "src": "a", 1087 "dst": "b", 1088 "kind": "not-a-kind", 1089 "source": "participation", 1090 "path": "synthetic", 1091 "weight": 1, 1092 } 1093 ], 1094 ) 1095 conn.close() 1096 1097 1098@pytest.mark.parametrize("day", [None, "20260430"]) 1099def test_insert_edges_accepts_null_and_valid_day_values(edges_journal, day): 1100 conn = _conn(edges_journal) 1101 path = f"synthetic/day-ok/{day}" 1102 1103 assert insert_edges(conn, [_valid_insert_edge_row(day, path=path)]) == 1 1104 1105 stored = conn.execute("SELECT day FROM edges WHERE path=?", (path,)).fetchone()[0] 1106 assert stored == day 1107 conn.close() 1108 1109 1110@pytest.mark.parametrize("day", ["", "notaday", "2026430", 20260430]) 1111def test_insert_edges_rejects_invalid_day_values_atomically(edges_journal, day): 1112 conn = _conn(edges_journal) 1113 before = conn.execute("SELECT count(*) FROM edges").fetchone()[0] 1114 1115 with pytest.raises(ValueError, match="Invalid edge day"): 1116 insert_edges( 1117 conn, 1118 [ 1119 _valid_insert_edge_row( 1120 "20260430", 1121 path="synthetic/day-validation/valid", 1122 ), 1123 _valid_insert_edge_row( 1124 day, 1125 path="synthetic/day-validation/invalid", 1126 ), 1127 ], 1128 ) 1129 1130 assert conn.execute("SELECT count(*) FROM edges").fetchone()[0] == before 1131 assert ( 1132 conn.execute( 1133 "SELECT count(*) FROM edges WHERE path LIKE 'synthetic/day-validation/%'" 1134 ).fetchone()[0] 1135 == 0 1136 ) 1137 conn.close() 1138 1139 1140def test_insert_edges_validates_whole_batch_before_insert(edges_journal): 1141 conn = _conn(edges_journal) 1142 before = conn.execute("SELECT count(*) FROM edges").fetchone()[0] 1143 with pytest.raises(ValueError, match="Unknown edge kind"): 1144 insert_edges( 1145 conn, 1146 [ 1147 { 1148 "src": "edge_ada", 1149 "dst": "edge_byron", 1150 "kind": "attended-with", 1151 "src_name": None, 1152 "dst_name": None, 1153 "day": "20260430", 1154 "facet": "edges-activity", 1155 "source": "participation", 1156 "path": "synthetic", 1157 "anchor": "ok", 1158 "label": "valid row", 1159 "ts": 0, 1160 "weight": 1, 1161 }, 1162 { 1163 "src": "edge_ada", 1164 "dst": "edge_byron", 1165 "kind": "not-a-kind", 1166 "source": "participation", 1167 "path": "synthetic", 1168 "weight": 1, 1169 }, 1170 ], 1171 ) 1172 after = conn.execute("SELECT count(*) FROM edges").fetchone()[0] 1173 assert after == before 1174 conn.close() 1175 1176 1177def test_schema_version_migration_preserves_chunks_and_files( 1178 edges_journal, 1179 monkeypatch, 1180): 1181 import solstone.think.indexer.edges as edge_index 1182 1183 scan_journal(str(edges_journal), full=True) 1184 conn = _conn(edges_journal) 1185 chunks_hash = table_content_hash(conn, "chunks", CHUNK_COLUMNS) 1186 files_hash = table_content_hash(conn, "files", FILE_COLUMNS) 1187 assert conn.execute("SELECT count(*) FROM edges").fetchone()[0] == 24 1188 conn.close() 1189 1190 monkeypatch.setattr(edge_index, "EDGES_SCHEMA_VERSION", 2) 1191 conn = _conn(edges_journal) 1192 assert ( 1193 conn.execute( 1194 "SELECT mtime FROM edge_files WHERE path=?", (EDGES_SCHEMA_PATH,) 1195 ).fetchone()[0] 1196 == 2 1197 ) 1198 assert conn.execute("SELECT count(*) FROM edges").fetchone()[0] == 0 1199 assert table_content_hash(conn, "chunks", CHUNK_COLUMNS) == chunks_hash 1200 assert table_content_hash(conn, "files", FILE_COLUMNS) == files_hash 1201 conn.close() 1202 1203 assert scan_journal(str(edges_journal), full=True).changed is True 1204 conn = _conn(edges_journal) 1205 assert conn.execute("SELECT count(*) FROM edges").fetchone()[0] == 24 1206 conn.close() 1207 1208 1209def test_discover_files_day_rooted_paths_are_chronicle_free(tmp_path): 1210 journal = tmp_path / "journal" 1211 target = journal / "chronicle" / "20260430" / "synthetic" / "input.jsonl" 1212 target.parent.mkdir(parents=True) 1213 _write_jsonl(target, [{"ok": True}]) 1214 1215 files = discover_files(str(journal), [], ["*/synthetic/*.jsonl"]) 1216 1217 assert files == {"20260430/synthetic/input.jsonl": str(target)}