Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/backend/workflows/workflow_purity_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,8 @@
{
"src/backend/services/academic_roster_workflow_vontology_service.py",
"src/backend/services/concept_search_instance_retrieval_workflow_vontology_service.py",
# Startup seed support; runtime naming resolves the published Vontology graph.
"src/backend/services/conversation_naming_workflow_vontology_service.py",
"src/backend/services/conversation_turn_workflow_vontology_service.py",
"src/backend/services/email_source_representation_convergence_workflow_vontology_service.py",
"src/backend/services/entity_information_retrieval_workflow_vontology_service.py",
Expand Down
30 changes: 29 additions & 1 deletion tests/backend/test_conversation_naming_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,39 @@ def test_concurrent_title_is_preserved_and_later_page_still_progresses(world):
assert world["names"]["named"] == "User chosen title"


def test_bootstrap_preserves_published_authority(world):
def test_bootstrap_preserves_published_authority(world, monkeypatch):
from src.backend.services import (
conversation_naming_workflow_vontology_service as naming,
)
from src.backend.services.text_value_service import (
get_texts_for_concept,
upsert_singleton_text_relation,
)

custom_prompt = "Independently revised naming instructions in Vontology."
upsert_singleton_text_relation(
subject_concept_id=naming.CONVERSATION_NAMING_PROMPT_CONCEPT_ID,
predicate="hasContent",
text=custom_prompt,
lang="en-NZ",
)

def forbidden_prompt_seed():
pytest.fail("An existing live prompt must not be replaced with its repo seed")

monkeypatch.setattr(
naming, "_load_conversation_naming_prompt_seed_text", forbidden_prompt_seed
)
report = bootstrap_canonical_conversation_naming_workflow()
assert report["success"], report
assert report["prompt_support"]["seeded_prompt_count"] == 0
assert report["publication"]["counts"]["workflows_published"] == 0
texts = get_texts_for_concept(naming.CONVERSATION_NAMING_PROMPT_CONCEPT_ID)
assert any(item.get("text") == custom_prompt for item in texts)
assert (
load_workflow_definition_from_vontology(CONVERSATION_NAMING_WORKFLOW_ID)
is not None
)


def test_hourly_schedule_verifies_and_due_occurrence_executes(monkeypatch, world):
Expand Down
20 changes: 20 additions & 0 deletions tests/backend/test_repo_seed_authority_guardrails.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,23 @@ def test_vontology_first_seed_fallback_contracts_have_no_violations() -> None:
"before authoritative Vontology resolution in at least one guarded path.\n"
f"{violations}"
)


def test_naming_bootstrap_does_not_admit_runtime_seed_imports(tmp_path) -> None:
bootstrap = "src/backend/services/conversation_naming_workflow_vontology_service.py"
runtime = "src/backend/services/conversation_naming_service.py"
source = "from .workflow_repo_seed_bootstrap import bootstrap_repo_seed_workflow_bundle\n"
for relative_path in (bootstrap, runtime):
path = tmp_path / relative_path
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(source, encoding="utf-8")

report = build_workflow_purity_report(
registry=None, project_root=tmp_path, baseline_path=tmp_path / "baseline.json"
)
drift = report["details"]["repo_seed_authority_drift"]
assert drift["offending_paths"] == [runtime]
assert report["counters"]["repo_seed_authority_drift_path_count"] == 1
assert any(
item["path"] == bootstrap and item["allowed"] for item in drift["matches"]
)
Loading