feat(observatory): verify exact published result reuse

This commit is contained in:
DCCONSTRUCTIONS
2026-09-03 09:14:54 +03:00
parent 80fc0058cb
commit 1e4ddc2cff
10 changed files with 910 additions and 42 deletions
+18
View File
@@ -54,6 +54,7 @@ from k1link.observatory.portable_queue_binding import (
PortableQueueBindingError,
PortableRecordedQueueBindingService,
)
from k1link.observatory.portable_result_cache import PortableResultCache
from k1link.observatory.portable_result_contract import (
PortableCalculationProfileRegistry,
PortableResultContractValidatorRegistry,
@@ -538,24 +539,41 @@ else:
OBSERVATORY_PORTABLE_BINDING_SERVICE: PortableRecordedQueueBindingService | None
OBSERVATORY_PORTABLE_SETUP_PROJECTOR: PortableSetupProjector | None
OBSERVATORY_PORTABLE_SETUP_PROJECTOR_ERROR: str | None
OBSERVATORY_PORTABLE_RESULT_CACHE: PortableResultCache | None = None
try:
if OBSERVATORY_PORTABLE_DEFINITION_REGISTRY is None:
raise PortableSetupProjectionError(
OBSERVATORY_PORTABLE_DEFINITION_REGISTRY_ERROR
or "portable definition registry is unavailable"
)
if (
session_artifact_gateway is not None
and OBSERVATORY_RECORDED_JOB_QUEUE is not None
and OBSERVATORY_PORTABLE_CALCULATION_PROFILES is not None
):
OBSERVATORY_PORTABLE_RESULT_CACHE = PortableResultCache(
sessions=session_store, artifacts=session_artifact_gateway.store,
queue=OBSERVATORY_RECORDED_JOB_QUEUE,
definitions=OBSERVATORY_PORTABLE_DEFINITION_REGISTRY,
calculation_profiles=OBSERVATORY_PORTABLE_CALCULATION_PROFILES,
)
OBSERVATORY_PORTABLE_BINDING_SERVICE = PortableRecordedQueueBindingService(
data_dir=session_store.data_dir,
session_store=session_store,
media_inspector=session_recorded_media_inspector,
definitions=OBSERVATORY_PORTABLE_DEFINITION_REGISTRY,
queue=OBSERVATORY_RECORDED_JOB_QUEUE,
published_result_available=(
None if OBSERVATORY_PORTABLE_RESULT_CACHE is None
else OBSERVATORY_PORTABLE_RESULT_CACHE.available
),
)
OBSERVATORY_PORTABLE_SETUP_PROJECTOR = PortableSetupProjector(
registry=OBSERVATORY_PORTABLE_DEFINITION_REGISTRY,
capability_probe=OBSERVATORY_PORTABLE_BINDING_SERVICE,
dispatch_available=OBSERVATORY_WORKER_DISPATCH_READY,
equipment_capture_registry=session_store.equipment_capture_registry,
result_cache=OBSERVATORY_PORTABLE_RESULT_CACHE,
)
OBSERVATORY_PORTABLE_SETUP_PROJECTOR_ERROR = None
except (
+35 -1
View File
@@ -33,6 +33,7 @@ from k1link.observatory.portable_queue_binding import (
PortableQueueBindingStaleCheckError,
PortableRecordedQueueBindingService,
)
from k1link.observatory.portable_result_cache import PortableResultCacheCheckRequired
from k1link.observatory.portable_result_contract import PortableResultPublisherError
from k1link.observatory.portable_result_publisher import (
PortableObservatoryResultPublisher,
@@ -298,6 +299,29 @@ def build_observatory_router(
)
compatible = compatibility.get("compatible") is True
executor_ready = executor.get("state") == "ready" and executor.get("ready") is True
projected_preflight = projected.get("preflight")
if (
isinstance(projected_preflight, dict)
and projected_preflight.get("outcome") == "existing"
):
return {
"schema_version": OBSERVATORY_RUN_PREFLIGHT_SCHEMA,
"source_session_id": request.source_session_id,
"setup_id": request.setup_id,
"definition_sha256": expected_digest,
"check_sha256": None,
"outcome": "existing",
"submission_allowed": False,
"checks": [{
"check_id": "published-result",
"outcome": "pass",
"reason_code": "exact-published-result-verified",
"message": "Точный расчёт сохранён; повторный inference не требуется.",
}],
"existing_result_ids": projected_preflight["existing_result_ids"],
"executor": executor,
"authority": projected.get("authority", dict(_OBSERVATION_ONLY_AUTHORITY)),
}
checked = None
check_reason: str | None = None
if (
@@ -895,6 +919,11 @@ def build_observatory_router(
status_code=409,
detail="Идентичность RunDefinition изменилась; повторите preflight.",
)
if portable_projection.get("existing_results"):
raise HTTPException(
status_code=409,
detail="Точный расчёт уже сохранён. Обновите каталог и откройте результат.",
)
if (
projected_executor.get("state") != "ready"
or projected_executor.get("ready") is not True
@@ -930,11 +959,16 @@ def build_observatory_router(
status_code=409,
detail="Portable-привязка источника не прошла проверку целостности.",
) from exc
except PortableResultCacheCheckRequired as exc:
raise HTTPException(
status_code=409,
detail="Опубликованный результат требует проверки. Обновите каталог.",
) from exc
except ObservatoryRecordedQueueDuplicateError as exc:
raise HTTPException(
status_code=409,
detail=(
"Такой расчёт уже выполняется или ожидает публикации. "
"Такой расчёт уже выполняется, ожидает публикации или сохранён. "
"Обновите список расчётов; повторный запуск не создан."
),
) from exc