feat(stockplatform): surface data readiness receipt
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 53s
CD Pipeline / build-and-deploy (push) Successful in 6m56s
CD Pipeline / post-deploy-checks (push) Successful in 1m59s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 53s
CD Pipeline / build-and-deploy (push) Successful in 6m56s
CD Pipeline / post-deploy-checks (push) Successful in 1m59s
This commit is contained in:
@@ -994,6 +994,9 @@ def apply_stockplatform_public_api_runtime_readback(
|
||||
runtime_blockers = _strings(runtime_readback.get("active_blockers"))
|
||||
runtime_rollups = _dict(runtime_readback.get("rollups"))
|
||||
runtime_readback_body = _dict(runtime_readback.get("readback"))
|
||||
data_readiness_readback = _dict(
|
||||
runtime_readback.get("data_readiness_control_readback")
|
||||
)
|
||||
data_dependency_classification = str(
|
||||
runtime_rollups.get("data_dependency_classification")
|
||||
or runtime_readback_body.get("data_dependency_classification")
|
||||
@@ -1033,6 +1036,18 @@ def apply_stockplatform_public_api_runtime_readback(
|
||||
data_dependency_classification
|
||||
)
|
||||
state["stockplatform_public_api_postgres_not_ready"] = postgres_not_ready
|
||||
state["stockplatform_postgres_readiness_or_data_source_readback_status"] = str(
|
||||
data_readiness_readback.get("status") or "unknown"
|
||||
)
|
||||
state["stockplatform_postgres_readiness_or_data_source_readback_present"] = bool(
|
||||
data_readiness_readback.get("readback_present") is True
|
||||
)
|
||||
state["stockplatform_postgres_readiness_or_data_source_receipt_provided"] = bool(
|
||||
data_readiness_readback.get("receipt_provided") is True
|
||||
)
|
||||
state["stockplatform_postgres_readiness_or_data_source_postgres_ready"] = bool(
|
||||
data_readiness_readback.get("postgres_ready") is True
|
||||
)
|
||||
|
||||
for item in _list(payload.get("in_progress_or_blocked_in_priority_order")):
|
||||
workplan = _dict(item)
|
||||
@@ -1063,6 +1078,18 @@ def apply_stockplatform_public_api_runtime_readback(
|
||||
data_dependency_classification
|
||||
)
|
||||
evidence["stockplatform_postgres_not_ready"] = postgres_not_ready
|
||||
evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_status"
|
||||
] = state["stockplatform_postgres_readiness_or_data_source_readback_status"]
|
||||
evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_present"
|
||||
] = state["stockplatform_postgres_readiness_or_data_source_readback_present"]
|
||||
evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_receipt_provided"
|
||||
] = state["stockplatform_postgres_readiness_or_data_source_receipt_provided"]
|
||||
evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_postgres_ready"
|
||||
] = state["stockplatform_postgres_readiness_or_data_source_postgres_ready"]
|
||||
evidence["stockplatform_recovery_control_path_status"] = (
|
||||
recovery_control_path_status
|
||||
)
|
||||
@@ -1079,12 +1106,14 @@ def apply_stockplatform_public_api_runtime_readback(
|
||||
)
|
||||
workplan["reason"] = (
|
||||
_stockplatform_runtime_drift_reason(
|
||||
data_dependency_classification=data_dependency_classification,
|
||||
postgres_not_ready=postgres_not_ready,
|
||||
)
|
||||
)
|
||||
professional_fix = _dict(workplan.setdefault("professional_fix", {}))
|
||||
professional_fix["action"] = (
|
||||
_stockplatform_runtime_drift_action(
|
||||
data_dependency_classification=data_dependency_classification,
|
||||
postgres_not_ready=postgres_not_ready,
|
||||
)
|
||||
)
|
||||
@@ -1108,11 +1137,15 @@ def apply_stockplatform_public_api_runtime_readback(
|
||||
"P0-006-STOCKPLATFORM-PUBLIC-API-RUNTIME-READBACK"
|
||||
)
|
||||
state["next_executable_mainline_state"] = (
|
||||
_stockplatform_runtime_next_state(postgres_not_ready=postgres_not_ready)
|
||||
_stockplatform_runtime_next_state(
|
||||
data_dependency_classification=data_dependency_classification,
|
||||
postgres_not_ready=postgres_not_ready,
|
||||
)
|
||||
)
|
||||
payload["status"] = "p0_006_blocked_stockplatform_public_api_runtime_drift"
|
||||
payload["next_execution_order"] = [
|
||||
_stockplatform_runtime_first_order(
|
||||
data_dependency_classification=data_dependency_classification,
|
||||
postgres_not_ready=postgres_not_ready,
|
||||
),
|
||||
(
|
||||
@@ -1152,6 +1185,9 @@ def apply_stockplatform_public_api_controlled_recovery_preflight(
|
||||
source_diff = _dict(recovery_preflight.get("source_of_truth_diff"))
|
||||
target_selector = _dict(recovery_preflight.get("target_selector"))
|
||||
controlled_policy = _dict(recovery_preflight.get("controlled_apply_policy"))
|
||||
data_readiness_readback = _dict(
|
||||
recovery_preflight.get("data_readiness_control_readback")
|
||||
)
|
||||
candidate_packaged = bool(
|
||||
rollups.get("controlled_recovery_candidate_packaged") is True
|
||||
)
|
||||
@@ -1167,6 +1203,18 @@ def apply_stockplatform_public_api_controlled_recovery_preflight(
|
||||
state["stockplatform_public_api_controlled_recovery_data_dependency"] = (
|
||||
data_dependency_classification
|
||||
)
|
||||
state["stockplatform_postgres_readiness_or_data_source_readback_status"] = str(
|
||||
data_readiness_readback.get("status") or "unknown"
|
||||
)
|
||||
state["stockplatform_postgres_readiness_or_data_source_readback_present"] = bool(
|
||||
data_readiness_readback.get("readback_present") is True
|
||||
)
|
||||
state["stockplatform_postgres_readiness_or_data_source_receipt_provided"] = bool(
|
||||
data_readiness_readback.get("receipt_provided") is True
|
||||
)
|
||||
state["stockplatform_postgres_readiness_or_data_source_postgres_ready"] = bool(
|
||||
data_readiness_readback.get("postgres_ready") is True
|
||||
)
|
||||
state["stockplatform_public_api_controlled_recovery_active_blockers"] = _strings(
|
||||
recovery_preflight.get("active_blockers")
|
||||
)
|
||||
@@ -1208,6 +1256,18 @@ def apply_stockplatform_public_api_controlled_recovery_preflight(
|
||||
evidence["stockplatform_source_of_truth_diff_status"] = str(
|
||||
source_diff.get("status") or ""
|
||||
)
|
||||
evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_status"
|
||||
] = state["stockplatform_postgres_readiness_or_data_source_readback_status"]
|
||||
evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_present"
|
||||
] = state["stockplatform_postgres_readiness_or_data_source_readback_present"]
|
||||
evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_receipt_provided"
|
||||
] = state["stockplatform_postgres_readiness_or_data_source_receipt_provided"]
|
||||
evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_postgres_ready"
|
||||
] = state["stockplatform_postgres_readiness_or_data_source_postgres_ready"]
|
||||
evidence["stockplatform_controlled_recovery_candidate_packaged"] = (
|
||||
candidate_packaged
|
||||
)
|
||||
@@ -1291,6 +1351,14 @@ def apply_stockplatform_public_api_controlled_recovery_preflight(
|
||||
summary["stockplatform_public_api_host_control_path_readback_required"] = bool(
|
||||
rollups.get("host_control_path_readback_required") is True
|
||||
)
|
||||
summary["stockplatform_postgres_readiness_or_data_source_readback_status"] = str(
|
||||
state.get("stockplatform_postgres_readiness_or_data_source_readback_status")
|
||||
or "unknown"
|
||||
)
|
||||
summary["stockplatform_postgres_readiness_or_data_source_receipt_provided"] = bool(
|
||||
state.get("stockplatform_postgres_readiness_or_data_source_receipt_provided")
|
||||
is True
|
||||
)
|
||||
|
||||
|
||||
def apply_harbor_registry_controlled_recovery_preflight(
|
||||
@@ -2493,19 +2561,32 @@ def _stockplatform_recovery_first_order(
|
||||
)
|
||||
|
||||
|
||||
def _stockplatform_runtime_next_state(*, postgres_not_ready: bool) -> str:
|
||||
def _stockplatform_runtime_next_state(
|
||||
*,
|
||||
data_dependency_classification: str,
|
||||
postgres_not_ready: bool,
|
||||
) -> str:
|
||||
if postgres_not_ready:
|
||||
return (
|
||||
"blocked_live_stockplatform_public_api_requires_production_"
|
||||
"migration_control_channel_readback"
|
||||
)
|
||||
if data_dependency_classification == "freshness_or_ingestion_not_ready":
|
||||
return (
|
||||
"blocked_live_stockplatform_public_api_requires_data_dependency_"
|
||||
"source_contract_readback"
|
||||
)
|
||||
return (
|
||||
"blocked_live_stockplatform_public_api_requires_separate_runtime_"
|
||||
"control_path_recovery_without_daemon_restart"
|
||||
)
|
||||
|
||||
|
||||
def _stockplatform_runtime_drift_reason(*, postgres_not_ready: bool) -> str:
|
||||
def _stockplatform_runtime_drift_reason(
|
||||
*,
|
||||
data_dependency_classification: str,
|
||||
postgres_not_ready: bool,
|
||||
) -> str:
|
||||
if postgres_not_ready:
|
||||
return (
|
||||
"StockPlatform public API health is now HTTP 200, but freshness and "
|
||||
@@ -2513,6 +2594,13 @@ def _stockplatform_runtime_drift_reason(*, postgres_not_ready: bool) -> str:
|
||||
"the production migration/control-channel path before claiming the "
|
||||
"reboot SLO lane is only waiting on a fresh boot window."
|
||||
)
|
||||
if data_dependency_classification == "freshness_or_ingestion_not_ready":
|
||||
return (
|
||||
"StockPlatform public API health is now HTTP 200, but freshness and "
|
||||
"ingestion readbacks are not green. P0-006 must stay on the data "
|
||||
"dependency/source contract readback path before claiming the reboot "
|
||||
"SLO lane is only waiting on a fresh boot window."
|
||||
)
|
||||
return (
|
||||
"Committed P0-006 scorecard says StockPlatform freshness and ingestion "
|
||||
"are ok, but live public API readback is blocked. This must remain "
|
||||
@@ -2521,7 +2609,11 @@ def _stockplatform_runtime_drift_reason(*, postgres_not_ready: bool) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _stockplatform_runtime_drift_action(*, postgres_not_ready: bool) -> str:
|
||||
def _stockplatform_runtime_drift_action(
|
||||
*,
|
||||
data_dependency_classification: str,
|
||||
postgres_not_ready: bool,
|
||||
) -> str:
|
||||
if postgres_not_ready:
|
||||
return (
|
||||
"Recover the StockPlatform production migration/control channel "
|
||||
@@ -2531,6 +2623,15 @@ def _stockplatform_runtime_drift_action(*, postgres_not_ready: bool) -> str:
|
||||
"reload Nginx, manually write DB rows, fake freshness, trigger "
|
||||
"unrelated workflows, or read secrets from this lane."
|
||||
)
|
||||
if data_dependency_classification == "freshness_or_ingestion_not_ready":
|
||||
return (
|
||||
"Run the StockPlatform data dependency/source contract readback "
|
||||
"with target selector, source-of-truth diff, dry-run/check-mode, "
|
||||
"rollback, and public API verifier, then rerun the public readback. "
|
||||
"Do not restart Docker daemon, reboot hosts, reload Nginx, manually "
|
||||
"write DB rows, fake freshness, trigger unrelated workflows, or read "
|
||||
"secrets from this lane."
|
||||
)
|
||||
return (
|
||||
"Recover the StockPlatform API container/runtime control path with a "
|
||||
"separate bounded plan, then rerun public API readback. Do not restart "
|
||||
@@ -2539,7 +2640,11 @@ def _stockplatform_runtime_drift_action(*, postgres_not_ready: bool) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _stockplatform_runtime_first_order(*, postgres_not_ready: bool) -> str:
|
||||
def _stockplatform_runtime_first_order(
|
||||
*,
|
||||
data_dependency_classification: str,
|
||||
postgres_not_ready: bool,
|
||||
) -> str:
|
||||
if postgres_not_ready:
|
||||
return (
|
||||
"P0-006-STOCKPLATFORM-PUBLIC-API-RUNTIME-READBACK: live public "
|
||||
@@ -2547,6 +2652,13 @@ def _stockplatform_runtime_first_order(*, postgres_not_ready: bool) -> str:
|
||||
"postgres_not_ready; run the production migration/control-channel "
|
||||
"path before claiming the lane is only reboot-window gated."
|
||||
)
|
||||
if data_dependency_classification == "freshness_or_ingestion_not_ready":
|
||||
return (
|
||||
"P0-006-STOCKPLATFORM-PUBLIC-API-RUNTIME-READBACK: live public "
|
||||
"StockPlatform API health is 200 but freshness/ingestion are not "
|
||||
"green; run the data dependency/source contract readback before "
|
||||
"claiming the lane is only reboot-window gated."
|
||||
)
|
||||
return (
|
||||
"P0-006-STOCKPLATFORM-PUBLIC-API-RUNTIME-READBACK: live public "
|
||||
"StockPlatform API is blocked while committed scorecard still says "
|
||||
@@ -3094,6 +3206,18 @@ def _refresh_rollups_after_stockplatform_overlay(
|
||||
rollups["stockplatform_public_api_recovery_control_path_blocker_count"] = len(
|
||||
recovery_control_path_blockers
|
||||
)
|
||||
rollups["stockplatform_postgres_readiness_or_data_source_readback_present"] = bool(
|
||||
state.get("stockplatform_postgres_readiness_or_data_source_readback_present")
|
||||
is True
|
||||
)
|
||||
rollups["stockplatform_postgres_readiness_or_data_source_receipt_provided"] = bool(
|
||||
state.get("stockplatform_postgres_readiness_or_data_source_receipt_provided")
|
||||
is True
|
||||
)
|
||||
rollups["stockplatform_postgres_readiness_or_data_source_postgres_ready"] = bool(
|
||||
state.get("stockplatform_postgres_readiness_or_data_source_postgres_ready")
|
||||
is True
|
||||
)
|
||||
rollups["active_p0_live_active_blocker_count"] = len(active_blockers)
|
||||
rollups["active_p0_event_gated_by_fresh_reboot_window_only"] = (
|
||||
rollups.get("active_p0_event_gated_by_fresh_reboot_window_only") is True
|
||||
@@ -3118,6 +3242,14 @@ def _refresh_rollups_after_stockplatform_overlay(
|
||||
summary["stockplatform_public_api_recovery_control_path_blockers"] = (
|
||||
recovery_control_path_blockers
|
||||
)
|
||||
summary["stockplatform_postgres_readiness_or_data_source_readback_status"] = str(
|
||||
state.get("stockplatform_postgres_readiness_or_data_source_readback_status")
|
||||
or "unknown"
|
||||
)
|
||||
summary["stockplatform_postgres_readiness_or_data_source_receipt_provided"] = bool(
|
||||
state.get("stockplatform_postgres_readiness_or_data_source_receipt_provided")
|
||||
is True
|
||||
)
|
||||
summary["next_executable_mainline_workplan_id"] = str(
|
||||
state.get("next_executable_mainline_workplan_id") or ""
|
||||
)
|
||||
|
||||
@@ -48,6 +48,9 @@ def _build_payload(
|
||||
runtime_ready = runtime.get("status") == "stockplatform_public_api_runtime_ready"
|
||||
readback = _dict(runtime.get("readback"))
|
||||
rollups = _dict(runtime.get("rollups"))
|
||||
data_readiness_control_readback = _dict(
|
||||
runtime.get("data_readiness_control_readback")
|
||||
)
|
||||
api_layer_classification = _classify_api_layer(readback, rollups, runtime_ready)
|
||||
data_dependency_classification = _data_dependency_classification(runtime)
|
||||
route_target_ready = route_contract.get("route_target_ready") is True
|
||||
@@ -84,6 +87,7 @@ def _build_payload(
|
||||
candidate_packaged=candidate_packaged,
|
||||
route_target_ready=route_target_ready,
|
||||
api_layer_classification=api_layer_classification,
|
||||
data_dependency_classification=data_dependency_classification,
|
||||
),
|
||||
"active_blockers": active_blockers,
|
||||
"active_blocker_count": len(active_blockers),
|
||||
@@ -118,9 +122,16 @@ def _build_payload(
|
||||
"ingestion_http_status": readback.get("ingestion_http_status"),
|
||||
"http_502_count": _int(rollups.get("http_502_count")),
|
||||
"data_dependency_classification": data_dependency_classification,
|
||||
"data_readiness_control_readback_status": (
|
||||
data_readiness_control_readback.get("status")
|
||||
),
|
||||
"data_readiness_control_receipt_provided": (
|
||||
data_readiness_control_readback.get("receipt_provided") is True
|
||||
),
|
||||
"route_source_file": str(route_contract.get("route_source_file") or ""),
|
||||
"route_target": str(route_contract.get("route_target") or ""),
|
||||
},
|
||||
"data_readiness_control_readback": data_readiness_control_readback,
|
||||
"controlled_apply_policy": {
|
||||
"risk_level": "high",
|
||||
"break_glass_required": False,
|
||||
@@ -184,6 +195,12 @@ def _build_payload(
|
||||
_post_apply_verifiers(runtime_ready=runtime_ready)
|
||||
),
|
||||
"learning_writeback_contract_count": len(_learning_writeback_contracts()),
|
||||
"postgres_readiness_or_data_source_readback_present": (
|
||||
data_readiness_control_readback.get("readback_present") is True
|
||||
),
|
||||
"postgres_readiness_or_data_source_receipt_provided": (
|
||||
data_readiness_control_readback.get("receipt_provided") is True
|
||||
),
|
||||
},
|
||||
"operation_boundaries": {
|
||||
"read_only_public_https_probe": True,
|
||||
@@ -258,12 +275,20 @@ def _classify_api_layer(
|
||||
|
||||
|
||||
def _data_dependency_classification(runtime: dict[str, Any]) -> str:
|
||||
runtime_rollups = _dict(runtime.get("rollups"))
|
||||
runtime_readback = _dict(runtime.get("readback"))
|
||||
live_classification = str(
|
||||
runtime_rollups.get("data_dependency_classification")
|
||||
or runtime_readback.get("data_dependency_classification")
|
||||
or ""
|
||||
)
|
||||
if live_classification and live_classification != "none":
|
||||
return live_classification
|
||||
blockers = _strings(runtime.get("active_blockers"))
|
||||
readback = _dict(runtime.get("readback"))
|
||||
if readback.get("api_health_http_status") != 200:
|
||||
if runtime_readback.get("api_health_http_status") != 200:
|
||||
return "none"
|
||||
if (
|
||||
readback.get("postgres_not_ready") is True
|
||||
runtime_readback.get("postgres_not_ready") is True
|
||||
or any("postgres_not_ready" in blocker for blocker in blockers)
|
||||
):
|
||||
return "postgres_not_ready"
|
||||
@@ -395,6 +420,7 @@ def _safe_next_step(
|
||||
candidate_packaged: bool,
|
||||
route_target_ready: bool,
|
||||
api_layer_classification: str,
|
||||
data_dependency_classification: str,
|
||||
) -> str:
|
||||
if runtime_ready:
|
||||
return "stockplatform_public_api_ready_keep_p0_006_reboot_window_gate"
|
||||
@@ -402,6 +428,11 @@ def _safe_next_step(
|
||||
candidate_packaged
|
||||
and api_layer_classification == "api_live_data_dependency_not_ready"
|
||||
):
|
||||
if data_dependency_classification != "postgres_not_ready":
|
||||
return (
|
||||
"run_stockplatform_data_dependency_source_contract_readback_"
|
||||
"then_public_api_verifier"
|
||||
)
|
||||
return (
|
||||
"run_stockplatform_production_migration_path_after_control_channel_"
|
||||
"readback_then_public_api_verifier"
|
||||
|
||||
@@ -19,6 +19,12 @@ from src.services.reboot_auto_recovery_slo_scorecard import (
|
||||
from src.services.snapshot_paths import default_operations_dir
|
||||
|
||||
_API_SCHEMA_VERSION = "stockplatform_public_api_runtime_readback_v1"
|
||||
_DATA_READINESS_SCHEMA_VERSION = (
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_v1"
|
||||
)
|
||||
_DATA_READINESS_RECEIPT_KEY = (
|
||||
"stockplatform_postgres_readiness_or_data_source_readback"
|
||||
)
|
||||
_DEFAULT_OPERATIONS_DIR = default_operations_dir(Path(__file__))
|
||||
_RECOVERY_RECEIPT_FILE = (
|
||||
"stockplatform-public-api-runtime-recovery-control-receipt.snapshot.json"
|
||||
@@ -125,19 +131,30 @@ def _build_payload(
|
||||
ingestion_json=ingestion_json,
|
||||
)
|
||||
ready = all(checks.values())
|
||||
recovery_control_path = _recovery_control_path_readback(
|
||||
runtime_ready=ready,
|
||||
receipt=recovery_control_receipt,
|
||||
)
|
||||
recovery_control_path_blockers = _strings(
|
||||
recovery_control_path.get("active_blockers")
|
||||
)
|
||||
active_blockers = _unique_strings(active_blockers + recovery_control_path_blockers)
|
||||
data_dependency_classification = _data_dependency_classification(
|
||||
active_blockers,
|
||||
api_health_ok=checks["public_api_health_ok"],
|
||||
)
|
||||
postgres_not_ready = data_dependency_classification == "postgres_not_ready"
|
||||
data_readiness_control_readback = _data_readiness_control_readback(
|
||||
checks=checks,
|
||||
freshness=freshness,
|
||||
ingestion=ingestion,
|
||||
freshness_json=freshness_json,
|
||||
ingestion_json=ingestion_json,
|
||||
data_dependency_classification=data_dependency_classification,
|
||||
postgres_not_ready=postgres_not_ready,
|
||||
data_dependency_blockers=active_blockers,
|
||||
)
|
||||
recovery_control_path = _recovery_control_path_readback(
|
||||
runtime_ready=ready,
|
||||
receipt=recovery_control_receipt,
|
||||
data_readiness_control_readback=data_readiness_control_readback,
|
||||
)
|
||||
recovery_control_path_blockers = _strings(
|
||||
recovery_control_path.get("active_blockers")
|
||||
)
|
||||
active_blockers = _unique_strings(active_blockers + recovery_control_path_blockers)
|
||||
committed_freshness_ok = committed_stockplatform.get("freshness_status") == "ok"
|
||||
committed_ingestion_ok = committed_stockplatform.get("ingestion_status") == "ok"
|
||||
committed_ok = committed_freshness_ok and committed_ingestion_ok
|
||||
@@ -151,7 +168,7 @@ def _build_payload(
|
||||
"stockplatform_public_api_ready_keep_p0_006_reboot_window_gate"
|
||||
if ready
|
||||
else _blocked_safe_next_step(
|
||||
postgres_not_ready=postgres_not_ready,
|
||||
data_dependency_classification=data_dependency_classification,
|
||||
)
|
||||
)
|
||||
http_statuses = {
|
||||
@@ -194,6 +211,12 @@ def _build_payload(
|
||||
),
|
||||
"recovery_control_path_status": recovery_control_path.get("status"),
|
||||
"recovery_control_path_active_blockers": recovery_control_path_blockers,
|
||||
"data_readiness_control_readback_status": (
|
||||
data_readiness_control_readback.get("status")
|
||||
),
|
||||
"data_readiness_control_readback_present": (
|
||||
data_readiness_control_readback.get("readback_present") is True
|
||||
),
|
||||
},
|
||||
"rollups": {
|
||||
"runtime_ready": ready,
|
||||
@@ -224,7 +247,14 @@ def _build_payload(
|
||||
),
|
||||
"data_dependency_classification": data_dependency_classification,
|
||||
"postgres_not_ready": postgres_not_ready,
|
||||
"postgres_readiness_or_data_source_readback_present": (
|
||||
data_readiness_control_readback.get("readback_present") is True
|
||||
),
|
||||
"postgres_readiness_or_data_source_receipt_provided": (
|
||||
data_readiness_control_readback.get("receipt_provided") is True
|
||||
),
|
||||
},
|
||||
"data_readiness_control_readback": data_readiness_control_readback,
|
||||
"recovery_control_path": recovery_control_path,
|
||||
"probes": probes,
|
||||
"operation_boundaries": {
|
||||
@@ -249,20 +279,38 @@ def _recovery_control_path_readback(
|
||||
*,
|
||||
runtime_ready: bool,
|
||||
receipt: dict[str, Any],
|
||||
data_readiness_control_readback: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
if runtime_ready:
|
||||
return {
|
||||
"status": "not_required_stockplatform_runtime_ready",
|
||||
"active_blockers": [],
|
||||
"receipt": receipt,
|
||||
"provided_receipts": _provided_receipts_with_live_readback(
|
||||
_strings(receipt.get("provided_receipts")),
|
||||
data_readiness_control_readback,
|
||||
),
|
||||
"missing_receipts": _missing_receipts_after_live_readback(
|
||||
_strings(receipt.get("missing_receipts")),
|
||||
data_readiness_control_readback,
|
||||
),
|
||||
"safe_recovery_channels": [
|
||||
"keep_monitoring_public_api_runtime_readback",
|
||||
],
|
||||
}
|
||||
if receipt:
|
||||
active_blockers = _unique_strings(
|
||||
_strings(receipt.get("active_blockers"))
|
||||
or _strings(receipt.get("missing_receipts"))
|
||||
provided_receipts = _provided_receipts_with_live_readback(
|
||||
_strings(receipt.get("provided_receipts")),
|
||||
data_readiness_control_readback,
|
||||
)
|
||||
missing_receipts = _missing_receipts_after_live_readback(
|
||||
_strings(receipt.get("missing_receipts")),
|
||||
data_readiness_control_readback,
|
||||
)
|
||||
active_blockers = _recovery_control_active_blockers(
|
||||
receipt_blockers=_strings(receipt.get("active_blockers")),
|
||||
missing_receipts=missing_receipts,
|
||||
data_readiness_control_readback=data_readiness_control_readback,
|
||||
)
|
||||
return {
|
||||
"status": str(
|
||||
@@ -271,8 +319,13 @@ def _recovery_control_path_readback(
|
||||
),
|
||||
"active_blockers": active_blockers,
|
||||
"receipt": receipt,
|
||||
"provided_receipts": _strings(receipt.get("provided_receipts")),
|
||||
"missing_receipts": _strings(receipt.get("missing_receipts")),
|
||||
"provided_receipts": provided_receipts,
|
||||
"missing_receipts": missing_receipts,
|
||||
"live_data_readiness_receipt_key": (
|
||||
_DATA_READINESS_RECEIPT_KEY
|
||||
if data_readiness_control_readback.get("receipt_provided") is True
|
||||
else ""
|
||||
),
|
||||
"safe_recovery_channels": _strings(
|
||||
receipt.get("safe_recovery_channels")
|
||||
),
|
||||
@@ -310,6 +363,130 @@ def _recovery_control_path_readback(
|
||||
}
|
||||
|
||||
|
||||
def _data_readiness_control_readback(
|
||||
*,
|
||||
checks: dict[str, bool],
|
||||
freshness: dict[str, Any],
|
||||
ingestion: dict[str, Any],
|
||||
freshness_json: dict[str, Any],
|
||||
ingestion_json: dict[str, Any],
|
||||
data_dependency_classification: str,
|
||||
postgres_not_ready: bool,
|
||||
data_dependency_blockers: list[str],
|
||||
) -> dict[str, Any]:
|
||||
freshness_http_status = freshness.get("http_status")
|
||||
ingestion_http_status = ingestion.get("http_status")
|
||||
readback_present = freshness_http_status == 200 and ingestion_http_status == 200
|
||||
data_ready = (
|
||||
readback_present
|
||||
and freshness_json.get("status") == "ok"
|
||||
and ingestion_json.get("status") == "ok"
|
||||
)
|
||||
if data_ready:
|
||||
status = "stockplatform_data_readiness_ready"
|
||||
safe_next_step = "run_post_apply_public_api_verifier_and_clear_p0_006_blocker"
|
||||
elif readback_present and postgres_not_ready:
|
||||
status = "blocked_stockplatform_postgres_not_ready"
|
||||
safe_next_step = (
|
||||
"run_stockplatform_production_migration_path_after_control_channel_"
|
||||
"readback_no_manual_db_write"
|
||||
)
|
||||
elif readback_present:
|
||||
status = "blocked_stockplatform_data_dependency_not_ready"
|
||||
safe_next_step = (
|
||||
"diff_stockplatform_freshness_ingestion_source_contract_then_rerun_"
|
||||
"public_api_verifier"
|
||||
)
|
||||
else:
|
||||
status = "blocked_stockplatform_data_readiness_readback_unavailable"
|
||||
safe_next_step = (
|
||||
"restore_public_api_data_endpoints_or_control_channel_readback_then_"
|
||||
"rerun_data_readiness_readback"
|
||||
)
|
||||
missing_evidence = []
|
||||
if freshness_http_status != 200:
|
||||
missing_evidence.append("freshness_endpoint_http_200")
|
||||
if ingestion_http_status != 200:
|
||||
missing_evidence.append("ingestion_endpoint_http_200")
|
||||
return {
|
||||
"schema_version": _DATA_READINESS_SCHEMA_VERSION,
|
||||
"receipt_key": _DATA_READINESS_RECEIPT_KEY,
|
||||
"status": status,
|
||||
"source": "stockplatform_public_api_freshness_ingestion_probe",
|
||||
"readback_present": readback_present,
|
||||
"receipt_provided": readback_present,
|
||||
"safe_next_step": safe_next_step,
|
||||
"data_ready": data_ready,
|
||||
"postgres_ready": data_ready and not postgres_not_ready,
|
||||
"postgres_not_ready": postgres_not_ready,
|
||||
"data_dependency_classification": data_dependency_classification,
|
||||
"freshness_http_status": freshness_http_status,
|
||||
"ingestion_http_status": ingestion_http_status,
|
||||
"freshness_status": str(freshness_json.get("status") or "unknown"),
|
||||
"ingestion_status": str(ingestion_json.get("status") or "unknown"),
|
||||
"freshness_blockers": _strings(freshness_json.get("blockers")),
|
||||
"ingestion_blockers": _strings(ingestion_json.get("blockers")),
|
||||
"active_blockers": [
|
||||
blocker
|
||||
for blocker in data_dependency_blockers
|
||||
if blocker.startswith("stockplatform_freshness_")
|
||||
or blocker.startswith("stockplatform_ingestion_")
|
||||
],
|
||||
"missing_evidence": missing_evidence,
|
||||
"operation_boundaries": {
|
||||
"public_https_probe_only": True,
|
||||
"ssh_used": False,
|
||||
"docker_command_performed": False,
|
||||
"database_write_or_restore_performed": False,
|
||||
"stockplatform_manual_data_write_performed": False,
|
||||
"secret_value_collection_allowed": False,
|
||||
"runtime_action_performed": False,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _provided_receipts_with_live_readback(
|
||||
provided_receipts: list[str],
|
||||
data_readiness_control_readback: dict[str, Any],
|
||||
) -> list[str]:
|
||||
if data_readiness_control_readback.get("receipt_provided") is not True:
|
||||
return provided_receipts
|
||||
return _unique_strings([*provided_receipts, _DATA_READINESS_RECEIPT_KEY])
|
||||
|
||||
|
||||
def _missing_receipts_after_live_readback(
|
||||
missing_receipts: list[str],
|
||||
data_readiness_control_readback: dict[str, Any],
|
||||
) -> list[str]:
|
||||
if data_readiness_control_readback.get("receipt_provided") is not True:
|
||||
return missing_receipts
|
||||
return [
|
||||
receipt
|
||||
for receipt in missing_receipts
|
||||
if receipt != _DATA_READINESS_RECEIPT_KEY
|
||||
]
|
||||
|
||||
|
||||
def _recovery_control_active_blockers(
|
||||
*,
|
||||
receipt_blockers: list[str],
|
||||
missing_receipts: list[str],
|
||||
data_readiness_control_readback: dict[str, Any],
|
||||
) -> list[str]:
|
||||
blockers = receipt_blockers or missing_receipts
|
||||
if data_readiness_control_readback.get("receipt_provided") is not True:
|
||||
return _unique_strings(blockers)
|
||||
if data_readiness_control_readback.get("postgres_not_ready") is True:
|
||||
return _unique_strings(blockers)
|
||||
return _unique_strings(
|
||||
[
|
||||
blocker
|
||||
for blocker in blockers
|
||||
if blocker != "stockplatform_postgres_not_ready"
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def _load_recovery_control_receipt(operations_dir: Path) -> dict[str, Any]:
|
||||
path = operations_dir / _RECOVERY_RECEIPT_FILE
|
||||
if not path.exists():
|
||||
@@ -327,12 +504,17 @@ def _load_recovery_control_receipt(operations_dir: Path) -> dict[str, Any]:
|
||||
return payload
|
||||
|
||||
|
||||
def _blocked_safe_next_step(*, postgres_not_ready: bool) -> str:
|
||||
if postgres_not_ready:
|
||||
def _blocked_safe_next_step(*, data_dependency_classification: str) -> str:
|
||||
if data_dependency_classification == "postgres_not_ready":
|
||||
return (
|
||||
"run_stockplatform_production_migration_path_after_control_channel_"
|
||||
"readback_then_rerun_public_api_readback"
|
||||
)
|
||||
if data_dependency_classification == "freshness_or_ingestion_not_ready":
|
||||
return (
|
||||
"run_stockplatform_data_dependency_source_contract_readback_then_"
|
||||
"rerun_public_api_verifier"
|
||||
)
|
||||
return (
|
||||
"recover_stockplatform_api_container_runtime_or_docker_control_path_"
|
||||
"without_daemon_restart_then_rerun_public_api_readback"
|
||||
|
||||
@@ -1255,6 +1255,27 @@ def test_awoooi_priority_work_order_readback_routes_stockplatform_data_dependenc
|
||||
assert evidence["stockplatform_controlled_recovery_data_dependency"] == (
|
||||
"postgres_not_ready"
|
||||
)
|
||||
assert evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_status"
|
||||
] == "blocked_stockplatform_postgres_not_ready"
|
||||
assert evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_present"
|
||||
] is True
|
||||
assert evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_receipt_provided"
|
||||
] is True
|
||||
assert evidence[
|
||||
"stockplatform_postgres_readiness_or_data_source_postgres_ready"
|
||||
] is False
|
||||
assert payload["rollups"][
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_present"
|
||||
] is True
|
||||
assert payload["rollups"][
|
||||
"stockplatform_postgres_readiness_or_data_source_receipt_provided"
|
||||
] is True
|
||||
assert payload["summary"][
|
||||
"stockplatform_postgres_readiness_or_data_source_receipt_provided"
|
||||
] is True
|
||||
assert "production migration" in in_progress["professional_fix"]["action"]
|
||||
assert "Do not restart Docker daemon" in in_progress["professional_fix"]["action"]
|
||||
assert "manually write DB rows" in in_progress["professional_fix"]["action"]
|
||||
@@ -1581,6 +1602,17 @@ def _stockplatform_runtime_data_dependency_blocked() -> dict:
|
||||
"stockplatform_controlled_deploy_or_ssh_readback_required",
|
||||
],
|
||||
},
|
||||
"data_readiness_control_readback": {
|
||||
"schema_version": (
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_v1"
|
||||
),
|
||||
"receipt_key": "stockplatform_postgres_readiness_or_data_source_readback",
|
||||
"status": "blocked_stockplatform_postgres_not_ready",
|
||||
"readback_present": True,
|
||||
"receipt_provided": True,
|
||||
"postgres_ready": False,
|
||||
"postgres_not_ready": True,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -102,6 +102,21 @@ def test_stockplatform_controlled_recovery_preflight_packages_data_dependency(
|
||||
assert payload["source_of_truth_diff"]["data_dependency_classification"] == (
|
||||
"postgres_not_ready"
|
||||
)
|
||||
assert payload["source_of_truth_diff"][
|
||||
"data_readiness_control_readback_status"
|
||||
] == "blocked_stockplatform_postgres_not_ready"
|
||||
assert payload["source_of_truth_diff"][
|
||||
"data_readiness_control_receipt_provided"
|
||||
] is True
|
||||
assert payload["data_readiness_control_readback"]["schema_version"] == (
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_v1"
|
||||
)
|
||||
assert payload["rollups"][
|
||||
"postgres_readiness_or_data_source_readback_present"
|
||||
] is True
|
||||
assert payload["rollups"][
|
||||
"postgres_readiness_or_data_source_receipt_provided"
|
||||
] is True
|
||||
assert payload["controlled_apply_policy"]["current_apply_blocker"] == (
|
||||
"postgres_readiness_and_production_migration_control_path_readback_"
|
||||
"required_before_apply"
|
||||
@@ -131,6 +146,34 @@ def test_stockplatform_controlled_recovery_preflight_packages_data_dependency(
|
||||
assert payload["operation_boundaries"]["database_write_or_restore_performed"] is False
|
||||
|
||||
|
||||
def test_stockplatform_controlled_recovery_preflight_prefers_live_data_classification(
|
||||
tmp_path: Path,
|
||||
):
|
||||
payload = load_latest_stockplatform_public_api_controlled_recovery_preflight(
|
||||
runtime_readback=_runtime_data_dependency_non_postgres_blocked(),
|
||||
route_source_path=_route_source(tmp_path),
|
||||
)
|
||||
|
||||
assert payload["status"] == (
|
||||
"controlled_data_dependency_preflight_packaged_waiting_control_path_readback"
|
||||
)
|
||||
assert payload["api_layer_classification"] == "api_live_data_dependency_not_ready"
|
||||
assert payload["data_dependency_classification"] == (
|
||||
"freshness_or_ingestion_not_ready"
|
||||
)
|
||||
assert payload["safe_next_step"] == (
|
||||
"run_stockplatform_data_dependency_source_contract_readback_"
|
||||
"then_public_api_verifier"
|
||||
)
|
||||
assert payload["controlled_apply_policy"]["current_apply_blocker"] == (
|
||||
"data_dependency_control_path_readback_required_before_source_"
|
||||
"freshness_or_postgres_contract_apply"
|
||||
)
|
||||
assert payload["controlled_action_candidates"][1]["id"] == (
|
||||
"source_freshness_contract_preflight"
|
||||
)
|
||||
|
||||
|
||||
def test_stockplatform_controlled_recovery_preflight_endpoint_returns_payload(
|
||||
monkeypatch,
|
||||
tmp_path: Path,
|
||||
@@ -251,4 +294,48 @@ def _runtime_data_dependency_blocked() -> dict:
|
||||
"rollups": {
|
||||
"http_502_count": 0,
|
||||
},
|
||||
"data_readiness_control_readback": {
|
||||
"schema_version": (
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_v1"
|
||||
),
|
||||
"receipt_key": "stockplatform_postgres_readiness_or_data_source_readback",
|
||||
"status": "blocked_stockplatform_postgres_not_ready",
|
||||
"readback_present": True,
|
||||
"receipt_provided": True,
|
||||
"postgres_ready": False,
|
||||
"postgres_not_ready": True,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _runtime_data_dependency_non_postgres_blocked() -> dict:
|
||||
payload = _runtime_data_dependency_blocked()
|
||||
payload["active_blockers"] = [
|
||||
"stockplatform_freshness_core_margin_short_daily_missing",
|
||||
"stockplatform_freshness_ai_recommendations_stale",
|
||||
"stockplatform_ingestion_status_not_ok",
|
||||
"stockplatform_postgres_not_ready",
|
||||
]
|
||||
payload["readback"] = {
|
||||
"web_health_http_status": 200,
|
||||
"api_health_http_status": 200,
|
||||
"freshness_http_status": 200,
|
||||
"ingestion_http_status": 200,
|
||||
"data_dependency_classification": "freshness_or_ingestion_not_ready",
|
||||
"postgres_not_ready": False,
|
||||
}
|
||||
payload["rollups"] = {
|
||||
"http_502_count": 0,
|
||||
"data_dependency_classification": "freshness_or_ingestion_not_ready",
|
||||
"postgres_not_ready": False,
|
||||
}
|
||||
payload["data_readiness_control_readback"] = {
|
||||
"schema_version": "stockplatform_postgres_readiness_or_data_source_readback_v1",
|
||||
"receipt_key": "stockplatform_postgres_readiness_or_data_source_readback",
|
||||
"status": "blocked_stockplatform_data_dependency_not_ready",
|
||||
"readback_present": True,
|
||||
"receipt_provided": True,
|
||||
"postgres_ready": False,
|
||||
"postgres_not_ready": False,
|
||||
}
|
||||
return payload
|
||||
|
||||
@@ -127,6 +127,42 @@ def test_stockplatform_public_api_runtime_readback_routes_postgres_not_ready():
|
||||
assert payload["readback"]["postgres_not_ready"] is True
|
||||
assert payload["rollups"]["http_502_count"] == 0
|
||||
assert payload["rollups"]["postgres_not_ready"] is True
|
||||
assert (
|
||||
payload["rollups"][
|
||||
"postgres_readiness_or_data_source_readback_present"
|
||||
]
|
||||
is True
|
||||
)
|
||||
assert (
|
||||
payload["rollups"][
|
||||
"postgres_readiness_or_data_source_receipt_provided"
|
||||
]
|
||||
is True
|
||||
)
|
||||
data_readiness = payload["data_readiness_control_readback"]
|
||||
assert data_readiness["schema_version"] == (
|
||||
"stockplatform_postgres_readiness_or_data_source_readback_v1"
|
||||
)
|
||||
assert data_readiness["receipt_key"] == (
|
||||
"stockplatform_postgres_readiness_or_data_source_readback"
|
||||
)
|
||||
assert data_readiness["status"] == "blocked_stockplatform_postgres_not_ready"
|
||||
assert data_readiness["readback_present"] is True
|
||||
assert data_readiness["receipt_provided"] is True
|
||||
assert data_readiness["postgres_ready"] is False
|
||||
assert data_readiness["postgres_not_ready"] is True
|
||||
assert data_readiness["operation_boundaries"]["public_https_probe_only"] is True
|
||||
assert data_readiness["operation_boundaries"]["database_write_or_restore_performed"] is False
|
||||
assert data_readiness["operation_boundaries"]["secret_value_collection_allowed"] is False
|
||||
assert "stockplatform_postgres_readiness_or_data_source_readback" in payload[
|
||||
"recovery_control_path"
|
||||
]["provided_receipts"]
|
||||
assert "stockplatform_postgres_readiness_or_data_source_readback" not in payload[
|
||||
"recovery_control_path"
|
||||
]["missing_receipts"]
|
||||
assert payload["recovery_control_path"]["missing_receipts"] == [
|
||||
"post_apply_public_api_verifier_green"
|
||||
]
|
||||
assert payload["safe_next_step"] == (
|
||||
"run_stockplatform_production_migration_path_after_control_channel_"
|
||||
"readback_then_rerun_public_api_readback"
|
||||
@@ -139,6 +175,36 @@ def test_stockplatform_public_api_runtime_readback_routes_postgres_not_ready():
|
||||
]
|
||||
|
||||
|
||||
def test_stockplatform_public_api_runtime_readback_routes_data_dependency_without_postgres():
|
||||
payload = load_latest_stockplatform_public_api_runtime_readback(
|
||||
probe=_probe_public_api_ok_data_dependency_blocked
|
||||
)
|
||||
|
||||
assert payload["status"] == "blocked_stockplatform_public_api_runtime_drift"
|
||||
assert payload["readback"]["api_health_http_status"] == 200
|
||||
assert payload["readback"]["freshness_status"] == "blocked"
|
||||
assert payload["readback"]["ingestion_status"] == "unknown"
|
||||
assert payload["readback"]["data_dependency_classification"] == (
|
||||
"freshness_or_ingestion_not_ready"
|
||||
)
|
||||
assert payload["readback"]["postgres_not_ready"] is False
|
||||
assert payload["safe_next_step"] == (
|
||||
"run_stockplatform_data_dependency_source_contract_readback_then_"
|
||||
"rerun_public_api_verifier"
|
||||
)
|
||||
assert payload["data_readiness_control_readback"]["status"] == (
|
||||
"blocked_stockplatform_data_dependency_not_ready"
|
||||
)
|
||||
assert payload["data_readiness_control_readback"]["receipt_provided"] is True
|
||||
assert "stockplatform_postgres_not_ready" not in payload["active_blockers"]
|
||||
assert payload["recovery_control_path"]["active_blockers"] == [
|
||||
"stockplatform_post_apply_public_api_verifier_not_green"
|
||||
]
|
||||
assert payload["recovery_control_path"]["missing_receipts"] == [
|
||||
"post_apply_public_api_verifier_green"
|
||||
]
|
||||
|
||||
|
||||
def test_stockplatform_public_api_runtime_endpoint_returns_readback(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
agents,
|
||||
@@ -201,3 +267,31 @@ def _probe_public_api_ok_postgres_not_ready(
|
||||
"error": "",
|
||||
}
|
||||
return {"http_status": 200, "body": "ok", "error": ""}
|
||||
|
||||
|
||||
def _probe_public_api_ok_data_dependency_blocked(
|
||||
url: str,
|
||||
timeout_seconds: float,
|
||||
) -> dict:
|
||||
del timeout_seconds
|
||||
if url.endswith("/api/v1/system/freshness"):
|
||||
return {
|
||||
"http_status": 200,
|
||||
"body": json.dumps(
|
||||
{
|
||||
"status": "blocked",
|
||||
"blockers": [
|
||||
"core_margin_short_daily_missing",
|
||||
"ai_recommendations_stale",
|
||||
],
|
||||
}
|
||||
),
|
||||
"error": "",
|
||||
}
|
||||
if url.endswith("/api/v1/system/ingestion"):
|
||||
return {
|
||||
"http_status": 200,
|
||||
"body": json.dumps({"status": "unknown", "blockers": []}),
|
||||
"error": "",
|
||||
}
|
||||
return {"http_status": 200, "body": "ok", "error": ""}
|
||||
|
||||
@@ -9180,6 +9180,31 @@
|
||||
"evidenceBoundary": "證據邊界"
|
||||
}
|
||||
},
|
||||
"stockplatformP0Receipt": {
|
||||
"eyebrow": "P0-006 Runtime receipt",
|
||||
"title": "StockPlatform data dependency readback",
|
||||
"subtitle": "Shows the public API data readiness receipt, dependency classification, and next controlled step.",
|
||||
"preflight": "Controlled preflight",
|
||||
"next": "Current next step",
|
||||
"empty": "Waiting for priority readback",
|
||||
"metrics": {
|
||||
"runtime": "Runtime",
|
||||
"receipt": "Receipt",
|
||||
"dataDependency": "Data dependency",
|
||||
"postgres": "Postgres"
|
||||
},
|
||||
"values": {
|
||||
"received": "received",
|
||||
"missing": "missing",
|
||||
"ready": "ready",
|
||||
"notReady": "not ready"
|
||||
},
|
||||
"boundaries": {
|
||||
"publicHttps": "public HTTPS only",
|
||||
"noDbWrite": "no DB write",
|
||||
"noSecret": "no secret read"
|
||||
}
|
||||
},
|
||||
"commanderInsertedRequirements": {
|
||||
"eyebrow": "主線優先序",
|
||||
"title": "統帥插入需求工作項",
|
||||
|
||||
@@ -9180,6 +9180,31 @@
|
||||
"evidenceBoundary": "證據邊界"
|
||||
}
|
||||
},
|
||||
"stockplatformP0Receipt": {
|
||||
"eyebrow": "P0-006 Runtime receipt",
|
||||
"title": "StockPlatform 資料依賴回讀",
|
||||
"subtitle": "顯示公開 API 回讀出的 data readiness receipt、資料依賴分類與下一個受控步驟。",
|
||||
"preflight": "Controlled preflight",
|
||||
"next": "目前下一步",
|
||||
"empty": "等待 priority readback",
|
||||
"metrics": {
|
||||
"runtime": "Runtime",
|
||||
"receipt": "Receipt",
|
||||
"dataDependency": "Data dependency",
|
||||
"postgres": "Postgres"
|
||||
},
|
||||
"values": {
|
||||
"received": "已收件",
|
||||
"missing": "缺收件",
|
||||
"ready": "ready",
|
||||
"notReady": "not ready"
|
||||
},
|
||||
"boundaries": {
|
||||
"publicHttps": "public HTTPS only",
|
||||
"noDbWrite": "no DB write",
|
||||
"noSecret": "no secret read"
|
||||
}
|
||||
},
|
||||
"commanderInsertedRequirements": {
|
||||
"eyebrow": "主線優先序",
|
||||
"title": "統帥插入需求工作項",
|
||||
|
||||
@@ -1127,6 +1127,12 @@ type PriorityWorkOrderResponse = {
|
||||
ai_automation_node_receipt_metadata_only?: boolean | null;
|
||||
ai_automation_node_receipt_lanes?: string[] | null;
|
||||
ai_automation_node_receipt_work_items?: string[] | null;
|
||||
stockplatform_public_api_runtime_status?: string | null;
|
||||
stockplatform_public_api_controlled_recovery_preflight_status?: string | null;
|
||||
stockplatform_public_api_controlled_recovery_data_dependency?: string | null;
|
||||
stockplatform_public_api_recovery_control_path_status?: string | null;
|
||||
stockplatform_postgres_readiness_or_data_source_readback_status?: string | null;
|
||||
stockplatform_postgres_readiness_or_data_source_receipt_provided?: boolean | null;
|
||||
} | null;
|
||||
in_progress_or_blocked_in_priority_order?: Array<{
|
||||
evidence?: {
|
||||
@@ -1161,8 +1167,21 @@ type PriorityWorkOrderResponse = {
|
||||
controlled_cd_lane_live_gitea_actions_active_process_count?: number | null;
|
||||
controlled_cd_lane_live_metric_blocker_count?: number | null;
|
||||
controlled_cd_lane_live_metric_blockers?: string[] | null;
|
||||
stockplatform_public_api_runtime_status?: string | null;
|
||||
stockplatform_public_api_runtime_ready?: boolean | null;
|
||||
stockplatform_public_api_health_http_status?: number | null;
|
||||
stockplatform_freshness_http_status?: number | null;
|
||||
stockplatform_ingestion_http_status?: number | null;
|
||||
stockplatform_controlled_recovery_preflight_status?: string | null;
|
||||
stockplatform_controlled_recovery_data_dependency?: string | null;
|
||||
stockplatform_recovery_control_path_status?: string | null;
|
||||
stockplatform_postgres_readiness_or_data_source_readback_status?: string | null;
|
||||
stockplatform_postgres_readiness_or_data_source_readback_present?: boolean | null;
|
||||
stockplatform_postgres_readiness_or_data_source_receipt_provided?: boolean | null;
|
||||
stockplatform_postgres_readiness_or_data_source_postgres_ready?: boolean | null;
|
||||
} | null;
|
||||
}>;
|
||||
next_execution_order?: string[] | null;
|
||||
commander_inserted_requirement_work_items?: CommanderInsertedRequirementWorkItem[];
|
||||
ai_automation_node_receipts?: AiAutomationNodeReceipt[];
|
||||
ai_automation_node_receipt_schema?: {
|
||||
@@ -8691,6 +8710,166 @@ function AiLoopLogSourceTagsPanel({
|
||||
);
|
||||
}
|
||||
|
||||
function stockplatformReceiptTone(value: string | boolean | null | undefined) {
|
||||
if (value === true || value === "ready" || String(value).includes("_ready")) {
|
||||
return "border-[#b9d9c2] bg-[#f2fbf3] text-[#236332]";
|
||||
}
|
||||
if (String(value).includes("blocked") || value === false) {
|
||||
return "border-[#f0c6a8] bg-[#fff8f1] text-[#9a4d16]";
|
||||
}
|
||||
return "border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]";
|
||||
}
|
||||
|
||||
function StockPlatformP0ReceiptPanel({
|
||||
priority,
|
||||
loading,
|
||||
}: {
|
||||
priority: PriorityWorkOrderResponse | null;
|
||||
loading: boolean;
|
||||
}) {
|
||||
const t = useTranslations("awooop.workItems.stockplatformP0Receipt");
|
||||
const summary = priority?.summary;
|
||||
const evidence = priority?.in_progress_or_blocked_in_priority_order?.find(
|
||||
(item) =>
|
||||
item.evidence?.stockplatform_postgres_readiness_or_data_source_readback_status ||
|
||||
item.evidence?.stockplatform_controlled_recovery_preflight_status ||
|
||||
item.evidence?.stockplatform_public_api_runtime_status
|
||||
)?.evidence;
|
||||
const readbackStatus =
|
||||
summary?.stockplatform_postgres_readiness_or_data_source_readback_status ??
|
||||
evidence?.stockplatform_postgres_readiness_or_data_source_readback_status ??
|
||||
"--";
|
||||
const receiptProvided =
|
||||
summary?.stockplatform_postgres_readiness_or_data_source_receipt_provided ??
|
||||
evidence?.stockplatform_postgres_readiness_or_data_source_receipt_provided ??
|
||||
false;
|
||||
const dataDependency =
|
||||
summary?.stockplatform_public_api_controlled_recovery_data_dependency ??
|
||||
evidence?.stockplatform_controlled_recovery_data_dependency ??
|
||||
"--";
|
||||
const preflightStatus =
|
||||
summary?.stockplatform_public_api_controlled_recovery_preflight_status ??
|
||||
evidence?.stockplatform_controlled_recovery_preflight_status ??
|
||||
"--";
|
||||
const runtimeStatus =
|
||||
summary?.stockplatform_public_api_runtime_status ??
|
||||
evidence?.stockplatform_public_api_runtime_status ??
|
||||
"--";
|
||||
const recoveryStatus =
|
||||
summary?.stockplatform_public_api_recovery_control_path_status ??
|
||||
evidence?.stockplatform_recovery_control_path_status ??
|
||||
"--";
|
||||
const postgresReady =
|
||||
evidence?.stockplatform_postgres_readiness_or_data_source_postgres_ready ??
|
||||
false;
|
||||
const nextOrder = priority?.next_execution_order?.[0] ?? "";
|
||||
const metrics = [
|
||||
{
|
||||
key: "runtime",
|
||||
label: t("metrics.runtime"),
|
||||
value: runtimeStatus,
|
||||
tone: stockplatformReceiptTone(runtimeStatus),
|
||||
},
|
||||
{
|
||||
key: "receipt",
|
||||
label: t("metrics.receipt"),
|
||||
value: receiptProvided ? t("values.received") : t("values.missing"),
|
||||
tone: stockplatformReceiptTone(receiptProvided),
|
||||
},
|
||||
{
|
||||
key: "data",
|
||||
label: t("metrics.dataDependency"),
|
||||
value: dataDependency,
|
||||
tone: stockplatformReceiptTone(readbackStatus),
|
||||
},
|
||||
{
|
||||
key: "postgres",
|
||||
label: t("metrics.postgres"),
|
||||
value: postgresReady ? t("values.ready") : t("values.notReady"),
|
||||
tone: stockplatformReceiptTone(postgresReady),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section
|
||||
className="border border-[#d8d3c7] bg-white"
|
||||
data-testid="stockplatform-p0-runtime-receipt"
|
||||
>
|
||||
<div className="p-4">
|
||||
<div className="flex flex-wrap items-start justify-between gap-4">
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<Database className="h-5 w-5 shrink-0 text-[#1f5b9b]" aria-hidden="true" />
|
||||
<div className="min-w-0">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.08em] text-[#9a968c]">
|
||||
{t("eyebrow")}
|
||||
</p>
|
||||
<h3 className="mt-1 text-base font-semibold tracking-normal text-[#141413]">
|
||||
{t("title")}
|
||||
</h3>
|
||||
<p className="mt-2 max-w-3xl text-xs leading-5 text-[#77736a]">
|
||||
{t("subtitle")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
"border px-3 py-2 font-mono text-[11px] font-semibold",
|
||||
stockplatformReceiptTone(readbackStatus)
|
||||
)}
|
||||
>
|
||||
{loading ? "--" : readbackStatus}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 grid gap-2 sm:grid-cols-2 xl:grid-cols-4">
|
||||
{metrics.map((metric) => (
|
||||
<div key={metric.key} className={cn("border px-3 py-2", metric.tone)}>
|
||||
<div className="text-[11px] font-semibold">{metric.label}</div>
|
||||
<div className="mt-1 break-words font-mono text-xs font-semibold text-[#141413]">
|
||||
{loading ? "--" : metric.value}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-3 grid gap-2 border border-[#c9d8ea] bg-[#eef5ff] p-3 lg:grid-cols-[minmax(0,0.75fr)_minmax(0,1.8fr)]">
|
||||
<div className="min-w-0">
|
||||
<div className="text-[11px] font-semibold text-[#1f5b9b]">
|
||||
{t("preflight")}
|
||||
</div>
|
||||
<div className="mt-1 break-words font-mono text-xs font-semibold text-[#141413]">
|
||||
{loading ? "--" : preflightStatus}
|
||||
</div>
|
||||
<div className="mt-2 break-words font-mono text-[10px] leading-4 text-[#5f5b52]">
|
||||
{loading ? "--" : recoveryStatus}
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="text-[11px] font-semibold text-[#1f5b9b]">
|
||||
{t("next")}
|
||||
</div>
|
||||
<div className="mt-1 break-words text-xs leading-5 text-[#141413]">
|
||||
{loading ? "--" : nextOrder || t("empty")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex flex-wrap gap-2 text-[11px] font-semibold">
|
||||
<span className="border border-[#d8d3c7] bg-[#faf9f3] px-2 py-1 text-[#5f5b52]">
|
||||
{t("boundaries.publicHttps")}
|
||||
</span>
|
||||
<span className="border border-[#d8d3c7] bg-[#faf9f3] px-2 py-1 text-[#5f5b52]">
|
||||
{t("boundaries.noDbWrite")}
|
||||
</span>
|
||||
<span className="border border-[#d8d3c7] bg-[#faf9f3] px-2 py-1 text-[#5f5b52]">
|
||||
{t("boundaries.noSecret")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function commanderPriorityTone(priority: string) {
|
||||
switch (priority) {
|
||||
case "P0":
|
||||
@@ -9318,6 +9497,11 @@ export default function AwoooPWorkItemsPage() {
|
||||
loading={priorityWorkOrderLoading && !priorityWorkOrder}
|
||||
/>
|
||||
|
||||
<StockPlatformP0ReceiptPanel
|
||||
priority={priorityWorkOrder}
|
||||
loading={priorityWorkOrderLoading && !priorityWorkOrder}
|
||||
/>
|
||||
|
||||
<CommanderInsertedRequirementsPanel
|
||||
priority={commanderInsertedRequirementWorkOrder}
|
||||
loading={false}
|
||||
|
||||
Reference in New Issue
Block a user