from __future__ import annotations import hashlib import importlib.util import json import re import stat from pathlib import Path from types import SimpleNamespace import pytest ROOT = Path(__file__).resolve().parents[1] KB_TOOL = ROOT / "hermes-agent" / "leoclean-bin" / "kb_tool.py" def load_module(): spec = importlib.util.spec_from_file_location("kb_tool_propose_source", KB_TOOL) assert spec and spec.loader module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def source_bundle(module) -> dict: payload = { "apply_payload": { "contract_version": 2, "claims": [], "sources": [], "evidence": [], "edges": [], "reasoning_tools": [], } } payload_sha256 = module.canonical_json_sha256(payload) child = { "id": "3cf4f2de-ea69-4e3b-a7ec-128b604d4f08", "proposal_type": "approve_claim", "status": "pending_review", "proposed_by_handle": "leo", "proposed_by_agent_id": None, "channel": "source_document_compiler", "source_ref": "normalized:source-parent:0123456789abcdef", "rationale": "Strict normalized child of one hash-bound source packet.", "payload": payload, "payload_sha256": payload_sha256, } bundle = { "schema": module.SOURCE_COMPILER_BUNDLE_SCHEMA, "status": "pending_review", "build_only": True, "database_write_performed": False, "canonical_apply_performed": False, "strict_child_proposal": child, "validated_manifest": { "artifact_sha256": "a" * 64, "extracted_text_sha256": "b" * 64, "source": { "identity": "document:sha256:" + "a" * 64, "locator": "artifact://sha256/" + "a" * 64, } }, "hashes": { "artifact_sha256": "a" * 64, "extracted_text_sha256": "b" * 64, "manifest_canonical_sha256": "c" * 64, "strict_child_payload_sha256": payload_sha256, }, } bundle["hashes"]["bundle_content_sha256"] = module.canonical_json_sha256(bundle) return bundle def status_row(proposal_count: int) -> dict: return { "artifact": "teleo_kb_status", "backend": "teleo|postgres", "high_signal_rows": { "claims": 1837, "sources": 4145, "claim_edges": 4916, "claim_evidence": 4670, "kb_proposals": proposal_count, }, "proposal_status_counts": {"pending_review": 14}, } def test_propose_source_stages_only_pending_review_and_writes_private_receipt( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: module = load_module() bundle = source_bundle(module) child = bundle["strict_child_proposal"] compiler = tmp_path / "compile_kb_source_packet.py" compiler.write_text("# compiler fixture\n", encoding="utf-8") monkeypatch.setattr(module, "SOURCE_COMPILER_PATH", compiler) monkeypatch.setattr(module, "compile_source_bundle", lambda _args: bundle) calls: list[str] = [] status_reads = iter((status_row(26), status_row(27))) def fake_psql_json(_args, sql): calls.append(sql) if "insert into kb_stage.kb_proposals" not in sql: return [next(status_reads)] return [ { "created": True, "proposal_id": child["id"], "proposal_type": child["proposal_type"], "status": "pending_review", "source_ref": child["source_ref"], "payload": child["payload"], "payload_sha256": child["payload_sha256"], "created_at": "2026-07-13T18:00:00+00:00", "reviewed_at": None, "applied_at": None, } ] monkeypatch.setattr(module, "psql_json", fake_psql_json) receipt_path = tmp_path / "receipts" / "source.json" args = SimpleNamespace( local=True, artifact=tmp_path / "source.pdf", text=tmp_path / "source.txt", manifest=tmp_path / "source-manifest.json", receipt=receipt_path, ) receipt = module.propose_source(args) assert receipt["status"] == "pending_review" assert receipt["created"] is True assert receipt["proposal"]["id"] == child["id"] assert receipt["database"]["canonical_counts_unchanged"] is True assert receipt["database"]["proposal_count_delta"] == 1 assert receipt["database"]["proposal_count_matches_command"] is True assert receipt["write_contract"] == { "target_table": "kb_stage.kb_proposals", "database_write_performed_by_command": True, "canonical_apply_performed": False, "production_db_apply_ran": False, "public_table_write_path_present": False, } assert json.loads(receipt_path.read_text(encoding="utf-8")) == receipt assert stat.S_IMODE(receipt_path.stat().st_mode) == 0o600 stage_sql = next(sql for sql in calls if "insert into kb_stage.kb_proposals" in sql) assert "on conflict (id) do nothing" in stage_sql assert "marker_count <> 1 or exact_count <> 1" in stage_sql assert not re.search(r"\b(?:insert|update|delete)\s+(?:into\s+|from\s+)?public\.", stage_sql, re.I) assert len(calls) == 3 def test_source_bundle_rejects_tampered_child_payload() -> None: module = load_module() bundle = source_bundle(module) bundle["strict_child_proposal"]["payload"]["apply_payload"]["claims"].append({"id": "tampered"}) with pytest.raises(SystemExit, match="payload hash does not match"): module._validated_source_bundle(bundle) def test_deployed_bridge_accepts_real_source_compiler_bundle(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: module = load_module() artifact_bytes = b"%PDF-1.7\nbridge compatibility fixture\n%%EOF\n" extracted_text = ( "Bridge compatibility source.\n\n" "Prepared source inputs may become a pending review proposal.\n\n" "Canonical rows still require review and guarded apply." ) artifact = tmp_path / "source.pdf" text = tmp_path / "source.txt" manifest_path = tmp_path / "source-manifest.json" artifact.write_bytes(artifact_bytes) text.write_text(extracted_text, encoding="utf-8") artifact_sha256 = hashlib.sha256(artifact_bytes).hexdigest() text_sha256 = hashlib.sha256(extracted_text.encode()).hexdigest() manifest = { "schema": "livingip.kbSourceExtractionManifest.v1", "artifact_sha256": artifact_sha256, "extracted_text_sha256": text_sha256, "extractor": {"name": "bridge-test", "version": "1"}, "source": { "identity": f"document:sha256:{artifact_sha256}", "source_key": "bridge_compatibility_source", "source_type": "article", "title": "Bridge compatibility source", "locator": f"artifact://sha256/{artifact_sha256}", }, "claims": [ { "claim_key": "prepared_input_staging", "type": "structural", "text": "Prepared inputs can be staged for review without becoming canonical.", "quote": "Prepared source inputs may become a pending review proposal.", "confidence": 0.99, "tags": ["source-intake"], "evidence": [ { "quote": "Canonical rows still require review and guarded apply.", "role": "grounds", } ], } ], } manifest_path.write_text(json.dumps(manifest), encoding="utf-8") monkeypatch.setattr(module, "SOURCE_COMPILER_PATH", ROOT / "scripts" / "compile_kb_source_packet.py") bundle = module.compile_source_bundle(SimpleNamespace(artifact=artifact, text=text, manifest=manifest_path)) child, validated_manifest = module._validated_source_bundle(bundle) assert child["status"] == "pending_review" assert child["proposal_type"] == "approve_claim" assert validated_manifest["artifact_sha256"] == artifact_sha256 def test_propose_source_refuses_nonlocal_database_route(tmp_path: Path) -> None: module = load_module() args = SimpleNamespace( local=False, artifact=tmp_path / "source.pdf", text=tmp_path / "source.txt", manifest=tmp_path / "source-manifest.json", receipt=tmp_path / "receipt.json", ) with pytest.raises(SystemExit, match="VPS-local only"): module.propose_source(args) def test_source_stage_sql_allows_idempotent_readback_after_review_state_advances() -> None: module = load_module() child, _manifest = module._validated_source_bundle(source_bundle(module)) sql = module.build_source_stage_sql(child) assert "on conflict (id) do nothing" in sql exact_clause = sql.split("select count(*) into exact_count", 1)[1].split("if marker_count", 1)[0] assert "status = 'pending_review'" not in exact_clause assert "reviewed_at is null" not in exact_clause assert "applied_at is null" not in exact_clause