feat: implement GEWU service and local agent workflows
CI / quality (push) Canceled after 0s

This commit is contained in:
vasily committed 2026-10-14 19:21:35 +08:00
1 parent 4db3f45164
commit d25c04cdd2
175 files changed
+56243 -15

No files matched your search

+231
View File
@@ -0,0 +1,231 @@
import json
from datetime import UTC, date, datetime
from pathlib import Path
import pytest
from pydantic import ValidationError
from gewu.domain.contracts import (
AutoAcceptPolicy,
Confidence,
Locator,
NodeType,
ProjectRecord,
ProjectStatus,
ProposalReceipt,
ProposalState,
ReliabilityGrade,
Scope,
SearchRequest,
SourceRevisionRecord,
)
def test_project_contract_rejects_unknown_fields() -> None:
with pytest.raises(ValidationError):
ProjectRecord.model_validate(
{
"project_id": "dome",
"title": "Dome",
"status": ProjectStatus.ACTIVE,
"north_star": "Understand interactive environments",
"key_questions": ("What works?",),
"autonomy_policy_id": "pilot",
"untrusted_extra": "ignored?",
}
)
def test_project_status_supports_pause_without_losing_registry_record() -> None:
project = ProjectRecord(
project_id="dome",
title="Dome",
status=ProjectStatus.PAUSED,
north_star="Understand interactive environments",
key_questions=("What works?",),
autonomy_policy_id="pilot",
)
assert (
ProjectRecord.model_validate_json(project.model_dump_json()).status == ProjectStatus.PAUSED
)
@pytest.mark.parametrize("project_id", ["Dome", "two_words", "project.slug"])
def test_project_ids_must_be_lowercase_slugs(project_id: str) -> None:
with pytest.raises(ValidationError):
ProjectRecord(
project_id=project_id,
title="Invalid project ID",
status=ProjectStatus.ACTIVE,
north_star="A stable project lens.",
key_questions=("What matters?",),
autonomy_policy_id="policy",
)
def test_scope_requires_explicit_all_projects_flag() -> None:
with pytest.raises(ValidationError):
Scope(projects=("all",))
assert Scope(projects=("all",), all_projects=True)
@pytest.mark.parametrize("unsafe_id", ["../private", "bad/id", " leading-space"])
def test_scope_rejects_unsafe_project_ids(unsafe_id: str) -> None:
with pytest.raises(ValidationError):
Scope(projects=(unsafe_id,))
def test_search_facets_are_typed_and_reject_duplicate_node_types() -> None:
request = SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
node_types=(NodeType.PROBLEM, NodeType.TECHNIQUE),
tags=("attendance", "real-time"),
audience=("product",),
use_for=("comparison",),
domain=("production",),
confidences=(Confidence.MEDIUM,),
as_of_from=date(2024, 1, 1),
as_of_to=date(2024, 12, 31),
stale_only=True,
budget_tokens=1000,
)
assert request.stale_only
assert request.tags == ("attendance", "real-time")
assert request.domain == ("production",)
assert request.confidences == (Confidence.MEDIUM,)
assert request.as_of_from == date(2024, 1, 1)
with pytest.raises(ValidationError, match="must not be later"):
SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
as_of_from=date(2025, 1, 1),
as_of_to=date(2024, 12, 31),
budget_tokens=1000,
)
with pytest.raises(ValidationError, match="confidences must be unique"):
SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
confidences=(Confidence.MEDIUM, Confidence.MEDIUM),
budget_tokens=1000,
)
with pytest.raises(ValidationError):
SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
node_types=(NodeType.PROBLEM, NodeType.PROBLEM),
budget_tokens=1000,
)
with pytest.raises(ValidationError):
SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
tags=("attendance", "attendance"),
budget_tokens=1000,
)
with pytest.raises(ValidationError):
SearchRequest(
query="audience",
scope=Scope(projects=("dome-performance",)),
domain=(" production",),
budget_tokens=1000,
)
def test_search_allows_empty_query_only_for_a_typed_browse() -> None:
request = SearchRequest(
scope=Scope(projects=("dome-performance",)),
node_types=(NodeType.PROBLEM,),
budget_tokens=1000,
)
assert request.query == ""
with pytest.raises(ValidationError, match="search requires text"):
SearchRequest(
scope=Scope(projects=("dome-performance",)),
budget_tokens=1000,
)
with pytest.raises(ValidationError, match="search requires text"):
SearchRequest(
query=" ",
scope=Scope(projects=("dome-performance",)),
budget_tokens=1000,
)
def test_pdf_locator_uses_one_based_page_and_nonempty_span() -> None:
with pytest.raises(ValidationError):
Locator(kind="pdf_page_span", block_id="p1-b1", start=8, end=8, page=0)
def test_unarchived_source_requires_a_url_and_cannot_claim_artifact_bytes() -> None:
base = {
"source_id": "project:bookmark",
"revision_id": "project:bookmark:r1",
"media_type": "application/octet-stream",
"byte_length": 0,
"captured_at": datetime.now(UTC),
"original_url": "https://example.org/paper",
"title": "Referenced paper",
"license": "unspecified",
"source_type": "paper",
"reliability_grade": ReliabilityGrade.MEDIUM,
"ingested_for": ("dome-performance",),
"archive_status": "reference-only",
}
record = SourceRevisionRecord.model_validate(base)
assert record.artifact_sha256 is None
with pytest.raises(ValidationError):
SourceRevisionRecord.model_validate({**base, "byte_length": 10})
with pytest.raises(ValidationError):
SourceRevisionRecord.model_validate({**base, "original_url": None})
def test_receipt_is_immutable_and_versioned() -> None:
receipt = ProposalReceipt(
proposal_id="proposal-1",
revision=1,
payload_digest="a" * 64,
state=ProposalState.ACCEPTED,
outcome="accepted",
commit_id="b" * 64,
decided_at=datetime.now(UTC),
)
assert receipt.schema_version == 1
with pytest.raises(ValidationError):
receipt.revision = 2 # type: ignore[misc]
def test_pilot_registry_fixture_matches_project_and_policy_contracts() -> None:
fixture_path = (
Path(__file__).resolve().parents[2] / "schema" / "fixtures" / "pilot-projects.json"
)
def reject_duplicate_keys(pairs: list[tuple[str, object]]) -> dict[str, object]:
result: dict[str, object] = {}
for key, value in pairs:
if key in result:
raise ValueError(f"duplicate JSON key: {key}")
result[key] = value
return result
fixture = json.loads(
fixture_path.read_text(encoding="utf-8"), object_pairs_hook=reject_duplicate_keys
)
projects = tuple(
ProjectRecord.model_validate_json(json.dumps(record), strict=True)
for record in fixture["projects"]
)
policies = tuple(
AutoAcceptPolicy.model_validate_json(json.dumps(record), strict=True)
for record in fixture["test_policies"]
)
assert {project.project_id for project in projects} == {
"dome-performance",
"interactive-video",
}
assert {policy.project_id for policy in policies} == {
"dome-performance",
"interactive-video",
"shared",
}