fix(stockplatform): classify ai recommendations stale after core recovery
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 58s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 58s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped
This commit is contained in:
@@ -2577,6 +2577,11 @@ def _stockplatform_recovery_next_state(
|
||||
"controlled_recovery_preflight_waiting_stockplatform_production_"
|
||||
"migration_control_channel_readback_and_public_api_verifier"
|
||||
)
|
||||
if _stockplatform_ai_recommendations_wait(data_dependency_classification):
|
||||
return (
|
||||
"controlled_recovery_preflight_waiting_stockplatform_ai_"
|
||||
"recommendation_pipeline_readback_and_public_api_verifier"
|
||||
)
|
||||
if api_layer == "api_live_data_dependency_not_ready":
|
||||
return (
|
||||
"controlled_recovery_preflight_waiting_stockplatform_data_dependency_"
|
||||
@@ -2610,6 +2615,15 @@ def _stockplatform_recovery_reason(
|
||||
"KM/RAG/MCP/PlayBook writeback receipts. This is not a generic API "
|
||||
"container recovery task and not a manual end state."
|
||||
)
|
||||
if _stockplatform_ai_recommendations_wait(data_dependency_classification):
|
||||
return (
|
||||
"Live StockPlatform API health is reachable and official core data "
|
||||
"readbacks have recovered, but AI recommendations remain stale. "
|
||||
"The P0-006 lane now points at the AI recommendation pipeline "
|
||||
"readback, public API verifier, and KM/RAG/MCP/PlayBook writeback "
|
||||
"receipts. This is not a generic DB/source repair task and not a "
|
||||
"manual end state."
|
||||
)
|
||||
if api_layer == "api_live_data_dependency_not_ready":
|
||||
return (
|
||||
"Live StockPlatform API health is reachable, but freshness and/or "
|
||||
@@ -2661,6 +2675,18 @@ def _stockplatform_recovery_action(
|
||||
"hosts, reload Nginx, manually write DB rows, fake freshness, "
|
||||
"trigger unrelated workflows, or read secrets from this lane."
|
||||
)
|
||||
if _stockplatform_ai_recommendations_wait(data_dependency_classification):
|
||||
return (
|
||||
"Run the StockPlatform AI recommendation pipeline controlled "
|
||||
"readback in order: verify core freshness and ingestion are still "
|
||||
"green, inspect the AI recommendation queue/pipeline receipt in "
|
||||
"read-only mode, use only the existing bounded pipeline path after "
|
||||
"dry-run if apply is needed, rerun public freshness/selection "
|
||||
"brief verifiers, and write metadata-only KM/RAG/MCP/PlayBook "
|
||||
"receipts. Do not restart Docker daemon, reboot hosts, reload "
|
||||
"Nginx, write DB rows, fake freshness, trigger unrelated "
|
||||
"workflows, or read secrets from this lane."
|
||||
)
|
||||
if api_layer == "api_live_data_dependency_not_ready":
|
||||
return (
|
||||
"Run the StockPlatform data dependency controlled preflight in order: "
|
||||
@@ -2697,6 +2723,14 @@ def _stockplatform_recovery_first_order(
|
||||
"production migration/control-channel path with rollback and public "
|
||||
"API verifier; do not manually DB-write or fake freshness."
|
||||
)
|
||||
if _stockplatform_ai_recommendations_wait(data_dependency_classification):
|
||||
return (
|
||||
"P0-006-STOCKPLATFORM-PUBLIC-API-CONTROLLED-RECOVERY-PREFLIGHT: "
|
||||
"StockPlatform API and official core data are recovered, but AI "
|
||||
"recommendations are stale; run AI recommendation pipeline "
|
||||
"readback, public API verifier, and metadata writeback before "
|
||||
"claiming the lane is only reboot-window gated."
|
||||
)
|
||||
if api_layer == "api_live_data_dependency_not_ready":
|
||||
return (
|
||||
"P0-006-STOCKPLATFORM-PUBLIC-API-CONTROLLED-RECOVERY-PREFLIGHT: "
|
||||
@@ -2724,6 +2758,11 @@ def _stockplatform_runtime_next_state(
|
||||
"blocked_live_stockplatform_public_api_requires_production_"
|
||||
"migration_control_channel_readback"
|
||||
)
|
||||
if _stockplatform_ai_recommendations_wait(data_dependency_classification):
|
||||
return (
|
||||
"blocked_live_stockplatform_public_api_requires_ai_recommendation_"
|
||||
"pipeline_readback"
|
||||
)
|
||||
if data_dependency_classification == "freshness_or_ingestion_not_ready":
|
||||
return (
|
||||
"blocked_live_stockplatform_public_api_requires_data_dependency_"
|
||||
@@ -2747,6 +2786,14 @@ def _stockplatform_runtime_drift_reason(
|
||||
"the production migration/control-channel path before claiming the "
|
||||
"reboot SLO lane is only waiting on a fresh boot window."
|
||||
)
|
||||
if _stockplatform_ai_recommendations_wait(data_dependency_classification):
|
||||
return (
|
||||
"StockPlatform public API health is now HTTP 200 and official core "
|
||||
"data readbacks are recovered, but AI recommendations are stale. "
|
||||
"P0-006 must stay on the AI recommendation pipeline readback and "
|
||||
"public verifier 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 "
|
||||
@@ -2776,6 +2823,15 @@ def _stockplatform_runtime_drift_action(
|
||||
"reload Nginx, manually write DB rows, fake freshness, trigger "
|
||||
"unrelated workflows, or read secrets from this lane."
|
||||
)
|
||||
if _stockplatform_ai_recommendations_wait(data_dependency_classification):
|
||||
return (
|
||||
"Run the StockPlatform AI recommendation pipeline 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."
|
||||
)
|
||||
if data_dependency_classification == "freshness_or_ingestion_not_ready":
|
||||
return (
|
||||
"Run the StockPlatform data dependency/source contract readback "
|
||||
@@ -2805,6 +2861,14 @@ def _stockplatform_runtime_first_order(
|
||||
"postgres_not_ready; run the production migration/control-channel "
|
||||
"path before claiming the lane is only reboot-window gated."
|
||||
)
|
||||
if _stockplatform_ai_recommendations_wait(data_dependency_classification):
|
||||
return (
|
||||
"P0-006-STOCKPLATFORM-PUBLIC-API-RUNTIME-READBACK: live public "
|
||||
"StockPlatform API health is 200 and official core data are "
|
||||
"recovered, but AI recommendations are stale; run AI recommendation "
|
||||
"pipeline readback and public verifier 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 "
|
||||
@@ -3519,7 +3583,11 @@ def _stockplatform_external_data_wait_active(state: dict[str, Any]) -> bool:
|
||||
return (
|
||||
state.get("stockplatform_source_contract_readback_present") is True
|
||||
and state.get("stockplatform_source_contract_ready") is not True
|
||||
and data_dependency == "freshness_or_ingestion_not_ready"
|
||||
and data_dependency
|
||||
in {
|
||||
"freshness_or_ingestion_not_ready",
|
||||
"ai_recommendations_stale_after_core_recovered",
|
||||
}
|
||||
and any(
|
||||
blocker
|
||||
in {
|
||||
@@ -3532,6 +3600,15 @@ def _stockplatform_external_data_wait_active(state: dict[str, Any]) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def _stockplatform_ai_recommendations_wait(
|
||||
data_dependency_classification: str,
|
||||
) -> bool:
|
||||
return (
|
||||
data_dependency_classification
|
||||
== "ai_recommendations_stale_after_core_recovered"
|
||||
)
|
||||
|
||||
|
||||
def _apply_parallel_mainline_execution_overlay(payload: dict[str, Any]) -> None:
|
||||
state = _dict(payload.setdefault("mainline_execution_state", {}))
|
||||
items = _list(payload.get("commander_inserted_requirement_work_items"))
|
||||
|
||||
@@ -27,6 +27,9 @@ _DEFAULT_ROUTE_SOURCE = (
|
||||
/ "templates"
|
||||
/ "188-all-sites.conf.j2"
|
||||
)
|
||||
_AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED = (
|
||||
"ai_recommendations_stale_after_core_recovered"
|
||||
)
|
||||
|
||||
|
||||
def load_latest_stockplatform_public_api_controlled_recovery_preflight(
|
||||
@@ -383,6 +386,14 @@ def _current_apply_blocker(
|
||||
"postgres_readiness_and_production_migration_control_path_readback_"
|
||||
"required_before_apply"
|
||||
)
|
||||
if (
|
||||
data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
):
|
||||
return (
|
||||
"ai_recommendation_pipeline_readback_required_before_public_api_"
|
||||
"verifier"
|
||||
)
|
||||
if api_layer_classification == "api_live_data_dependency_not_ready":
|
||||
return (
|
||||
"data_dependency_control_path_readback_required_before_source_"
|
||||
@@ -401,6 +412,15 @@ def _allowed_scope(
|
||||
"km_rag_mcp_playbook_metadata_writeback",
|
||||
]
|
||||
if api_layer_classification == "api_live_data_dependency_not_ready":
|
||||
if (
|
||||
data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
):
|
||||
return [
|
||||
"read_only_ai_recommendation_queue_status",
|
||||
"read_only_ai_recommendation_pipeline_receipt",
|
||||
*common,
|
||||
]
|
||||
scope = [
|
||||
"read_only_data_dependency_status_inspection",
|
||||
"read_only_source_freshness_contract_diff",
|
||||
@@ -459,6 +479,14 @@ def _safe_next_step(
|
||||
candidate_packaged
|
||||
and api_layer_classification == "api_live_data_dependency_not_ready"
|
||||
):
|
||||
if (
|
||||
data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
):
|
||||
return (
|
||||
"run_stockplatform_ai_recommendation_pipeline_readback_then_"
|
||||
"public_api_verifier"
|
||||
)
|
||||
if data_dependency_classification != "postgres_not_ready":
|
||||
return (
|
||||
"run_stockplatform_data_dependency_source_contract_readback_"
|
||||
@@ -489,6 +517,47 @@ def _controlled_work_items(
|
||||
return []
|
||||
base_status = "queued_waiting_control_path_readback" if candidate_packaged else "blocked_waiting_route_evidence"
|
||||
if api_layer_classification == "api_live_data_dependency_not_ready":
|
||||
if (
|
||||
data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
):
|
||||
return [
|
||||
{
|
||||
"id": "stockplatform-ai-recommendation-control-path-readback",
|
||||
"title": "StockPlatform AI recommendation control path readback",
|
||||
"owner_agent": "DevOps Agent",
|
||||
"status": base_status,
|
||||
"required_output": "non_secret_pipeline_liveness_and_runtime_contract_match",
|
||||
},
|
||||
{
|
||||
"id": "stockplatform-ai-recommendation-queue-inspection",
|
||||
"title": "Read-only AI recommendation queue inspection",
|
||||
"owner_agent": "SRE Agent",
|
||||
"status": "waiting_control_path_readback",
|
||||
"required_output": "ai_recommendations_latest_date_and_queue_receipt",
|
||||
},
|
||||
{
|
||||
"id": "stockplatform-ai-recommendation-pipeline-preflight",
|
||||
"title": "AI recommendation pipeline verifier preflight",
|
||||
"owner_agent": "Data Reliability Agent",
|
||||
"status": "waiting_read_only_inspection",
|
||||
"required_output": "dry_run_pipeline_receipt_no_manual_db_write",
|
||||
},
|
||||
{
|
||||
"id": "stockplatform-api-post-apply-verifier",
|
||||
"title": "Public API post-apply verifier",
|
||||
"owner_agent": "Verifier Agent",
|
||||
"status": "ready",
|
||||
"required_output": "public_health_freshness_ingestion_green",
|
||||
},
|
||||
{
|
||||
"id": "stockplatform-ai-learning-writeback",
|
||||
"title": "KM / RAG / MCP / PlayBook trust writeback",
|
||||
"owner_agent": "Learning Agent",
|
||||
"status": "ready",
|
||||
"required_output": "metadata_only_ai_pipeline_receipt_and_trust_delta",
|
||||
},
|
||||
]
|
||||
contract_title = "Source freshness and Postgres contract preflight"
|
||||
contract_output = "dry_run_contract_diff_no_db_write"
|
||||
if data_dependency_classification == "postgres_not_ready":
|
||||
@@ -582,6 +651,46 @@ def _controlled_action_candidates(
|
||||
if runtime_ready:
|
||||
return []
|
||||
if api_layer_classification == "api_live_data_dependency_not_ready":
|
||||
if (
|
||||
data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
):
|
||||
return [
|
||||
{
|
||||
"id": "read_only_ai_recommendation_pipeline_readback",
|
||||
"execution_route": "stockplatform_ai_recommendation_executor",
|
||||
"current_status": (
|
||||
"queued_waiting_control_path_readback"
|
||||
if candidate_packaged
|
||||
else "blocked_waiting_route_evidence"
|
||||
),
|
||||
"check_mode": "read_only_ai_recommendation_queue_and_pipeline_receipt",
|
||||
"apply_allowed_when": [
|
||||
"host_control_path_liveness_readback_ok",
|
||||
"stockplatform_api_health_ok",
|
||||
"core_freshness_and_ingestion_ok",
|
||||
"post_apply_public_verifier_ready",
|
||||
],
|
||||
"forbidden_actions": [
|
||||
"secret_read",
|
||||
"docker_daemon_restart",
|
||||
"host_reboot",
|
||||
"database_write",
|
||||
"fake_freshness_marker",
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "ai_recommendation_pipeline_verifier_preflight",
|
||||
"execution_route": "stockplatform_ai_recommendation_executor",
|
||||
"current_status": "waiting_read_only_pipeline_readback",
|
||||
"check_mode": "dry_run_ai_recommendation_pipeline_contract",
|
||||
"bounded_apply": (
|
||||
"use the existing AI recommendation pipeline path only "
|
||||
"after control-channel readback and dry-run; no manual "
|
||||
"DB rows, no fake freshness marker, no destructive restore"
|
||||
),
|
||||
},
|
||||
]
|
||||
second_candidate = {
|
||||
"id": "source_freshness_contract_preflight",
|
||||
"execution_route": "stockplatform_data_contract_executor",
|
||||
|
||||
@@ -27,6 +27,9 @@ _DATA_READINESS_RECEIPT_KEY = (
|
||||
)
|
||||
_SOURCE_CONTRACT_SCHEMA_VERSION = "stockplatform_source_contract_control_readback_v1"
|
||||
_SOURCE_CONTRACT_RECEIPT_KEY = "stockplatform_source_contract_readback"
|
||||
_AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED = (
|
||||
"ai_recommendations_stale_after_core_recovered"
|
||||
)
|
||||
_DEFAULT_OPERATIONS_DIR = default_operations_dir(Path(__file__))
|
||||
_RECOVERY_RECEIPT_FILE = (
|
||||
"stockplatform-public-api-runtime-recovery-control-receipt.snapshot.json"
|
||||
@@ -224,6 +227,10 @@ def _build_payload(
|
||||
freshness_sources
|
||||
),
|
||||
"data_dependency_classification": data_dependency_classification,
|
||||
"ai_recommendations_stale_after_core_recovered": (
|
||||
data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
),
|
||||
"postgres_not_ready": postgres_not_ready,
|
||||
"committed_freshness_status": str(
|
||||
committed_stockplatform.get("freshness_status") or "unknown"
|
||||
@@ -287,6 +294,10 @@ def _build_payload(
|
||||
recovery_control_path_blockers
|
||||
),
|
||||
"data_dependency_classification": data_dependency_classification,
|
||||
"ai_recommendations_stale_after_core_recovered": (
|
||||
data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
),
|
||||
"postgres_not_ready": postgres_not_ready,
|
||||
"postgres_readiness_or_data_source_readback_present": (
|
||||
data_readiness_control_readback.get("readback_present") is True
|
||||
@@ -454,6 +465,15 @@ def _data_readiness_control_readback(
|
||||
if data_ready:
|
||||
status = "stockplatform_data_readiness_ready"
|
||||
safe_next_step = "run_post_apply_public_api_verifier_and_clear_p0_006_blocker"
|
||||
elif (
|
||||
data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
):
|
||||
status = "blocked_stockplatform_ai_recommendations_stale_after_core_recovered"
|
||||
safe_next_step = (
|
||||
"run_stockplatform_ai_recommendation_pipeline_readback_then_rerun_"
|
||||
"public_api_verifier"
|
||||
)
|
||||
elif readback_present and postgres_not_ready:
|
||||
status = "blocked_stockplatform_postgres_not_ready"
|
||||
safe_next_step = (
|
||||
@@ -490,6 +510,10 @@ def _data_readiness_control_readback(
|
||||
"postgres_ready": data_ready and not postgres_not_ready,
|
||||
"postgres_not_ready": postgres_not_ready,
|
||||
"data_dependency_classification": data_dependency_classification,
|
||||
"ai_recommendations_stale_after_core_recovered": (
|
||||
data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
),
|
||||
"freshness_http_status": freshness_http_status,
|
||||
"ingestion_http_status": ingestion_http_status,
|
||||
"freshness_status": str(freshness_json.get("status") or "unknown"),
|
||||
@@ -556,6 +580,16 @@ def _source_contract_control_readback(
|
||||
if data_ready:
|
||||
status = "stockplatform_source_contract_ready"
|
||||
safe_next_step = "run_post_apply_public_api_verifier_and_clear_p0_006_blocker"
|
||||
elif (
|
||||
readback_present
|
||||
and data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
):
|
||||
status = "stockplatform_source_contract_core_ready_ai_recommendations_stale"
|
||||
safe_next_step = (
|
||||
"run_stockplatform_ai_recommendation_pipeline_readback_then_rerun_"
|
||||
"public_api_verifier"
|
||||
)
|
||||
elif readback_present and data_dependency_classification == (
|
||||
"freshness_or_ingestion_not_ready"
|
||||
):
|
||||
@@ -604,6 +638,10 @@ def _source_contract_control_readback(
|
||||
"safe_next_step": safe_next_step,
|
||||
"source_contract_ready": data_ready,
|
||||
"data_dependency_classification": data_dependency_classification,
|
||||
"ai_recommendations_stale_after_core_recovered": (
|
||||
data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
),
|
||||
"postgres_not_ready": postgres_not_ready,
|
||||
"freshness_status": str(freshness_json.get("status") or "unknown"),
|
||||
"ingestion_status": str(ingestion_json.get("status") or "unknown"),
|
||||
@@ -715,6 +753,14 @@ def _blocked_safe_next_step(*, data_dependency_classification: str) -> str:
|
||||
"run_stockplatform_production_migration_path_after_control_channel_"
|
||||
"readback_then_rerun_public_api_readback"
|
||||
)
|
||||
if (
|
||||
data_dependency_classification
|
||||
== _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
):
|
||||
return (
|
||||
"run_stockplatform_ai_recommendation_pipeline_readback_then_rerun_"
|
||||
"public_api_verifier"
|
||||
)
|
||||
if data_dependency_classification == "freshness_or_ingestion_not_ready":
|
||||
return (
|
||||
"run_stockplatform_data_dependency_source_contract_readback_then_"
|
||||
@@ -735,6 +781,14 @@ def _data_dependency_classification(
|
||||
return "none"
|
||||
if any("postgres_not_ready" in blocker for blocker in active_blockers):
|
||||
return "postgres_not_ready"
|
||||
data_blockers = [
|
||||
blocker
|
||||
for blocker in active_blockers
|
||||
if blocker.startswith("stockplatform_freshness_")
|
||||
or blocker.startswith("stockplatform_ingestion_")
|
||||
]
|
||||
if data_blockers == ["stockplatform_freshness_ai_recommendations_stale"]:
|
||||
return _AI_RECOMMENDATIONS_STALE_AFTER_CORE_RECOVERED
|
||||
if any(
|
||||
blocker.startswith("stockplatform_freshness_")
|
||||
or blocker.startswith("stockplatform_ingestion_")
|
||||
|
||||
Reference in New Issue
Block a user