generated from coulomb/repo-seed
Implement credentialed live hardening workplan
This commit is contained in:
@@ -13,12 +13,32 @@ from .contracts import graph_from_markitect, profile_from_markitect
|
||||
from .credentialed_drills import (
|
||||
CREDENTIALED_ADAPTER_ENV_VARS,
|
||||
CREDENTIALED_DRILL_SCHEMA,
|
||||
CREDENTIALED_OPERATOR_REPORT_SCHEMA,
|
||||
CREDENTIALED_TELEMETRY_DRILL_SCHEMA,
|
||||
CredentialedDrillConfig,
|
||||
credentialed_adapter_smoke_report,
|
||||
credentialed_drill_config_from_env,
|
||||
credentialed_operator_report,
|
||||
credentialed_telemetry_retention_drill,
|
||||
missing_credentialed_adapter_env,
|
||||
write_credentialed_operator_report,
|
||||
)
|
||||
from .deployment import (
|
||||
MANAGED_DEPLOYMENT_SCHEMA,
|
||||
MANAGED_DEPLOYMENT_VALIDATION_SCHEMA,
|
||||
managed_deployment_manifest,
|
||||
validate_managed_deployment_manifest,
|
||||
)
|
||||
from .evaluation import (
|
||||
EVALUATION_REPORT_SCHEMA,
|
||||
EVALUATION_TREND_HISTORY_SCHEMA,
|
||||
EVALUATION_TREND_SCHEMA,
|
||||
evaluation_threshold_report,
|
||||
evaluation_trend_artifact,
|
||||
evaluation_trend_history,
|
||||
load_evaluation_trend_history,
|
||||
write_evaluation_trend_history,
|
||||
)
|
||||
from .evaluation import EVALUATION_REPORT_SCHEMA, EVALUATION_TREND_SCHEMA, evaluation_threshold_report, evaluation_trend_artifact
|
||||
from .external_adapters import (
|
||||
ADAPTER_PACK_MANIFEST_SCHEMA,
|
||||
ExternalAdapterPack,
|
||||
@@ -88,16 +108,25 @@ from .service_app import SERVICE_APP_SCHEMA, ServiceAppConfig, build_service_bin
|
||||
from .service_binding import READINESS_REPORT_SCHEMA, SERVICE_BINDING_SCHEMA, ServiceBinding, ServiceResponse, service_binding_from_config
|
||||
from .planner import plan_profile_execution
|
||||
from .runtime import PhaseMemoryRuntime
|
||||
from .troubleshooting import (
|
||||
TROUBLESHOOTING_MATRIX_SCHEMA,
|
||||
TROUBLESHOOTING_REQUIRED_CATEGORIES,
|
||||
operator_troubleshooting_matrix,
|
||||
validate_operator_troubleshooting_matrix,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"ActivationPlan",
|
||||
"ADAPTER_PACK_MANIFEST_SCHEMA",
|
||||
"CREDENTIALED_ADAPTER_ENV_VARS",
|
||||
"CREDENTIALED_DRILL_SCHEMA",
|
||||
"CREDENTIALED_OPERATOR_REPORT_SCHEMA",
|
||||
"CREDENTIALED_TELEMETRY_DRILL_SCHEMA",
|
||||
"CredentialedDrillConfig",
|
||||
"Diagnostic",
|
||||
"ExternalAdapterPack",
|
||||
"EVALUATION_REPORT_SCHEMA",
|
||||
"EVALUATION_TREND_HISTORY_SCHEMA",
|
||||
"EVALUATION_TREND_SCHEMA",
|
||||
"FakeExternalEventLog",
|
||||
"FakeExternalGraphStore",
|
||||
@@ -137,6 +166,8 @@ __all__ = [
|
||||
"MemoryOperation",
|
||||
"MARKITECT_PACKAGE_REQUEST_SCHEMA",
|
||||
"MARKITECT_PACKAGE_RESPONSE_SCHEMA",
|
||||
"MANAGED_DEPLOYMENT_SCHEMA",
|
||||
"MANAGED_DEPLOYMENT_VALIDATION_SCHEMA",
|
||||
"LocalMarkitectValidator",
|
||||
"OptionalMarkitectValidator",
|
||||
"abandon_path",
|
||||
@@ -145,11 +176,15 @@ __all__ = [
|
||||
"create_path",
|
||||
"credentialed_adapter_smoke_report",
|
||||
"credentialed_drill_config_from_env",
|
||||
"credentialed_operator_report",
|
||||
"credentialed_telemetry_retention_drill",
|
||||
"graph_from_markitect",
|
||||
"evaluation_threshold_report",
|
||||
"evaluation_trend_artifact",
|
||||
"evaluation_trend_history",
|
||||
"merge_path",
|
||||
"make_review_record",
|
||||
"managed_deployment_manifest",
|
||||
"plan_activation",
|
||||
"plan_compaction",
|
||||
"plan_lifecycle_from_profile",
|
||||
@@ -167,6 +202,7 @@ __all__ = [
|
||||
"adapter_pack_manifest",
|
||||
"validate_adapter_pack_manifest",
|
||||
"path_event",
|
||||
"operator_troubleshooting_matrix",
|
||||
"package_request_from_selection",
|
||||
"package_response_envelope",
|
||||
"WordCountTokenEstimator",
|
||||
@@ -183,6 +219,8 @@ __all__ = [
|
||||
"ServiceBinding",
|
||||
"ServiceAppConfig",
|
||||
"ServiceResponse",
|
||||
"TROUBLESHOOTING_MATRIX_SCHEMA",
|
||||
"TROUBLESHOOTING_REQUIRED_CATEGORIES",
|
||||
"build_service_binding",
|
||||
"create_wsgi_app",
|
||||
"health_report",
|
||||
@@ -191,6 +229,11 @@ __all__ = [
|
||||
"service_binding_from_config",
|
||||
"service_app_metadata",
|
||||
"service_contracts",
|
||||
"load_evaluation_trend_history",
|
||||
"validate_managed_deployment_manifest",
|
||||
"validate_operator_troubleshooting_matrix",
|
||||
"write_credentialed_operator_report",
|
||||
"write_evaluation_trend_history",
|
||||
]
|
||||
|
||||
__version__ = "0.1.0"
|
||||
|
||||
@@ -7,14 +7,20 @@ manifest/conformance path used by live-shaped adapter fixtures.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Mapping
|
||||
|
||||
from .external_adapters import live_shaped_adapter_pack, validate_adapter_pack_manifest
|
||||
from .runtime import PhaseMemoryRuntime
|
||||
from .service import default_conformance_adapters
|
||||
from .utils import stable_digest
|
||||
|
||||
CREDENTIALED_DRILL_SCHEMA = "phase_memory.credentialed_adapter_drill.v1"
|
||||
CREDENTIALED_OPERATOR_REPORT_SCHEMA = "phase_memory.credentialed_operator_report.v1"
|
||||
CREDENTIALED_TELEMETRY_DRILL_SCHEMA = "phase_memory.credentialed_telemetry_retention_drill.v1"
|
||||
CREDENTIALED_ADAPTER_ENV_VARS = (
|
||||
"PHASE_MEMORY_MARKITECT_URL",
|
||||
"PHASE_MEMORY_MARKITECT_TOKEN",
|
||||
@@ -30,8 +36,8 @@ class CredentialedDrillConfig:
|
||||
|
||||
def to_dict(self) -> dict[str, str]:
|
||||
return {
|
||||
"markitect_url": self.markitect_url,
|
||||
"kontextual_url": self.kontextual_url,
|
||||
"markitect_endpoint_fingerprint": stable_digest(self.markitect_url),
|
||||
"kontextual_endpoint_fingerprint": stable_digest(self.kontextual_url),
|
||||
"credential_fingerprint": stable_digest([self.markitect_url, self.kontextual_url]),
|
||||
}
|
||||
|
||||
@@ -84,3 +90,110 @@ def credentialed_adapter_smoke_report(environ: Mapping[str, str] | None = None)
|
||||
"conformance_helpers": sorted(conformance_adapters),
|
||||
"diagnostics": [diagnostic.to_dict() for diagnostic in diagnostics],
|
||||
}
|
||||
|
||||
|
||||
def credentialed_operator_report(
|
||||
environ: Mapping[str, str] | None = None,
|
||||
*,
|
||||
run_id: str = "operator-drill",
|
||||
mode: str = "credentialed",
|
||||
) -> dict:
|
||||
smoke = credentialed_adapter_smoke_report(environ)
|
||||
present = sorted(name for name in CREDENTIALED_ADAPTER_ENV_VARS if (environ or {}).get(name))
|
||||
return {
|
||||
"schema_version": CREDENTIALED_OPERATOR_REPORT_SCHEMA,
|
||||
"id": f"credentialed-operator-report:{stable_digest([run_id, smoke])}",
|
||||
"run": {"id": run_id, "mode": mode},
|
||||
"valid": bool(smoke.get("valid")),
|
||||
"skipped": bool(smoke.get("skipped")),
|
||||
"redacted_env": {
|
||||
"present": present,
|
||||
"missing": list(smoke.get("missing_env", ())),
|
||||
"secrets_redacted": True,
|
||||
},
|
||||
"smoke_report": smoke,
|
||||
"diagnostics": list(smoke.get("diagnostics", ())),
|
||||
}
|
||||
|
||||
|
||||
def write_credentialed_operator_report(
|
||||
path: str | Path,
|
||||
environ: Mapping[str, str] | None = None,
|
||||
*,
|
||||
run_id: str = "operator-drill",
|
||||
mode: str = "credentialed",
|
||||
) -> dict:
|
||||
report = credentialed_operator_report(environ, run_id=run_id, mode=mode)
|
||||
path = Path(path)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
return report
|
||||
|
||||
|
||||
def credentialed_telemetry_retention_drill(
|
||||
environ: Mapping[str, str] | None = None,
|
||||
*,
|
||||
retention_days: int = 30,
|
||||
operator_approved_fixture: bool = False,
|
||||
now: datetime | None = None,
|
||||
) -> dict:
|
||||
missing = missing_credentialed_adapter_env(environ)
|
||||
if missing and not operator_approved_fixture:
|
||||
return {
|
||||
"schema_version": CREDENTIALED_TELEMETRY_DRILL_SCHEMA,
|
||||
"valid": False,
|
||||
"skipped": True,
|
||||
"missing_env": list(missing),
|
||||
"diagnostics": [
|
||||
{
|
||||
"severity": "warn",
|
||||
"code": "credential_env_missing",
|
||||
"message": "Telemetry retention drill skipped because required environment variables are missing.",
|
||||
"metadata": {"required_env": list(CREDENTIALED_ADAPTER_ENV_VARS), "missing_env": list(missing)},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
pack = live_shaped_adapter_pack()
|
||||
runtime = PhaseMemoryRuntime(audit_sink=pack.adapters["audit_sink"])
|
||||
runtime.audit_sink.record(_audit_fixture("op:old", "2026-01-01T00:00:00+00:00"))
|
||||
runtime.audit_sink.record(_audit_fixture("op:new", "2026-05-18T00:00:00+00:00"))
|
||||
now = now or datetime(2026, 5, 19, tzinfo=timezone.utc)
|
||||
plan = runtime.audit_retention_plan(retention_days=retention_days, now=now, source_ref="credentialed-telemetry-drill")
|
||||
applied = runtime.apply_audit_retention(plan["plan"], source_ref="credentialed-telemetry-drill")
|
||||
export = runtime.export_audit_events(source_ref="credentialed-telemetry-drill")
|
||||
retained_ids = [
|
||||
str(event.get("operation_id") or "")
|
||||
for event in export["batch"]["events"]
|
||||
if event.get("operation") != "audit.export"
|
||||
]
|
||||
return {
|
||||
"schema_version": CREDENTIALED_TELEMETRY_DRILL_SCHEMA,
|
||||
"valid": plan["valid"] and applied["valid"],
|
||||
"skipped": False,
|
||||
"operator_approved_fixture": operator_approved_fixture,
|
||||
"redacted_env": {
|
||||
"present": sorted(name for name in CREDENTIALED_ADAPTER_ENV_VARS if (environ or {}).get(name)),
|
||||
"missing": list(missing),
|
||||
"secrets_redacted": True,
|
||||
},
|
||||
"plan": plan["plan"],
|
||||
"apply_result": applied["result"],
|
||||
"pruned_operation_ids": applied["result"].get("pruned_operation_ids", []),
|
||||
"retained_operation_ids": retained_ids,
|
||||
"audit_operations": [event.get("operation") for event in export["batch"]["events"]],
|
||||
"diagnostics": plan["diagnostics"] + applied["diagnostics"],
|
||||
}
|
||||
|
||||
|
||||
def _audit_fixture(operation_id: str, timestamp: str) -> dict:
|
||||
return {
|
||||
"schema_version": "phase_memory.audit.event.v1",
|
||||
"operation_id": operation_id,
|
||||
"operation": "credentialed.telemetry.fixture",
|
||||
"timestamp": timestamp,
|
||||
"subject": {"kind": "audit_events", "id": operation_id},
|
||||
"source": {"ref": "credentialed-telemetry-drill"},
|
||||
"dry_run": True,
|
||||
"allowed": True,
|
||||
}
|
||||
|
||||
128
src/phase_memory/deployment.py
Normal file
128
src/phase_memory/deployment.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""Managed deployment manifest helpers for the service app."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .models import Diagnostic
|
||||
from .service_app import ServiceAppConfig, service_app_metadata
|
||||
from .utils import stable_digest
|
||||
|
||||
MANAGED_DEPLOYMENT_SCHEMA = "phase_memory.managed_deployment.v1"
|
||||
MANAGED_DEPLOYMENT_VALIDATION_SCHEMA = "phase_memory.managed_deployment.validation.v1"
|
||||
|
||||
|
||||
def managed_deployment_manifest(
|
||||
config: ServiceAppConfig | None = None,
|
||||
*,
|
||||
image: str = "phase-memory:local",
|
||||
namespace: str = "phase-memory",
|
||||
replicas: int = 1,
|
||||
) -> dict[str, Any]:
|
||||
config = config or ServiceAppConfig(host="0.0.0.0")
|
||||
command = [
|
||||
"phase-memory-service",
|
||||
"--host",
|
||||
config.host,
|
||||
"--port",
|
||||
str(config.port),
|
||||
"--store",
|
||||
config.local_store_path,
|
||||
]
|
||||
manifest = {
|
||||
"schema_version": MANAGED_DEPLOYMENT_SCHEMA,
|
||||
"service": {
|
||||
"name": "phase-memory-service",
|
||||
"namespace": namespace,
|
||||
"image": image,
|
||||
"replicas": replicas,
|
||||
"command": command,
|
||||
"ports": [{"name": "http", "container_port": config.port}],
|
||||
},
|
||||
"runtime": service_app_metadata(config),
|
||||
"storage": {
|
||||
"volumes": [
|
||||
{
|
||||
"name": "phase-memory-local-store",
|
||||
"mount_path": config.local_store_path,
|
||||
"purpose": "local graph, event, and audit state",
|
||||
}
|
||||
]
|
||||
},
|
||||
"probes": {
|
||||
"liveness": {"path": "/health", "port": config.port},
|
||||
"readiness": {"path": "/ready", "port": config.port},
|
||||
},
|
||||
"rollback": {
|
||||
"requires_store_snapshot": True,
|
||||
"checks": ["phase-memory-service --check", "GET /ready", "runtime.repair_diagnostics"],
|
||||
},
|
||||
}
|
||||
manifest["id"] = f"managed-deployment:{stable_digest(manifest)}"
|
||||
return manifest
|
||||
|
||||
|
||||
def validate_managed_deployment_manifest(manifest: dict[str, Any]) -> dict[str, Any]:
|
||||
diagnostics: list[Diagnostic] = []
|
||||
service = manifest.get("service") if isinstance(manifest.get("service"), dict) else {}
|
||||
command = service.get("command") if isinstance(service.get("command"), list) else []
|
||||
probes = manifest.get("probes") if isinstance(manifest.get("probes"), dict) else {}
|
||||
storage = manifest.get("storage") if isinstance(manifest.get("storage"), dict) else {}
|
||||
volumes = storage.get("volumes") if isinstance(storage.get("volumes"), list) else []
|
||||
|
||||
if manifest.get("schema_version") != MANAGED_DEPLOYMENT_SCHEMA:
|
||||
diagnostics.append(
|
||||
Diagnostic(
|
||||
"error",
|
||||
"managed_deployment_schema_mismatch",
|
||||
"Managed deployment manifest has an unexpected schema version.",
|
||||
"schema_version",
|
||||
{"expected": MANAGED_DEPLOYMENT_SCHEMA},
|
||||
)
|
||||
)
|
||||
if "phase-memory-service" not in command:
|
||||
diagnostics.append(
|
||||
Diagnostic(
|
||||
"error",
|
||||
"managed_deployment_missing_service_entrypoint",
|
||||
"Managed deployment command must invoke phase-memory-service.",
|
||||
"service.command",
|
||||
)
|
||||
)
|
||||
for name, path in (("liveness", "/health"), ("readiness", "/ready")):
|
||||
probe = probes.get(name) if isinstance(probes.get(name), dict) else {}
|
||||
if probe.get("path") != path:
|
||||
diagnostics.append(
|
||||
Diagnostic(
|
||||
"error",
|
||||
"managed_deployment_probe_missing",
|
||||
"Managed deployment manifest must declare health and readiness probes.",
|
||||
f"probes.{name}.path",
|
||||
{"expected": path},
|
||||
)
|
||||
)
|
||||
if not any(volume.get("mount_path") for volume in volumes if isinstance(volume, dict)):
|
||||
diagnostics.append(
|
||||
Diagnostic(
|
||||
"error",
|
||||
"managed_deployment_store_mount_missing",
|
||||
"Managed deployment manifest must include a writable local-store mount.",
|
||||
"storage.volumes",
|
||||
)
|
||||
)
|
||||
if int(service.get("replicas") or 0) < 1:
|
||||
diagnostics.append(
|
||||
Diagnostic(
|
||||
"error",
|
||||
"managed_deployment_replica_count_invalid",
|
||||
"Managed deployment manifest must request at least one replica.",
|
||||
"service.replicas",
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
"schema_version": MANAGED_DEPLOYMENT_VALIDATION_SCHEMA,
|
||||
"valid": not any(diagnostic.severity == "error" for diagnostic in diagnostics),
|
||||
"manifest_id": str(manifest.get("id") or ""),
|
||||
"diagnostics": [diagnostic.to_dict() for diagnostic in diagnostics],
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .adapters import InMemorySemanticIndex
|
||||
@@ -14,6 +16,7 @@ from .utils import stable_digest, utc_now_iso
|
||||
|
||||
EVALUATION_REPORT_SCHEMA = "phase_memory.evaluation.threshold_report.v1"
|
||||
EVALUATION_TREND_SCHEMA = "phase_memory.evaluation.trend_artifact.v1"
|
||||
EVALUATION_TREND_HISTORY_SCHEMA = "phase_memory.evaluation.trend_history.v1"
|
||||
|
||||
DEFAULT_THRESHOLDS = {
|
||||
"policy_denial_count": 1,
|
||||
@@ -115,6 +118,68 @@ def evaluation_trend_artifact(
|
||||
"previous_report_id": (previous_report or {}).get("id", ""),
|
||||
"diagnostics": diagnostics,
|
||||
}
|
||||
|
||||
|
||||
def evaluation_trend_history(artifacts: list[dict[str, Any]] | tuple[dict[str, Any], ...]) -> dict[str, Any]:
|
||||
ordered = sorted(
|
||||
(dict(artifact) for artifact in artifacts),
|
||||
key=lambda artifact: (
|
||||
str((artifact.get("run") or {}).get("created_at") or ""),
|
||||
str((artifact.get("run") or {}).get("run_id") or ""),
|
||||
str(artifact.get("id") or ""),
|
||||
),
|
||||
)
|
||||
metric_keys = sorted({str(key) for artifact in ordered for key in (artifact.get("metrics") or {})})
|
||||
diagnostics = [
|
||||
Diagnostic(
|
||||
"error",
|
||||
"evaluation_trend_history_invalid_artifact",
|
||||
"Trend history can only contain evaluation trend artifacts.",
|
||||
f"artifacts.{index}.schema_version",
|
||||
{"artifact_id": artifact.get("id", "")},
|
||||
).to_dict()
|
||||
for index, artifact in enumerate(ordered)
|
||||
if artifact.get("schema_version") != EVALUATION_TREND_SCHEMA
|
||||
]
|
||||
return {
|
||||
"schema_version": EVALUATION_TREND_HISTORY_SCHEMA,
|
||||
"id": f"evaluation-trend-history:{stable_digest([artifact.get('id', '') for artifact in ordered])}",
|
||||
"valid": not diagnostics,
|
||||
"count": len(ordered),
|
||||
"metric_keys": metric_keys,
|
||||
"latest_artifact_id": ordered[-1].get("id", "") if ordered else "",
|
||||
"artifacts": ordered,
|
||||
"diagnostics": diagnostics,
|
||||
}
|
||||
|
||||
|
||||
def load_evaluation_trend_history(path: str | Path) -> dict[str, Any]:
|
||||
path = Path(path)
|
||||
if not path.exists():
|
||||
return evaluation_trend_history(())
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
if data.get("schema_version") == EVALUATION_TREND_HISTORY_SCHEMA:
|
||||
return data
|
||||
if data.get("schema_version") == EVALUATION_TREND_SCHEMA:
|
||||
return evaluation_trend_history((data,))
|
||||
return evaluation_trend_history((data,))
|
||||
|
||||
|
||||
def write_evaluation_trend_history(path: str | Path, artifact: dict[str, Any]) -> dict[str, Any]:
|
||||
path = Path(path)
|
||||
existing = load_evaluation_trend_history(path)
|
||||
artifacts = list(existing.get("artifacts") or ())
|
||||
artifact_id = str(artifact.get("id") or "")
|
||||
if artifact_id and not any(str(item.get("id") or "") == artifact_id for item in artifacts):
|
||||
artifacts.append(dict(artifact))
|
||||
elif not artifact_id:
|
||||
artifacts.append(dict(artifact))
|
||||
history = evaluation_trend_history(tuple(artifacts))
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(history, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
return history
|
||||
|
||||
|
||||
def _policy_scenario(scenario: dict[str, Any]) -> dict[str, Any]:
|
||||
runtime = PhaseMemoryRuntime()
|
||||
response = runtime.plan_activation(
|
||||
|
||||
112
src/phase_memory/troubleshooting.py
Normal file
112
src/phase_memory/troubleshooting.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""Operator troubleshooting matrix for readiness and deployment drills."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from .models import Diagnostic
|
||||
from .utils import stable_digest
|
||||
|
||||
TROUBLESHOOTING_MATRIX_SCHEMA = "phase_memory.operator_troubleshooting.v1"
|
||||
TROUBLESHOOTING_REQUIRED_CATEGORIES = (
|
||||
"credentials",
|
||||
"readiness",
|
||||
"migrations",
|
||||
"audit_retention",
|
||||
"adapter_manifest",
|
||||
)
|
||||
|
||||
|
||||
def operator_troubleshooting_matrix() -> dict[str, Any]:
|
||||
rows = [
|
||||
{
|
||||
"category": "credentials",
|
||||
"diagnostic_code": "credential_env_missing",
|
||||
"signal": "Credentialed drill reports skipped: true.",
|
||||
"likely_cause": "One or more required operator environment variables are absent.",
|
||||
"operator_action": "Set the Markitect and Kontextual URL/token variables in the shell that runs the drill; do not persist them to files.",
|
||||
},
|
||||
{
|
||||
"category": "readiness",
|
||||
"diagnostic_code": "unsupported_operation",
|
||||
"signal": "Readiness reports unsupported operations or a failing /ready response.",
|
||||
"likely_cause": "The service binding and local runner operation catalogs are out of sync.",
|
||||
"operator_action": "Run the public API snapshot and service contract tests, then update operation dispatch or release notes.",
|
||||
},
|
||||
{
|
||||
"category": "migrations",
|
||||
"diagnostic_code": "store_migration_unsupported",
|
||||
"signal": "Migration plan/apply returns an error diagnostic.",
|
||||
"likely_cause": "The configured graph store does not expose migration planning.",
|
||||
"operator_action": "Switch to a file-backed local store or run repair diagnostics before accepting traffic.",
|
||||
},
|
||||
{
|
||||
"category": "audit_retention",
|
||||
"diagnostic_code": "audit_retention_apply_unsupported",
|
||||
"signal": "Retention apply reports unsupported behavior.",
|
||||
"likely_cause": "The configured audit sink cannot apply retention plans.",
|
||||
"operator_action": "Use a JSONL or telemetry audit sink with retention support, then rerun the retention drill.",
|
||||
},
|
||||
{
|
||||
"category": "adapter_manifest",
|
||||
"diagnostic_code": "adapter_pack_manifest_invalid",
|
||||
"signal": "Adapter pack validation returns an error diagnostic.",
|
||||
"likely_cause": "A required external adapter capability is missing from the pack.",
|
||||
"operator_action": "Regenerate the adapter pack manifest and confirm graph, event, policy, package, audit, semantic, and registry adapters are declared.",
|
||||
},
|
||||
]
|
||||
return {
|
||||
"schema_version": TROUBLESHOOTING_MATRIX_SCHEMA,
|
||||
"id": f"operator-troubleshooting:{stable_digest(rows)}",
|
||||
"required_categories": list(TROUBLESHOOTING_REQUIRED_CATEGORIES),
|
||||
"rows": rows,
|
||||
}
|
||||
|
||||
|
||||
def validate_operator_troubleshooting_matrix(matrix: dict[str, Any]) -> dict[str, Any]:
|
||||
diagnostics: list[Diagnostic] = []
|
||||
rows = matrix.get("rows") if isinstance(matrix.get("rows"), list) else []
|
||||
categories = {str(row.get("category") or "") for row in rows if isinstance(row, dict)}
|
||||
missing = sorted(set(TROUBLESHOOTING_REQUIRED_CATEGORIES) - categories)
|
||||
if matrix.get("schema_version") != TROUBLESHOOTING_MATRIX_SCHEMA:
|
||||
diagnostics.append(
|
||||
Diagnostic(
|
||||
"error",
|
||||
"troubleshooting_matrix_schema_mismatch",
|
||||
"Troubleshooting matrix has an unexpected schema version.",
|
||||
"schema_version",
|
||||
{"expected": TROUBLESHOOTING_MATRIX_SCHEMA},
|
||||
)
|
||||
)
|
||||
if missing:
|
||||
diagnostics.append(
|
||||
Diagnostic(
|
||||
"error",
|
||||
"troubleshooting_matrix_missing_category",
|
||||
"Troubleshooting matrix must cover every required operator category.",
|
||||
"rows",
|
||||
{"missing": missing},
|
||||
)
|
||||
)
|
||||
required_fields = ("category", "diagnostic_code", "signal", "likely_cause", "operator_action")
|
||||
for index, row in enumerate(rows):
|
||||
if not isinstance(row, dict):
|
||||
diagnostics.append(
|
||||
Diagnostic("error", "troubleshooting_matrix_invalid_row", "Troubleshooting row must be an object.", f"rows.{index}")
|
||||
)
|
||||
continue
|
||||
for field in required_fields:
|
||||
if not row.get(field):
|
||||
diagnostics.append(
|
||||
Diagnostic(
|
||||
"error",
|
||||
"troubleshooting_matrix_missing_field",
|
||||
"Troubleshooting row is missing a required field.",
|
||||
f"rows.{index}.{field}",
|
||||
)
|
||||
)
|
||||
return {
|
||||
"schema_version": f"{TROUBLESHOOTING_MATRIX_SCHEMA}.validation",
|
||||
"valid": not any(diagnostic.severity == "error" for diagnostic in diagnostics),
|
||||
"diagnostics": [diagnostic.to_dict() for diagnostic in diagnostics],
|
||||
}
|
||||
Reference in New Issue
Block a user