diff --git a/docs/adr/0011-laboratory-plugin-runtime-handshake-and-transport-seam.md b/docs/adr/0011-laboratory-plugin-runtime-handshake-and-transport-seam.md index fa52929..596830a 100644 --- a/docs/adr/0011-laboratory-plugin-runtime-handshake-and-transport-seam.md +++ b/docs/adr/0011-laboratory-plugin-runtime-handshake-and-transport-seam.md @@ -64,7 +64,10 @@ separate future gate. `GET /api/v1/device-plugin-runtimes` exposes the small lifecycle snapshots. The main health response includes ready/total runtime counts as one component beside -recording-cache, shared-artifact, media-tool and catalog-reconciler readiness. +recording-cache, optional shared-artifact capability, media-tool and +catalog-reconciler readiness. A missing optional shared-artifact provider does +not degrade the complete local contour. + The plugin values still do not claim device connectivity, sensor health, process containment or durable supervision. diff --git a/docs/adr/0022-operator-surface-ownership.md b/docs/adr/0022-operator-surface-ownership.md index da05e39..0b257be 100644 --- a/docs/adr/0022-operator-surface-ownership.md +++ b/docs/adr/0022-operator-surface-ownership.md @@ -78,7 +78,7 @@ Contour health reads only live contracts: - current Mission Runtime state for active device and perception metrics. The base health contract now separates local operational readiness from -production shared-artifact readiness and reports bounded cache, catalog +optional shared-artifact capability and reports bounded cache, catalog reconciler, media-tool and Map Gateway configuration states. It performs no remote fan-out and never invents Worker 006 or device health. diff --git a/docs/adr/0033-central-session-artifacts-and-bounded-host-cache.md b/docs/adr/0033-central-session-artifacts-and-bounded-host-cache.md index a8fc8af..7c45da1 100644 --- a/docs/adr/0033-central-session-artifacts-and-bounded-host-cache.md +++ b/docs/adr/0033-central-session-artifacts-and-bounded-host-cache.md @@ -5,17 +5,18 @@ Status: accepted and implemented ## Decision -Mission Core stores portable recorded-session products in a provider-neutral, -content-addressed artifact store. The current central provider is the existing -Synology SMB mount: +Mission Core can exchange portable recorded-session products through an +optional provider-neutral, content-addressed artifact gateway. The canonical +current operator contour is local and complete without a shared store. An +external filesystem provider is an auxiliary transport/prefetch capability, +not an operational-readiness dependency and not a production topology +decision. -```text -/Volumes/docker/nodedc-mission-core/artifact-store -``` - -This is product runtime data. It is not a deploy artifact, a Map Gateway cache, -a Device Plane package, or a new Docker service. Authentication remains owned -by the operating-system SMB mount; Mission Core does not receive a NAS password. +The historical RAVNOVES00 migration acceptance used an existing SMB-backed +filesystem as one experimental provider. That provider, its mount and its host +configuration are outside the current Mission Core runtime contract. Mission +Core does not own its authentication, does not require it at startup and does +not receive a NAS password. Every immutable object is stored under: @@ -55,7 +56,7 @@ MISSIONCORE_ARTIFACT_CACHE_MAX_BYTES MISSIONCORE_ARTIFACT_CACHE_FREE_SPACE_RESERVE_BYTES ``` -The central root is configured with: +An optional shared root is configured explicitly with: ```text MISSIONCORE_ARTIFACT_STORE_ROOT @@ -115,16 +116,19 @@ When the central root is unavailable: `GET /api/health` exposes the effective shared/standalone mode, central-store availability, bounded CAS and RRD-cache occupancy, media-tool availability and the background catalog reconciler state without disclosing filesystem paths. -Operational readiness remains distinct from production shared-artifact -readiness so an offline, already pinned field host stays observable rather than -being mistaken for a fully connected host. +`GET /api/health` reports local operational readiness independently. Optional +shared artifacts are exposed as a non-required capability with separate +`configured` and `ready` values. Their absence or temporary unavailability does +not degrade a healthy local contour. When an operator explicitly configures +that capability, its fail-closed cache-miss semantics still apply to artifacts +that were expected from the configured gateway. ## RAVNOVES00 migration acceptance The source session is `20260720T065719Z_viewer_live`, displayed as `RAVNOVES00`. -The accepted central reference currently points to: +The reference used by that bounded migration acceptance pointed to: ```text 092f758c09965be98fd76d8191474c528ee20540dce5f4761c2a087f6056a6d5 diff --git a/src/k1link/artifact_cli.py b/src/k1link/artifact_cli.py index c5ea8d9..e768654 100644 --- a/src/k1link/artifact_cli.py +++ b/src/k1link/artifact_cli.py @@ -1,4 +1,4 @@ -"""Operator commands for the Mission Core central artifact gateway.""" +"""Operator commands for the optional Mission Core artifact gateway.""" from __future__ import annotations diff --git a/src/k1link/artifact_gateway.py b/src/k1link/artifact_gateway.py index 879f08c..6c74d6c 100644 --- a/src/k1link/artifact_gateway.py +++ b/src/k1link/artifact_gateway.py @@ -1,4 +1,4 @@ -"""Content-addressed central artifacts with a bounded, offline-capable host cache.""" +"""Optional content-addressed artifact exchange with a bounded local cache.""" from __future__ import annotations diff --git a/src/k1link/web/runtime_readiness.py b/src/k1link/web/runtime_readiness.py index 6c67632..5fd1aec 100644 --- a/src/k1link/web/runtime_readiness.py +++ b/src/k1link/web/runtime_readiness.py @@ -100,8 +100,9 @@ def build_runtime_readiness( "central_available": None, } shared_artifacts_ready = False - shared_artifact_degraded = False + shared_artifacts_configured = False else: + shared_artifacts_configured = True try: artifact = artifact_gateway.status() cache = artifact.cache @@ -127,7 +128,6 @@ def build_runtime_readiness( }, } shared_artifacts_ready = artifact.central_status == "ready" - shared_artifact_degraded = not shared_artifacts_ready except (OSError, RuntimeError): artifact_document = { "mode": "shared", @@ -135,7 +135,6 @@ def build_runtime_readiness( "central_available": False, } shared_artifacts_ready = False - shared_artifact_degraded = True media_tools_ready = ffmpeg_available and ffprobe_available reconciler_document = reconciler.snapshot() @@ -151,11 +150,7 @@ def build_runtime_readiness( and media_tools_ready and reconciler_failures < 3 ) - degraded = ( - shared_artifact_degraded - or reconciler_failures > 0 - or not operational_ready - ) + degraded = reconciler_failures > 0 or not operational_ready status = ( "unavailable" if not operational_ready @@ -170,9 +165,11 @@ def build_runtime_readiness( "version": version, "readiness": { "operational": operational_ready, - "production_shared_artifacts": ( - operational_ready and shared_artifacts_ready - ), + "optional_shared_artifacts": { + "required": False, + "configured": shared_artifacts_configured, + "ready": shared_artifacts_ready, + }, }, "plugin_runtimes": { "ready": ready_runtime_count, diff --git a/tests/test_runtime_readiness.py b/tests/test_runtime_readiness.py index a8c9b56..9b87288 100644 --- a/tests/test_runtime_readiness.py +++ b/tests/test_runtime_readiness.py @@ -57,7 +57,11 @@ def test_runtime_readiness_reports_shared_store_and_bounded_cache( assert document["status"] == "ok" assert document["readiness"] == { "operational": True, - "production_shared_artifacts": True, + "optional_shared_artifacts": { + "required": False, + "configured": True, + "ready": True, + }, } components = document["components"] assert isinstance(components, dict) @@ -85,16 +89,47 @@ def test_runtime_readiness_exposes_offline_cache_only_mode( document = _document(tmp_path, gateway=gateway, reconciler=reconciler) assert document["ok"] is True - assert document["status"] == "degraded" + assert document["status"] == "ok" assert document["readiness"] == { "operational": True, - "production_shared_artifacts": False, + "optional_shared_artifacts": { + "required": False, + "configured": True, + "ready": False, + }, } components = document["components"] assert isinstance(components, dict) assert components["artifact_store"]["status"] == "offline-cache-only" +def test_runtime_readiness_treats_standalone_as_complete_local_contour( + tmp_path: Path, +) -> None: + reconciler = BackgroundReconcilerReadiness() + reconciler.record_success() + + document = _document(tmp_path, gateway=None, reconciler=reconciler) + + assert document["ok"] is True + assert document["status"] == "ok" + assert document["readiness"] == { + "operational": True, + "optional_shared_artifacts": { + "required": False, + "configured": False, + "ready": False, + }, + } + components = document["components"] + assert isinstance(components, dict) + assert components["artifact_store"] == { + "mode": "standalone", + "status": "not-configured", + "central_available": None, + } + + def test_runtime_readiness_fails_after_repeated_reconciler_errors( tmp_path: Path, ) -> None: