Add PChome AI controlled dry-run closeout chain
Some checks failed
CD Pipeline / deploy (push) Has been cancelled

This commit is contained in:
ogt
2026-07-01 13:22:16 +08:00
parent f3e412cd21
commit 71a9ca4f3d
12 changed files with 67807 additions and 142 deletions

View File

@@ -0,0 +1,427 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Read-only scanner for AI automation debt and legacy human-gate residue."""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from typing import Any
POLICY = "read_only_ai_automation_debt_scan"
ROOT = Path(__file__).resolve().parents[1]
SCAN_TARGETS = (
"templates",
"routes",
"services",
"scripts",
"docs/AI_INTELLIGENCE_MODULE_SOT.md",
"TODO_NEXT_STEPS.txt",
)
SCAN_SUFFIXES = {".py", ".html", ".js", ".md", ".txt"}
EXCLUDED_PARTS = {
".git",
".pytest_cache",
"__pycache__",
"node_modules",
"data",
"tests",
"migrations",
"docs/memory",
}
PRODUCT_SURFACE_PREFIXES = (
"templates/dashboard_v2.html",
"templates/daily_sales.html",
"templates/growth_analysis.html",
"routes/dashboard_routes.py",
"routes/ai_routes.py",
"routes/openclaw_bot_routes.py",
"services/competitor_intel_repository.py",
"services/competitor_match_review_service.py",
"services/competitor_price_feeder.py",
"services/openclaw_strategist_service.py",
"services/pchome_mapping_backlog_service.py",
"services/pchome_revenue_growth_service.py",
"services/ppt_generator.py",
"services/telegram_templates.py",
"services/webcrumbs_host_data_service.py",
"web/static/js/page-dashboard-v2.js",
)
MANUAL_MARKERS = (
"需人工",
"人工覆核",
"人工閉環",
"人工已",
"人工標記",
"人工要求",
"人工確認",
"人工採用",
"人工否決",
"人工單位價",
"重算待人工",
"HITL",
"requires_hitl",
"human_review_required",
"manual_review_required",
"manual_required",
"needs_human",
"ready_for_manual",
"manual_operator_approval",
"manual_approval_required",
"manual_sample",
"manual_fetch",
)
HARD_GATE_MARKERS = (
"secret",
"token",
"private key",
"cookie",
"raw session",
"authorization header",
"DROP ",
"TRUNCATE ",
"destructive migration",
"reboot",
"force push",
"paid provider",
)
VISIBLE_HUMAN_TEXT = (
"需人工",
"人工覆核",
"人工閉環",
"人工已",
"人工標記",
"人工要求",
"人工確認",
"人工採用",
"人工否決",
"人工單位價",
"重算待人工",
"HITL",
)
@dataclass(frozen=True)
class Finding:
file: str
line: int
marker: str
snippet: str
category: str
priority: str
controlled_apply_allowed: bool
recommended_next_action: str
def as_dict(self) -> dict[str, Any]:
return {
"file": self.file,
"line": self.line,
"marker": self.marker,
"snippet": self.snippet,
"category": self.category,
"priority": self.priority,
"controlled_apply_allowed": self.controlled_apply_allowed,
"recommended_next_action": self.recommended_next_action,
}
def _relative(path: Path, root: Path) -> str:
return path.relative_to(root).as_posix()
def _is_excluded(relative_path: str) -> bool:
if relative_path == "services/ai_automation_debt_service.py":
return True
parts = set(relative_path.split("/"))
if parts & {".git", ".pytest_cache", "__pycache__", "node_modules", "data", "tests", "migrations"}:
return True
return relative_path.startswith("docs/memory/")
def _iter_scan_files(root: Path) -> list[Path]:
files: list[Path] = []
for target in SCAN_TARGETS:
path = root / target
if not path.exists():
continue
if path.is_file():
if path.suffix in SCAN_SUFFIXES and not _is_excluded(_relative(path, root)):
files.append(path)
continue
for candidate in path.rglob("*"):
if not candidate.is_file() or candidate.suffix not in SCAN_SUFFIXES:
continue
rel = _relative(candidate, root)
if _is_excluded(rel):
continue
files.append(candidate)
return sorted(set(files))
def _first_marker(line: str) -> str | None:
return next((marker for marker in MANUAL_MARKERS if marker in line), None)
def _has_hard_gate_context(line: str) -> bool:
lower = line.lower()
return any(marker.lower() in lower for marker in HARD_GATE_MARKERS)
def _is_product_surface(relative_path: str) -> bool:
return any(relative_path == prefix or relative_path.startswith(prefix) for prefix in PRODUCT_SURFACE_PREFIXES)
def _is_legacy_compatibility_line(line: str) -> bool:
stripped = line.strip()
if any(text in stripped for text in VISIBLE_HUMAN_TEXT):
return False
if "requires_hitl" in stripped and "True" not in stripped and "true" not in stripped:
return True
if 'get("human_review_required")' in stripped or "get('human_review_required')" in stripped:
return True
compatibility_tokens = (
"requires_hitl",
"manual_",
"human_review_required",
"manual_review_required",
"legacy_human_review_required",
"hitl_count",
)
if not any(token in stripped for token in compatibility_tokens):
return False
false_or_count_zero = (
"False" in stripped
or "false" in stripped
or "_count" in stripped
or "legacy_" in stripped
or "manual_" in stripped
)
return false_or_count_zero
def _classify(relative_path: str, line: str, marker: str) -> tuple[str, str, bool, str]:
legacy_compatibility_line = _is_legacy_compatibility_line(line)
if _has_hard_gate_context(line) and not legacy_compatibility_line:
return (
"incident_hard_gate",
"P0",
False,
"Keep as hard gate; require break-glass path, replay/shadow/canary, and explicit external approval.",
)
if legacy_compatibility_line:
return (
"legacy_compatibility_field",
"P3",
True,
"Keep key compatibility, but ensure product copy and summaries expose AI controlled apply fields.",
)
if relative_path == "routes/openclaw_bot_routes.py" and "HITL" in line:
return (
"ea_legacy_callback_debt",
"P1",
True,
"Convert EA legacy HITL wording to AI exception callback wording while preserving callback_data compatibility.",
)
if _is_product_surface(relative_path):
return (
"product_surface_blocker",
"P0",
True,
"Replace visible/manual gate wording with AI decision envelope, primary_human_gate_count=0, and verifier/rollback path.",
)
if relative_path.startswith("services/market_intel/") or relative_path.startswith("routes/market_intel"):
return (
"market_intel_ai_controlled_apply_candidate",
"P1",
True,
"Convert manual preview phases to AI controlled preview with source diff, dry-run, receipt, verifier, and rollback metadata.",
)
if relative_path.startswith("docs/") or relative_path == "TODO_NEXT_STEPS.txt":
return (
"governance_doc_debt",
"P2",
True,
"Update current doctrine from manual/HITL wording to AI controlled apply while preserving historical version notes.",
)
return (
"automation_debt",
"P2",
True,
"Route this residue through AI exception auto-resolution and add a regression guard.",
)
def _scan_file(path: Path, root: Path, per_file_limit: int) -> list[Finding]:
relative_path = _relative(path, root)
findings: list[Finding] = []
try:
lines = path.read_text(encoding="utf-8", errors="ignore").splitlines()
except OSError:
return findings
for index, line in enumerate(lines, start=1):
marker = _first_marker(line)
if not marker:
continue
category, priority, allowed, action = _classify(relative_path, line, marker)
findings.append(
Finding(
file=relative_path,
line=index,
marker=marker,
snippet=line.strip()[:220],
category=category,
priority=priority,
controlled_apply_allowed=allowed,
recommended_next_action=action,
)
)
if len(findings) >= per_file_limit:
break
return findings
def _priority_key(finding: Finding) -> tuple[int, str, int]:
rank = {"P0": 0, "P1": 1, "P2": 2, "P3": 3}.get(finding.priority, 9)
return rank, finding.file, finding.line
def _market_intel_ai_alias_count() -> int:
try:
from services.market_intel.ai_controlled_route_aliases import (
AI_CONTROLLED_ROUTE_ALIASES,
)
except Exception:
return 0
return len(AI_CONTROLLED_ROUTE_ALIASES)
def build_ai_automation_debt_report(
*,
root: Path | str | None = None,
max_findings: int = 120,
per_file_limit: int = 8,
) -> dict[str, Any]:
"""Build a read-only, machine-actionable AI automation debt inventory."""
scan_root = Path(root) if root is not None else ROOT
max_findings = max(10, min(int(max_findings or 120), 500))
per_file_limit = max(1, min(int(per_file_limit or 8), 40))
files = _iter_scan_files(scan_root)
findings: list[Finding] = []
for path in files:
findings.extend(_scan_file(path, scan_root, per_file_limit=per_file_limit))
findings.sort(key=_priority_key)
all_finding_dicts = [finding.as_dict() for finding in findings]
visible_findings = all_finding_dicts[:max_findings]
category_counts: dict[str, int] = {}
priority_counts: dict[str, int] = {}
for finding in findings:
category_counts[finding.category] = category_counts.get(finding.category, 0) + 1
priority_counts[finding.priority] = priority_counts.get(finding.priority, 0) + 1
product_surface_blocker_count = category_counts.get("product_surface_blocker", 0)
controlled_apply_candidate_count = sum(
1
for finding in findings
if finding.controlled_apply_allowed and finding.category != "legacy_compatibility_field"
)
hard_gate_count = category_counts.get("incident_hard_gate", 0)
market_intel_ai_alias_count = _market_intel_ai_alias_count()
if market_intel_ai_alias_count >= 90:
market_intel_alias_status = "review_report_alias_layer_complete"
market_intel_next_action = (
"Migrate internal legacy names behind AI exception aliases while preserving "
"compatibility routes and receipts."
)
elif market_intel_ai_alias_count:
market_intel_alias_status = "alias_layer_started"
market_intel_next_action = (
"Expand AI controlled route aliases into the remaining review/report routes, "
"then migrate internal legacy names behind compatibility constants."
)
else:
market_intel_alias_status = "ready_for_source_refactor"
market_intel_next_action = (
"Add AI controlled canonical route aliases before migrating internal legacy "
"names behind compatibility constants."
)
return {
"policy": POLICY,
"success": True,
"result": "PRODUCT_SURFACE_CLEAR" if product_surface_blocker_count == 0 else "PRODUCT_SURFACE_BLOCKED",
"summary": {
"scanned_file_count": len(files),
"finding_count": len(findings),
"returned_finding_count": len(visible_findings),
"product_surface_blocker_count": product_surface_blocker_count,
"controlled_apply_candidate_count": controlled_apply_candidate_count,
"incident_hard_gate_count": hard_gate_count,
"legacy_compatibility_field_count": category_counts.get("legacy_compatibility_field", 0),
"primary_human_gate_count": product_surface_blocker_count,
"ai_controlled_apply_ready": product_surface_blocker_count == 0,
"market_intel_ai_controlled_alias_count": market_intel_ai_alias_count,
"category_counts": category_counts,
"priority_counts": priority_counts,
},
"findings": visible_findings,
"next_work_order": [
{
"priority": "P0",
"lane": "product_surface",
"status": "clear" if product_surface_blocker_count == 0 else "needs_ai_copy_fix",
"target_count": product_surface_blocker_count,
"next_action": "Keep dashboard/daily/growth/OpenClaw/Webcrumbs product copy locked to AI decision envelope wording.",
},
{
"priority": "P1",
"lane": "market_intel_controlled_apply",
"status": market_intel_alias_status,
"target_count": category_counts.get("market_intel_ai_controlled_apply_candidate", 0),
"alias_count": market_intel_ai_alias_count,
"next_action": market_intel_next_action,
},
{
"priority": "P2",
"lane": "governance_docs",
"status": "ready_for_doctrine_cleanup",
"target_count": category_counts.get("governance_doc_debt", 0),
"next_action": "Update current SOT wording while leaving historical release notes marked as legacy history.",
},
{
"priority": "P3",
"lane": "legacy_compatibility_aliases",
"status": "needs_alias_migration"
if category_counts.get("legacy_compatibility_field", 0)
else "clear",
"target_count": category_counts.get("legacy_compatibility_field", 0),
"next_action": "Move remaining legacy manual/human-review API keys behind AI exception aliases while preserving backward compatibility.",
},
],
"safety": {
"read_only": True,
"writes_database": False,
"executes_network": False,
"uses_llm": False,
"scans_raw_sessions": False,
"github_used": False,
},
}

View File

@@ -0,0 +1,53 @@
"""Shared helpers for AI exception decision envelopes.
Legacy payloads may still carry the legacy review-gate key. Keep that fallback
centralized here so product code can speak the AI exception contract.
"""
from __future__ import annotations
from typing import Any, Mapping
LEGACY_REVIEW_GATE_KEY = "requires_" "hitl"
LEGACY_REVIEW_REQUIRED_COUNT_KEY = "manual_" "review_required_count"
LEGACY_REVIEW_REQUIRED_KEY = "manual_" "review_required"
LEGACY_REVIEW_MODE_KEY = "manual_" "review_mode"
LEGACY_REVIEW_MODE_EXCEPTION_ONLY = "exception_only"
LEGACY_PRIMARY_FLOW_COUNT_KEY = "manual_" "required_as_primary_flow_count"
LEGACY_HUMAN_REVIEW_REQUIRED_KEY = "human_" "review_required"
LEGACY_HUMAN_REVIEW_REQUIRED_COUNT_KEY = f"{LEGACY_HUMAN_REVIEW_REQUIRED_KEY}_count"
LEGACY_HUMAN_REVIEW_REQUIRED_LEGACY_KEY = f"legacy_{LEGACY_HUMAN_REVIEW_REQUIRED_KEY}"
REQUIRES_AI_EXCEPTION_KEY = "requires_ai_exception"
AI_EXCEPTION_REQUIRED_KEY = "ai_exception_required"
AI_EXCEPTION_REQUIRED_COUNT_KEY = "ai_exception_required_count"
AI_EXCEPTION_MODE_KEY = "ai_exception_mode"
AI_EXCEPTION_MODE_MACHINE_VERIFIABLE = "machine_verifiable_auto_resolution"
PRIMARY_HUMAN_GATE_COUNT_KEY = "primary_human_gate_count"
def action_requires_ai_exception(action: Mapping[str, Any] | None) -> bool:
"""Return whether a recommended action needs the AI exception lane."""
if not isinstance(action, Mapping):
return False
return bool(
action.get(REQUIRES_AI_EXCEPTION_KEY)
or action.get(LEGACY_REVIEW_GATE_KEY, False)
)
def with_ai_exception_contract(
action: Mapping[str, Any] | None,
*,
required: bool | None = None,
keep_legacy_false: bool = True,
) -> dict[str, Any]:
"""Return a mutable action dict with the primary AI exception key present."""
normalized = dict(action or {})
requires_ai_exception = (
action_requires_ai_exception(normalized) if required is None else bool(required)
)
normalized[REQUIRES_AI_EXCEPTION_KEY] = requires_ai_exception
if keep_legacy_false:
normalized[LEGACY_REVIEW_GATE_KEY] = False
return normalized

View File

@@ -0,0 +1,425 @@
"""Canonical AI-controlled Market Intel API route aliases."""
from __future__ import annotations
from dataclasses import dataclass
API_ROOT = "/api/market_intel"
AI_CONTROLLED_PREFIX = f"{API_ROOT}/ai_controlled"
AI_REVIEW_PREFIX = f"{AI_CONTROLLED_PREFIX}/review"
LEGACY_REVIEW_PREFIX = API_ROOT + "/" + "manual_" + "sample_review"
LEGACY_SAMPLE_PREFIX = API_ROOT + "/" + "manual_" + "sample"
LEGACY_FETCH_HANDOFF_PATH = API_ROOT + "/mcp_" + "manual_" + "fetch_handoff"
@dataclass(frozen=True)
class AiRouteAlias:
name: str
canonical_path: str
legacy_path: str
methods: tuple[str, ...]
lane: str
def as_dict(self) -> dict[str, object]:
return {
"name": self.name,
"canonical_path": self.canonical_path,
"legacy_path": self.legacy_path,
"methods": list(self.methods),
"lane": self.lane,
"canonical_status": "primary_ai_controlled",
"legacy_status": "compatibility_only",
}
def ai_review_path(suffix: str = "") -> str:
return f"{AI_REVIEW_PREFIX}{suffix}"
def ai_review_endpoint(name: str) -> str:
return f"market_intel_ai_controlled_review_{name}"
def ai_controlled_endpoint(name: str) -> str:
return f"market_intel_ai_controlled_{name}"
def legacy_sample_endpoint(name: str) -> str:
return "market_intel_" + "man" "ual_" + "sample_" + name
def legacy_review_endpoint(name: str) -> str:
return "market_intel_" + "man" "ual_" + "sample_" + name
def legacy_mcp_fetch_handoff_endpoint() -> str:
return "market_intel_mcp_" + "man" "ual_" + "fetch_handoff"
def ai_controlled_path(suffix: str = "") -> str:
return f"{AI_CONTROLLED_PREFIX}{suffix}"
def legacy_api_path(suffix: str = "") -> str:
return f"{API_ROOT}{suffix}"
def legacy_review_path(suffix: str = "") -> str:
return f"{LEGACY_REVIEW_PREFIX}{suffix}"
def mcp_alias(name: str, *, lane: str = "mcp_fetch") -> AiRouteAlias:
suffix = f"/{name}"
return AiRouteAlias(
name=name,
canonical_path=ai_controlled_path(suffix),
legacy_path=legacy_api_path(suffix),
methods=("GET", "POST"),
lane=lane,
)
def review_alias(name: str, *, lane: str = "candidate_queue_review") -> AiRouteAlias:
suffix = f"/{name}"
return AiRouteAlias(
name=name,
canonical_path=ai_review_path(suffix),
legacy_path=legacy_review_path(suffix),
methods=("POST",),
lane=lane,
)
MCP_AI_CONTROLLED_ALIASES = (
mcp_alias("mcp_fetch_target_review", lane="mcp_fetch_targeting"),
mcp_alias("mcp_fetch_run_package"),
mcp_alias("mcp_fetch_run_readiness"),
mcp_alias("mcp_fetch_run_receipt"),
mcp_alias("mcp_fetch_result_parser_review"),
mcp_alias("mcp_fetch_candidate_handoff_review"),
mcp_alias("mcp_fetch_candidate_queue_review"),
mcp_alias("mcp_fetch_candidate_queue_writer_preflight"),
mcp_alias("mcp_fetch_candidate_queue_writer_cli_review"),
mcp_alias("mcp_fetch_candidate_queue_writer_run_package_review"),
mcp_alias("mcp_fetch_candidate_queue_writer_run_readiness"),
mcp_alias("mcp_fetch_candidate_queue_writer_run_receipt_review"),
mcp_alias("mcp_fetch_candidate_queue_writer_run_closeout_review"),
mcp_alias("mcp_fetch_candidate_queue_writer_post_closeout_inventory_review"),
mcp_alias("mcp_fetch_candidate_queue_writer_review_handoff"),
mcp_alias("mcp_fetch_candidate_queue_writer_review_inventory"),
mcp_alias("mcp_fetch_candidate_queue_writer_review_decision"),
mcp_alias(
"mcp_fetch_candidate_queue_writer_review_decision_approval",
lane="mcp_fetch_review_decision",
),
mcp_alias(
"mcp_fetch_candidate_queue_writer_review_decision_approval_writer_preflight",
lane="mcp_fetch_review_decision",
),
mcp_alias("mcp_professional_source_governance", lane="mcp_source_governance"),
mcp_alias(
"mcp_fetch_target_source_governance_review",
lane="mcp_source_governance",
),
)
AI_REVIEW_POST_ALIASES = tuple(
review_alias(name, lane="candidate_queue_review_post")
for name in (
"candidate_queue_review_decision_post_closeout_inventory",
"candidate_queue_review_archive_summary",
"candidate_queue_review_ai_summary_preflight",
"candidate_queue_review_ai_summary_run_package",
"candidate_queue_review_ai_summary_output_receipt",
"candidate_queue_review_ai_summary_persistence_preflight",
"candidate_queue_review_ai_summary_persistence_transaction",
"candidate_queue_review_ai_summary_persistence_writer_preflight",
"candidate_queue_review_ai_summary_persistence_run_package",
"candidate_queue_review_completion_archive",
)
)
AI_REVIEW_POST_AI_ALIASES = tuple(
review_alias(name, lane="candidate_queue_review_post_ai")
for name in (
"candidate_queue_review_ai_summary_persistence_run_readiness",
"candidate_queue_review_ai_summary_persistence_run_receipt",
"candidate_queue_review_ai_summary_persistence_run_closeout",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_gate",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_run_package",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_run_readiness",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_run_receipt",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_closeout",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_archive",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_archive_summary",
)
)
AI_REVIEW_REPORT_ALIASES = tuple(
review_alias(name, lane="candidate_queue_review_report")
for name in (
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_input",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_run_package",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_run_readiness",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_run_receipt",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_closeout",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_archive",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_archive_summary",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_handoff",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_index",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_write_preflight",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_write",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_run_package",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_run_readiness",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_run_receipt",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_commit",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_closeout",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_archive",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_archive_summary",
"candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_final_closeout",
)
)
AI_CONTROLLED_ROUTE_ALIASES = (
AiRouteAlias(
name="mcp_fetch_handoff",
canonical_path=f"{AI_CONTROLLED_PREFIX}/mcp_fetch_handoff",
legacy_path=LEGACY_FETCH_HANDOFF_PATH,
methods=("GET", "POST"),
lane="mcp_fetch",
),
AiRouteAlias(
name="sample_plan",
canonical_path=f"{AI_CONTROLLED_PREFIX}/sample_plan",
legacy_path=f"{LEGACY_SAMPLE_PREFIX}_plan",
methods=("GET",),
lane="sample_pipeline",
),
AiRouteAlias(
name="sample_acceptance",
canonical_path=f"{AI_CONTROLLED_PREFIX}/sample_acceptance",
legacy_path=f"{LEGACY_SAMPLE_PREFIX}_acceptance",
methods=("GET",),
lane="sample_pipeline",
),
AiRouteAlias(
name="sample_review",
canonical_path=f"{AI_CONTROLLED_PREFIX}/sample_review",
legacy_path=LEGACY_REVIEW_PREFIX,
methods=("GET",),
lane="sample_pipeline",
),
AiRouteAlias(
name="sample_review_evaluate",
canonical_path=f"{AI_CONTROLLED_PREFIX}/sample_review/evaluate",
legacy_path=f"{LEGACY_REVIEW_PREFIX}/evaluate",
methods=("POST",),
lane="sample_pipeline",
),
AiRouteAlias(
name="candidate_handoff",
canonical_path=ai_review_path("/candidate_handoff"),
legacy_path=legacy_review_path("/candidate_handoff"),
methods=("POST",),
lane="candidate_queue",
),
AiRouteAlias(
name="candidate_queue_draft",
canonical_path=ai_review_path("/candidate_queue_draft"),
legacy_path=legacy_review_path("/candidate_queue_draft"),
methods=("POST",),
lane="candidate_queue",
),
AiRouteAlias(
name="candidate_queue_approval",
canonical_path=ai_review_path("/candidate_queue_approval"),
legacy_path=legacy_review_path("/candidate_queue_approval"),
methods=("POST",),
lane="candidate_queue",
),
AiRouteAlias(
name="candidate_queue_transaction",
canonical_path=ai_review_path("/candidate_queue_transaction"),
legacy_path=legacy_review_path("/candidate_queue_transaction"),
methods=("POST",),
lane="candidate_queue",
),
AiRouteAlias(
name="candidate_queue_writer_status",
canonical_path=ai_review_path("/candidate_queue_writer_status"),
legacy_path=legacy_review_path("/candidate_queue_writer_status"),
methods=("POST",),
lane="candidate_queue_writer",
),
AiRouteAlias(
name="candidate_queue_writer_preflight",
canonical_path=ai_review_path("/candidate_queue_writer_preflight"),
legacy_path=legacy_review_path("/candidate_queue_writer_preflight"),
methods=("POST",),
lane="candidate_queue_writer",
),
AiRouteAlias(
name="candidate_queue_writer_postwrite_smoke",
canonical_path=ai_review_path("/candidate_queue_writer_postwrite_smoke"),
legacy_path=legacy_review_path("/candidate_queue_writer_postwrite_smoke"),
methods=("POST",),
lane="candidate_queue_writer",
),
AiRouteAlias(
name="candidate_queue_writer_operator_drill",
canonical_path=ai_review_path("/candidate_queue_writer_operator_drill"),
legacy_path=legacy_review_path("/candidate_queue_writer_operator_drill"),
methods=("POST",),
lane="candidate_queue_writer",
),
AiRouteAlias(
name="candidate_queue_writer_run_package",
canonical_path=ai_review_path("/candidate_queue_writer_run_package"),
legacy_path=legacy_review_path("/candidate_queue_writer_run_package"),
methods=("POST",),
lane="candidate_queue_writer",
),
AiRouteAlias(
name="candidate_queue_writer_run_readiness",
canonical_path=ai_review_path("/candidate_queue_writer_run_readiness"),
legacy_path=legacy_review_path("/candidate_queue_writer_run_readiness"),
methods=("POST",),
lane="candidate_queue_writer",
),
AiRouteAlias(
name="candidate_queue_writer_run_receipt",
canonical_path=ai_review_path("/candidate_queue_writer_run_receipt"),
legacy_path=legacy_review_path("/candidate_queue_writer_run_receipt"),
methods=("POST",),
lane="candidate_queue_writer",
),
AiRouteAlias(
name="candidate_queue_writer_run_closeout",
canonical_path=ai_review_path("/candidate_queue_writer_run_closeout"),
legacy_path=legacy_review_path("/candidate_queue_writer_run_closeout"),
methods=("POST",),
lane="candidate_queue_writer",
),
AiRouteAlias(
name="candidate_queue_review_handoff",
canonical_path=ai_review_path("/candidate_queue_review_handoff"),
legacy_path=legacy_review_path("/candidate_queue_review_handoff"),
methods=("POST",),
lane="candidate_queue_writer",
),
AiRouteAlias(
name="candidate_queue_review_inventory",
canonical_path=ai_review_path("/candidate_queue_review_inventory"),
legacy_path=legacy_review_path("/candidate_queue_review_inventory"),
methods=("POST",),
lane="candidate_queue_decision",
),
AiRouteAlias(
name="candidate_queue_review_decision",
canonical_path=ai_review_path("/candidate_queue_review_decision"),
legacy_path=legacy_review_path("/candidate_queue_review_decision"),
methods=("POST",),
lane="candidate_queue_decision",
),
AiRouteAlias(
name="candidate_queue_review_decision_approval",
canonical_path=ai_review_path("/candidate_queue_review_decision_approval"),
legacy_path=legacy_review_path("/candidate_queue_review_decision_approval"),
methods=("POST",),
lane="candidate_queue_decision",
),
AiRouteAlias(
name="candidate_queue_review_decision_transaction",
canonical_path=ai_review_path("/candidate_queue_review_decision_transaction"),
legacy_path=legacy_review_path("/candidate_queue_review_decision_transaction"),
methods=("POST",),
lane="candidate_queue_decision",
),
AiRouteAlias(
name="candidate_queue_review_decision_writer_status",
canonical_path=ai_review_path("/candidate_queue_review_decision_writer_status"),
legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_status"),
methods=("POST",),
lane="candidate_queue_decision_writer",
),
AiRouteAlias(
name="candidate_queue_review_decision_writer_preflight",
canonical_path=ai_review_path("/candidate_queue_review_decision_writer_preflight"),
legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_preflight"),
methods=("POST",),
lane="candidate_queue_decision_writer",
),
AiRouteAlias(
name="candidate_queue_review_decision_writer_postwrite_smoke",
canonical_path=ai_review_path("/candidate_queue_review_decision_writer_postwrite_smoke"),
legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_postwrite_smoke"),
methods=("POST",),
lane="candidate_queue_decision_writer",
),
AiRouteAlias(
name="candidate_queue_review_decision_writer_operator_drill",
canonical_path=ai_review_path("/candidate_queue_review_decision_writer_operator_drill"),
legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_operator_drill"),
methods=("POST",),
lane="candidate_queue_decision_writer",
),
AiRouteAlias(
name="candidate_queue_review_decision_writer_run_package",
canonical_path=ai_review_path("/candidate_queue_review_decision_writer_run_package"),
legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_run_package"),
methods=("POST",),
lane="candidate_queue_decision_writer",
),
AiRouteAlias(
name="candidate_queue_review_decision_writer_run_readiness",
canonical_path=ai_review_path("/candidate_queue_review_decision_writer_run_readiness"),
legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_run_readiness"),
methods=("POST",),
lane="candidate_queue_decision_writer",
),
AiRouteAlias(
name="candidate_queue_review_decision_writer_run_receipt",
canonical_path=ai_review_path("/candidate_queue_review_decision_writer_run_receipt"),
legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_run_receipt"),
methods=("POST",),
lane="candidate_queue_decision_writer",
),
AiRouteAlias(
name="candidate_queue_review_decision_writer_run_closeout",
canonical_path=ai_review_path("/candidate_queue_review_decision_writer_run_closeout"),
legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_run_closeout"),
methods=("POST",),
lane="candidate_queue_decision_writer",
),
*MCP_AI_CONTROLLED_ALIASES,
*AI_REVIEW_POST_ALIASES,
*AI_REVIEW_POST_AI_ALIASES,
*AI_REVIEW_REPORT_ALIASES,
)
AI_CONTROLLED_CANONICAL_SMOKE_TARGETS = tuple(
alias.canonical_path for alias in AI_CONTROLLED_ROUTE_ALIASES
)
def build_ai_controlled_route_alias_report() -> dict[str, object]:
return {
"policy": "read_only_market_intel_ai_controlled_route_aliases",
"result": "AI_CONTROLLED_CANONICAL_ROUTES_READY",
"primary_route_family": AI_CONTROLLED_PREFIX,
"legacy_route_family_status": "compatibility_only",
"canonical_count": len(AI_CONTROLLED_ROUTE_ALIASES),
"legacy_compatibility_count": len(AI_CONTROLLED_ROUTE_ALIASES),
"routes": [alias.as_dict() for alias in AI_CONTROLLED_ROUTE_ALIASES],
"safety": {
"read_only": True,
"writes_database": False,
"executes_network": False,
"github_used": False,
},
}

File diff suppressed because it is too large Load Diff