"""
Reward functions for Notion SPA tasks - V2 Architecture (Dojo Bench Customer Colossus).

This module contains backend-only validation functions for database management tasks.
Each function validates complex multi-database operations and page/block structures.
"""

import logging
import re
from datetime import datetime
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
from .backend import Backend

# TaskScore type definitions for accumulated error/success tracking
from typing import Literal, TypedDict as TaskTypedDict

class ScoreMetadata(TaskTypedDict):
    success_accumulator: list[str]
    error_accumulator: list[str]

class TaskScore(TaskTypedDict):
    score: Literal[1.0, 0.0]
    metadata: ScoreMetadata

logger = logging.getLogger(__name__)

# Helper Functions
# ==============================================================================

def _find_pages_by_title(backend: Backend, title_pattern: str) -> List[Dict[str, Any]]:
    """Find pages matching title pattern (case-insensitive regex matching)."""
    # Escape special regex characters but allow the pattern to match anywhere in the title
    escaped_pattern = re.escape(title_pattern)
    result = backend.query({
        "collection": "pages",
        "filter": {"title": {"$regex": escaped_pattern, "$options": "i"}}
    })
    if isinstance(result, list):
        return result
    return []

def _find_page_by_title(backend: Backend, title_pattern: str) -> Optional[Dict[str, Any]]:
    """Find a single page by title pattern (case-insensitive regex matching)."""
    pages = _find_pages_by_title(backend, title_pattern)
    return pages[0] if pages else None

def _find_page_by_id(backend: Backend, page_id: str) -> Dict[str, Any] | None:
    """Find a page by its _id, filtering at query level."""
    result = backend.query({"collection": "pages", "filter": {"_id": page_id}})
    if isinstance(result, list) and result:
        return result[0]
    return None

def _filter_pages_by_title(pages: List[Dict[str, Any]], title_pattern: str) -> List[Dict[str, Any]]:
    """Filter a list of pages by title pattern (case-insensitive regex matching)."""
    escaped_pattern = re.escape(title_pattern)
    pattern = re.compile(escaped_pattern, re.IGNORECASE)
    return [p for p in pages if pattern.search(p.get("title", ""))]

def _filter_page_by_title(pages: List[Dict[str, Any]], title_pattern: str) -> Optional[Dict[str, Any]]:
    """Find a single page from a list by title pattern (case-insensitive regex matching)."""
    filtered = _filter_pages_by_title(pages, title_pattern)
    return filtered[0] if filtered else None

def _get_child_pages(backend: Backend, parent_id: str) -> List[Dict[str, Any]]:
    """Get all pages with a specific parentId."""
    result = backend.query({"collection": "pages", "filter": {"parentId": parent_id}})
    if isinstance(result, list):
        return result
    return []

def _get_workspace_pages(backend: Backend, workspace_id: str) -> List[Dict[str, Any]]:
    """Get all pages in a workspace at query level."""
    result = backend.query({"collection": "pages", "filter": {"workspaceId": workspace_id}})
    if isinstance(result, list):
        return result
    return []

def _get_blocks_for_page(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get all blocks for a page."""
    result = backend.query({"collection": "blocks", "filter": {"pageId": page_id}})
    if isinstance(result, list):
        return result
    return []

def _get_blocks_by_type(backend: Backend, page_id: str, block_types: List[str]) -> List[Dict[str, Any]]:
    """Get blocks for a page filtered by type(s) at query level.
    
    Args:
        backend: The backend to query
        page_id: The page ID to get blocks for
        block_types: List of block types to filter by (uses $in operator)
    
    Returns:
        List of blocks matching the page_id and any of the block_types
    """
    result = backend.query({
        "collection": "blocks",
        "filter": {
            "pageId": page_id,
            "type": {"$in": block_types}
        }
    })
    if isinstance(result, list):
        return result
    return []

def _get_todo_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get todo/checkbox blocks for a page at query level."""
    return _get_blocks_by_type(backend, page_id, ["todo", "to_do", "checkbox"])

def _get_heading_blocks(backend: Backend, page_id: str, level: Optional[int] = None) -> List[Dict[str, Any]]:
    """Get heading blocks for a page at query level.
    
    Args:
        backend: The backend to query
        page_id: The page ID to get blocks for
        level: Optional heading level (1, 2, or 3). If None, gets all headings.
    """
    if level is not None:
        types = [f"heading{level}", f"heading_{level}"]
    else:
        types = ["heading1", "heading_1", "heading2", "heading_2", "heading3", "heading_3"]
    return _get_blocks_by_type(backend, page_id, types)

def _get_paragraph_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get paragraph/text blocks for a page at query level."""
    return _get_blocks_by_type(backend, page_id, ["paragraph", "text"])

def _get_callout_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get callout blocks for a page at query level."""
    return _get_blocks_by_type(backend, page_id, ["callout"])

def _get_code_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get code blocks for a page at query level."""
    return _get_blocks_by_type(backend, page_id, ["code"])

def _get_bookmark_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get bookmark blocks for a page at query level."""
    return _get_blocks_by_type(backend, page_id, ["bookmark"])

def _get_bulleted_list_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get bulleted list blocks for a page at query level."""
    return _get_blocks_by_type(backend, page_id, ["bulletedList", "bulleted_list", "bulleted_list_item", "bulleted"])

def _get_numbered_list_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get numbered list blocks for a page at query level."""
    return _get_blocks_by_type(backend, page_id, ["numberedList", "numbered_list", "numbered_list_item", "numbered"])

def _get_quote_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get quote blocks for a page at query level."""
    return _get_blocks_by_type(backend, page_id, ["quote"])

def _get_divider_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get divider blocks for a page at query level."""
    return _get_blocks_by_type(backend, page_id, ["divider"])

def _get_toggle_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get toggle blocks for a page at query level."""
    return _get_blocks_by_type(backend, page_id, ["toggle"])

def _get_image_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get image blocks for a page at query level."""
    return _get_blocks_by_type(backend, page_id, ["image"])

def _check_page_is_published(page: Dict[str, Any]) -> bool:
    """Check if page is published."""
    published_metadata = page.get("publishedMetadata")
    if isinstance(published_metadata, dict):
        return published_metadata.get("isPublished", False)
    return False

def _find_child_by_title(backend: Backend, parent_page: Dict[str, Any], child_title: str) -> Optional[Dict[str, Any]]:
    """Find a child page by title under a parent (case-insensitive regex matching)."""
    parent_id = parent_page.get("_id")
    escaped_pattern = re.escape(child_title)
    result = backend.query({
        "collection": "pages",
        "filter": {
            "parentId": parent_id,
            "title": {"$regex": escaped_pattern, "$options": "i"}
        }
    })
    if isinstance(result, list) and result:
        return result[0]
    return None

def _find_child_by_title_with_id(backend: Backend, parent_id: str, child_title: str) -> Optional[Dict[str, Any]]:
    """Find a child page by title under a parent ID (case-insensitive regex matching)."""
    escaped_pattern = re.escape(child_title)
    result = backend.query({
        "collection": "pages",
        "filter": {
            "parentId": parent_id,
            "title": {"$regex": escaped_pattern, "$options": "i"}
        }
    })
    if isinstance(result, list) and result:
        return result[0]
    return None

def _find_root_pages_by_title(backend: Backend, title_pattern: str) -> List[Dict[str, Any]]:
    """Find root-level pages (no parentId) matching title pattern."""
    escaped_pattern = re.escape(title_pattern)
    result = backend.query({
        "collection": "pages",
        "filter": {
            "parentId": None,
            "title": {"$regex": escaped_pattern, "$options": "i"}
        }
    })
    if isinstance(result, list):
        return result
    return []

def _find_root_page_by_title(backend: Backend, title: str) -> Optional[Dict[str, Any]]:
    """Find a root-level page (no parent) by exact title at query level."""
    result = backend.query({
        "collection": "pages",
        "filter": {
            "title": title,
            "parentId": None
        }
    })
    if isinstance(result, list) and result:
        return result[0]
    return None

def _get_root_pages(backend: Backend) -> List[Dict[str, Any]]:
    """Get all root-level pages (no parentId)."""
    result = backend.query({
        "collection": "pages",
        "filter": {"parentId": None}
    })
    if isinstance(result, list):
        return result
    return []

def _get_page_by_id(backend: Backend, page_id: str) -> Optional[Dict[str, Any]]:
    """Get a single page by its ID."""
    result = backend.query({
        "collection": "pages",
        "filter": {"_id": page_id}
    })
    if isinstance(result, list) and result:
        return result[0]
    return None

def _find_pages_with_all_title_patterns(backend: Backend, patterns: List[str]) -> List[Dict[str, Any]]:
    """Find pages whose title contains ALL of the given patterns (case-insensitive)."""
    if not patterns:
        return []

    # Build regex that matches all patterns (in any order)
    # Use positive lookahead for each pattern
    regex_parts = [f"(?=.*{re.escape(p)})" for p in patterns]
    combined_regex = "".join(regex_parts) + ".*"

    result = backend.query({
        "collection": "pages",
        "filter": {
            "title": {"$regex": combined_regex, "$options": "i"}
        }
    })
    if isinstance(result, list):
        return result
    return []

def _find_page_with_all_title_patterns(backend: Backend, patterns: List[str]) -> Optional[Dict[str, Any]]:
    """Find a single page whose title contains ALL of the given patterns (case-insensitive)."""
    pages = _find_pages_with_all_title_patterns(backend, patterns)
    return pages[0] if pages else None

def _find_page_by_title_and_parent(backend: Backend, title: str, parent_id: str) -> Optional[Dict[str, Any]]:
    """Find a page by exact title and parent ID at query level."""
    result = backend.query({
        "collection": "pages",
        "filter": {
            "title": title,
            "parentId": parent_id
        }
    })
    if isinstance(result, list) and result:
        return result[0]
    return None

def _find_page_by_title_pattern_and_parent(backend: Backend, title_pattern: str, parent_id: str) -> Optional[Dict[str, Any]]:
    """Find a page by title pattern (case-insensitive) and parent ID at query level."""
    escaped_pattern = re.escape(title_pattern)
    result = backend.query({
        "collection": "pages",
        "filter": {
            "title": {"$regex": escaped_pattern, "$options": "i"},
            "parentId": parent_id
        }
    })
    if isinstance(result, list) and result:
        return result[0]
    return None

def _find_page_by_title_and_workspace(backend: Backend, title_pattern: str, workspace_id: str) -> Optional[Dict[str, Any]]:
    """Find a page by title pattern (case-insensitive) and workspace ID at query level."""
    escaped_pattern = re.escape(title_pattern)
    result = backend.query({
        "collection": "pages",
        "filter": {
            "title": {"$regex": escaped_pattern, "$options": "i"},
            "workspaceId": workspace_id
        }
    })
    if isinstance(result, list) and result:
        return result[0]
    return None

def _check_page_is_child_of(parent_page: Dict[str, Any], child_page: Dict[str, Any]) -> bool:
    """Check if child_page is a child of parent_page (by ID relationship)."""
    return child_page.get("parentId") == parent_page.get("_id")

def _check_page_has_collaborator(page: Dict[str, Any], email: str, access_level: Optional[str] = None) -> bool:
    """Check if page has a collaborator with given email and optionally access level (case-insensitive)."""
    sharing_metadata = page.get("sharingMetadata")
    if not isinstance(sharing_metadata, dict):
        return False

    collaborators = sharing_metadata.get("collaborators", [])
    if not isinstance(collaborators, list):
        return False

    email_lower = email.lower()
    for collab in collaborators:
        if isinstance(collab, dict):
            collab_email = collab.get("email", "")
            if collab_email and email_lower in collab_email.lower():
                if access_level is None:
                    return True
                if collab.get("accessLevel") == access_level:
                    return True
    return False

def _check_page_is_public(page: Dict[str, Any]) -> bool:
    """Check if page is publicly shared or published to web."""
    # Check publishedMetadata.isPublished (from "Publish to web" button)
    published_metadata = page.get("publishedMetadata")
    if isinstance(published_metadata, dict):
        if published_metadata.get("isPublished", False):
            return True
    # Also check sharingMetadata.isPublic (legacy/alternative public sharing)
    sharing_metadata = page.get("sharingMetadata")
    if isinstance(sharing_metadata, dict):
        if sharing_metadata.get("isPublic", False):
            return True
    return False

def _check_page_is_archived(page: Dict[str, Any]) -> bool:
    """Check if page is archived."""
    return page.get("isArchived", False)

def _check_page_is_favorite(page: Dict[str, Any]) -> bool:
    """Check if page is favorited."""
    return page.get("isFavorite", False)

def _check_page_icon(page: Dict[str, Any], expected_icon: str) -> bool:
    """Check if page has the expected icon."""
    return page.get("icon") == expected_icon

def _count_descendant_pages(backend: Backend, parent_id: str) -> int:
    """Count all descendant pages (children, grandchildren, etc.) of a parent."""
    count = 0
    children = _get_child_pages(backend, parent_id)
    for child in children:
        count += 1
        count += _count_descendant_pages(backend, child.get("_id", ""))
    return count

def _has_block_type(blocks: List[Dict[str, Any]], block_type: str) -> bool:
    """Check if any block matches the specified type."""
    return any(b.get("type") == block_type for b in blocks)

def _has_block_with_content(blocks: List[Dict], content_pattern: str, block_type: str = None) -> bool:
    """Check if blocks contain content pattern (case-insensitive)."""
    pattern_lower = content_pattern.lower()
    for b in blocks:
        if block_type and b.get("type") != block_type:
            continue
        if pattern_lower in b.get("content", "").lower():
            return True
    return False

def _find_block_index(blocks: List[Dict], content_pattern: str, block_type: str = None) -> Optional[int]:
    """Find index of block with content pattern."""
    pattern_lower = content_pattern.lower()
    for idx, b in enumerate(blocks):
        if block_type and b.get("type") != block_type:
            continue
        if pattern_lower in b.get("content", "").lower():
            return idx
    return None

def _count_blocks_by_type(blocks: List[Dict], block_type: str) -> int:
    """Count blocks of a specific type."""
    return sum(1 for b in blocks if b.get("type") == block_type)

def _count_todos(blocks: List[Dict]) -> Tuple[int, int, int]:
    """Count total, checked, unchecked todos."""
    todos = [b for b in blocks if b.get("type") == "todo"]
    total = len(todos)
    checked = sum(1 for t in todos if t.get("properties", {}).get("checked", False))
    return total, checked, total - checked

def _has_callout_with_bg(blocks: List[Dict], bg_pattern: str) -> bool:
    """Check if there's a callout with specific background color."""
    bg_lower = bg_pattern.lower()
    for b in blocks:
        if b.get("type") == "callout":
            props = b.get("properties", {})
            bg = props.get("backgroundColor", "")
            if bg_lower in bg.lower():
                return True
    return False

def _has_code_block_with_lang(blocks: List[Dict], language: str) -> bool:
    """Check if there's a code block with specific language."""
    lang_lower = language.lower()
    for b in blocks:
        if b.get("type") == "code":
            props = b.get("properties", {})
            lang = props.get("language", "")
            if lang_lower in lang.lower():
                return True
    return False

def _check_text_is_bold(content: str, props: Dict[str, Any], text_pattern: Optional[str] = None) -> bool:
    """Check if text is bold via markdown (**text**) or bold property.
    
    Args:
        content: The text content to check
        props: Block properties dict (may contain 'bold' key)
        text_pattern: Optional pattern to match within bold markers. If None, checks for any bold.
    
    Returns:
        True if text is bold (via markdown or property), False otherwise
    """
    # Check block-level bold property
    if props.get("bold", False):
        return True
    
    # Check markdown-style bold (**text**)
    if text_pattern:
        # Escape the text pattern for regex
        escaped_pattern = re.escape(text_pattern)
        # Replace escaped spaces with \s+ to allow flexible whitespace matching
        escaped_pattern = escaped_pattern.replace(r'\ ', r'\s+')
        
        # Check for exact match: **text_pattern** (with optional whitespace around pattern)
        # This ensures the text_pattern is directly within bold markers, not just somewhere in a larger bold section
        pattern = r'\*\*\s*' + escaped_pattern + r'\s*\*\*'
        if re.search(pattern, content, re.IGNORECASE):
            return True
    else:
        # Check for any bold markers
        if re.search(r'\*\*[^*]+\*\*', content):
            return True
    
    return False

def _check_text_is_italic(content: str, props: Dict[str, Any], text_pattern: Optional[str] = None) -> bool:
    """Check if text is italic via markdown (*text* or _text_) or italic property.
    
    Args:
        content: The text content to check
        props: Block properties dict (may contain 'italic' key)
        text_pattern: Optional pattern to match within italic markers. If None, checks for any italic.
    
    Returns:
        True if text is italic (via markdown or property), False otherwise
    """
    # Check block-level italic property
    if props.get("italic", False):
        return True
    
    # Check markdown-style italic (*text* or _text_)
    if text_pattern:
        # Escape the text pattern for regex
        escaped_pattern = re.escape(text_pattern)
        # Replace escaped spaces with \s+ to allow flexible whitespace matching
        escaped_pattern = escaped_pattern.replace(r'\ ', r'\s+')
        
        # Check *text* style - ensure text_pattern is directly within asterisk markers
        # This ensures the text_pattern is directly within italic markers, not just somewhere in a larger italic section
        pattern_asterisk = r'\*\s*' + escaped_pattern + r'\s*\*'
        if re.search(pattern_asterisk, content, re.IGNORECASE):
            return True
        # Check _text_ style - ensure text_pattern is directly within underscore markers
        pattern_underscore = r'_\s*' + escaped_pattern + r'\s*_'
        if re.search(pattern_underscore, content, re.IGNORECASE):
            return True
    else:
        # Check for any italic markers
        if re.search(r'\*[^*]+\*', content) or re.search(r'_[^_]+_', content):
            return True
    
    return False

def _check_email_matches(collab_email: str, target_email: str) -> bool:
    """Check if collaborator email matches target (case-insensitive)."""
    return collab_email.lower() == target_email.lower()

def _check_page_has_links(backend: Backend, page_id: str) -> bool:
    """Check if a page has links (bookmark blocks or content with URLs)."""
    blocks = _get_blocks_for_page(backend, page_id)
    for block in blocks:
        block_type = block.get("type")
        properties = block.get("properties", {})
        if block_type == "bookmark" or properties.get("url"):
            return True
        # Check if content contains links (basic check)
        content = block.get("content", "")
        if "http" in content.lower() or "link" in content.lower():
            return True
    return False

def _count_heading_blocks(blocks: List[Dict[str, Any]]) -> int:
    """Count the number of heading blocks (heading1, heading2, heading3)."""
    heading_types = {"heading1", "heading2", "heading3"}
    return sum(1 for b in blocks if b.get("type") in heading_types)

def _is_calendar_icon(icon: str | None) -> bool:
    """Check if the icon is a calendar emoji."""
    if not icon:
        return False
    calendar_icons = {"📅", "🗓️", "🗓", "📆"}
    return icon in calendar_icons

def _is_graduation_cap_icon(icon: str | None) -> bool:
    """Check if the icon is a graduation cap emoji."""
    if not icon:
        return False
    graduation_icons = {"🎓", "👩‍🎓", "👨‍🎓"}
    return icon in graduation_icons

def _count_blocks_of_type(blocks: List[Dict[str, Any]], block_type: str) -> int:
    """Count blocks of a specified type."""
    return sum(1 for b in blocks if b.get("type") == block_type)

def _is_document_icon(icon: str | None) -> bool:
    """Check if the icon is a document emoji."""
    if not icon:
        return False
    document_icons = {"📄", "📃", "📋", "📝", "📑", "🗎", "🗏"}
    return icon in document_icons

def _has_heading_block(blocks: List[Dict[str, Any]]) -> bool:
    """Check if any block is a heading (heading1, heading2, or heading3)."""
    heading_types = {"heading1", "heading2", "heading3"}
    return any(b.get("type") in heading_types for b in blocks)

def _is_lightbulb_icon(icon: str | None) -> bool:
    """Check if the icon is a lightbulb emoji."""
    if not icon:
        return False
    return icon in {"💡", "🔦", "✨"}

def _is_brain_icon(icon: str | None) -> bool:
    """Check if the icon is a brain emoji."""
    if not icon:
        return False
    return icon in {"🧠", "🧩"}

def _is_people_icon(icon: str | None) -> bool:
    """Check if the icon is a people/team emoji."""
    if not icon:
        return False
    people_icons = {"👥", "👨‍👩‍👧‍👦", "👪", "🧑‍🤝‍🧑", "👫", "👬", "👭", "🤝", "👨‍👩‍👧", "👨‍👩‍👦"}
    return icon in people_icons

def _is_gear_icon(icon: str | None) -> bool:
    """Check if the icon is a gear/tool emoji."""
    if not icon:
        return False
    gear_icons = {"⚙️", "🔧", "🛠️", "⚒️", "🔩", "⚙"}
    return icon in gear_icons

def _is_heart_icon(icon: str | None) -> bool:
    """Check if the icon is a heart emoji."""
    if not icon:
        return False
    heart_icons = {"❤️", "💖", "💗", "💕", "♥️", "💜", "💙", "💚", "🧡", "💛", "🤍", "🖤", "💝", "💞", "💓", "💘"}
    return icon in heart_icons

def _is_target_icon(icon: str | None) -> bool:
    """Check if the icon is a target emoji."""
    if not icon:
        return False
    return icon in {"🎯"}

def _is_handshake_icon(icon: str | None) -> bool:
    """Check if the icon is a handshake emoji."""
    if not icon:
        return False
    return icon in {"🤝"}

def _is_trophy_icon(icon: str | None) -> bool:
    """Check if the icon is a trophy emoji."""
    if not icon:
        return False
    trophy_icons = {"🏆", "🥇", "🏅", "🎖️"}
    return icon in trophy_icons

def _is_folder_icon(icon: str | None) -> bool:
    """Check if the icon is a folder emoji."""
    if not icon:
        return False
    folder_icons = {"📁", "📂", "🗂️", "🗂"}
    return icon in folder_icons

def _is_rocket_icon(icon: str | None) -> bool:
    """Check if the icon is a rocket emoji."""
    if not icon:
        return False
    return icon in {"🚀"}

def _get_all_descendant_pages_from_backend(backend: Backend, parent_id: str) -> List[Dict[str, Any]]:
    """Recursively get all descendant pages of a given parent using query-level filtering."""
    descendants = []
    children = _get_child_pages(backend, parent_id)

    for child in children:
        descendants.append(child)
        child_id = child.get("_id")
        if child_id:
            # Recursively get children of this child
            descendants.extend(_get_all_descendant_pages_from_backend(backend, child_id))

    return descendants

def _extract_number_from_bullet(text: str) -> int | None:
    """Extract number from bullet text like '• Block count: 5' -> 5."""
    import re
    match = re.search(r':\s*(\d+)', text)
    if match:
        return int(match.group(1))
    return None

def _extract_type_from_bullet(text: str) -> str | None:
    """Extract type from bullet text like '• Most common block type: paragraph' -> 'paragraph'."""
    import re
    match = re.search(r':\s*(\w+)', text)
    if match:
        return match.group(1)
    return None

def _extract_date_from_bullet(text: str) -> str | None:
    """Extract date from bullet text like '• Last updated: 2024-01-15' -> '2024-01-15'."""
    import re
    match = re.search(r':\s*(\d{4}-\d{2}-\d{2})', text)
    if match:
        return match.group(1)
    return None

def _format_date_from_iso(iso_string: str) -> str | None:
    """Convert ISO timestamp to YYYY-MM-DD format."""
    if not iso_string:
        return None
    try:
        from datetime import datetime
        dt = datetime.fromisoformat(iso_string.replace('Z', '+00:00'))
        return dt.strftime('%Y-%m-%d')
    except Exception:
        return None

def _get_most_common_block_type(blocks: List[Dict[str, Any]]) -> str | None:
    """Return the most frequent block type. In case of tie, returns one of the tied types."""
    if not blocks:
        return None
    
    from collections import Counter
    type_counts = Counter(b.get("type") for b in blocks if b.get("type"))
    
    if not type_counts:
        return None
    
    # Get the most common type(s)
    most_common = type_counts.most_common(1)
    return most_common[0][0] if most_common else None

def _get_code_blocks_by_page_from_backend(backend: Backend) -> Dict[str, List[Dict[str, Any]]]:
    """Get all code blocks from backend and group by pageId using query-level filtering."""
    result = backend.query({"collection": "blocks", "filter": {"type": "code"}})
    code_blocks = result if isinstance(result, list) else []
    from collections import defaultdict
    by_page = defaultdict(list)
    for block in code_blocks:
        page_id = block.get("pageId")
        if page_id:
            by_page[page_id].append(block)
    return dict(by_page)

def _count_languages_on_page(code_blocks: List[Dict[str, Any]]) -> Dict[str, int]:
    """Count code blocks by language on a specific page."""
    from collections import Counter
    languages = [b.get("properties", {}).get("language", "unknown") 
                 for b in code_blocks]
    return dict(Counter(languages))

def _parse_language_count_from_numbered_list(content: str) -> tuple[str, int] | None:
    """Parse '1. JavaScript - 5 examples' -> ('javascript', 5)."""
    import re
    match = re.search(r'(\w+)\s*-\s*(\d+)\s*examples?', content, re.IGNORECASE)
    if match:
        return (match.group(1).lower(), int(match.group(2)))
    return None

def _parse_language_count_from_todo(content: str) -> tuple[str, int] | None:
    """Parse 'Review JavaScript fundamentals (5 examples available)' -> ('javascript', 5)."""
    import re
    match = re.search(r'Review\s+(\w+)\s+fundamentals\s*\((\d+)\s*examples?\s+available\)', 
                      content, re.IGNORECASE)
    if match:
        return (match.group(1).lower(), int(match.group(2)))
    return None

def _extract_icon_and_title_from_heading(content: str) -> tuple[str, str] | None:
    """Extract icon and title from '💻 Code Examples Repository'."""
    if len(content) < 2:
        return None
    # First character is likely emoji, rest is title
    icon = content[0]
    title = content[1:].strip()
    return (icon, title)

def _build_page_hierarchy(
    final_state_backend: Backend,
    root_page_id: str,
    visited: Optional[set] = None
) -> Dict[str, Any]:
    """Build a nested hierarchy structure for pages starting from root."""
    if visited is None:
        visited = set()
    
    if root_page_id in visited:
        return {}
    
    visited.add(root_page_id)
    
    root_page = _get_page_by_id(final_state_backend, root_page_id)
    if not root_page:
        return {}
    
    children = _get_child_pages(final_state_backend, root_page_id)
    hierarchy = {
        "page": root_page,
        "children": []
    }
    
    for child in children:
        child_hierarchy = _build_page_hierarchy(final_state_backend, child.get("_id"), visited)
        if child_hierarchy:
            hierarchy["children"].append(child_hierarchy)
    
    return hierarchy

def _count_pages_in_hierarchy(hierarchy: Dict[str, Any]) -> int:
    """Count total pages in a hierarchy tree."""
    count = 1  # Count the root page
    for child in hierarchy.get("children", []):
        count += _count_pages_in_hierarchy(child)
    return count

def _get_all_pages_in_hierarchy(hierarchy: Dict[str, Any]) -> List[Dict[str, Any]]:
    """Get all pages in a hierarchy tree as a flat list."""
    pages = [hierarchy["page"]]
    for child in hierarchy.get("children", []):
        pages.extend(_get_all_pages_in_hierarchy(child))
    return pages

def _get_pages_from_backend(backend: Backend, filter: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
    """Get pages from backend with optional filter.
    
    Args:
        backend: Backend instance
        filter: Optional filter dict, e.g. {"title": {"$regex": "pattern", "$options": "i"}}
    """
    result = backend.query({"collection": "pages", "filter": filter or {}})
    if isinstance(result, list):
        return result
    return []

def _get_blocks_from_backend(backend: Backend, filter: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
    """Get blocks from backend with optional filter.
    
    Args:
        backend: Backend instance
        filter: Optional filter dict, e.g. {"pageId": "some-id"}
    """
    result = backend.query({"collection": "blocks", "filter": filter or {}})
    if isinstance(result, list):
        return result
    return []

def _find_page_by_title_direct(backend: Backend, title_pattern: str) -> Optional[Dict[str, Any]]:
    """Find a single page by title pattern using direct backend query with $regex.
    
    Args:
        backend: Backend instance
        title_pattern: Title to search for (case-insensitive)
    
    Returns:
        First matching page or None
    """
    pages = _get_pages_from_backend(backend, {"title": {"$regex": title_pattern, "$options": "i"}})
    return pages[0] if pages else None

def _find_pages_by_title_direct(backend: Backend, title_pattern: str) -> List[Dict[str, Any]]:
    """Find pages matching title pattern using direct backend query with $regex.
    
    Args:
        backend: Backend instance
        title_pattern: Title pattern to search for (case-insensitive)
    
    Returns:
        List of matching pages
    """
    return _get_pages_from_backend(backend, {"title": {"$regex": title_pattern, "$options": "i"}})

def _get_child_pages_direct(backend: Backend, parent_id: str) -> List[Dict[str, Any]]:
    """Get all pages with a specific parentId using direct backend query.
    
    Args:
        backend: Backend instance
        parent_id: Parent page ID
    
    Returns:
        List of child pages
    """
    return _get_pages_from_backend(backend, {"parentId": parent_id})

def _get_blocks_for_page_direct(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get all blocks for a page using direct backend query.
    
    Args:
        backend: Backend instance
        page_id: Page ID
    
    Returns:
        List of blocks for the page
    """
    return _get_blocks_from_backend(backend, {"pageId": page_id})

def _find_child_by_title_direct(backend: Backend, parent_id: str, child_title: str) -> Optional[Dict[str, Any]]:
    """Find a child page by title under a parent using direct backend queries.
    
    Args:
        backend: Backend instance
        parent_id: Parent page ID
        child_title: Title of child to find (case-insensitive)
    
    Returns:
        Matching child page or None
    """
    child_pages = _get_child_pages_direct(backend, parent_id)
    # Filter by title locally since we already filtered by parentId
    title_lower = child_title.lower()
    for p in child_pages:
        page_title = p.get("title", "").lower()
        if title_lower in page_title or page_title in title_lower:
            return p
    return None

def _get_cell_value_by_property_name(
    cell_values: Dict[str, Any],
    db_properties: List[Dict[str, Any]],
    property_name: str
) -> Any:
    """Get a cell value by property name, handling both ID-based and name-based keys.

    This helper resolves the issue where MCP stores cellValues with property IDs (UUIDs)
    as keys, but we want to look up values by property name.

    Args:
        cell_values: The cellValues dict from a database row
        db_properties: The properties list from databaseMetadata
        property_name: The property name to look up (case-insensitive)

    Returns:
        The cell value if found, None otherwise
    """
    property_name_lower = property_name.lower()

    # First, try to find directly by checking if property name is in the key (legacy behavior)
    for key, value in cell_values.items():
        if property_name_lower in key.lower():
            return value

    # If not found, create a mapping from property ID to name and look up
    prop_id_to_name = {p.get("_id"): p.get("name", "").lower() for p in db_properties}

    for key, value in cell_values.items():
        prop_name = prop_id_to_name.get(key, "")
        if property_name_lower in prop_name or prop_name in property_name_lower:
            return value

    return None

def _get_all_cell_values_by_name(
    cell_values: Dict[str, Any],
    db_properties: List[Dict[str, Any]]
) -> Dict[str, Any]:
    """Convert cellValues from property ID keys to property name keys.

    Args:
        cell_values: The cellValues dict from a database row
        db_properties: The properties list from databaseMetadata

    Returns:
        Dict with property names (lowercased) as keys
    """
    # Create mapping from property ID to name
    prop_id_to_name = {p.get("_id"): p.get("name", "").lower() for p in db_properties}

    result = {}
    for key, value in cell_values.items():
        # Try to get the property name from the mapping
        prop_name = prop_id_to_name.get(key)
        if prop_name:
            result[prop_name] = value
        else:
            # Fallback: use the key itself (for legacy property IDs that contain the name)
            result[key.lower()] = value

    return result

def _validate_page_structure(
    backend: Backend,
    parent_page: Dict[str, Any],
    required_children: List[str],
) -> Tuple[bool, Optional[str], List[str]]:
    """Validate that a parent page has all required child pages (case-insensitive, flexible matching).

    Returns:
        (success, error_message, found_children)
    """
    found_children = []
    missing_children = []

    for required in required_children:
        found = _find_child_by_title(backend, parent_page, required)
        if found:
            found_children.append(required)
        else:
            missing_children.append(required)

    if missing_children:
        return False, f"Missing required children: {', '.join(missing_children)}", found_children

    return True, None, found_children

def _validate_frontend_viewing_page(final_state_frontend: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
    """Validate that frontend is viewing a page."""
    current_page_id = final_state_frontend.get("currentPageId")
    if not current_page_id:
        return False, "Not viewing any page"
    return True, None

def _validate_pages_have_content(
    backend: Backend,
    page_ids: List[str],
    min_blocks: int = 1
) -> Tuple[bool, Optional[str]]:
    """Validate that pages have at least min_blocks content blocks."""
    for page_id in page_ids:
        blocks = _get_blocks_for_page(backend, page_id)
        if len(blocks) < min_blocks:
            # Query the page to get its title
            page_result = backend.query({"collection": "pages", "filter": {"_id": page_id}})
            page = page_result[0] if page_result else None
            page_title = page.get("title", "Unknown") if page else "Unknown"
            return False, f"Page '{page_title}' has fewer than {min_blocks} content blocks"
    return True, None

def _validate_pages_contain_text(
    backend: Backend,
    page_ids: List[str],
    required_text: Union[str, List[str]],
    case_sensitive: bool = False
) -> Tuple[bool, Optional[str]]:
    """Validate that pages contain required text in their body content.

    Args:
        backend: Backend to query
        page_ids: List of page IDs to validate
        required_text: Text or list of texts that must appear in page content.
                      If a list is provided, ALL texts must be present.
        case_sensitive: Whether text matching should be case-sensitive (default: False)

    Returns:
        Tuple of (success, error_message)
    """
    # Convert single string to list for uniform processing
    text_patterns = [required_text] if isinstance(required_text, str) else required_text

    for page_id in page_ids:
        blocks = _get_blocks_for_page(backend, page_id)
        page = _find_page_by_id(backend, page_id)
        page_title = page.get("title", "Unknown") if page else "Unknown"

        # Collect all content from blocks
        page_content = ""
        for block in blocks:
            content = block.get("content", "")
            if isinstance(content, str):
                page_content += content + " "

        # Check each required text pattern
        for pattern in text_patterns:
            search_content = page_content if case_sensitive else page_content.lower()
            search_pattern = pattern if case_sensitive else pattern.lower()

            if search_pattern not in search_content:
                return False, f"Page '{page_title}' does not contain required text: '{pattern}'"

    return True, None

def _validate_access_levels(
    page: Dict[str, Any],
    required_levels: List[str]
) -> Tuple[bool, Optional[str]]:
    """Validate that page has collaborators with required access levels."""
    sharing_metadata = page.get("sharingMetadata", {})
    collaborators = sharing_metadata.get("collaborators", [])
    
    found_levels = set()
    for collab in collaborators:
        if isinstance(collab, dict):
            level = collab.get("accessLevel")
            if level:
                found_levels.add(level)
    
    missing_levels = [level for level in required_levels if level not in found_levels]
    if missing_levels:
        return False, f"Missing access levels: {', '.join(missing_levels)}"
    
    return True, None

def _get_child_pages_from_backend(backend: Backend, parent_id: str) -> List[Dict[str, Any]]:
    """Get all child pages of a parent directly from backend using filter."""
    result = backend.query({
        "collection": "pages", 
        "filter": {"parentId": parent_id}
    })
    return result if isinstance(result, list) else []

def _get_pages(backend: Backend, title_pattern: str) -> List[Dict[str, Any]]:
    """Get pages from backend matching a title pattern (case-insensitive).
    
    Uses regex search at database level for efficiency instead of loading all pages.
    """
    # Escape special regex characters in the title pattern
    escaped_pattern = re.escape(title_pattern)
    
    # First try exact match with regex
    result = backend.query({
        "collection": "pages", 
        "filter": {"title": {"$regex": f"^{escaped_pattern}$", "$options": "i"}}
    })
    if isinstance(result, list) and result:
        return result
    
    # Then try contains match
    result = backend.query({
        "collection": "pages", 
        "filter": {"title": {"$regex": escaped_pattern, "$options": "i"}}
    })
    if isinstance(result, list) and result:
        return result
    
    # Fallback to fetching all pages (for partial reverse matches)
    all_pages = _get_pages_from_backend(backend)
    title_lower = title_pattern.lower()
    return [p for p in all_pages if p.get("title", "").lower() in title_lower]

def _get_blocks(backend: Backend, page_id: str) -> List[Dict[str, Any]]:
    """Get all blocks for a specific page from backend, sorted by order.
    
    Uses filter at database level for efficiency instead of loading all blocks.
    """
    result = backend.query({
        "collection": "blocks", 
        "filter": {"pageId": page_id}
    })
    blocks = result if isinstance(result, list) else []
    blocks.sort(key=lambda b: b.get("order", 0))
    return blocks

def _get_pages_by_filter(backend: Backend, filter: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
    """Get pages from backend with optional filter.
    Args:
        backend: Backend instance
        filter: Optional filter dict, e.g. {"title": "query string"}
    """
    result = backend.query({"collection": "pages", "filter": filter or {}})
    if isinstance(result, list):
        return result
    return []

def _get_blocks_by_filter(backend: Backend, filter: Optional[Dict[str, Any]] = None) -> List[Dict[str, Any]]:
    """Get blocks from backend with optional filter.
    Args:
        backend: Backend instance
        filter: Optional filter dict, e.g. {"title": "query string"}
    """
    result = backend.query({"collection": "blocks", "filter": filter or {}})
    if isinstance(result, list):
        return result
    return []

def _find_database_by_title(backend: Backend, title: str) -> Optional[Dict[str, Any]]:
    """Find a database page by title."""
    result = backend.query({
        "collection": "pages",
        "filter": {"title": title, "type": "database"}
    })
    if isinstance(result, list) and result:
        return result[0]
    return None

def _get_database_rows(backend: Backend, database_id: str) -> List[Dict[str, Any]]:
    """Get all rows for a database."""
    result = backend.query({
        "collection": "pages",
        "filter": {"databaseId": database_id, "type": "row"}
    })
    if isinstance(result, list):
        return result
    return []

# Reward Functions
# ==============================================================================

# Reward Functions
# ==============================================================================

# Reward Functions
# ==============================================================================

# =============================================================================
# Overdue Tasks Dashboard
# =============================================================================
def _validate_agent_overdue_tasks_dashboard(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate overdue tasks dashboard creation with database updates.
    
    Checks:
    1. Task Tracker rows with due date < 2026-01-15 and Status not 'Done' have Priority='Urgent' and 'Overdue' tag
    2. Dashboard page exists under 'Getting Started' with correct structure
    3. Correct counts and department breakdown
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Query Task Tracker database
    try:
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Query database rows for Task Tracker (rows are stored as pages with type='row')
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Check overdue tasks (due date < 2026-01-15 and status not Done)
        overdue_count = 0
        overdue_tasks = []
    
        for row in task_rows:
            cell_values = row.get("cellValues", {})
        
            # Extract due date - it's stored as an object with 'start' field
            due_date_obj = cell_values.get("db-tasks-prop-due-date", {})
            if isinstance(due_date_obj, dict):
                due_date = due_date_obj.get("start")
            else:
                due_date = None
            
            status = cell_values.get("db-tasks-prop-status")
        
            # Check if task is overdue
            if due_date and due_date < "2026-01-15" and status in ["Not Started", "In Progress"]:
                overdue_count += 1
                overdue_tasks.append(row)
            
                # Verify Priority is Urgent
                priority = cell_values.get("db-tasks-prop-priority")
                if priority != "Urgent":
                    errors.append(f"[X] Overdue task {row.get('_id')} does not have Priority='Urgent' (found: {priority})")
                else:
                    checks_passed.append(f"[C] Overdue task has Priority='Urgent'")
            
                # Verify 'Overdue' in tags
                tags = cell_values.get("db-tasks-prop-tags", [])
                if not isinstance(tags, list):
                    tags = []
                if "Overdue" not in tags:
                    errors.append(f"[X] Overdue task {row.get('_id')} does not have 'Overdue' in tags")
                else:
                    checks_passed.append(f"[C] Overdue task has 'Overdue' in tags")
    
        if overdue_count == 0:
            errors.append("[X] No overdue tasks found to validate updates")
        else:
            checks_passed.append("[C] overdue tasks found to validate updates")
    
        # Find Getting Started page
        getting_started_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Getting Started"}}
        })
    
        if not isinstance(getting_started_pages, list) or len(getting_started_pages) == 0:
            errors.append("[X] Getting Started page not found")
        else:
            checks_passed.append("[C] Getting Started page")
    
        getting_started_id = getting_started_pages[0].get("_id")
    
        # Find dashboard page as child of Getting Started
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": getting_started_id, "title": {"$regex": "Overdue Tasks Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Overdue Tasks Dashboard page not found under Getting Started")
        else:
            checks_passed.append("[C] Overdue Tasks Dashboard page found under Getting Started")
    
        dashboard_page_id = dashboard_pages[0].get("_id")
    
        # Get blocks for dashboard page
        dashboard_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page_id}
        })
    
        if not isinstance(dashboard_blocks, list):
            dashboard_blocks = []
    
        dashboard_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_summary_h2 = False
        found_red_callout = False
        found_details_h2 = False
        found_numbered_list = False
        found_department_h2 = False
        found_department_bullets = False
    
        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "overdue tasks dashboard" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "summary statistics" in content.lower():
                found_summary_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "red" in bg_color.lower() and str(overdue_count) in content:
                    found_red_callout = True
        
            if block_type == "heading2" and "task details" in content.lower():
                found_details_h2 = True
        
            if block_type == "numberedList" and any(tag in content for tag in ["Urgent", "High", "Medium", "Low"]):
                found_numbered_list = True
        
            if block_type == "heading2" and "department impact" in content.lower():
                found_department_h2 = True
        
            if block_type == "bulletedList" and any(dept in content for dept in ["Backend", "Frontend", "Design"]):
                found_department_bullets = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Overdue Tasks Dashboard'")
        else:
            checks_passed.append("[C] has heading_1 'Overdue Tasks Dashboard'")
        if not found_summary_h2:
            errors.append("[X] Missing heading_2 'Summary Statistics'")
        else:
            checks_passed.append("[C] has heading_2 'Summary Statistics'")
        if not found_red_callout:
            errors.append("[X] Missing red callout with overdue count")
        else:
            checks_passed.append("[C] has red callout with overdue count")
        if not found_details_h2:
            errors.append("[X] Missing heading_2 'Task Details'")
        else:
            checks_passed.append("[C] has heading_2 'Task Details'")
        if not found_numbered_list:
            errors.append("[X] Missing numbered list with task details")
        else:
            checks_passed.append("[C] has numbered list with task details")
        if not found_department_h2:
            errors.append("[X] Missing heading_2 'Department Impact'")
        else:
            checks_passed.append("[C] has heading_2 'Department Impact'")
        if not found_department_bullets:
            errors.append("[X] Missing bulleted list with department breakdown")
        else:
            checks_passed.append("[C] has bulleted list with department breakdown")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Performance Metrics Database
# =============================================================================
def _validate_agent_sprint_performance_metrics_database(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Sprint Performance Metrics database creation and population.
    
    Checks:
    1. Database exists under Project Roadmap with correct schema
    2. Sprint 1 and Sprint 2 rows exist with calculated values
    3. Velocity calculations are correct
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Roadmap page
    try:
        roadmap_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Roadmap"}}
        })
    
        if not isinstance(roadmap_pages, list) or len(roadmap_pages) == 0:
            errors.append("[X] Project Roadmap page not found")
        else:
            checks_passed.append("[C] Project Roadmap page")
    
        roadmap_id = roadmap_pages[0].get("_id")
    
        # Find Sprint Performance Metrics database as child
        metrics_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": roadmap_id, "title": {"$regex": "Sprint Performance Metrics"}}
        })
    
        if not isinstance(metrics_db_pages, list) or len(metrics_db_pages) == 0:
            errors.append("[X] Sprint Performance Metrics database not found under Project Roadmap")
        else:
            checks_passed.append("[C] Sprint Performance Metrics database found under Project Roadmap")
    
        metrics_db_id = metrics_db_pages[0].get("_id")
        metrics_db_page = metrics_db_pages[0]
    
        # Verify database properties schema
        db_metadata = metrics_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        required_props = ["Sprint Name", "Start Date", "End Date", "Team", "Sprint Goal", 
                          "Tasks Planned", "Tasks Completed", "Velocity", "Blockers"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query Task Tracker for validation counts
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found for validation")
        else:
            checks_passed.append("[C] Task Tracker database found for validation")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Query database rows (rows are stored as pages with type='row')
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Count Sprint 1 tasks (Status=Done, created between Dec 28 - Jan 10)
        sprint1_count = 0
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            status = cell_values.get("db-tasks-prop-status")
            created_at = row.get("createdAt", "")
        
            if status == "Done" and "2025-12-28" <= created_at <= "2026-01-10":
                sprint1_count += 1
    
        # Count Sprint 2 tasks (Status=In Progress or In Review, Jan 11-24)
        sprint2_count = 0
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            status = cell_values.get("db-tasks-prop-status")
            created_at = row.get("createdAt", "")
        
            if status in ["In Progress", "In Review"] and "2026-01-11" <= created_at <= "2026-01-24":
                sprint2_count += 1
    
        # Query Sprint Performance Metrics rows (rows are stored as pages with type='row')
        metrics_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": metrics_db_id, "type": "row"}
        })
    
        if not isinstance(metrics_rows, list):
            metrics_rows = []
    
        # Find Sprint 1 and Sprint 2 rows
        sprint1_row = None
        sprint2_row = None
    
        for row in metrics_rows:
            # Sprint Name is the title property for this database
            sprint_name = row.get("title", "")
        
            if "sprint 1" in sprint_name.lower():
                sprint1_row = row
            elif "sprint 2" in sprint_name.lower():
                sprint2_row = row
    
        if not sprint1_row:
            errors.append("[X] Sprint 1 row not found in Sprint Performance Metrics database")
        else:
            checks_passed.append("[C] Sprint 1 row found in Sprint Performance Metrics database")
    
        if not sprint2_row:
            errors.append("[X] Sprint 2 row not found in Sprint Performance Metrics database")
        else:
            checks_passed.append("[C] Sprint 2 row found in Sprint Performance Metrics database")
    
        # Validate Sprint 1 velocity calculation
        sprint1_cells = sprint1_row.get("cellValues", {})
        sprint1_completed = sprint1_cells.get("tasksCompleted", 0)
        sprint1_velocity = sprint1_cells.get("velocity", 0)
        expected_velocity1 = (sprint1_completed / 14) * 10
    
        if abs(sprint1_velocity - expected_velocity1) > 0.1:
            errors.append(f"[X] Sprint 1 velocity incorrect. Expected: {expected_velocity1:.2f}, Found: {sprint1_velocity}")
        else:
            checks_passed.append(f"[C] Sprint 1 velocity correct. Expected, Found")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Inventory Reorder Report
# =============================================================================
def _validate_agent_inventory_reorder_report(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Inventory Reorder Report creation.
    
    Checks:
    1. Inventory Tracker queried for out-of-stock and low-stock items
    2. Report page created under Engineering Wiki
    3. Correct structure with callouts, lists, and budget calculation
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Inventory Tracker database
    try:
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_id = inventory_pages[0].get("_id")
    
        # Query inventory rows (rows are stored as pages with type='row')
        inventory_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_id, "type": "row"}
        })
    
        if not isinstance(inventory_rows, list):
            inventory_rows = []
    
        # Categorize items
        out_of_stock_items = []
        low_stock_items = []
    
        for row in inventory_rows:
            cell_values = row.get("cellValues", {})
            # Property IDs would be like db-inventory-prop-instock, db-inventory-prop-quantity
            # We'll need to check what exists in cellValues
            in_stock = cell_values.get("inStock", True)
            quantity = cell_values.get("quantity", 0)
        
            # Try alternate keys if the above don't work
            if "inStock" not in cell_values:
                for key, val in cell_values.items():
                    if "stock" in key.lower() and isinstance(val, bool):
                        in_stock = val
                        break
        
            if "quantity" not in cell_values:
                for key, val in cell_values.items():
                    if "quantity" in key.lower() and isinstance(val, (int, float)):
                        quantity = val
                        break
        
            if not in_stock:
                out_of_stock_items.append(row)
            elif quantity < 5:
                low_stock_items.append(row)
    
        total_items_needing_reorder = len(out_of_stock_items) + len(low_stock_items)
    
        if total_items_needing_reorder == 0:
            errors.append("[X] No items found that need reordering")
        else:
            checks_passed.append("[C] items found that need reordering")
    
        # Find Engineering Wiki page
        wiki_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Engineering Wiki"}}
        })
    
        if not isinstance(wiki_pages, list) or len(wiki_pages) == 0:
            errors.append("[X] Engineering Wiki page not found")
        else:
            checks_passed.append("[C] Engineering Wiki page")
    
        wiki_id = wiki_pages[0].get("_id")
    
        # Find report page
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": wiki_id, "title": {"$regex": "Inventory Reorder Report"}}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] Inventory Reorder Report page not found under Engineering Wiki")
        else:
            checks_passed.append("[C] Inventory Reorder Report page found under Engineering Wiki")
    
        report_page_id = report_pages[0].get("_id")
    
        # Get blocks for report page
        report_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page_id}
        })
    
        if not isinstance(report_blocks, list):
            report_blocks = []
    
        report_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_yellow_callout = False
        found_out_of_stock_h2 = False
        found_numbered_list = False
        found_low_stock_h2 = False
        found_bulleted_list = False
        found_budget_h2 = False
        found_budget_paragraph = False
    
        for block in report_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "critical inventory levels" in content.lower():
                found_h1 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "yellow" in bg_color.lower() and str(total_items_needing_reorder) in content:
                    found_yellow_callout = True
        
            if block_type == "heading2" and "out of stock" in content.lower():
                found_out_of_stock_h2 = True
        
            if block_type == "numberedList" and ("sku" in content.lower() or "(" in content):
                found_numbered_list = True
        
            if block_type == "heading2" and "low stock" in content.lower():
                found_low_stock_h2 = True
        
            if block_type == "bulletedList" and ("units" in content.lower() or "remaining" in content.lower()):
                found_bulleted_list = True
        
            if block_type == "heading2" and "budget estimate" in content.lower():
                found_budget_h2 = True
        
            if block_type == "paragraph" and ("reorder" in content.lower() or "$" in content):
                found_budget_paragraph = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Critical Inventory Levels'")
        else:
            checks_passed.append("[C] has heading_1 'Critical Inventory Levels'")
        if not found_yellow_callout:
            errors.append("[X] Missing yellow callout with item count")
        else:
            checks_passed.append("[C] has yellow callout with item count")
        if not found_out_of_stock_h2:
            errors.append("[X] Missing heading_2 'Out of Stock Items'")
        else:
            checks_passed.append("[C] has heading_2 'Out of Stock Items'")
        if not found_numbered_list:
            errors.append("[X] Missing numbered list with out-of-stock items")
        else:
            checks_passed.append("[C] has numbered list with out-of-stock items")
        if not found_low_stock_h2:
            errors.append("[X] Missing heading_2 'Low Stock Items'")
        else:
            checks_passed.append("[C] has heading_2 'Low Stock Items'")
        if not found_bulleted_list:
            errors.append("[X] Missing bulleted list with low-stock items")
        else:
            checks_passed.append("[C] has bulleted list with low-stock items")
        if not found_budget_h2:
            errors.append("[X] Missing heading_2 'Reorder Budget Estimate'")
        else:
            checks_passed.append("[C] has heading_2 'Reorder Budget Estimate'")
        if not found_budget_paragraph:
            errors.append("[X] Missing paragraph with budget calculation")
        else:
            checks_passed.append("[C] has paragraph with budget calculation")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Weekly Standup Sprint Metrics
# =============================================================================
def _validate_agent_weekly_standup_sprint_metrics(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Sprint Metrics Integration section added to Weekly Standup Template.
    
    Checks:
    1. Section appears after Blockers heading
    2. Blue callout with auto-sync text
    3. Task counts for user-1
    4. High/Urgent priority todos
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Weekly Standup Template page
    try:
        standup_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Weekly Standup Template"}}
        })
    
        if not isinstance(standup_pages, list) or len(standup_pages) == 0:
            errors.append("[X] Weekly Standup Template page not found")
        else:
            checks_passed.append("[C] Weekly Standup Template page")
    
        standup_page_id = standup_pages[0].get("_id")
    
        # Get blocks for standup page
        standup_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": standup_page_id}
        })
    
        if not isinstance(standup_blocks, list):
            standup_blocks = []
    
        standup_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find Blockers heading index
        blockers_idx = -1
        for idx, block in enumerate(standup_blocks):
            if block.get("type") in ["heading2", "heading3"] and "blockers" in block.get("content", "").lower():
                blockers_idx = idx
                break
    
        if blockers_idx == -1:
            errors.append("[X] Blockers heading not found in Weekly Standup Template")
        else:
            checks_passed.append("[C] Blockers heading found in Weekly Standup Template")
    
        # Verify new content after Blockers
        found_sprint_metrics_h2 = False
        found_blue_callout = False
        found_task_paragraph = False
        found_divider = False
        found_priorities_h3 = False
        found_todo_items = False
    
        for idx, block in enumerate(standup_blocks):
            if idx <= blockers_idx:
                continue
        
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading2" and "sprint metrics integration" in content.lower():
                found_sprint_metrics_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "blue" in bg_color.lower() and "auto-synced" in content.lower():
                    found_blue_callout = True
        
            if block_type == "paragraph" and "my active tasks" in content.lower():
                found_task_paragraph = True
        
            if block_type == "divider":
                found_divider = True
        
            if block_type == "heading3" and "priorities" in content.lower():
                found_priorities_h3 = True
        
            if block_type == "todo":
                checked = properties.get("checked", True)
                if not checked:
                    found_todo_items = True
    
        if not found_sprint_metrics_h2:
            errors.append("[X] Missing heading_2 'Sprint Metrics Integration' after Blockers")
        else:
            checks_passed.append("[C] has heading_2 'Sprint Metrics Integration' after Blockers")
        if not found_blue_callout:
            errors.append("[X] Missing blue callout with auto-sync text")
        else:
            checks_passed.append("[C] has blue callout with auto-sync text")
        if not found_task_paragraph:
            errors.append("[X] Missing paragraph with task counts")
        else:
            checks_passed.append("[C] has paragraph with task counts")
        if not found_divider:
            errors.append("[X] Missing divider block")
        else:
            checks_passed.append("[C] has divider block")
        if not found_priorities_h3:
            errors.append("[X] Missing heading_3 'This Week's Priorities'")
        else:
            checks_passed.append("[C] has heading_3 'This Week's Priorities'")
        if not found_todo_items:
            errors.append("[X] Missing unchecked todo items")
        else:
            checks_passed.append("[C] has unchecked todo items")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Financial Overview Dashboard
# =============================================================================
def _validate_agent_financial_overview_dashboard(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Financial Overview Dashboard inline database creation.
    
    Checks:
    1. Inline database in Project Roadmap after quote block
    2. Correct schema with gallery layout
    3. Department rows with aggregated data
    4. Budget status calculations
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Roadmap page
    try:
        roadmap_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Roadmap"}}
        })
    
        if not isinstance(roadmap_pages, list) or len(roadmap_pages) == 0:
            errors.append("[X] Project Roadmap page not found")
        else:
            checks_passed.append("[C] Project Roadmap page")
    
        roadmap_id = roadmap_pages[0].get("_id")
    
        # Get blocks to find quote block
        roadmap_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": roadmap_id}
        })
    
        if not isinstance(roadmap_blocks, list):
            roadmap_blocks = []
    
        roadmap_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find quote block and database block
        quote_idx = -1
        database_block = None
    
        for idx, block in enumerate(roadmap_blocks):
            if block.get("type") == "quote":
                quote_idx = idx
        
            if block.get("type") == "database" and idx > quote_idx:
                database_block = block
                break
    
        if quote_idx == -1:
            errors.append("[X] Quote block not found in Project Roadmap")
        else:
            checks_passed.append("[C] Quote block found in Project Roadmap")
    
        if not database_block:
            errors.append("[X] Database block not found after quote block in Project Roadmap")
        else:
            checks_passed.append("[C] Database block found after quote block in Project Roadmap")
    
        # Get the database ID from the block properties
        database_id = database_block.get("properties", {}).get("databaseId")
        if not database_id:
            errors.append("[X] Database block does not have a databaseId")
        else:
            checks_passed.append("[C] Database block has a databaseId")
    
        # Find the actual database page using the database_id from the block
        financial_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": database_id}
        })
    
        if not isinstance(financial_db_pages, list) or len(financial_db_pages) == 0:
            errors.append(f"[X] Financial Overview Dashboard database page not found with id: {database_id}")
        else:
            checks_passed.append(f"[C] Financial Overview Dashboard database page found with id")
    
        financial_db_id = financial_db_pages[0].get("_id")
        financial_db_page = financial_db_pages[0]
    
        # Verify it's the Financial Overview Dashboard
        db_title = financial_db_page.get("title", "")
        if "financial overview dashboard" not in db_title.lower():
            errors.append(f"[X] Inline database has wrong title: {db_title}")
        else:
            checks_passed.append(f"[C] Inline database has correct title")
    
        # Verify schema
        db_metadata = financial_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Department", "Q1 Budget", "Q1 Spent", "Projects Count", "Budget Status", "Top Project"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query database rows (rows are stored as pages with type='row')
        financial_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": financial_db_id, "type": "row"}
        })
    
        if not isinstance(financial_rows, list):
            financial_rows = []
    
        # Check for 4 department rows
        departments_found = set()
        required_departments = {"Engineering", "Marketing", "Sales", "HR"}
    
        for row in financial_rows:
            # Department is likely the title property for this database
            dept = row.get("title", "")
        
            if dept in required_departments:
                departments_found.add(dept)
    
        missing_depts = required_departments - departments_found
        if missing_depts:
            errors.append(f"[X] Missing department rows: {', '.join(missing_depts)}")
        else:
            checks_passed.append(f"[C] has department rows")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Client Engagement Report
# =============================================================================
def _validate_agent_client_engagement_report(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Client Engagement Report with cross-referenced data.
    
    Checks:
    1. Cross-reference between Contacts Directory and Project Portfolio
    2. Report page under Meeting Notes
    3. Correct structure with engaged/unengaged clients
    4. Todo items with client names
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Contacts Directory
    try:
        contacts_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Contacts Directory"}}
        })
    
        if not isinstance(contacts_pages, list) or len(contacts_pages) == 0:
            errors.append("[X] Contacts Directory database not found")
        else:
            checks_passed.append("[C] Contacts Directory database")
    
        contacts_id = contacts_pages[0].get("_id")
    
        # Find Meeting Notes page
        meeting_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Meeting Notes"}}
        })
    
        if not isinstance(meeting_pages, list) or len(meeting_pages) == 0:
            errors.append("[X] Meeting Notes page not found")
        else:
            checks_passed.append("[C] Meeting Notes page")
    
        meeting_id = meeting_pages[0].get("_id")
    
        # Find report page
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": meeting_id, "title": {"$regex": "Client Engagement Report"}}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] Client Engagement Report page not found under Meeting Notes")
        else:
            checks_passed.append("[C] Client Engagement Report page found under Meeting Notes")
    
        report_page_id = report_pages[0].get("_id")
    
        # Get blocks for report page
        report_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page_id}
        })
    
        if not isinstance(report_blocks, list):
            report_blocks = []
    
        report_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_summary_h2 = False
        found_green_callout = False
        found_engaged_h2 = False
        found_numbered_list = False
        found_unengaged_h2 = False
        found_bulleted_list = False
        found_opportunities_h2 = False
        found_todos = 0
    
        for block in report_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "client engagement report" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "executive summary" in content.lower():
                found_summary_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "green" in bg_color.lower() and "active client relationships" in content.lower():
                    found_green_callout = True
        
            if block_type == "heading2" and "engaged clients" in content.lower():
                found_engaged_h2 = True
        
            if block_type == "numberedList":
                found_numbered_list = True
        
            if block_type == "heading2" and "unengaged clients" in content.lower():
                found_unengaged_h2 = True
        
            if block_type == "bulletedList":
                found_bulleted_list = True
        
            if block_type == "heading2" and "engagement opportunities" in content.lower():
                found_opportunities_h2 = True
        
            if block_type == "todo":
                checked = properties.get("checked", True)
                if not checked:
                    found_todos += 1
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Client Engagement Report'")
        else:
            checks_passed.append("[C] has heading_1 'Client Engagement Report'")
        if not found_summary_h2:
            errors.append("[X] Missing heading_2 'Executive Summary'")
        else:
            checks_passed.append("[C] has heading_2 'Executive Summary'")
        if not found_green_callout:
            errors.append("[X] Missing green callout with client relationship summary")
        else:
            checks_passed.append("[C] has green callout with client relationship summary")
        if not found_engaged_h2:
            errors.append("[X] Missing heading_2 'Engaged Clients'")
        else:
            checks_passed.append("[C] has heading_2 'Engaged Clients'")
        if not found_numbered_list:
            errors.append("[X] Missing numbered list with engaged clients")
        else:
            checks_passed.append("[C] has numbered list with engaged clients")
        if not found_unengaged_h2:
            errors.append("[X] Missing heading_2 'Unengaged Clients'")
        else:
            checks_passed.append("[C] has heading_2 'Unengaged Clients'")
        if not found_bulleted_list:
            errors.append("[X] Missing bulleted list with unengaged clients")
        else:
            checks_passed.append("[C] has bulleted list with unengaged clients")
        if not found_opportunities_h2:
            errors.append("[X] Missing heading_2 'Engagement Opportunities'")
        else:
            checks_passed.append("[C] has heading_2 'Engagement Opportunities'")
        if found_todos < 3:
            errors.append(f"[X] Expected 3 todo items, found {found_todos}")
        else:
            checks_passed.append(f"[C] Found 3 todo items as expected")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Meeting Analytics Section
# =============================================================================
def _validate_agent_meeting_analytics_section(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Meeting Analytics section added to Meeting Notes.
    
    Checks:
    1. Section appears after last bullet, before quote
    2. Markdown table with task counts
    3. Gray callout with department breakdown
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Meeting Notes page
    try:
        meeting_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Meeting Notes"}}
        })
    
        if not isinstance(meeting_pages, list) or len(meeting_pages) == 0:
            errors.append("[X] Meeting Notes page not found")
        else:
            checks_passed.append("[C] Meeting Notes page")
    
        meeting_page_id = meeting_pages[0].get("_id")
    
        # Get blocks for meeting page
        meeting_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": meeting_page_id}
        })
    
        if not isinstance(meeting_blocks, list):
            meeting_blocks = []
    
        meeting_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find last bulleted list and quote block
        last_bullet_idx = -1
        quote_idx = -1
    
        for idx, block in enumerate(meeting_blocks):
            if block.get("type") == "bulletedList":
                last_bullet_idx = idx
            if block.get("type") == "quote":
                quote_idx = idx
    
        # Verify new content between last bullet and quote
        found_divider = False
        found_analytics_h3 = False
        found_paragraph = False
        found_code_block = False
        found_gray_callout = False
    
        for idx, block in enumerate(meeting_blocks):
            if idx <= last_bullet_idx or (quote_idx != -1 and idx >= quote_idx):
                continue
        
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "divider":
                found_divider = True
        
            if block_type == "heading3" and "meeting analytics" in content.lower():
                found_analytics_h3 = True
        
            if block_type == "paragraph" and "current sprint data" in content.lower():
                found_paragraph = True
        
            if block_type == "code":
                language = properties.get("language", "")
                if language == "markdown" and "department" in content.lower() and "active tasks" in content.lower():
                    found_code_block = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "gray" in bg_color.lower() and "workload" in content.lower():
                    found_gray_callout = True
    
        if not found_divider:
            errors.append("[X] Missing divider after last bullet")
        else:
            checks_passed.append("[C] has divider after last bullet")
        if not found_analytics_h3:
            errors.append("[X] Missing heading_3 'Meeting Analytics'")
        else:
            checks_passed.append("[C] has heading_3 'Meeting Analytics'")
        if not found_paragraph:
            errors.append("[X] Missing paragraph with sprint data text")
        else:
            checks_passed.append("[C] has paragraph with sprint data text")
        if not found_code_block:
            errors.append("[X] Missing code block with markdown table")
        else:
            checks_passed.append("[C] has code block with markdown table")
        if not found_gray_callout:
            errors.append("[X] Missing gray callout with workload information")
        else:
            checks_passed.append("[C] has gray callout with workload information")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Resource Allocation Tracker
# =============================================================================
def _validate_agent_resource_allocation_tracker(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Resource Allocation Tracker database creation with cross-referenced data.
    
    Checks:
    1. Database exists under Project Roadmap
    2. Correct schema with select options
    3. Resource entries with different types (Human, Equipment, Software)
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Roadmap page
    try:
        roadmap_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Roadmap"}}
        })
    
        if not isinstance(roadmap_pages, list) or len(roadmap_pages) == 0:
            errors.append("[X] Project Roadmap page not found")
        else:
            checks_passed.append("[C] Project Roadmap page")
    
        roadmap_id = roadmap_pages[0].get("_id")
    
        # Find Resource Allocation Tracker database
        tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": roadmap_id, "title": {"$regex": "Resource Allocation Tracker"}}
        })
    
        if not isinstance(tracker_pages, list) or len(tracker_pages) == 0:
            errors.append("[X] Resource Allocation Tracker database not found under Project Roadmap")
        else:
            checks_passed.append("[C] Resource Allocation Tracker database found under Project Roadmap")
    
        tracker_id = tracker_pages[0].get("_id")
        tracker_page = tracker_pages[0]
    
        # Verify schema and get Resource Type property ID
        db_metadata = tracker_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Resource Name", "Resource Type", "Resource ID", "Project", "Allocation %", 
                          "Start Date", "End Date", "Cost", "Status"]
    
        resource_type_prop_id = None
        for prop_data in db_properties:
            if isinstance(prop_data, dict):
                prop_name = prop_data.get("name", "")
                if prop_name.lower() == "resource type":
                    resource_type_prop_id = prop_data.get("_id")
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        if not resource_type_prop_id:
            errors.append("[X] Resource Type property ID not found")
        else:
            checks_passed.append("[C] Resource Type property ID")
    
        # Query resource rows (rows are stored as pages with type='row')
        resource_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": tracker_id, "type": "row"}
        })
    
        if not isinstance(resource_rows, list):
            resource_rows = []
    
        if len(resource_rows) == 0:
            errors.append("[X] No resource entries found in Resource Allocation Tracker")
        else:
            checks_passed.append("[C] resource entries found in Resource Allocation Tracker")
    
        # Count resource types using the property ID
        human_resources = 0
        equipment_resources = 0
        software_resources = 0
    
        for row in resource_rows:
            cell_values = row.get("cellValues", {})
            resource_type = cell_values.get(resource_type_prop_id, "")
        
            if resource_type == "Human":
                human_resources += 1
            elif resource_type == "Equipment":
                equipment_resources += 1
            elif resource_type == "Software":
                software_resources += 1
    
        if human_resources == 0:
            errors.append("[X] No Human resources found in tracker")
        else:
            checks_passed.append("[C] Human resources found in tracker")
        if equipment_resources == 0:
            errors.append("[X] No Equipment resources found in tracker")
        else:
            checks_passed.append("[C] Equipment resources found in tracker")
        if software_resources == 0:
            errors.append("[X] No Software resources found in tracker")
        else:
            checks_passed.append("[C] Software resources found in tracker")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Capacity Planning
# =============================================================================
def _validate_agent_sprint_capacity_planning(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Sprint Capacity Planning with schema updates and calculations.
    
    Checks:
    1. Task Tracker schema has Estimated Hours property and Critical priority option
    2. All task rows have calculated Estimated Hours values
    3. Summary page with correct calculations
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Task Tracker database
    try:
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
        task_tracker_page = task_tracker_pages[0]
    
        # Check schema for Estimated Hours property
        db_metadata = task_tracker_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        found_estimated_hours = False
        found_critical_priority = False
        estimated_hours_prop_id = None
    
        for prop_data in db_properties:
            if isinstance(prop_data, dict):
                prop_name = prop_data.get("name", "")
                if prop_name.lower() == "estimated hours":
                    found_estimated_hours = True
                    estimated_hours_prop_id = prop_data.get("_id")
            
                if prop_name.lower() == "priority":
                    options = prop_data.get("options", [])
                    if isinstance(options, list):
                        for option in options:
                            if isinstance(option, dict) and option.get("name") == "Critical":
                                found_critical_priority = True
    
        if not found_estimated_hours:
            errors.append("[X] Estimated Hours property not found in Task Tracker schema")
        else:
            checks_passed.append("[C] Estimated Hours property found in Task Tracker schema")
    
        if not found_critical_priority:
            errors.append("[X] Critical priority option not found in Task Tracker schema")
        else:
            checks_passed.append("[C] Critical priority option found in Task Tracker schema")
    
        # Query task rows to verify Estimated Hours values (rows are stored as pages with type='row')
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        rows_with_hours = 0
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            # Use the property ID to check for estimated hours
            estimated_hours = cell_values.get(estimated_hours_prop_id)
        
            if estimated_hours is not None and estimated_hours > 0:
                rows_with_hours += 1
    
        if rows_with_hours == 0:
            errors.append("[X] No task rows have Estimated Hours values set")
        else:
            checks_passed.append("[C] task rows have Estimated Hours values set")
    
        # Find Project Roadmap page
        roadmap_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Roadmap"}}
        })
    
        if not isinstance(roadmap_pages, list) or len(roadmap_pages) == 0:
            errors.append("[X] Project Roadmap page not found")
        else:
            checks_passed.append("[C] Project Roadmap page")
    
        roadmap_id = roadmap_pages[0].get("_id")
    
        # Find Sprint Capacity Planning page
        capacity_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": roadmap_id, "title": {"$regex": "Sprint Capacity Planning"}}
        })
    
        if not isinstance(capacity_pages, list) or len(capacity_pages) == 0:
            errors.append("[X] Sprint Capacity Planning page not found under Project Roadmap")
        else:
            checks_passed.append("[C] Sprint Capacity Planning page found under Project Roadmap")
    
        capacity_page_id = capacity_pages[0].get("_id")
    
        # Get blocks for capacity page
        capacity_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": capacity_page_id}
        })
    
        if not isinstance(capacity_blocks, list):
            capacity_blocks = []
    
        capacity_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_purple_callout = False
        found_workload_h2 = False
        found_priority_h2 = False
    
        for block in capacity_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "sprint capacity planning" in content.lower():
                found_h1 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "purple" in bg_color.lower() and "total estimated hours" in content.lower():
                    found_purple_callout = True
        
            if block_type == "heading2" and "workload by category" in content.lower():
                found_workload_h2 = True
        
            if block_type == "heading2" and "priority distribution" in content.lower():
                found_priority_h2 = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Sprint Capacity Planning'")
        else:
            checks_passed.append("[C] has heading_1 'Sprint Capacity Planning'")
        if not found_purple_callout:
            errors.append("[X] Missing purple callout with total hours")
        else:
            checks_passed.append("[C] has purple callout with total hours")
        if not found_workload_h2:
            errors.append("[X] Missing heading_2 'Workload by Category'")
        else:
            checks_passed.append("[C] has heading_2 'Workload by Category'")
        if not found_priority_h2:
            errors.append("[X] Missing heading_2 'Priority Distribution'")
        else:
            checks_passed.append("[C] has heading_2 'Priority Distribution'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Quarterly Business Review
# =============================================================================
def _validate_agent_quarterly_business_review(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Quarterly Business Review with multi-database aggregation.
    
    Checks:
    1. Page exists under Getting Started
    2. All 4 numbered sections with correct data
    3. Different block types for each section (callout, code, quote)
    4. Todo items at end
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Getting Started page
    try:
        getting_started_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Getting Started"}}
        })
    
        if not isinstance(getting_started_pages, list) or len(getting_started_pages) == 0:
            errors.append("[X] Getting Started page not found")
        else:
            checks_passed.append("[C] Getting Started page")
    
        getting_started_id = getting_started_pages[0].get("_id")
    
        # Find Quarterly Business Review page
        review_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": getting_started_id, "title": {"$regex": "Business Review"}}
        })
    
        if not isinstance(review_pages, list) or len(review_pages) == 0:
            errors.append("[X] Quarterly Business Review page not found under Getting Started")
        else:
            checks_passed.append("[C] Quarterly Business Review page found under Getting Started")
    
        review_page_id = review_pages[0].get("_id")
    
        # Get blocks for review page
        review_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": review_page_id}
        })
    
        if not isinstance(review_blocks, list):
            review_blocks = []
    
        review_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_date_paragraph = False
        found_project_h2 = False
        found_blue_callout = False
        found_task_h2 = False
        found_code_block = False
        found_client_h2 = False
        found_quote_block = False
        found_inventory_h2 = False
        found_yellow_callout = False
        found_actions_h2 = False
        found_todos = 0
    
        for block in review_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "business review" in content.lower():
                found_h1 = True
        
            if block_type == "paragraph" and "generated on" in content.lower():
                found_date_paragraph = True
        
            if block_type == "heading2" and "project performance" in content.lower():
                found_project_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "blue" in bg_color.lower() and "active projects" in content.lower():
                    found_blue_callout = True
                elif "yellow" in bg_color.lower() and "stock value" in content.lower():
                    found_yellow_callout = True
        
            if block_type == "heading2" and "task execution" in content.lower():
                found_task_h2 = True
        
            if block_type == "code" and "status distribution" in content.lower():
                found_code_block = True
        
            if block_type == "heading2" and "client relationships" in content.lower():
                found_client_h2 = True
        
            if block_type == "quote" and ("clients" in content.lower() or "total" in content.lower()):
                found_quote_block = True
        
            if block_type == "heading2" and "inventory status" in content.lower():
                found_inventory_h2 = True
        
            if block_type == "heading2" and "key actions" in content.lower():
                found_actions_h2 = True
        
            if block_type == "todo":
                checked = properties.get("checked", True)
                if not checked:
                    found_todos += 1
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Q1 2026 Business Review'")
        else:
            checks_passed.append("[C] has heading_1 'Q1 2026 Business Review'")
        if not found_date_paragraph:
            errors.append("[X] Missing paragraph with generated date")
        else:
            checks_passed.append("[C] has paragraph with generated date")
        if not found_project_h2:
            errors.append("[X] Missing heading_2 '1️⃣ Project Performance'")
        else:
            checks_passed.append("[C] has heading_2 '1️⃣ Project Performance'")
        if not found_blue_callout:
            errors.append("[X] Missing blue callout for Project Performance")
        else:
            checks_passed.append("[C] has blue callout for Project Performance")
        if not found_task_h2:
            errors.append("[X] Missing heading_2 '2️⃣ Task Execution'")
        else:
            checks_passed.append("[C] has heading_2 '2️⃣ Task Execution'")
        if not found_code_block:
            errors.append("[X] Missing code block for Task Execution")
        else:
            checks_passed.append("[C] has code block for Task Execution")
        if not found_client_h2:
            errors.append("[X] Missing heading_2 '3️⃣ Client Relationships'")
        else:
            checks_passed.append("[C] has heading_2 '3️⃣ Client Relationships'")
        if not found_quote_block:
            errors.append("[X] Missing quote block for Client Relationships")
        else:
            checks_passed.append("[C] has quote block for Client Relationships")
        if not found_inventory_h2:
            errors.append("[X] Missing heading_2 '4️⃣ Inventory Status'")
        else:
            checks_passed.append("[C] has heading_2 '4️⃣ Inventory Status'")
        if not found_yellow_callout:
            errors.append("[X] Missing yellow callout for Inventory Status")
        else:
            checks_passed.append("[C] has yellow callout for Inventory Status")
        if not found_actions_h2:
            errors.append("[X] Missing heading_2 'Key Actions'")
        else:
            checks_passed.append("[C] has heading_2 'Key Actions'")
        if found_todos < 3:
            errors.append(f"[X] Expected 3 todo items, found {found_todos}")
        else:
            checks_passed.append(f"[C] Found 3 todo items as expected")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 1: Department Budget Allocations
# =============================================================================
def _validate_agent_department_budget_allocations(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Department Budget Allocations inline database creation.
    
    Checks:
    1. Calculate inventory value by category from Inventory Tracker
    2. Inline database exists in Project Portfolio page
    3. Correct schema with Department, Category, budgets, and Alert Status
    4. Rows for departments with correct calculations
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Query Inventory Tracker database
    try:
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_id = inventory_pages[0].get("_id")
    
        # Query inventory rows
        inventory_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_id, "type": "row"}
        })
    
        if not isinstance(inventory_rows, list):
            inventory_rows = []
    
        # Calculate total value by category
        category_values = {}
        for row in inventory_rows:
            cell_values = row.get("cellValues", {})
            category = None
            quantity = 0
            unit_price = 0
        
            # Find category, quantity, and unit price in cell values
            for key, val in cell_values.items():
                if "category" in key.lower():
                    category = val
                elif "quantity" in key.lower() and isinstance(val, (int, float)):
                    quantity = val
                elif "price" in key.lower() and isinstance(val, (int, float)):
                    unit_price = val
        
            if category:
                if category not in category_values:
                    category_values[category] = 0
                category_values[category] += quantity * unit_price
    
        # Find Project Portfolio page
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio page not found")
        else:
            checks_passed.append("[C] Project Portfolio page")
    
        portfolio_page_id = portfolio_pages[0].get("_id")
    
        # Get blocks to find inline database
        portfolio_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": portfolio_page_id}
        })
    
        if not isinstance(portfolio_blocks, list):
            portfolio_blocks = []
    
        portfolio_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find the Department Budget Allocations database block
        budget_db_block = None
        for block in portfolio_blocks:
            if block.get("type") == "database":
                db_id = block.get("properties", {}).get("databaseId")
                if db_id:
                    # Check if this is the budget allocations database
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "budget allocation" in db_title.lower():
                            budget_db_block = block
                            break
    
        if not budget_db_block:
            errors.append("[X] Department Budget Allocations inline database not found in Project Portfolio page")
        else:
            checks_passed.append("[C] Department Budget Allocations inline database found in Project Portfolio page")
    
        budget_db_id = budget_db_block.get("properties", {}).get("databaseId")
        budget_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": budget_db_id}
        })
    
        if not isinstance(budget_db_pages, list) or len(budget_db_pages) == 0:
            errors.append("[X] Budget database page not found")
        else:
            checks_passed.append("[C] Budget database page")
    
        budget_db_page = budget_db_pages[0]
    
        # Verify schema
        db_metadata = budget_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Department", "Category", "Allocated Budget", "Current Spend", "Remaining Budget", "Alert Status"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query database rows
        budget_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": budget_db_id, "type": "row"}
        })
    
        if not isinstance(budget_rows, list) or len(budget_rows) == 0:
            errors.append("[X] No department rows found in Budget Allocations database")
        else:
            checks_passed.append("[C] department rows found in Budget Allocations database")
    
        # Verify calculations
        for row in budget_rows:
            cell_values = row.get("cellValues", {})
            allocated = None
            current_spend = None
            remaining = None
            alert_status = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "allocated" in key_lower and "budget" in key_lower:
                    allocated = val
                elif "current" in key_lower and "spend" in key_lower:
                    current_spend = val
                elif "remaining" in key_lower:
                    remaining = val
                elif "alert" in key_lower or "status" in key_lower:
                    alert_status = val
        
            if allocated is not None and current_spend is not None:
                expected_remaining = allocated - current_spend
                if remaining is not None and abs(remaining - expected_remaining) > 0.01:
                    errors.append(f"[X] Remaining Budget calculation incorrect for row {row.get('_id')}")
                else:
                    checks_passed.append(f"[C] Remaining Budget calculation correct for row")
            
                # Check Alert Status
                if allocated > 0:
                    remaining_pct = (expected_remaining / allocated) * 100
                    if remaining_pct > 20 and alert_status != "Under Budget":
                        errors.append(f"[X] Alert Status should be 'Under Budget' for row with {remaining_pct:.1f}% remaining")
                    elif 5 <= remaining_pct <= 20 and alert_status != "Near Limit":
                        errors.append(f"[X] Alert Status should be 'Near Limit' for row with {remaining_pct:.1f}% remaining")
                    elif remaining_pct < 5 and alert_status != "Over Budget":
                        errors.append(f"[X] Alert Status should be 'Over Budget' for row with {remaining_pct:.1f}% remaining")
                    else:
                        checks_passed.append(f"[C] Alert Status should be 'Over Budget' for row with % remaining")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 2: Project Risk Dashboard
# =============================================================================
def _validate_agent_project_risk_dashboard(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Project Risk Dashboard creation with Task Tracker integration.
    
    Checks:
    1. Tasks created in Task Tracker for Active projects ending within 30 days
    2. Project Portfolio has Risk Level property with correct options
    3. Risk Level set correctly for all projects
    4. Dashboard page exists under Project Roadmap with correct structure
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Portfolio database
    try:
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
        portfolio_page = portfolio_pages[0]
    
        # Check Risk Level property exists
        db_metadata = portfolio_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        risk_level_prop = None
        for prop_data in db_properties:
            if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == "risk level":
                risk_level_prop = prop_data
                break
    
        if not risk_level_prop:
            errors.append("[X] Risk Level property not found in Project Portfolio")
        else:
            checks_passed.append("[C] Risk Level property found in Project Portfolio")
    
        # Verify Risk Level options
        options = risk_level_prop.get("options", [])
        required_options = {"Low", "Medium", "High", "Critical"}
        found_options = set()
        for opt in options:
            if isinstance(opt, dict):
                found_options.add(opt.get("name", ""))
    
        if required_options != found_options:
            errors.append(f"[X] Risk Level options incorrect. Expected {required_options}, found {found_options}")
        else:
            checks_passed.append(f"[C] Risk Level options correct")
    
        # Create property ID to name mapping
        prop_id_to_name = {}
        for prop_data in db_properties:
            if isinstance(prop_data, dict):
                prop_id = prop_data.get("_id")
                prop_name = prop_data.get("name", "")
                if prop_id and prop_name:
                    prop_id_to_name[prop_id] = prop_name.lower()
    
        # Query project rows
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        # Find active projects ending within 30 days
        at_risk_projects = []
        critical_projects = []
    
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
            timeline_end = None
            risk_level = None
        
            # Use property ID to name mapping
            for prop_id, val in cell_values.items():
                prop_name = prop_id_to_name.get(prop_id, "").lower()
                if "status" in prop_name and "risk" not in prop_name:
                    status = val
                elif "timeline" in prop_name and isinstance(val, dict):
                    timeline_end = val.get("end")
                elif "risk" in prop_name and "level" in prop_name:
                    risk_level = val
        
            if status == "Active" and timeline_end:
                # Check if within 30 days
                if timeline_end < "2026-02-14":  # 30 days from 2026-01-15
                    at_risk_projects.append(row)
                    if timeline_end < "2026-01-29":  # 14 days
                        critical_projects.append(row)
        
            # Verify Risk Level is set
            if risk_level is None:
                errors.append(f"[X] Risk Level not set for project {row.get('_id')}")
            else:
                checks_passed.append(f"[C] Risk Level set for project")
    
        # Find Task Tracker database
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Check for Final Review tasks
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        final_review_tasks = [t for t in task_rows if "final review" in t.get("title", "").lower()]
    
        if len(final_review_tasks) < len(at_risk_projects):
            errors.append(f"[X] Expected {len(at_risk_projects)} Final Review tasks, found {len(final_review_tasks)}")
        else:
            checks_passed.append(f"[C] Expected Final Review tasks")
    
        # Find Project Roadmap page
        roadmap_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Roadmap"}}
        })
    
        if not isinstance(roadmap_pages, list) or len(roadmap_pages) == 0:
            errors.append("[X] Project Roadmap page not found")
        else:
            checks_passed.append("[C] Project Roadmap page")
    
        roadmap_id = roadmap_pages[0].get("_id")
    
        # Find dashboard page
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": roadmap_id, "title": {"$regex": "Project Risk Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Project Risk Dashboard page not found under Project Roadmap")
        else:
            checks_passed.append("[C] Project Risk Dashboard page found under Project Roadmap")
    
        dashboard_page_id = dashboard_pages[0].get("_id")
    
        # Get blocks for dashboard
        dashboard_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page_id}
        })
    
        if not isinstance(dashboard_blocks, list):
            dashboard_blocks = []
    
        dashboard_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_critical_h2 = False
        found_orange_callout = False
        found_numbered_list = False
    
        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "risk assessment" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "critical projects" in content.lower():
                found_critical_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "orange" in bg_color.lower() and "at risk" in content.lower():
                    found_orange_callout = True
        
            # Accept any numbered list after Critical Projects heading (even if empty/placeholder)
            if block_type == "numberedList":
                found_numbered_list = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Project Risk Assessment'")
        else:
            checks_passed.append("[C] has heading_1 'Project Risk Assessment'")
        if not found_critical_h2:
            errors.append("[X] Missing heading_2 'Critical Projects'")
        else:
            checks_passed.append("[C] has heading_2 'Critical Projects'")
        if not found_orange_callout:
            errors.append("[X] Missing orange callout with risk count")
        else:
            checks_passed.append("[C] has orange callout with risk count")
        # Only require numbered list if there are critical projects
        if len(critical_projects) > 0 and not found_numbered_list:
            errors.append("[X] Missing numbered list of critical projects")
        else:
            checks_passed.append("[C] has numbered list of critical projects")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 3: Engineering Resource Allocation
# =============================================================================
def _validate_agent_engineering_resource_allocation(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Engineering Resource Allocation report creation.
    
    Checks:
    1. Count Frontend/Backend tasks from Task Tracker
    2. Find Active Engineering projects
    3. Report page exists under Getting Started
    4. Correct structure with task counts and project lists
    5. Todo added to Getting Started page
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Task Tracker
    try:
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Query tasks
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Count Frontend/Backend tasks
        frontend_count = 0
        backend_count = 0
    
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            tags = None
        
            for key, val in cell_values.items():
                if "tag" in key.lower() and isinstance(val, list):
                    tags = val
                    break
        
            if tags:
                if "Frontend" in tags:
                    frontend_count += 1
                if "Backend" in tags:
                    backend_count += 1
    
        # Find Project Portfolio
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        # Query active engineering projects
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        active_eng_projects = []
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
            department = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "status" in key_lower:
                    status = val
                elif "department" in key_lower:
                    department = val
        
            if status == "Active" and department == "Engineering":
                active_eng_projects.append(row)
    
        # Find Getting Started page
        getting_started_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Getting Started"}}
        })
    
        if not isinstance(getting_started_pages, list) or len(getting_started_pages) == 0:
            errors.append("[X] Getting Started page not found")
        else:
            checks_passed.append("[C] Getting Started page")
    
        getting_started_id = getting_started_pages[0].get("_id")
    
        # Find report page
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": getting_started_id, "title": {"$regex": "Engineering Resource Allocation"}}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] Engineering Resource Allocation page not found under Getting Started")
        else:
            checks_passed.append("[C] Engineering Resource Allocation page found under Getting Started")
    
        report_page_id = report_pages[0].get("_id")
    
        # Get blocks for report
        report_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page_id}
        })
    
        if not isinstance(report_blocks, list):
            report_blocks = []
    
        report_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_task_dist_h2 = False
        found_frontend_backend_list = False
        found_projects_h2 = False
        found_blue_callout = False
    
        for block in report_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "resource allocation" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "task distribution" in content.lower():
                found_task_dist_h2 = True
        
            if block_type == "bulletedList" and ("frontend" in content.lower() or "backend" in content.lower()):
                found_frontend_backend_list = True
        
            if block_type == "heading2" and "active engineering projects" in content.lower():
                found_projects_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "blue" in bg_color.lower() and "workload" in content.lower():
                    found_blue_callout = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Engineering Resource Allocation Report'")
        else:
            checks_passed.append("[C] has heading_1 'Engineering Resource Allocation Report'")
        if not found_task_dist_h2:
            errors.append("[X] Missing heading_2 'Task Distribution by Technology'")
        else:
            checks_passed.append("[C] has heading_2 'Task Distribution by Technology'")
        if not found_frontend_backend_list:
            errors.append("[X] Missing bulleted list with Frontend/Backend task counts")
        else:
            checks_passed.append("[C] has bulleted list with Frontend/Backend task counts")
        if not found_projects_h2:
            errors.append("[X] Missing heading_2 'Active Engineering Projects'")
        else:
            checks_passed.append("[C] has heading_2 'Active Engineering Projects'")
        if not found_blue_callout:
            errors.append("[X] Missing blue callout with workload summary")
        else:
            checks_passed.append("[C] has blue callout with workload summary")
    
        # Check for todo on Getting Started page
        getting_started_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": getting_started_id}
        })
    
        if not isinstance(getting_started_blocks, list):
            getting_started_blocks = []
    
        found_todo = False
        for block in getting_started_blocks:
            if block.get("type") == "todo":
                content = block.get("content", "")
                if "engineering resource allocation" in content.lower():
                    found_todo = True
                    break
    
        if not found_todo:
            errors.append("[X] Missing todo item on Getting Started page for resource allocation review")
        else:
            checks_passed.append("[C] has todo item on Getting Started page for resource allocation review")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 4: Cross-Database Analytics Report
# =============================================================================
def _validate_agent_cross_database_analytics_report(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Cross-Database Analytics Report creation.
    
    Checks:
    1. Task counts grouped by Status
    2. Task counts by department for Active projects
    3. Report page with correct structure under Getting Started
    4. Key Insights section with 3 numbered items
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Task Tracker
    try:
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Query tasks
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Count by status
        status_counts = {}
        total_tasks = len(task_rows)
        done_count = 0
    
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            status = None
        
            for key, val in cell_values.items():
                if "status" in key.lower() and isinstance(val, str):
                    status = val
                    break
        
            if status:
                status_counts[status] = status_counts.get(status, 0) + 1
                if status == "Done":
                    done_count += 1
    
        completion_rate = (done_count / total_tasks * 100) if total_tasks > 0 else 0
    
        # Find Getting Started page
        getting_started_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Getting Started"}}
        })
    
        if not isinstance(getting_started_pages, list) or len(getting_started_pages) == 0:
            errors.append("[X] Getting Started page not found")
        else:
            checks_passed.append("[C] Getting Started page")
    
        getting_started_id = getting_started_pages[0].get("_id")
    
        # Find report page
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": getting_started_id, "title": {"$regex": "Cross-Database Analytics"}}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] Cross-Database Analytics Report page not found under Getting Started")
        else:
            checks_passed.append("[C] Cross-Database Analytics Report page found under Getting Started")
    
        report_page_id = report_pages[0].get("_id")
    
        # Get blocks for report
        report_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page_id}
        })
    
        if not isinstance(report_blocks, list):
            report_blocks = []
    
        report_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_task_overview_h2 = False
        found_yellow_callout = False
        found_status_list = False
        found_active_projects_h2 = False
        found_insights_h2 = False
        insights_count = 0
    
        for block in report_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "analytics report" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "task status overview" in content.lower():
                found_task_overview_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "yellow" in bg_color.lower() and "completion rate" in content.lower():
                    found_yellow_callout = True
        
            if block_type == "bulletedList" and any(status in content for status in ["Not Started", "In Progress", "Done"]):
                found_status_list = True
        
            if block_type == "heading2" and "active projects" in content.lower():
                found_active_projects_h2 = True
        
            if block_type == "heading2" and "key insights" in content.lower():
                found_insights_h2 = True
        
            if block_type == "numberedList" and found_insights_h2:
                insights_count += 1
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Cross-Database Analytics Report'")
        else:
            checks_passed.append("[C] has heading_1 'Cross-Database Analytics Report'")
        if not found_task_overview_h2:
            errors.append("[X] Missing heading_2 'Task Status Overview'")
        else:
            checks_passed.append("[C] has heading_2 'Task Status Overview'")
        if not found_yellow_callout:
            errors.append("[X] Missing yellow callout with completion rate")
        else:
            checks_passed.append("[C] has yellow callout with completion rate")
        if not found_status_list:
            errors.append("[X] Missing bulleted list with status counts")
        else:
            checks_passed.append("[C] has bulleted list with status counts")
        if not found_active_projects_h2:
            errors.append("[X] Missing heading_2 'Active Projects Task Distribution'")
        else:
            checks_passed.append("[C] has heading_2 'Active Projects Task Distribution'")
        if not found_insights_h2:
            errors.append("[X] Missing heading_2 'Key Insights'")
        else:
            checks_passed.append("[C] has heading_2 'Key Insights'")
        if insights_count < 3:
            errors.append(f"[X] Expected 3 numbered items in Key Insights, found {insights_count}")
        else:
            checks_passed.append(f"[C] Found 3 numbered items in Key Insights as expected")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 5: Q1 Budget Tracker
# =============================================================================
def _validate_agent_q1_budget_tracker(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Q1 Budget Tracker database creation.
    
    Checks:
    1. Database exists under Company Handbook
    2. Correct schema with 8 properties
    3. Rows for each Active project from Project Portfolio
    4. Database description updated
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Company Handbook page
    try:
        handbook_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Company Handbook"}}
        })
    
        if not isinstance(handbook_pages, list) or len(handbook_pages) == 0:
            errors.append("[X] Company Handbook page not found")
        else:
            checks_passed.append("[C] Company Handbook page")
    
        handbook_id = handbook_pages[0].get("_id")
    
        # Find Q1 Budget Tracker database
        budget_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": handbook_id, "title": {"$regex": "Q1 Budget Tracker"}}
        })
    
        if not isinstance(budget_tracker_pages, list) or len(budget_tracker_pages) == 0:
            errors.append("[X] Q1 Budget Tracker database not found under Company Handbook")
        else:
            checks_passed.append("[C] Q1 Budget Tracker database found under Company Handbook")
    
        budget_tracker_id = budget_tracker_pages[0].get("_id")
        budget_tracker_page = budget_tracker_pages[0]
    
        # Verify schema
        db_metadata = budget_tracker_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Expense Item", "Department", "Category", "Amount", "Approved", "Purchase Date", "Vendor", "Notes"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Check description (in databaseMetadata)
        description = db_metadata.get("description", "")
        if "auto-populated" not in description.lower():
            errors.append("[X] Database description not updated correctly")
        else:
            checks_passed.append("[C] Database description updated correctly")
    
        # Query Project Portfolio for Active projects
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        active_projects = []
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
        
            for key, val in cell_values.items():
                if "status" in key.lower():
                    status = val
                    break
        
            if status == "Active":
                active_projects.append(row)
    
        # Query budget tracker rows
        budget_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": budget_tracker_id, "type": "row"}
        })
    
        if not isinstance(budget_rows, list):
            budget_rows = []
    
        # Check for rows matching active projects
        initial_budget_rows = [r for r in budget_rows if "initial budget" in r.get("title", "").lower()]
    
        if len(initial_budget_rows) < len(active_projects):
            errors.append(f"[X] Expected {len(active_projects)} budget rows for Active projects, found {len(initial_budget_rows)}")
        else:
            checks_passed.append(f"[C] Expected budget rows for Active projects")
    
        # Build property ID to name mapping
        prop_id_to_name = {}
        for prop in db_properties:
            if isinstance(prop, dict):
                prop_id = prop.get("_id")
                prop_name = prop.get("name", "")
                if prop_id:
                    prop_id_to_name[prop_id] = prop_name.lower()
    
        # Verify row values
        for row in initial_budget_rows:
            cell_values = row.get("cellValues", {})
            category = None
            approved = None
            vendor = None
        
            for key, val in cell_values.items():
                prop_name = prop_id_to_name.get(key, key).lower()
                if "category" in prop_name:
                    category = val
                elif "approved" in prop_name:
                    approved = val
                elif "vendor" in prop_name:
                    vendor = val
        
            if category != "Software":
                errors.append(f"[X] Budget row should have Category='Software', found '{category}'")
            else:
                checks_passed.append(f"[C] Budget row should have Category='Software'")
            if not approved:
                errors.append("[X] Budget row should have Approved=true")
            else:
                checks_passed.append("[C] Budget row should have Approved=true")
            if vendor != "Internal Allocation":
                errors.append(f"[X] Budget row should have Vendor='Internal Allocation', found '{vendor}'")
            else:
                checks_passed.append(f"[C] Budget row should have Vendor='Internal Allocation'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 6: Contact Engagement Dashboard
# =============================================================================
def _validate_agent_contact_engagement_dashboard(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Contact Engagement Dashboard system creation.
    
    Checks:
    1. Analytics page exists as sibling of Contacts Directory
    2. Correct structure with callout and type distribution
    3. Engagement Activities database with correct schema
    4. Rows for each VIP contact
    
    Note: Following user_prompt (success_criteria has conflict with different task)
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Contacts Directory
    try:
        contacts_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Contacts Directory"}}
        })
    
        if not isinstance(contacts_pages, list) or len(contacts_pages) == 0:
            errors.append("[X] Contacts Directory database not found")
        else:
            checks_passed.append("[C] Contacts Directory database")
    
        contacts_id = contacts_pages[0].get("_id")
        contacts_parent_id = contacts_pages[0].get("parentId")
    
        # Query contacts
        contact_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": contacts_id, "type": "row"}
        })
    
        if not isinstance(contact_rows, list):
            contact_rows = []
    
        # Count active and VIP contacts
        active_count = 0
        vip_followup_count = 0
        vip_contacts = []
    
        for row in contact_rows:
            cell_values = row.get("cellValues", {})
            tags = []
        
            for key, val in cell_values.items():
                if "tag" in key.lower() and isinstance(val, list):
                    tags = val
                    break
        
            if "Active" in tags:
                active_count += 1
            if "VIP" in tags:
                vip_contacts.append(row)
                if "Follow Up" in tags:
                    vip_followup_count += 1
    
        # Find analytics page as sibling
        analytics_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": contacts_parent_id, "title": {"$regex": "Q1 2026 Contact Analytics"}}
        })
    
        if not isinstance(analytics_pages, list) or len(analytics_pages) == 0:
            errors.append("[X] Q1 2026 Contact Analytics page not found as sibling of Contacts Directory")
        else:
            checks_passed.append("[C] Q1 2026 Contact Analytics page found as sibling of Contacts Directory")
    
        analytics_page_id = analytics_pages[0].get("_id")
    
        # Get blocks for analytics page
        analytics_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": analytics_page_id}
        })
    
        if not isinstance(analytics_blocks, list):
            analytics_blocks = []
    
        analytics_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_summary_h2 = False
        found_green_callout = False
        found_distribution_h2 = False
    
        for block in analytics_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "contact engagement analytics" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "executive summary" in content.lower():
                found_summary_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "green" in bg_color.lower() and "active contacts" in content.lower():
                    found_green_callout = True
        
            if block_type == "heading2" and "distribution by type" in content.lower():
                found_distribution_h2 = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Contact Engagement Analytics - Q1 2026'")
        else:
            checks_passed.append("[C] has heading_1 'Contact Engagement Analytics - Q1 2026'")
        if not found_summary_h2:
            errors.append("[X] Missing heading_2 'Executive Summary'")
        else:
            checks_passed.append("[C] has heading_2 'Executive Summary'")
        if not found_green_callout:
            errors.append("[X] Missing green callout with contact counts")
        else:
            checks_passed.append("[C] has green callout with contact counts")
        if not found_distribution_h2:
            errors.append("[X] Missing heading_2 'Contact Distribution by Type'")
        else:
            checks_passed.append("[C] has heading_2 'Contact Distribution by Type'")
    
        # Find Engagement Activities database
        engagement_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": analytics_page_id, "title": {"$regex": "Engagement Activities"}}
        })
    
        if not isinstance(engagement_db_pages, list) or len(engagement_db_pages) == 0:
            errors.append("[X] Engagement Activities database not found under analytics page")
        else:
            checks_passed.append("[C] Engagement Activities database found under analytics page")
    
        engagement_db_id = engagement_db_pages[0].get("_id")
        engagement_db_page = engagement_db_pages[0]
    
        # Verify schema
        db_metadata = engagement_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Activity", "Contact Name", "Contact Type", "Activity Type", "Date", "Next Action", "Priority"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query engagement rows
        engagement_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": engagement_db_id, "type": "row"}
        })
    
        if not isinstance(engagement_rows, list):
            engagement_rows = []
    
        # Check for VIP outreach rows
        vip_outreach_rows = [r for r in engagement_rows if "q1 outreach" in r.get("title", "").lower()]
    
        if len(vip_outreach_rows) < len(vip_contacts):
            errors.append(f"[X] Expected {len(vip_contacts)} VIP outreach activities, found {len(vip_outreach_rows)}")
        else:
            checks_passed.append(f"[C] Expected VIP outreach activities")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 7: Inventory Reorder System
# =============================================================================
def _validate_agent_inventory_reorder_system(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Inventory Reorder System with schema enhancements.
    
    Checks:
    1. New properties added to Inventory Tracker (Reorder Point, Lead Time Days, Supplier, Last Order Date)
    2. Reorder logic applied correctly by category
    3. In Stock updated for items where Quantity <= Reorder Point
    4. Reorder page exists with correct structure
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Inventory Tracker database
    try:
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_id = inventory_pages[0].get("_id")
        inventory_page = inventory_pages[0]
    
        # Verify schema has new properties
        db_metadata = inventory_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_new_props = ["Reorder Point", "Lead Time Days", "Supplier", "Last Order Date"]
    
        for prop_name in required_new_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing new property: {prop_name}")
            else:
                checks_passed.append(f"[C] has new property")
    
        # Get Supplier property to check options
        supplier_prop = None
        for prop_data in db_properties:
            if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == "supplier":
                supplier_prop = prop_data
                break
    
        if supplier_prop:
            options = supplier_prop.get("options", [])
            required_suppliers = {"TechSupply Co", "Office Direct", "Furniture Plus", "Software Hub"}
            found_suppliers = set()
            for opt in options:
                if isinstance(opt, dict):
                    found_suppliers.add(opt.get("name", ""))
        
            if not required_suppliers.issubset(found_suppliers):
                errors.append(f"[X] Supplier options incomplete. Expected {required_suppliers}, found {found_suppliers}")
            else:
                checks_passed.append(f"[C] Supplier options incomplete")
    
        # Create property ID to name mapping
        prop_id_to_name = {}
        for prop_data in db_properties:
            if isinstance(prop_data, dict):
                prop_id = prop_data.get("_id")
                prop_name = prop_data.get("name", "")
                if prop_id and prop_name:
                    prop_id_to_name[prop_id] = prop_name.lower()
    
        # Query inventory rows
        inventory_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_id, "type": "row"}
        })
    
        if not isinstance(inventory_rows, list):
            inventory_rows = []
    
        # Check reorder logic applied
        items_needing_reorder = []
        for row in inventory_rows:
            cell_values = row.get("cellValues", {})
            quantity = None
            reorder_point = None
            in_stock = True
            category = None
        
            # Use property ID to name mapping
            for prop_id, val in cell_values.items():
                prop_name = prop_id_to_name.get(prop_id, "").lower()
                if "quantity" in prop_name and isinstance(val, (int, float)):
                    quantity = val
                elif "reorder" in prop_name and "point" in prop_name:
                    reorder_point = val
                elif "stock" in prop_name and isinstance(val, bool):
                    in_stock = val
                elif "category" in prop_name:
                    category = val
        
            # Verify reorder point set correctly by category
            if category == "Electronics" and reorder_point != 10:
                errors.append(f"[X] Electronics should have Reorder Point=10, found {reorder_point}")
            elif category == "Office Supplies" and reorder_point != 20:
                errors.append(f"[X] Office Supplies should have Reorder Point=20, found {reorder_point}")
            elif category == "Furniture" and reorder_point != 2:
                errors.append(f"[X] Furniture should have Reorder Point=2, found {reorder_point}")
            elif category == "Software" and reorder_point != 5:
                errors.append(f"[X] Software should have Reorder Point=5, found {reorder_point}")
            else:
                checks_passed.append(f"[C] Software should have Reorder Point=5")
        
            # Check if quantity <= reorder point
            if quantity is not None and reorder_point is not None:
                if quantity <= reorder_point:
                    items_needing_reorder.append(row)
                    if in_stock:
                        errors.append(f"[X] Item with Quantity {quantity} <= Reorder Point {reorder_point} should have In Stock=false")
                    else:
                        checks_passed.append(f"[C] Item with Quantity <= Reorder Point should have In Stock=false")
    
        # Find reorder page under Inventory Tracker (should be named procurement@company.com per user prompt)
        reorder_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": inventory_id, "title": {"$regex": "procurement"}}
        })
    
        if not isinstance(reorder_pages, list) or len(reorder_pages) == 0:
            errors.append("[X] Reorder page not found under Inventory Tracker")
        else:
            checks_passed.append("[C] Reorder page found under Inventory Tracker")
    
        reorder_page_id = reorder_pages[0].get("_id")
    
        # Get blocks for reorder page
        reorder_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": reorder_page_id}
        })
    
        if not isinstance(reorder_blocks, list):
            reorder_blocks = []
    
        reorder_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_orange_callout = False
        found_h3_items = 0
    
        for block in reorder_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "reorder required" in content.lower():
                found_h1 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "orange" in bg_color.lower() and "urgent" in content.lower():
                    found_orange_callout = True
        
            if block_type == "heading3":
                found_h3_items += 1
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Reorder Required'")
        else:
            checks_passed.append("[C] has heading_1 'Reorder Required'")
        if not found_orange_callout:
            errors.append("[X] Missing orange callout with reorder count")
        else:
            checks_passed.append("[C] has orange callout with reorder count")
        if found_h3_items < len(items_needing_reorder):
            errors.append(f"[X] Expected {len(items_needing_reorder)} heading_3 blocks for items needing reorder")
        else:
            checks_passed.append(f"[C] Expected heading_3 blocks for items needing reorder")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 8: Contacts Outreach Priority
# =============================================================================
def _validate_agent_contacts_outreach_priority(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Contacts Outreach Priority segmentation.
    
    Checks:
    1. Outreach Priority property added to Contacts Directory
    2. Priority set correctly based on Type and Tags
    3. Outreach plan page exists under Team Directory
    4. Correct structure with priority sections
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Contacts Directory
    try:
        contacts_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Contacts Directory"}}
        })
    
        if not isinstance(contacts_pages, list) or len(contacts_pages) == 0:
            errors.append("[X] Contacts Directory database not found")
        else:
            checks_passed.append("[C] Contacts Directory database")
    
        contacts_id = contacts_pages[0].get("_id")
        contacts_page = contacts_pages[0]
    
        # Check Outreach Priority property
        db_metadata = contacts_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        outreach_prop = None
        for prop_data in db_properties:
            if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == "outreach priority":
                outreach_prop = prop_data
                break
    
        if not outreach_prop:
            errors.append("[X] Outreach Priority property not found in Contacts Directory")
        else:
            checks_passed.append("[C] Outreach Priority property found in Contacts Directory")
    
        # Verify options
        options = outreach_prop.get("options", [])
        required_options = {"Immediate", "This Quarter", "Next Quarter", "Low Priority"}
        found_options = set()
        for opt in options:
            if isinstance(opt, dict):
                found_options.add(opt.get("name", ""))
    
        if required_options != found_options:
            errors.append(f"[X] Outreach Priority options incorrect. Expected {required_options}, found {found_options}")
        else:
            checks_passed.append(f"[C] Outreach Priority options correct")
    
        # Query contacts to verify priority assignment
        contact_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": contacts_id, "type": "row"}
        })
    
        if not isinstance(contact_rows, list):
            contact_rows = []
    
        immediate_contacts = []
        this_quarter_contacts = []
    
        outreach_prop_id = outreach_prop.get("_id")
    
        for row in contact_rows:
            cell_values = row.get("cellValues", {})
            contact_type = None
            tags = []
            outreach_priority = cell_values.get(outreach_prop_id)
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "type" in key_lower and not "outreach" in key_lower:
                    contact_type = val
                elif "tag" in key_lower and isinstance(val, list):
                    tags = val
        
            # Verify priority logic
            if contact_type == "Client" and "VIP" in tags:
                if outreach_priority != "Immediate":
                    errors.append(f"[X] VIP Client should have Outreach Priority='Immediate', found '{outreach_priority}'")
                else:
                    checks_passed.append(f"[C] VIP Client should have Outreach Priority='Immediate'")
                immediate_contacts.append(row)
            elif contact_type in ["Partner", "Vendor"] and "Active" in tags:
                if outreach_priority != "This Quarter":
                    errors.append(f"[X] Active Partner/Vendor should have Outreach Priority='This Quarter', found '{outreach_priority}'")
                else:
                    checks_passed.append(f"[C] Active Partner/Vendor should have Outreach Priority='This Quarter'")
                this_quarter_contacts.append(row)
        
            if outreach_priority == "Immediate":
                immediate_contacts.append(row)
            elif outreach_priority == "This Quarter":
                this_quarter_contacts.append(row)
    
        # Find Team Directory page
        team_dir_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Directory"}}
        })
    
        if not isinstance(team_dir_pages, list) or len(team_dir_pages) == 0:
            errors.append("[X] Team Directory page not found")
        else:
            checks_passed.append("[C] Team Directory page")
    
        team_dir_id = team_dir_pages[0].get("_id")
    
        # Find outreach plan page
        plan_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": team_dir_id, "title": {"$regex": "Q1 Outreach Plan"}}
        })
    
        if not isinstance(plan_pages, list) or len(plan_pages) == 0:
            errors.append("[X] Q1 Outreach Plan page not found under Team Directory")
        else:
            checks_passed.append("[C] Q1 Outreach Plan page found under Team Directory")
    
        plan_page_id = plan_pages[0].get("_id")
    
        # Get blocks for plan page
        plan_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": plan_page_id}
        })
    
        if not isinstance(plan_blocks, list):
            plan_blocks = []
    
        plan_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_blue_callout = False
        found_immediate_h2 = False
        found_this_quarter_h2 = False
    
        for block in plan_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "outreach plan" in content.lower():
                found_h1 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "blue" in bg_color.lower() and "outreach targets" in content.lower():
                    found_blue_callout = True
        
            if block_type == "heading2" and "immediate priority" in content.lower():
                found_immediate_h2 = True
        
            if block_type == "heading2" and "this quarter" in content.lower():
                found_this_quarter_h2 = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Q1 Outreach Plan'")
        else:
            checks_passed.append("[C] has heading_1 'Q1 Outreach Plan'")
        if not found_blue_callout:
            errors.append("[X] Missing blue callout with outreach targets")
        else:
            checks_passed.append("[C] has blue callout with outreach targets")
        if not found_immediate_h2:
            errors.append("[X] Missing heading_2 'Immediate Priority Contacts'")
        else:
            checks_passed.append("[C] has heading_2 'Immediate Priority Contacts'")
        if not found_this_quarter_h2:
            errors.append("[X] Missing heading_2 'This Quarter'")
        else:
            checks_passed.append("[C] has heading_2 'This Quarter'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 9: Performance Metrics
# =============================================================================
def _validate_agent_performance_metrics(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Performance Metrics quarterly review system.
    
    Checks:
    1. Performance Metrics database exists under Team Directory
    2. Correct schema with 7 properties
    3. Rows for each team member with Q4 2025 metrics
    4. Performance ratings follow thresholds
    5. Top Performers section on Team Directory page
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Team Directory page
    try:
        team_dir_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Directory"}}
        })
    
        if not isinstance(team_dir_pages, list) or len(team_dir_pages) == 0:
            errors.append("[X] Team Directory page not found")
        else:
            checks_passed.append("[C] Team Directory page")
    
        team_dir_id = team_dir_pages[0].get("_id")
    
        # Find Performance Metrics database
        metrics_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": team_dir_id, "title": {"$regex": "Performance Metrics"}}
        })
    
        if not isinstance(metrics_db_pages, list) or len(metrics_db_pages) == 0:
            errors.append("[X] Performance Metrics database not found under Team Directory")
        else:
            checks_passed.append("[C] Performance Metrics database found under Team Directory")
    
        metrics_db_id = metrics_db_pages[0].get("_id")
        metrics_db_page = metrics_db_pages[0]
    
        # Verify schema
        db_metadata = metrics_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Quarter", "Team Member", "Tasks Completed", "Projects Led", "Avg Task Duration (days)", "Performance Rating", "Bonus Eligible"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query metrics rows
        metrics_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": metrics_db_id, "type": "row"}
        })
    
        if not isinstance(metrics_rows, list):
            metrics_rows = []
    
        # Verify Q4 2025 data
        q4_rows = []
        exceeds_members = []
    
        for row in metrics_rows:
            quarter = row.get("title", "")
            if "Q4 2025" in quarter or "q4 2025" in quarter.lower():
                q4_rows.append(row)
            
                cell_values = row.get("cellValues", {})
                tasks_completed = None
                performance_rating = None
                bonus_eligible = None
            
                for key, val in cell_values.items():
                    key_lower = key.lower()
                    if "tasks completed" in key_lower:
                        tasks_completed = val
                    elif "performance rating" in key_lower:
                        performance_rating = val
                    elif "bonus eligible" in key_lower:
                        bonus_eligible = val
            
                # Verify rating logic
                if tasks_completed is not None:
                    if tasks_completed > 5:
                        if performance_rating != "Exceeds":
                            errors.append(f"[X] Team member with {tasks_completed} completed tasks should have 'Exceeds' rating")
                        else:
                            checks_passed.append(f"[C] Team member with completed tasks should have 'Exceeds' rating")
                        if not bonus_eligible:
                            errors.append("[X] Team member with 'Exceeds' rating should have Bonus Eligible=true")
                        else:
                            checks_passed.append("[C] Team member with 'Exceeds' rating should have Bonus Eligible=true")
                        exceeds_members.append(row)
                    elif 2 <= tasks_completed <= 5:
                        if performance_rating != "Meets":
                            errors.append(f"[X] Team member with {tasks_completed} completed tasks should have 'Meets' rating")
                    elif tasks_completed < 2:
                        if performance_rating != "Needs Improvement":
                            errors.append(f"[X] Team member with {tasks_completed} completed tasks should have 'Needs Improvement' rating")
                        else:
                            checks_passed.append(f"[C] Team member with completed tasks should have 'Needs Improvement' rating")
    
        if len(q4_rows) == 0:
            errors.append("[X] No Q4 2025 performance metrics found")
        else:
            checks_passed.append("[C] Q4 2025 performance metrics")
    
        # Check for Top Performers section on Team Directory page
        team_dir_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": team_dir_id}
        })
    
        if not isinstance(team_dir_blocks, list):
            team_dir_blocks = []
    
        team_dir_blocks.sort(key=lambda b: b.get("order", 0))
    
        found_top_performers_h2 = False
        found_quote = False
    
        for block in team_dir_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "heading2" and "top performers" in content.lower():
                found_top_performers_h2 = True
        
            if block_type == "quote" and found_top_performers_h2:
                found_quote = True
    
        if not found_top_performers_h2:
            errors.append("[X] Missing heading_2 'Q4 2025 Top Performers' on Team Directory page")
        else:
            checks_passed.append("[C] has heading_2 'Q4 2025 Top Performers' on Team Directory page")
        if not found_quote:
            errors.append("[X] Missing quote block with top performers list")
        else:
            checks_passed.append("[C] has quote block with top performers list")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 10: Executive Project Dashboard
# =============================================================================
def _validate_agent_executive_project_dashboard(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Executive Project Dashboard creation.
    
    Checks:
    1. Identify at-risk projects (Active, ending within 30 days)
    2. Dashboard page exists under Project Roadmap
    3. Correct structure with urgent callout, at-risk list, budget analysis
    4. Department distribution
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Portfolio database
    try:
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        # Query project rows
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        # Find at-risk projects (Active, ending within 30 days)
        at_risk_projects = []
        active_budget_total = 0
        department_counts = {}
    
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
            timeline_end = None
            budget = 0
            department = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "status" in key_lower:
                    status = val
                elif "timeline" in key_lower and isinstance(val, dict):
                    timeline_end = val.get("end")
                elif "budget" in key_lower and isinstance(val, (int, float)):
                    budget = val
                elif "department" in key_lower:
                    department = val
        
            if status == "Active":
                active_budget_total += budget
            
                if department:
                    department_counts[department] = department_counts.get(department, 0) + 1
            
                if timeline_end and timeline_end < "2026-02-14":  # Within 30 days from 2026-01-15
                    at_risk_projects.append(row)
    
        # Find Project Roadmap page
        roadmap_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Roadmap"}}
        })
    
        if not isinstance(roadmap_pages, list) or len(roadmap_pages) == 0:
            errors.append("[X] Project Roadmap page not found")
        else:
            checks_passed.append("[C] Project Roadmap page")
    
        roadmap_id = roadmap_pages[0].get("_id")
    
        # Find dashboard page
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": roadmap_id, "title": {"$regex": "Executive Project Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Executive Project Dashboard page not found under Project Roadmap")
        else:
            checks_passed.append("[C] Executive Project Dashboard page found under Project Roadmap")
    
        dashboard_page_id = dashboard_pages[0].get("_id")
    
        # Get blocks for dashboard
        dashboard_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page_id}
        })
    
        if not isinstance(dashboard_blocks, list):
            dashboard_blocks = []
    
        dashboard_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_red_callout = False
        found_at_risk_h2 = False
        found_numbered_list = False
        found_budget_h2 = False
        found_budget_paragraph = False
        found_department_h2 = False
        found_department_list = False
    
        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "executive project dashboard" in content.lower():
                found_h1 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "red" in bg_color.lower() and "urgent" in content.lower():
                    found_red_callout = True
        
            if block_type == "heading2" and "at-risk projects" in content.lower():
                found_at_risk_h2 = True
        
            if block_type == "numberedList" and any(word in content.lower() for word in ["deadline", "lead", "budget"]):
                found_numbered_list = True
        
            if block_type == "heading2" and "budget analysis" in content.lower():
                found_budget_h2 = True
        
            if block_type == "paragraph" and "active projects" in content.lower() and str(active_budget_total) in content:
                found_budget_paragraph = True
        
            if block_type == "heading2" and "department distribution" in content.lower():
                found_department_h2 = True
        
            if block_type == "bulletedList" and found_department_h2:
                found_department_list = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Executive Project Dashboard'")
        else:
            checks_passed.append("[C] has heading_1 'Executive Project Dashboard'")
        if not found_red_callout:
            errors.append("[X] Missing red callout with urgent at-risk count")
        else:
            checks_passed.append("[C] has red callout with urgent at-risk count")
        if not found_at_risk_h2:
            errors.append("[X] Missing heading_2 'At-Risk Projects'")
        else:
            checks_passed.append("[C] has heading_2 'At-Risk Projects'")
        if not found_numbered_list:
            errors.append("[X] Missing numbered list of at-risk projects")
        else:
            checks_passed.append("[C] has numbered list of at-risk projects")
        if not found_budget_h2:
            errors.append("[X] Missing heading_2 'Budget Analysis'")
        else:
            checks_passed.append("[C] has heading_2 'Budget Analysis'")
        if not found_budget_paragraph:
            errors.append("[X] Missing paragraph with total active budget")
        else:
            checks_passed.append("[C] has paragraph with total active budget")
        if not found_department_h2:
            errors.append("[X] Missing heading_2 'Department Distribution'")
        else:
            checks_passed.append("[C] has heading_2 'Department Distribution'")
        if not found_department_list:
            errors.append("[X] Missing bulleted list with department counts")
        else:
            checks_passed.append("[C] has bulleted list with department counts")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 11: Procurement Recommendations
# =============================================================================
def _validate_agent_procurement_recommendations(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Procurement Recommendations system creation.
    
    Checks:
    1. Procurement Recommendations database exists under Product Specs
    2. Correct schema with 6 properties
    3. Entries for items where In Stock=false or Quantity < 10
    4. Urgency calculations correct
    5. Procurement Dashboard page exists
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Product Specs page
    try:
        product_specs_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Product Specs"}}
        })
    
        if not isinstance(product_specs_pages, list) or len(product_specs_pages) == 0:
            errors.append("[X] Product Specs page not found")
        else:
            checks_passed.append("[C] Product Specs page")
    
        product_specs_id = product_specs_pages[0].get("_id")
    
        # Find Procurement Recommendations database
        procurement_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": product_specs_id, "title": {"$regex": "Procurement Recommendations"}}
        })
    
        if not isinstance(procurement_db_pages, list) or len(procurement_db_pages) == 0:
            errors.append("[X] Procurement Recommendations database not found under Product Specs")
        else:
            checks_passed.append("[C] Procurement Recommendations database found under Product Specs")
    
        procurement_db_id = procurement_db_pages[0].get("_id")
        procurement_db_page = procurement_db_pages[0]
    
        # Verify schema
        db_metadata = procurement_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Item", "Current Stock", "Reorder Level", "Urgency", "Estimated Cost", "Supplier"]
    
        # Create property ID to name mapping for validating rows
        prop_id_to_name = {prop_data.get("_id"): prop_data.get("name") for prop_data in db_properties if isinstance(prop_data, dict)}
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query Inventory Tracker to verify source data
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_id = inventory_pages[0].get("_id")
    
        inventory_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_id, "type": "row"}
        })
    
        if not isinstance(inventory_rows, list):
            inventory_rows = []
    
        # Count items needing procurement
        items_needing_procurement = 0
        for row in inventory_rows:
            cell_values = row.get("cellValues", {})
            in_stock = True
            quantity = 0
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "stock" in key_lower and isinstance(val, bool):
                    in_stock = val
                elif "quantity" in key_lower and isinstance(val, (int, float)):
                    quantity = val
        
            if not in_stock or quantity < 10:
                items_needing_procurement += 1
    
        # Query procurement rows
        procurement_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": procurement_db_id, "type": "row"}
        })
    
        if not isinstance(procurement_rows, list):
            procurement_rows = []
    
        if len(procurement_rows) < items_needing_procurement:
            errors.append(f"[X] Expected {items_needing_procurement} procurement entries, found {len(procurement_rows)}")
        else:
            checks_passed.append(f"[C] Expected procurement entries")
    
        # Verify urgency logic
        for row in procurement_rows:
            cell_values = row.get("cellValues", {})
            current_stock = None
            urgency = None
        
            for key, val in cell_values.items():
                prop_name = prop_id_to_name.get(key, "").lower()
                if "current stock" in prop_name:
                    current_stock = val
                elif "urgency" in prop_name:
                    urgency = val
        
            if current_stock is not None:
                if current_stock == 0 and urgency != "Immediate":
                    errors.append(f"[X] Item with 0 stock should have Urgency='Immediate', found '{urgency}'")
                elif 0 < current_stock < 5 and urgency != "This Week":
                    errors.append(f"[X] Item with stock {current_stock} should have Urgency='This Week', found '{urgency}'")
                elif 5 <= current_stock < 10 and urgency != "This Month":
                    errors.append(f"[X] Item with stock {current_stock} should have Urgency='This Month', found '{urgency}'")
                else:
                    checks_passed.append(f"[C] Item with stock should have Urgency='This Month'")
    
        # Find Procurement Dashboard page
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": procurement_db_id, "title": {"$regex": "Procurement Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Procurement Dashboard page not found under database")
        else:
            checks_passed.append("[C] Procurement Dashboard page found under database")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 12: Project Portfolio Task Creation
# =============================================================================
def _validate_agent_project_portfolio_task_creation(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate task creation for projects ending within 30 days.
    
    Checks:
    1. Final Review tasks created for Active/On Hold projects ending within 30 days
    2. Tasks have correct properties (Status, Priority, Tags, Due Date)
    3. Project names updated with ' - Review Scheduled'
    4. On Hold projects changed to Active
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Portfolio database
    try:
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        # Query project rows
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        # Find projects ending within 30 days
        target_projects = []
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
            timeline_end = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "status" in key_lower:
                    status = val
                elif "timeline" in key_lower and isinstance(val, dict):
                    timeline_end = val.get("end")
        
            if status in ["Active", "On Hold"] and timeline_end:
                if timeline_end < "2026-02-14":  # Within 30 days from 2026-01-15
                    target_projects.append(row)
                
                    # Check if name updated
                    project_title = row.get("title", "")
                    if "review scheduled" not in project_title.lower():
                        errors.append(f"[X] Project {project_title} should have ' - Review Scheduled' appended")
                    else:
                        checks_passed.append(f"[C] Project should have ' - Review Scheduled' appended")
                
                    # Check if On Hold changed to Active
                    if "on hold" in project_title.lower() and status != "Active":
                        errors.append(f"[X] On Hold project should be changed to Active status")
                    else:
                        checks_passed.append(f"[C] On Hold project should be changed to Active status")
    
        # Find Task Tracker database
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Query tasks
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Find Final Review tasks
        final_review_tasks = []
        for row in task_rows:
            task_title = row.get("title", "")
            if "final review" in task_title.lower():
                final_review_tasks.append(row)
            
                cell_values = row.get("cellValues", {})
                status = None
                priority = None
                tags = []
                completed = None
            
                for key, val in cell_values.items():
                    key_lower = key.lower()
                    if "status" in key_lower:
                        status = val
                    elif "priority" in key_lower:
                        priority = val
                    elif "tag" in key_lower and isinstance(val, list):
                        tags = val
                    elif "completed" in key_lower:
                        completed = val
            
                # Verify properties
                if status != "Not Started":
                    errors.append(f"[X] Final Review task should have Status='Not Started', found '{status}'")
                else:
                    checks_passed.append(f"[C] Final Review task should have Status='Started'")
                if priority != "High":
                    errors.append(f"[X] Final Review task should have Priority='High', found '{priority}'")
                else:
                    checks_passed.append(f"[C] Final Review task should have Priority='High'")
                if "Documentation" not in tags or "Design" not in tags:
                    errors.append(f"[X] Final Review task should have both 'Documentation' and 'Design' tags")
                else:
                    checks_passed.append(f"[C] Final Review task should have both 'Documentation' and 'Design' tags")
                if completed:
                    errors.append("[X] Final Review task should have Completed=false")
                else:
                    checks_passed.append("[C] Final Review task should have Completed=false")
    
        if len(final_review_tasks) < len(target_projects):
            errors.append(f"[X] Expected {len(target_projects)} Final Review tasks, found {len(final_review_tasks)}")
        else:
            checks_passed.append(f"[C] Expected Final Review tasks")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 13: Resource Allocation Database
# =============================================================================
def _validate_agent_resource_allocation_database(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Resource Allocation database creation with cross-references.
    
    Checks:
    1. Database exists under Project Roadmap with 9 properties
    2. Human resource allocations for Active projects
    3. Software allocations from Inventory Tracker
    4. Correct calculations for Cost Per Day
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Roadmap page
    try:
        roadmap_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Roadmap"}}
        })
    
        if not isinstance(roadmap_pages, list) or len(roadmap_pages) == 0:
            errors.append("[X] Project Roadmap page not found")
        else:
            checks_passed.append("[C] Project Roadmap page")
    
        roadmap_id = roadmap_pages[0].get("_id")
    
        # Find Resource Allocation database
        resource_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": roadmap_id, "title": {"$regex": "Resource Allocation"}}
        })
    
        if not isinstance(resource_db_pages, list) or len(resource_db_pages) == 0:
            errors.append("[X] Resource Allocation database not found under Project Roadmap")
        else:
            checks_passed.append("[C] Resource Allocation database found under Project Roadmap")
    
        resource_db_id = resource_db_pages[0].get("_id")
        resource_db_page = resource_db_pages[0]
    
        # Verify schema
        db_metadata = resource_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Resource ID", "Resource Type", "Allocated To", "Project", "Allocation %", "Start Date", "End Date", "Cost Per Day", "Total Cost"]
    
        # Create property ID to name mapping for validating rows
        prop_id_to_name = {prop_data.get("_id"): prop_data.get("name") for prop_data in db_properties if isinstance(prop_data, dict)}
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query Project Portfolio for Active projects
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        active_projects = [r for r in project_rows if r.get("cellValues", {}).get("status") == "Active" or any("active" in str(v).lower() for v in r.get("cellValues", {}).values() if isinstance(v, str))]
    
        # Query resource allocations
        resource_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": resource_db_id, "type": "row"}
        })
    
        if not isinstance(resource_rows, list):
            resource_rows = []
    
        # Count human and software resources
        human_resources = []
        software_resources = []
    
        for row in resource_rows:
            resource_id = row.get("title", "")
            cell_values = row.get("cellValues", {})
            resource_type = None
            cost_per_day = None
            allocation_pct = None
        
            for key, val in cell_values.items():
                prop_name = prop_id_to_name.get(key, "").lower()
                if "resource type" in prop_name:
                    resource_type = val
                elif "cost per day" in prop_name:
                    cost_per_day = val
                elif "allocation" in prop_name and "%" in prop_name:
                    allocation_pct = val
        
            if resource_type == "Human":
                human_resources.append(row)
                if cost_per_day != 500:
                    errors.append(f"[X] Human resource should have Cost Per Day=500, found {cost_per_day}")
                else:
                    checks_passed.append(f"[C] Human resource should have Cost Per Day=500")
                if allocation_pct != 50:
                    errors.append(f"[X] Human resource should have Allocation %=50, found {allocation_pct}")
            elif resource_type == "Software":
                software_resources.append(row)
                if allocation_pct != 100:
                    errors.append(f"[X] Software resource should have Allocation %=100, found {allocation_pct}")
                else:
                    checks_passed.append(f"[C] Software resource should have Allocation %=100")
    
        if len(human_resources) < len(active_projects):
            errors.append(f"[X] Expected at least {len(active_projects)} human resource allocations, found {len(human_resources)}")
        else:
            checks_passed.append(f"[C] Expected at least human resource allocations")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 14: Project Health Monitor
# =============================================================================
def _validate_agent_project_health_monitor(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Project Health Monitor with risk scoring.
    
    Checks:
    1. Page exists under Project Roadmap
    2. Risk score calculation (10 pts per high/urgent incomplete task + 20 if ending within 14 days)
    3. Projects sorted by risk score descending
    4. Resource utilization stats
    5. Callout color based on highest risk score
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Portfolio and Task Tracker
    try:
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Calculate risk scores
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        max_risk_score = 0
        active_projects = 0
        from datetime import datetime, timedelta
        today = datetime(2026, 1, 14)
        deadline_threshold = (today + timedelta(days=14)).strftime("%Y-%m-%d")
    
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
            timeline_end = None
            project_name = row.get("title", "")
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "status" in key_lower:
                    status = val
                elif "timeline" in key_lower and isinstance(val, dict):
                    timeline_end = val.get("end")
        
            if status == "Active":
                active_projects += 1
                risk_score = 0
            
                # Count high/urgent incomplete tasks for THIS project
                # Match by project name in task content or tags
                high_urgent_tasks = 0
                for t in task_rows:
                    task_cv = t.get("cellValues", {})
                    task_title = t.get("title", "")
                    priority = None
                    status_val = None
                
                    for k, v in task_cv.items():
                        k_lower = k.lower()
                        if "priority" in k_lower:
                            priority = v
                        elif "status" in k_lower:
                            status_val = v
                
                    # Count if priority is High/Urgent and status is not Done
                    if priority in ["High", "Urgent"] and status_val != "Done":
                        # Match to project by name appearing in task title
                        if project_name.lower() in task_title.lower():
                            high_urgent_tasks += 1
            
                risk_score += high_urgent_tasks * 10
            
                # Add 20 if ending within 14 days
                if timeline_end and timeline_end <= deadline_threshold:
                    risk_score += 20
            
                max_risk_score = max(max_risk_score, risk_score)
    
        # Find Project Roadmap page
        roadmap_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Roadmap"}}
        })
    
        if not isinstance(roadmap_pages, list) or len(roadmap_pages) == 0:
            errors.append("[X] Project Roadmap page not found")
        else:
            checks_passed.append("[C] Project Roadmap page")
    
        roadmap_id = roadmap_pages[0].get("_id")
    
        # Find health monitor page
        monitor_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": roadmap_id, "title": {"$regex": "Project Health Monitor"}}
        })
    
        if not isinstance(monitor_pages, list) or len(monitor_pages) == 0:
            errors.append("[X] Project Health Monitor page not found under Project Roadmap")
        else:
            checks_passed.append("[C] Project Health Monitor page found under Project Roadmap")
    
        monitor_page_id = monitor_pages[0].get("_id")
    
        # Get blocks for monitor page
        monitor_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": monitor_page_id}
        })
    
        if not isinstance(monitor_blocks, list):
            monitor_blocks = []
    
        monitor_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_risk_h2 = False
        found_numbered_list = False
        found_resource_h2 = False
        found_callout = False
        callout_color = None
    
        for block in monitor_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "project health monitor" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "risk assessment" in content.lower():
                found_risk_h2 = True
        
            if block_type == "numberedList" and "risk score" in content.lower():
                found_numbered_list = True
        
            if block_type == "heading2" and "resource utilization" in content.lower():
                found_resource_h2 = True
        
            if block_type == "callout":
                found_callout = True
                callout_color = properties.get("backgroundColor", "")
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Project Health Monitor'")
        else:
            checks_passed.append("[C] has heading_1 'Project Health Monitor'")
        if not found_risk_h2:
            errors.append("[X] Missing heading_2 'Risk Assessment Matrix'")
        else:
            checks_passed.append("[C] has heading_2 'Risk Assessment Matrix'")
        if not found_numbered_list:
            errors.append("[X] Missing numbered list with risk scores")
        else:
            checks_passed.append("[C] has numbered list with risk scores")
        if not found_resource_h2:
            errors.append("[X] Missing heading_2 'Resource Utilization'")
        else:
            checks_passed.append("[C] has heading_2 'Resource Utilization'")
        if not found_callout:
            errors.append("[X] Missing callout with portfolio health status")
        else:
            checks_passed.append("[C] has callout with portfolio health status")
    
        # Verify callout color logic
        expected_color = "red" if max_risk_score >= 50 else ("yellow" if max_risk_score >= 30 else "green")
        if expected_color not in callout_color.lower():
            errors.append(f"[X] Expected {expected_color} callout for max risk score {max_risk_score}, found {callout_color}")
        else:
            checks_passed.append(f"[C] Expected callout for max risk score")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 15: Inventory Alerts Audit System
# =============================================================================
def _validate_agent_inventory_alerts_audit_system(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Inventory Alerts audit system.
    
    Checks:
    1. Items with Quantity < 10 have In Stock updated and Reorder Date set
    2. Inventory Alerts database exists under Bug Tracker with correct schema
    3. Alert rows for each low-stock item with correct urgency
    4. Red callout on Bug Tracker page
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Inventory Tracker database
    try:
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_id = inventory_pages[0].get("_id")
    
        # Query inventory rows
        inventory_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_id, "type": "row"}
        })
    
        if not isinstance(inventory_rows, list):
            inventory_rows = []
    
        # Check low-stock items that should have been updated
        # Per user_prompt: "find all items where Quantity < 10 and In Stock = true"
        # After update, these items should have In Stock=false and Reorder Date set to 3 days from today
        # We identify updated items by checking if Reorder Date = "2026-01-17" (today + 3 days)
        # This distinguishes newly updated items from items that were already In Stock=false
        expected_reorder_date = "2026-01-17"  # Today (2026-01-14) + 3 days
        low_stock_items = []
        for row in inventory_rows:
            cell_values = row.get("cellValues", {})
            quantity = None
            in_stock = True
            reorder_date = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "quantity" in key_lower and isinstance(val, (int, float)):
                    quantity = val
                elif "stock" in key_lower and isinstance(val, bool):
                    in_stock = val
                elif "reorder" in key_lower and "date" in key_lower:
                    reorder_date = val
        
            # Count items that were updated in this task (have Reorder Date = today + 3 days)
            if quantity is not None and quantity < 10:
                if not in_stock and reorder_date is not None:
                    # Check if this was updated in THIS task (reorder date = expected date)
                    reorder_date_str = reorder_date.get("start") if isinstance(reorder_date, dict) else reorder_date
                    if reorder_date_str == expected_reorder_date:
                        # This item was updated in this task
                        low_stock_items.append(row)
                elif in_stock:
                    # This item should have been updated but wasn't
                    errors.append(f"[X] Item with Quantity {quantity} < 10 should have In Stock=false")
                else:
                    checks_passed.append(f"[C] Item with Quantity < 10 should have In Stock=false")
    
        # Find Bug Tracker page
        bug_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Bug Tracker"}}
        })
    
        if not isinstance(bug_tracker_pages, list) or len(bug_tracker_pages) == 0:
            errors.append("[X] Bug Tracker page not found")
        else:
            checks_passed.append("[C] Bug Tracker page")
    
        bug_tracker_id = bug_tracker_pages[0].get("_id")
    
        # Find Inventory Alerts database
        alerts_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": bug_tracker_id, "title": {"$regex": "Inventory Alerts"}}
        })
    
        if not isinstance(alerts_db_pages, list) or len(alerts_db_pages) == 0:
            errors.append("[X] Inventory Alerts database not found under Bug Tracker")
        else:
            checks_passed.append("[C] Inventory Alerts database found under Bug Tracker")
    
        alerts_db_id = alerts_db_pages[0].get("_id")
        alerts_db_page = alerts_db_pages[0]
    
        # Verify schema
        db_metadata = alerts_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Alert", "Item SKU", "Current Stock", "Category", "Urgency", "Action Required"]
    
        # Create property ID to name mapping for validating rows
        prop_id_to_name = {prop_data.get("_id"): prop_data.get("name") for prop_data in db_properties if isinstance(prop_data, dict)}
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query alert rows
        alert_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": alerts_db_id, "type": "row"}
        })
    
        if not isinstance(alert_rows, list):
            alert_rows = []
    
        if len(alert_rows) < len(low_stock_items):
            errors.append(f"[X] Expected {len(low_stock_items)} alert rows, found {len(alert_rows)}")
        else:
            checks_passed.append(f"[C] Expected alert rows")
    
        # Verify urgency logic
        for row in alert_rows:
            cell_values = row.get("cellValues", {})
            current_stock = None
            urgency = None
            action_required = None
        
            for key, val in cell_values.items():
                prop_name = prop_id_to_name.get(key, "").lower()
                if "current stock" in prop_name:
                    current_stock = val
                elif "urgency" in prop_name:
                    urgency = val
                elif "action required" in prop_name:
                    action_required = val
        
            if current_stock is not None:
                if current_stock < 5 and urgency != "Critical":
                    errors.append(f"[X] Item with stock {current_stock} < 5 should have Urgency='Critical', found '{urgency}'")
                elif current_stock >= 5 and urgency != "High":
                    errors.append(f"[X] Item with stock {current_stock} >= 5 should have Urgency='High', found '{urgency}'")
                else:
                    checks_passed.append(f"[C] Item with stock >= 5 should have Urgency='High'")
        
            if not action_required:
                errors.append("[X] Alert row should have Action Required=true")
            else:
                checks_passed.append("[C] Alert row should have Action Required=true")
    
        # Check for red callout on Bug Tracker page
        bug_tracker_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": bug_tracker_id}
        })
    
        if not isinstance(bug_tracker_blocks, list):
            bug_tracker_blocks = []
    
        found_red_callout = False
        for block in bug_tracker_blocks:
            if block.get("type") == "callout":
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", "")
                content = block.get("content", "")
            
                if "red" in bg_color.lower() and "inventory alert" in content.lower():
                    found_red_callout = True
                    break
    
        if not found_red_callout:
            errors.append("[X] Missing red callout on Bug Tracker page with inventory alert")
        else:
            checks_passed.append("[C] has red callout on Bug Tracker page with inventory alert")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 16: Sprint Planning Database
# =============================================================================
def _validate_agent_sprint_planning_database(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Sprint Planning database creation.
    
    Checks:
    1. Database exists under Q1 Goals page with correct schema
    2. Rows for each active project with task counts
    3. Priority logic applied correctly
    
    Note: Following user_prompt (success_criteria has conflict with different task structure)
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Q1 Goals page
    try:
        q1_goals_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Q1 Goals"}}
        })
    
        if not isinstance(q1_goals_pages, list) or len(q1_goals_pages) == 0:
            errors.append("[X] Q1 Goals page not found")
        else:
            checks_passed.append("[C] Q1 Goals page")
    
        q1_goals_id = q1_goals_pages[0].get("_id")
    
        # Find Sprint Planning database
        sprint_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": q1_goals_id, "title": {"$regex": "Sprint Planning"}}
        })
    
        if not isinstance(sprint_db_pages, list) or len(sprint_db_pages) == 0:
            errors.append("[X] Sprint Planning database not found under Q1 Goals")
        else:
            checks_passed.append("[C] Sprint Planning database found under Q1 Goals")
    
        sprint_db_id = sprint_db_pages[0].get("_id")
        sprint_db_page = sprint_db_pages[0]
    
        # Verify schema
        db_metadata = sprint_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Sprint Item", "Project", "Task Count", "Priority", "Sprint"]
    
        # Create property ID to name mapping for validating rows
        prop_id_to_name = {prop_data.get("_id"): prop_data.get("name") for prop_data in db_properties if isinstance(prop_data, dict)}
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query Project Portfolio for active projects
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        active_projects = []
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
        
            for key, val in cell_values.items():
                if "status" in key.lower():
                    status = val
                    break
        
            if status == "Active":
                active_projects.append(row)
    
        # Query sprint rows
        sprint_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": sprint_db_id, "type": "row"}
        })
    
        if not isinstance(sprint_rows, list):
            sprint_rows = []
    
        if len(sprint_rows) < len(active_projects):
            errors.append(f"[X] Expected {len(active_projects)} sprint rows for active projects, found {len(sprint_rows)}")
        else:
            checks_passed.append(f"[C] Expected sprint rows for active projects")
    
        # Verify priority logic and sprint assignment
        for row in sprint_rows:
            cell_values = row.get("cellValues", {})
            task_count = None
            priority = None
            sprint = None
        
            for key, val in cell_values.items():
                prop_name = prop_id_to_name.get(key, "").lower()
                if "task count" in prop_name:
                    task_count = val
                elif "priority" in prop_name:
                    priority = val
                elif "sprint" in prop_name and not "item" in prop_name:
                    sprint = val
        
            # Verify priority logic (High if count>3 else Medium)
            # Accept both P1/P2 format and High/Medium/Low format
            if task_count is not None:
                if task_count > 3:
                    if priority not in ["P1", "High"]:
                        errors.append(f"[X] Task count {task_count} > 3 should have Priority='High' or 'P1', found '{priority}'")
                    else:
                        checks_passed.append(f"[C] Task count > 3 should have Priority='High' or 'P1'")
                else:
                    if priority not in ["P2", "Medium", "Low"]:
                        errors.append(f"[X] Task count {task_count} <= 3 should have Priority='Medium', 'Low', or 'P2', found '{priority}'")
                    else:
                        checks_passed.append(f"[C] Task count <= 3 should have Priority='Medium', 'Low', or 'P2'")
        
            # Verify sprint is assigned (Week 1, Sprint 1, Sprint 2, etc.)
            if not sprint or len(str(sprint).strip()) == 0:
                errors.append(f"[X] Sprint assignment is required, found empty value")
            else:
                checks_passed.append(f"[C] Sprint assignment is required, found empty value")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 17: Weekly Status Report
# =============================================================================
def _validate_agent_weekly_status_report(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Weekly Status Report generation.
    
    Checks:
    1. Report page exists under Project Documentation
    2. Task metrics with correct completion rate
    3. Priority breakdown with counts
    4. Department workload with cross-reference
    5. Upcoming deadlines (next 7 days)
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Documentation page
    try:
        project_doc_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Documentation"}}
        })
    
        if not isinstance(project_doc_pages, list) or len(project_doc_pages) == 0:
            errors.append("[X] Project Documentation page not found")
        else:
            checks_passed.append("[C] Project Documentation page")
    
        project_doc_id = project_doc_pages[0].get("_id")
    
        # Find report page
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": project_doc_id, "title": {"$regex": "Weekly Status Report"}}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] Weekly Status Report page not found under Project Documentation")
        else:
            checks_passed.append("[C] Weekly Status Report page found under Project Documentation")
    
        report_page_id = report_pages[0].get("_id")
    
        # Query Task Tracker
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Calculate completion rate
        total_tasks = len(task_rows)
        done_tasks = sum(1 for r in task_rows if any("done" in str(v).lower() for v in r.get("cellValues", {}).values()))
        completion_rate = (done_tasks / total_tasks * 100) if total_tasks > 0 else 0
    
        # Get blocks for report
        report_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page_id}
        })
    
        if not isinstance(report_blocks, list):
            report_blocks = []
    
        report_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_task_metrics_h2 = False
        found_callout = False
        callout_color = None
        found_priority_h2 = False
        found_department_h2 = False
        found_deadlines_h2 = False
    
        for block in report_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "weekly status report" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "task metrics" in content.lower():
                found_task_metrics_h2 = True
        
            if block_type == "callout":
                found_callout = True
                callout_color = properties.get("backgroundColor", "")
        
            if block_type == "heading2" and "priority" in content.lower():
                found_priority_h2 = True
        
            if block_type == "heading2" and "department workload" in content.lower():
                found_department_h2 = True
        
            if block_type == "heading2" and "upcoming deadlines" in content.lower():
                found_deadlines_h2 = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Weekly Status Report'")
        else:
            checks_passed.append("[C] has heading_1 'Weekly Status Report'")
        if not found_task_metrics_h2:
            errors.append("[X] Missing heading_2 'Task Metrics'")
        else:
            checks_passed.append("[C] has heading_2 'Task Metrics'")
        if not found_callout:
            errors.append("[X] Missing callout with completion rate")
        else:
            checks_passed.append("[C] has callout with completion rate")
    
        # Verify callout color (green if >50%, else yellow)
        expected_color = "green" if completion_rate > 50 else "yellow"
        if expected_color not in callout_color.lower():
            errors.append(f"[X] Expected {expected_color} callout for {completion_rate:.1f}% completion, found {callout_color}")
        else:
            checks_passed.append(f"[C] Expected callout for % completion")
    
        if not found_priority_h2:
            errors.append("[X] Missing heading_2 'Tasks by Priority'")
        else:
            checks_passed.append("[C] has heading_2 'Tasks by Priority'")
        if not found_department_h2:
            errors.append("[X] Missing heading_2 'Department Workload'")
        else:
            checks_passed.append("[C] has heading_2 'Department Workload'")
        if not found_deadlines_h2:
            errors.append("[X] Missing heading_2 'Upcoming Deadlines'")
        else:
            checks_passed.append("[C] has heading_2 'Upcoming Deadlines'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 18: Resource Allocation Analysis
# =============================================================================
def _validate_agent_resource_allocation_analysis(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Resource Allocation Analysis report.
    
    Checks:
    1. Page exists under Getting Started
    2. Task distribution by engineer with priority breakdown
    3. Active project budget by lead
    4. Recommendations section with workload alerts
    5. Todo items for overloaded assignees
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Getting Started page
    try:
        getting_started_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Getting Started"}}
        })
    
        if not isinstance(getting_started_pages, list) or len(getting_started_pages) == 0:
            errors.append("[X] Getting Started page not found")
        else:
            checks_passed.append("[C] Getting Started page")
    
        getting_started_id = getting_started_pages[0].get("_id")
    
        # Find analysis page
        analysis_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": getting_started_id, "title": {"$regex": "Resource Allocation Analysis"}}
        })
    
        if not isinstance(analysis_pages, list) or len(analysis_pages) == 0:
            errors.append("[X] Resource Allocation Analysis page not found under Getting Started")
        else:
            checks_passed.append("[C] Resource Allocation Analysis page found under Getting Started")
    
        analysis_page_id = analysis_pages[0].get("_id")
    
        # Query Task Tracker
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Count tasks by assignee and priority
        assignee_tasks = {}
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            assignee = None
            priority = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "assignee" in key_lower:
                    assignee = val
                elif "priority" in key_lower:
                    priority = val
        
            if assignee:
                if assignee not in assignee_tasks:
                    assignee_tasks[assignee] = {"total": 0, "high_urgent": 0}
                assignee_tasks[assignee]["total"] += 1
                if priority in ["High", "Urgent"]:
                    assignee_tasks[assignee]["high_urgent"] += 1
    
        # Get blocks for analysis page
        analysis_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": analysis_page_id}
        })
    
        if not isinstance(analysis_blocks, list):
            analysis_blocks = []
    
        analysis_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_task_dist_h2 = False
        found_budget_h2 = False
        found_divider = False
        found_recommendations_h2 = False
        found_blue_callout = False
        found_todos = 0
    
        for block in analysis_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "resource allocation analysis" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "task distribution" in content.lower():
                found_task_dist_h2 = True
        
            if block_type == "heading2" and "budget by lead" in content.lower():
                found_budget_h2 = True
        
            if block_type == "divider":
                found_divider = True
        
            if block_type == "heading2" and "recommendations" in content.lower():
                found_recommendations_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "blue" in bg_color.lower() and "workload alert" in content.lower():
                    found_blue_callout = True
        
            if block_type == "todo":
                found_todos += 1
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Resource Allocation Analysis'")
        else:
            checks_passed.append("[C] has heading_1 'Resource Allocation Analysis'")
        if not found_task_dist_h2:
            errors.append("[X] Missing heading_2 'Task Distribution by Engineer'")
        else:
            checks_passed.append("[C] has heading_2 'Task Distribution by Engineer'")
        if not found_budget_h2:
            errors.append("[X] Missing heading_2 'Active Project Budget by Lead'")
        else:
            checks_passed.append("[C] has heading_2 'Active Project Budget by Lead'")
        if not found_divider:
            errors.append("[X] Missing divider before recommendations")
        else:
            checks_passed.append("[C] has divider before recommendations")
        if not found_recommendations_h2:
            errors.append("[X] Missing heading_2 'Recommendations'")
        else:
            checks_passed.append("[C] has heading_2 'Recommendations'")
        if not found_blue_callout:
            errors.append("[X] Missing blue callout with workload alert")
        else:
            checks_passed.append("[C] has blue callout with workload alert")
    
        # Check for todos for overloaded assignees (3+ High/Urgent tasks)
        overloaded_count = sum(1 for data in assignee_tasks.values() if data["high_urgent"] >= 3)
        if found_todos < overloaded_count:
            errors.append(f"[X] Expected {overloaded_count} todo items for overloaded assignees, found {found_todos}")
        else:
            checks_passed.append(f"[C] Expected todo items for overloaded assignees")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 19: Engineering Project Health
# =============================================================================
def _validate_agent_engineering_project_health(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Engineering Project Health inline database.
    
    Checks:
    1. Inline database exists in Weekly Standup Template after Blockers section
    2. Correct schema with health score logic
    3. Rows for active engineering projects with task counts
    4. Heading and paragraph above database
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Weekly Standup Template page
    try:
        standup_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Weekly Standup Template"}}
        })
    
        if not isinstance(standup_pages, list) or len(standup_pages) == 0:
            errors.append("[X] Weekly Standup Template page not found")
        else:
            checks_passed.append("[C] Weekly Standup Template page")
    
        standup_page_id = standup_pages[0].get("_id")
    
        # Get blocks
        standup_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": standup_page_id}
        })
    
        if not isinstance(standup_blocks, list):
            standup_blocks = []
    
        standup_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find Blockers section
        blockers_idx = -1
        for idx, block in enumerate(standup_blocks):
            if "blockers" in block.get("content", "").lower():
                blockers_idx = idx
                break
    
        if blockers_idx == -1:
            errors.append("[X] Blockers section not found in Weekly Standup Template")
        else:
            checks_passed.append("[C] Blockers section found in Weekly Standup Template")
    
        # Find database block after Blockers
        database_block = None
        found_eng_status_h2 = False
        found_paragraph = False
    
        for idx, block in enumerate(standup_blocks):
            if idx <= blockers_idx:
                continue
        
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "heading2" and "engineering projects status" in content.lower():
                found_eng_status_h2 = True
        
            if block_type == "paragraph" and "last updated" in content.lower():
                found_paragraph = True
        
            if block_type == "database":
                db_id = block.get("properties", {}).get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "engineering project health" in db_title.lower():
                            database_block = block
                            break
    
        if not found_eng_status_h2:
            errors.append("[X] Missing heading_2 'Engineering Projects Status'")
        else:
            checks_passed.append("[C] has heading_2 'Engineering Projects Status'")
        if not found_paragraph:
            errors.append("[X] Missing paragraph with 'Last updated' text")
        else:
            checks_passed.append("[C] has paragraph with 'Last updated' text")
        if not database_block:
            errors.append("[X] Engineering Project Health inline database not found after Blockers section")
        else:
            checks_passed.append("[C] Engineering Project Health inline database found after Blockers section")
    
        health_db_id = database_block.get("properties", {}).get("databaseId")
        health_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": health_db_id}
        })
    
        if not isinstance(health_db_pages, list) or len(health_db_pages) == 0:
            errors.append("[X] Health database page not found")
        else:
            checks_passed.append("[C] Health database page")
    
        health_db_page = health_db_pages[0]
    
        # Verify schema
        db_metadata = health_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Project", "Tasks Count", "High Priority Tasks", "Blocked Tasks", "Health Score"]
    
        # Create property ID to name mapping for validating rows
        prop_id_to_name = {prop_data.get("_id"): prop_data.get("name") for prop_data in db_properties if isinstance(prop_data, dict)}
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query Project Portfolio for active engineering projects
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        active_eng_projects = []
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
            department = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "status" in key_lower:
                    status = val
                elif "department" in key_lower:
                    department = val
        
            if status == "Active" and department == "Engineering":
                active_eng_projects.append(row)
    
        # Query health database rows
        health_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": health_db_id, "type": "row"}
        })
    
        if not isinstance(health_rows, list):
            health_rows = []
    
        if len(health_rows) < len(active_eng_projects):
            errors.append(f"[X] Expected {len(active_eng_projects)} rows for active engineering projects, found {len(health_rows)}")
        else:
            checks_passed.append(f"[C] Expected rows for active engineering projects")
    
        # Verify health score logic (Critical if any blocked, At Risk if >50% high priority, else Healthy)
        for row in health_rows:
            cell_values = row.get("cellValues", {})
            blocked_tasks = 0
            high_priority_tasks = 0
            tasks_count = 0
            health_score = None
        
            for key, val in cell_values.items():
                prop_name = prop_id_to_name.get(key, "").lower()
                if "blocked tasks" in prop_name:
                    blocked_tasks = val or 0
                elif "high priority tasks" in prop_name:
                    high_priority_tasks = val or 0
                elif "tasks count" in prop_name:
                    tasks_count = val or 0
                elif "health score" in prop_name:
                    health_score = val
        
            # Verify health score logic
            if blocked_tasks > 0:
                if health_score != "Critical":
                    errors.append(f"[X] Project with {blocked_tasks} blocked tasks should have Health Score='Critical', found '{health_score}'")
            elif tasks_count > 0 and (high_priority_tasks / tasks_count) > 0.5:
                if health_score != "At Risk":
                    errors.append(f"[X] Project with >50% high priority tasks should have Health Score='At Risk', found '{health_score}'")
                else:
                    checks_passed.append(f"[C] Project with >50% high priority tasks should have Health Score='At Risk'")
            else:
                if health_score != "Healthy":
                    errors.append(f"[X] Project should have Health Score='Healthy', found '{health_score}'")
                else:
                    checks_passed.append(f"[C] Project should have Health Score='Healthy'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 20: Resource Allocation Matrix in Team Retrospective
# =============================================================================
def _validate_agent_resource_allocation_matrix_team_retrospective(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Resource Allocation Matrix inline database in Team Retrospective.
    
    Checks:
    1. Inline database exists in Team Retrospective after quote block
    2. Correct schema with multi_select and calculated rows
    3. Inventory Tracker schema modifications
    4. Heading and paragraph above database
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Team Retrospective page
    try:
        retrospective_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Retrospective"}}
        })
    
        if not isinstance(retrospective_pages, list) or len(retrospective_pages) == 0:
            errors.append("[X] Team Retrospective page not found")
        else:
            checks_passed.append("[C] Team Retrospective page")
    
        retrospective_page_id = retrospective_pages[0].get("_id")
    
        # Get blocks
        retrospective_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": retrospective_page_id}
        })
    
        if not isinstance(retrospective_blocks, list):
            retrospective_blocks = []
    
        retrospective_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find quote block
        quote_idx = -1
        for idx, block in enumerate(retrospective_blocks):
            if block.get("type") == "quote":
                quote_idx = idx
                break
    
        if quote_idx == -1:
            errors.append("[X] Quote block not found in Team Retrospective")
        else:
            checks_passed.append("[C] Quote block found in Team Retrospective")
    
        # Find database block after quote
        database_block = None
        found_h3 = False
        found_paragraph = False
    
        for idx, block in enumerate(retrospective_blocks):
            if idx <= quote_idx:
                continue
        
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "heading3" and "resource utilization snapshot" in content.lower():
                found_h3 = True
        
            if block_type == "paragraph" and "critical finding" in content.lower():
                found_paragraph = True
        
            if block_type == "database":
                db_id = block.get("properties", {}).get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "resource allocation matrix" in db_title.lower():
                            database_block = block
                            break
    
        if not found_h3:
            errors.append("[X] Missing heading_3 'Resource Utilization Snapshot'")
        else:
            checks_passed.append("[C] has heading_3 'Resource Utilization Snapshot'")
        if not found_paragraph:
            errors.append("[X] Missing paragraph with 'Critical Finding' text")
        else:
            checks_passed.append("[C] has paragraph with 'Critical Finding' text")
        if not database_block:
            errors.append("[X] Resource Allocation Matrix inline database not found after quote block")
        else:
            checks_passed.append("[C] Resource Allocation Matrix inline database found after quote block")
    
        matrix_db_id = database_block.get("properties", {}).get("databaseId")
        matrix_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": matrix_db_id}
        })
    
        if not isinstance(matrix_db_pages, list) or len(matrix_db_pages) == 0:
            errors.append("[X] Matrix database page not found")
        else:
            checks_passed.append("[C] Matrix database page")
    
        matrix_db_page = matrix_db_pages[0]
    
        # Verify schema
        db_metadata = matrix_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Resource Type", "Total Available", "Allocated", "Utilization %", "Department", "Priority Level", "Next Review Date"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query matrix rows
        matrix_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": matrix_db_id, "type": "row"}
        })
    
        if not isinstance(matrix_rows, list):
            matrix_rows = []
    
        # Check for specific resource types
        found_developer_hours = False
        found_hardware_budget = False
        found_office_equipment = False
    
        for row in matrix_rows:
            resource_type = row.get("title", "")
            if "developer hours" in resource_type.lower():
                found_developer_hours = True
            elif "hardware budget" in resource_type.lower():
                found_hardware_budget = True
            elif "office equipment" in resource_type.lower():
                found_office_equipment = True
    
        if not found_developer_hours:
            errors.append("[X] Missing 'Developer Hours' resource row")
        else:
            checks_passed.append("[C] has 'Developer Hours' resource row")
        if not found_hardware_budget:
            errors.append("[X] Missing 'Hardware Budget' resource row")
        else:
            checks_passed.append("[C] has 'Hardware Budget' resource row")
        if not found_office_equipment:
            errors.append("[X] Missing 'Office Equipment' resource row")
        else:
            checks_passed.append("[C] has 'Office Equipment' resource row")
    
        # Check Inventory Tracker modifications
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_page = inventory_pages[0]
        inventory_metadata = inventory_page.get("databaseMetadata", {})
        inventory_properties = inventory_metadata.get("properties", [])
    
        # Check for renamed property and new property
        found_next_review = False
        found_allocated_dept = False
    
        for prop_data in inventory_properties:
            if isinstance(prop_data, dict):
                prop_name = prop_data.get("name", "")
                if "next inventory review" in prop_name.lower():
                    found_next_review = True
                elif "allocated department" in prop_name.lower():
                    found_allocated_dept = True
    
        if not found_next_review:
            errors.append("[X] Inventory Tracker should have 'Next Inventory Review' property (renamed from 'Reorder Date')")
        else:
            checks_passed.append("[C] Inventory Tracker should have 'Next Inventory Review' property (renamed from 'Reorder Date')")
        if not found_allocated_dept:
            errors.append("[X] Inventory Tracker should have 'Allocated Department' property")
        else:
            checks_passed.append("[C] Inventory Tracker should have 'Allocated Department' property")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 21: Critical Tasks Dashboard
# =============================================================================
def _validate_agent_critical_tasks_dashboard(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Critical Tasks Dashboard creation.
    
    Checks:
    1. Tasks with Priority High/Urgent and Status not Done
    2. Dashboard page exists under Engineering Wiki
    3. Red callout with count
    4. Status breakdown and overdue analysis
    5. New task row created in Task Tracker
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Task Tracker database
    try:
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Query tasks
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Find critical tasks
        critical_tasks = []
        overdue_tasks = []
    
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            priority = None
            status = None
            due_date = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "priority" in key_lower:
                    priority = val
                elif "status" in key_lower:
                    status = val
                elif "due" in key_lower and isinstance(val, dict):
                    due_date = val.get("start")
        
            if priority in ["High", "Urgent"] and status != "Done":
                critical_tasks.append(row)
            
                if due_date and due_date < "2026-01-13":
                    overdue_tasks.append(row)
    
        # Find Engineering Wiki page
        eng_wiki_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Engineering Wiki"}}
        })
    
        if not isinstance(eng_wiki_pages, list) or len(eng_wiki_pages) == 0:
            errors.append("[X] Engineering Wiki page not found")
        else:
            checks_passed.append("[C] Engineering Wiki page")
    
        eng_wiki_id = eng_wiki_pages[0].get("_id")
    
        # Find dashboard page
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": eng_wiki_id, "title": {"$regex": "Critical Tasks Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Critical Tasks Dashboard page not found under Engineering Wiki")
        else:
            checks_passed.append("[C] Critical Tasks Dashboard page found under Engineering Wiki")
    
        dashboard_page_id = dashboard_pages[0].get("_id")
    
        # Get blocks for dashboard
        dashboard_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page_id}
        })
    
        if not isinstance(dashboard_blocks, list):
            dashboard_blocks = []
    
        dashboard_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_red_callout = False
        found_status_h2 = False
        found_overdue_h2 = False
        found_numbered_list = False
    
        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "critical tasks dashboard" in content.lower():
                found_h1 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "red" in bg_color.lower() and str(len(critical_tasks)) in content:
                    found_red_callout = True
        
            if block_type == "heading2" and "status breakdown" in content.lower():
                found_status_h2 = True
        
            if block_type == "heading2" and "overdue analysis" in content.lower():
                found_overdue_h2 = True
        
            if block_type == "numberedList" and found_overdue_h2:
                found_numbered_list = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Critical Tasks Dashboard'")
        else:
            checks_passed.append("[C] has heading_1 'Critical Tasks Dashboard'")
        if not found_red_callout:
            errors.append("[X] Missing red callout with critical tasks count")
        else:
            checks_passed.append("[C] has red callout with critical tasks count")
        if not found_status_h2:
            errors.append("[X] Missing heading_2 'Status Breakdown'")
        else:
            checks_passed.append("[C] has heading_2 'Status Breakdown'")
        if not found_overdue_h2:
            errors.append("[X] Missing heading_2 'Overdue Analysis'")
        else:
            checks_passed.append("[C] has heading_2 'Overdue Analysis'")
        if not found_numbered_list:
            errors.append("[X] Missing numbered list with overdue tasks")
        else:
            checks_passed.append("[C] has numbered list with overdue tasks")
    
        # Check for new task row
        review_task = None
        for row in task_rows:
            if "review critical tasks" in row.get("title", "").lower():
                review_task = row
                break
    
        if not review_task:
            errors.append("[X] Missing 'Review Critical Tasks' row in Task Tracker")
        else:
            checks_passed.append("[C] has 'Review Critical Tasks' row in Task Tracker")
    
        # Verify new task properties
        cell_values = review_task.get("cellValues", {})
        status = None
        priority = None
    
        for key, val in cell_values.items():
            key_lower = key.lower()
            if "status" in key_lower:
                status = val
            elif "priority" in key_lower:
                priority = val
    
        if status != "In Progress":
            errors.append(f"[X] Review task should have Status='In Progress', found '{status}'")
        else:
            checks_passed.append(f"[C] Review task should have Status='In Progress'")
        if priority != "Urgent":
            errors.append(f"[X] Review task should have Priority='Urgent', found '{priority}'")
        else:
            checks_passed.append(f"[C] Review task should have Priority='Urgent'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 22: Inventory Health Dashboard
# =============================================================================
def _validate_agent_inventory_health_dashboard(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Inventory Health Dashboard with Stock Status.
    
    Checks:
    1. Stock Status property added to Inventory Tracker with options
    2. Stock Status set correctly based on quantity ranges
    3. Dashboard page with correct structure
    4. Total inventory value calculation
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Inventory Tracker database
    try:
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_id = inventory_pages[0].get("_id")
        inventory_page = inventory_pages[0]
    
        # Check Stock Status property
        db_metadata = inventory_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        stock_status_prop = None
        for prop_data in db_properties:
            if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == "stock status":
                stock_status_prop = prop_data
                break
    
        if not stock_status_prop:
            errors.append("[X] Stock Status property not found in Inventory Tracker")
        else:
            checks_passed.append("[C] Stock Status property found in Inventory Tracker")
    
        # Verify options
        options = stock_status_prop.get("options", [])
        required_options = {"Critical", "Low", "Normal", "Overstock"}
        found_options = set()
        for opt in options:
            if isinstance(opt, dict):
                found_options.add(opt.get("name", ""))
    
        if required_options != found_options:
            errors.append(f"[X] Stock Status options incorrect. Expected {required_options}, found {found_options}")
        else:
            checks_passed.append(f"[C] Stock Status options correct")
    
        # Query inventory rows
        inventory_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_id, "type": "row"}
        })
    
        if not isinstance(inventory_rows, list):
            inventory_rows = []
    
        # Verify status logic and calculate total value
        stock_status_prop_id = stock_status_prop.get("_id")
        total_value = 0
        status_counts = {"Critical": 0, "Low": 0, "Normal": 0, "Overstock": 0}
    
        for row in inventory_rows:
            cell_values = row.get("cellValues", {})
            quantity = None
            unit_price = 0
            stock_status = cell_values.get(stock_status_prop_id)
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "quantity" in key_lower and isinstance(val, (int, float)):
                    quantity = val
                elif "price" in key_lower and isinstance(val, (int, float)):
                    unit_price = val
        
            if quantity is not None:
                total_value += quantity * unit_price
            
                # Verify stock status logic
                if quantity < 10:
                    if stock_status != "Critical":
                        errors.append(f"[X] Item with quantity {quantity} < 10 should have Stock Status='Critical', found '{stock_status}'")
                elif 10 <= quantity <= 25:
                    if stock_status != "Low":
                        errors.append(f"[X] Item with quantity {quantity} 10-25 should have Stock Status='Low', found '{stock_status}'")
                elif 26 <= quantity <= 100:
                    if stock_status != "Normal":
                        errors.append(f"[X] Item with quantity {quantity} 26-100 should have Stock Status='Normal', found '{stock_status}'")
                elif quantity > 100:
                    if stock_status != "Overstock":
                        errors.append(f"[X] Item with quantity {quantity} > 100 should have Stock Status='Overstock', found '{stock_status}'")
                    else:
                        checks_passed.append(f"[C] Item with quantity > 100 should have Stock Status='Overstock'")
            
                if stock_status in status_counts:
                    status_counts[stock_status] += 1
    
        # Find dashboard page under inventory parent
        inventory_parent_id = inventory_page.get("parentId")
    
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": inventory_parent_id, "title": {"$regex": "Inventory Health Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Inventory Health Dashboard page not found")
        else:
            checks_passed.append("[C] Inventory Health Dashboard page")
    
        dashboard_page_id = dashboard_pages[0].get("_id")
    
        # Get blocks for dashboard
        dashboard_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page_id}
        })
    
        if not isinstance(dashboard_blocks, list):
            dashboard_blocks = []
    
        dashboard_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_critical_h2 = False
        found_green_callout = False
        found_distribution_h2 = False
    
        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "inventory health dashboard" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "critical items" in content.lower():
                found_critical_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "green" in bg_color.lower() and str(int(total_value)) in content:
                    found_green_callout = True
        
            if block_type == "heading2" and "stock distribution" in content.lower():
                found_distribution_h2 = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Inventory Health Dashboard'")
        else:
            checks_passed.append("[C] has heading_1 'Inventory Health Dashboard'")
        if not found_critical_h2:
            errors.append("[X] Missing heading_2 'Critical Items'")
        else:
            checks_passed.append("[C] has heading_2 'Critical Items'")
        if not found_green_callout:
            errors.append("[X] Missing green callout with total inventory value")
        else:
            checks_passed.append("[C] has green callout with total inventory value")
        if not found_distribution_h2:
            errors.append("[X] Missing heading_2 'Stock Distribution'")
        else:
            checks_passed.append("[C] has heading_2 'Stock Distribution'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 23: Urgent Tasks Dashboard with Auto-Updates
# =============================================================================
def _validate_agent_urgent_tasks_dashboard_auto_updates(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Urgent Tasks Dashboard with automatic status updates.
    
    Checks:
    1. Tasks with Status=Not Started and Priority=High/Urgent updated to In Progress
    2. 'Prioritized' tag added to updated tasks
    3. Dashboard page with correct structure under Engineering Wiki
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Task Tracker database
    try:
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Query tasks
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Check updated tasks
        updated_tasks = []
        high_count = 0
        urgent_count = 0
    
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            priority = None
            status = None
            tags = []
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "priority" in key_lower:
                    priority = val
                elif "status" in key_lower:
                    status = val
                elif "tag" in key_lower and isinstance(val, list):
                    tags = val
        
            # Check if this was an originally Not Started High/Urgent task
            # (simplified: check if it has Prioritized tag and In Progress status)
            if "Prioritized" in tags and status == "In Progress" and priority in ["High", "Urgent"]:
                updated_tasks.append(row)
            
                if priority == "High":
                    high_count += 1
                elif priority == "Urgent":
                    urgent_count += 1
    
        if len(updated_tasks) == 0:
            errors.append("[X] No tasks found with Status='In Progress' and 'Prioritized' tag")
        else:
            checks_passed.append("[C] tasks found with Status='In Progress' and 'Prioritized' tag")
    
        # Find Engineering Wiki page
        eng_wiki_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Engineering Wiki"}}
        })
    
        if not isinstance(eng_wiki_pages, list) or len(eng_wiki_pages) == 0:
            errors.append("[X] Engineering Wiki page not found")
        else:
            checks_passed.append("[C] Engineering Wiki page")
    
        eng_wiki_id = eng_wiki_pages[0].get("_id")
    
        # Find dashboard page
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": eng_wiki_id, "title": {"$regex": "Urgent Tasks Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Urgent Tasks Dashboard page not found under Engineering Wiki")
        else:
            checks_passed.append("[C] Urgent Tasks Dashboard page found under Engineering Wiki")
    
        dashboard_page_id = dashboard_pages[0].get("_id")
    
        # Get blocks for dashboard
        dashboard_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page_id}
        })
    
        if not isinstance(dashboard_blocks, list):
            dashboard_blocks = []
    
        dashboard_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_yellow_callout = False
        found_breakdown_h2 = False
        found_updated_h2 = False
        found_numbered_list = False
    
        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "urgent tasks dashboard" in content.lower():
                found_h1 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "yellow" in bg_color.lower() and str(len(updated_tasks)) in content:
                    found_yellow_callout = True
        
            if block_type == "heading2" and "task breakdown" in content.lower():
                found_breakdown_h2 = True
        
            if block_type == "heading2" and "updated tasks" in content.lower():
                found_updated_h2 = True
        
            if block_type == "numberedList" and found_updated_h2:
                found_numbered_list = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Urgent Tasks Dashboard'")
        else:
            checks_passed.append("[C] has heading_1 'Urgent Tasks Dashboard'")
        if not found_yellow_callout:
            errors.append("[X] Missing yellow callout with updated count")
        else:
            checks_passed.append("[C] has yellow callout with updated count")
        if not found_breakdown_h2:
            errors.append("[X] Missing heading_2 'Task Breakdown by Priority'")
        else:
            checks_passed.append("[C] has heading_2 'Task Breakdown by Priority'")
        if not found_updated_h2:
            errors.append("[X] Missing heading_2 'Updated Tasks List'")
        else:
            checks_passed.append("[C] has heading_2 'Updated Tasks List'")
        if not found_numbered_list:
            errors.append("[X] Missing numbered list with updated tasks")
        else:
            checks_passed.append("[C] has numbered list with updated tasks")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 24: Resource Allocation Matrix under Team Directory
# =============================================================================
def _validate_agent_resource_allocation_matrix_team_directory(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Resource Allocation Matrix database under Team Directory.
    
    Checks:
    1. Database exists under Team Directory with correct schema
    2. Rows for each unique assignee from Task Tracker
    3. Allocation % calculations correct
    4. Skills mapped from task tags
    5. Summary row exists
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Team Directory page
    try:
        team_dir_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Directory"}}
        })
    
        if not isinstance(team_dir_pages, list) or len(team_dir_pages) == 0:
            errors.append("[X] Team Directory page not found")
        else:
            checks_passed.append("[C] Team Directory page")
    
        team_dir_id = team_dir_pages[0].get("_id")
    
        # Find Resource Allocation Matrix database
        matrix_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": team_dir_id, "title": {"$regex": "Resource Allocation Matrix"}}
        })
    
        if not isinstance(matrix_db_pages, list) or len(matrix_db_pages) == 0:
            errors.append("[X] Resource Allocation Matrix database not found under Team Directory")
        else:
            checks_passed.append("[C] Resource Allocation Matrix database found under Team Directory")
    
        matrix_db_id = matrix_db_pages[0].get("_id")
        matrix_db_page = matrix_db_pages[0]
    
        # Verify schema
        db_metadata = matrix_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Resource ID", "Team Member", "Current Project", "Allocation %", "Skills", "Availability", "Next Available"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query Task Tracker for assignees
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Get unique assignees
        assignees = set()
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            for key, val in cell_values.items():
                if "assignee" in key.lower() and val:
                    assignees.add(val)
                    break
    
        # Query matrix rows
        matrix_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": matrix_db_id, "type": "row"}
        })
    
        if not isinstance(matrix_rows, list):
            matrix_rows = []
    
        # Check for rows for each assignee
        assignee_rows = [r for r in matrix_rows if "RES-" in r.get("title", "") and "-2026" in r.get("title", "")]
    
        if len(assignee_rows) < len(assignees):
            errors.append(f"[X] Expected at least {len(assignees)} resource rows, found {len(assignee_rows)}")
        else:
            checks_passed.append(f"[C] Expected at least resource rows")
    
        # Check for summary row
        summary_row = None
        for row in matrix_rows:
            if "TOTAL-CAPACITY" in row.get("title", ""):
                summary_row = row
                break
    
        if not summary_row:
            errors.append("[X] Missing TOTAL-CAPACITY summary row")
        else:
            checks_passed.append("[C] has TOTAL-CAPACITY summary row")
    
        # Verify summary has averaged allocation
        # Create property ID to name mapping
        prop_id_to_name = {
            prop_data.get("_id"): prop_data.get("name")
            for prop_data in db_properties
            if isinstance(prop_data, dict)
        }
    
        summary_cells = summary_row.get("cellValues", {})
        summary_allocation = None
    
        for key, val in summary_cells.items():
            prop_name = prop_id_to_name.get(key, "").lower()
            if "allocation" in prop_name and isinstance(val, (int, float)):
                summary_allocation = val
                break
    
        if summary_allocation is None:
            errors.append("[X] Summary row should have Allocation % set")
        else:
            checks_passed.append("[C] Summary row should have Allocation % set")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 25: Quarterly Business Review Inline Database
# =============================================================================
def _validate_agent_quarterly_business_review_inline_database(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Quarterly Business Review inline database in 2024 Product Roadmap.
    
    Checks:
    1. Inline database exists immediately after Strategic Priorities section
    2. Correct schema with 8 properties
    3. Q1 row with calculated values from Project Portfolio
    4. Callout above database with correct color
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find 2024 Product Roadmap page
    try:
        roadmap_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "2024 Product Roadmap"}}
        })
    
        if not isinstance(roadmap_pages, list) or len(roadmap_pages) == 0:
            errors.append("[X] 2024 Product Roadmap page not found")
        else:
            checks_passed.append("[C] 2024 Product Roadmap page")
    
        roadmap_page_id = roadmap_pages[0].get("_id")
    
        # Get blocks
        roadmap_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": roadmap_page_id}
        })
    
        if not isinstance(roadmap_blocks, list):
            roadmap_blocks = []
    
        roadmap_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find Strategic Priorities section
        strategic_idx = -1
        for idx, block in enumerate(roadmap_blocks):
            if "strategic priorities" in block.get("content", "").lower():
                strategic_idx = idx
                break
    
        if strategic_idx == -1:
            errors.append("[X] Strategic Priorities section not found in 2024 Product Roadmap")
        else:
            checks_passed.append("[C] Strategic Priorities section found in 2024 Product Roadmap")
    
        # Find database block after Strategic Priorities
        database_block = None
        found_callout = False
        callout_color = None
    
        for idx, block in enumerate(roadmap_blocks):
            if idx <= strategic_idx:
                continue
        
            block_type = block.get("type", "")
            properties = block.get("properties", {})
        
            if block_type == "callout":
                found_callout = True
                callout_color = properties.get("backgroundColor", "")
        
            if block_type == "database":
                db_id = properties.get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "quarterly business review" in db_title.lower():
                            database_block = block
                            break
    
        if not database_block:
            errors.append("[X] Quarterly Business Review inline database not found after Strategic Priorities")
        else:
            checks_passed.append("[C] Quarterly Business Review inline database found after Strategic Priorities")
    
        review_db_id = database_block.get("properties", {}).get("databaseId")
        review_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": review_db_id}
        })
    
        if not isinstance(review_db_pages, list) or len(review_db_pages) == 0:
            errors.append("[X] Review database page not found")
        else:
            checks_passed.append("[C] Review database page")
    
        review_db_page = review_db_pages[0]
    
        # Verify schema
        db_metadata = review_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Quarter", "Revenue Target", "Actual Revenue", "Key Wins", "Challenges", "Overall Status", "Completion %", "Review Date"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query review rows
        review_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": review_db_id, "type": "row"}
        })
    
        if not isinstance(review_rows, list):
            review_rows = []
    
        # Find Q1 2024 row
        q1_row = None
        for row in review_rows:
            if "q1 2024" in row.get("title", "").lower():
                q1_row = row
                break
    
        if not q1_row:
            errors.append("[X] Q1 2024 row not found in Quarterly Business Review database")
        else:
            checks_passed.append("[C] Q1 2024 row found in Quarterly Business Review database")
    
        # Verify Q1 calculations
        # Create property ID to name mapping
        prop_id_to_name = {
            prop_data.get("_id"): prop_data.get("name")
            for prop_data in db_properties
            if isinstance(prop_data, dict)
        }
    
        cell_values = q1_row.get("cellValues", {})
        actual_revenue = None
        overall_status = None
    
        for key, val in cell_values.items():
            prop_name = prop_id_to_name.get(key, "").lower()
            if "actual revenue" in prop_name:
                actual_revenue = val
            elif "overall status" in prop_name:
                overall_status = val
    
        if actual_revenue is None:
            errors.append("[X] Q1 row should have Actual Revenue calculated")
        else:
            checks_passed.append("[C] Q1 row should have Actual Revenue calculated")
        if overall_status is None:
            errors.append("[X] Q1 row should have Overall Status set")
        else:
            checks_passed.append("[C] Q1 row should have Overall Status set")
    
        if not found_callout:
            errors.append("[X] Missing callout above database")
        else:
            checks_passed.append("[C] has callout above database")
    
        # Verify callout color matches status
        expected_color = "green" if overall_status == "Exceeded" else "yellow"
        if expected_color not in callout_color.lower():
            errors.append(f"[X] Expected {expected_color} callout for status '{overall_status}', found {callout_color}")
        else:
            checks_passed.append(f"[C] Expected callout for status")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 26: Resource Allocation Tracker in Company Handbook
# =============================================================================
def _validate_agent_resource_allocation_tracker_company_handbook(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Resource Allocation Tracker inline database in Company Handbook.
    
    Checks:
    1. Section exists after Benefits section
    2. Inline database with correct schema
    3. Rows for each Inventory category with calculations
    4. Department assignments correct
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Company Handbook page
    try:
        handbook_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Company Handbook"}}
        })
    
        if not isinstance(handbook_pages, list) or len(handbook_pages) == 0:
            errors.append("[X] Company Handbook page not found")
        else:
            checks_passed.append("[C] Company Handbook page")
    
        handbook_page_id = handbook_pages[0].get("_id")
    
        # Get blocks
        handbook_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": handbook_page_id}
        })
    
        if not isinstance(handbook_blocks, list):
            handbook_blocks = []
    
        handbook_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find Benefits section
        benefits_idx = -1
        for idx, block in enumerate(handbook_blocks):
            if "benefits" in block.get("content", "").lower() and block.get("type") in ["heading2", "heading3"]:
                benefits_idx = idx
                break
    
        if benefits_idx == -1:
            errors.append("[X] Benefits section not found in Company Handbook")
        else:
            checks_passed.append("[C] Benefits section found in Company Handbook")
    
        # Find new section after Benefits
        found_tracker_h2 = False
        database_block = None
    
        for idx, block in enumerate(handbook_blocks):
            if idx <= benefits_idx:
                continue
        
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "heading2" and "resource allocation tracker" in content.lower():
                found_tracker_h2 = True
        
            if block_type == "database":
                db_id = block.get("properties", {}).get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "resource allocation tracker" in db_title.lower():
                            database_block = block
                            break
    
        if not found_tracker_h2:
            errors.append("[X] Missing heading_2 'Resource Allocation Tracker' after Benefits")
        else:
            checks_passed.append("[C] has heading_2 'Resource Allocation Tracker' after Benefits")
        if not database_block:
            errors.append("[X] Resource Allocation Tracker inline database not found")
        else:
            checks_passed.append("[C] Resource Allocation Tracker inline database")
    
        tracker_db_id = database_block.get("properties", {}).get("databaseId")
        tracker_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": tracker_db_id}
        })
    
        if not isinstance(tracker_db_pages, list) or len(tracker_db_pages) == 0:
            errors.append("[X] Tracker database page not found")
        else:
            checks_passed.append("[C] Tracker database page")
    
        tracker_db_page = tracker_db_pages[0]
    
        # Verify schema
        db_metadata = tracker_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Resource Type", "Category", "Budget Allocated", "Budget Used", "Quarter", "Department"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query Inventory Tracker categories
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_id = inventory_pages[0].get("_id")
    
        inventory_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_id, "type": "row"}
        })
    
        if not isinstance(inventory_rows, list):
            inventory_rows = []
    
        # Get unique categories
        categories = set()
        for row in inventory_rows:
            cell_values = row.get("cellValues", {})
            for key, val in cell_values.items():
                if "category" in key.lower():
                    categories.add(val)
                    break
    
        # Query tracker rows
        tracker_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": tracker_db_id, "type": "row"}
        })
    
        if not isinstance(tracker_rows, list):
            tracker_rows = []
    
        # Check for rows for each category
        if len(tracker_rows) < len(categories):
            errors.append(f"[X] Expected at least {len(categories)} tracker rows for inventory categories, found {len(tracker_rows)}")
        else:
            checks_passed.append(f"[C] Expected at least tracker rows for inventory categories")
    
        # Create property ID to name mapping
        prop_id_to_name = {
            prop_data.get("_id"): prop_data.get("name")
            for prop_data in db_properties
            if isinstance(prop_data, dict)
        }
    
        # Verify calculations (Budget Allocated = total value * 1.2, Budget Used = total value)
        for row in tracker_rows:
            cell_values = row.get("cellValues", {})
            budget_allocated = None
            budget_used = None
        
            for key, val in cell_values.items():
                prop_name = prop_id_to_name.get(key, "").lower()
                if "budget allocated" in prop_name:
                    budget_allocated = val
                elif "budget used" in prop_name:
                    budget_used = val
        
            if budget_allocated is not None and budget_used is not None:
                # Verify 20% buffer (allocated = used * 1.2)
                expected_allocated = budget_used * 1.2
                if abs(budget_allocated - expected_allocated) > 1:
                    errors.append(f"[X] Budget Allocated should be Budget Used * 1.2, found {budget_allocated} vs expected {expected_allocated}")
                else:
                    checks_passed.append(f"[C] Budget Allocated should be Budget Used * 1.2 vs expected")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 27: Financial Overview Dashboard at Root
# =============================================================================
def _validate_agent_financial_overview_dashboard_root(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Financial Overview Dashboard at root workspace level.
    
    Checks:
    1. Page exists at root level
    2. Callouts for each department with correct colors
    3. Budget Tracking inline database with calculations
    4. Status and Alert logic correct
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Financial Overview Dashboard at root (no parent or root parent)
    try:
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Financial Overview Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Financial Overview Dashboard page not found")
        else:
            checks_passed.append("[C] Financial Overview Dashboard page")
    
        # Find the one at root level (parentId is None or empty)
        dashboard_page = None
        for page in dashboard_pages:
            parent_id = page.get("parentId")
            if not parent_id or parent_id == "":
                dashboard_page = page
                break
    
        if not dashboard_page:
            errors.append("[X] Financial Overview Dashboard not found at root level")
        else:
            checks_passed.append("[C] Financial Overview Dashboard found at root level")
    
        dashboard_page_id = dashboard_page.get("_id")
    
        # Get blocks
        dashboard_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page_id}
        })
    
        if not isinstance(dashboard_blocks, list):
            dashboard_blocks = []
    
        dashboard_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_budget_analysis_h2 = False
        found_utilization_h2 = False
        department_callouts = []
        database_block = None
    
        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "heading1" and "financial overview" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "budget analysis" in content.lower():
                found_budget_analysis_h2 = True
        
            if block_type == "heading2" and "budget utilization" in content.lower():
                found_utilization_h2 = True
        
            if block_type == "callout":
                department_callouts.append(block)
        
            if block_type == "database":
                db_id = block.get("properties", {}).get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "budget tracking" in db_title.lower():
                            database_block = block
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Financial Overview Dashboard'")
        else:
            checks_passed.append("[C] has heading_1 'Financial Overview Dashboard'")
        if not found_budget_analysis_h2:
            errors.append("[X] Missing heading_2 'Budget Analysis by Department'")
        else:
            checks_passed.append("[C] has heading_2 'Budget Analysis by Department'")
        if len(department_callouts) < 3:
            errors.append(f"[X] Expected at least 3 department callouts, found {len(department_callouts)}")
        else:
            checks_passed.append(f"[C] Found at least 3 department callouts")
        if not found_utilization_h2:
            errors.append("[X] Missing heading_2 'Project Budget Utilization'")
        else:
            checks_passed.append("[C] has heading_2 'Project Budget Utilization'")
        if not database_block:
            errors.append("[X] Budget Tracking inline database not found")
        else:
            checks_passed.append("[C] Budget Tracking inline database")
    
        # Verify Budget Tracking database
        tracking_db_id = database_block.get("properties", {}).get("databaseId")
        tracking_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": tracking_db_id}
        })
    
        if not isinstance(tracking_db_pages, list) or len(tracking_db_pages) == 0:
            errors.append("[X] Budget Tracking database page not found")
        else:
            checks_passed.append("[C] Budget Tracking database page")
    
        tracking_db_page = tracking_db_pages[0]
    
        # Verify schema
        db_metadata = tracking_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Project", "Original Budget", "Spent to Date", "Remaining", "Utilization %", "Status", "Alert"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query tracking rows
        tracking_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": tracking_db_id, "type": "row"}
        })
    
        if not isinstance(tracking_rows, list):
            tracking_rows = []
    
        # Create property ID to name mapping
        prop_id_to_name = {
            prop_data.get("_id"): prop_data.get("name")
            for prop_data in db_properties
            if isinstance(prop_data, dict)
        }
    
        # Verify calculations and status logic
        for row in tracking_rows:
            cell_values = row.get("cellValues", {})
            original_budget = None
            spent = None
            remaining = None
            utilization = None
            status = None
            alert = None
        
            for key, val in cell_values.items():
                prop_name = prop_id_to_name.get(key, "").lower()
                if "original budget" in prop_name:
                    original_budget = val
                elif "spent" in prop_name:
                    spent = val
                elif "remaining" in prop_name:
                    remaining = val
                elif "utilization" in prop_name:
                    utilization = val
                elif "status" in prop_name:
                    status = val
                elif "alert" in prop_name:
                    alert = val
        
            if original_budget and spent:
                expected_remaining = original_budget - spent
                if remaining is not None and abs(remaining - expected_remaining) > 0.01:
                    errors.append(f"[X] Remaining calculation incorrect for project row")
                else:
                    checks_passed.append(f"[C] Remaining calculation correct for project row")
            
                expected_utilization = (spent / original_budget * 100) if original_budget > 0 else 0
                if utilization is not None and abs(utilization - expected_utilization) > 0.1:
                    errors.append(f"[X] Utilization % calculation incorrect")
                else:
                    checks_passed.append(f"[C] Utilization % calculation correct")
            
                # Verify Status logic (<90% Under Budget, 90-110% On Track, >110% Over Budget)
                if utilization < 90 and status != "Under Budget":
                    errors.append(f"[X] Utilization {utilization}% < 90 should have Status='Under Budget'")
                elif 90 <= utilization <= 110 and status != "On Track":
                    errors.append(f"[X] Utilization {utilization}% 90-110 should have Status='On Track'")
                elif utilization > 110 and status != "Over Budget":
                    errors.append(f"[X] Utilization {utilization}% > 110 should have Status='Over Budget'")
                else:
                    checks_passed.append(f"[C] Utilization % > 110 should have Status='Over Budget'")
            
                # Verify Alert (true if Over Budget)
                if status == "Over Budget" and not alert:
                    errors.append("[X] Alert should be true for Over Budget status")
                else:
                    checks_passed.append("[C] Alert should be true for Over Budget status")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 28: Inventory Alert System with Schema Changes
# =============================================================================
def _validate_agent_inventory_alert_system_schema_changes(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate comprehensive inventory audit with schema updates.
    
    Checks:
    1. Inventory Tracker schema updated (Reorder Level added, Supplier added, In Stock renamed to Stock Status)
    2. Reorder Levels set correctly by category
    3. Stock Status updated based on Quantity vs Reorder Level
    4. Alert page with correct structure under Bug Tracker
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Inventory Tracker database
    try:
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_page = inventory_pages[0]
        inventory_id = inventory_page.get("_id")
    
        # Check schema changes
        db_metadata = inventory_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        found_reorder_level = False
        found_supplier = False
        found_stock_status = False
        found_old_in_stock = False
    
        for prop_data in db_properties:
            if isinstance(prop_data, dict):
                prop_name = prop_data.get("name", "")
                if "reorder level" in prop_name.lower():
                    found_reorder_level = True
                elif "supplier" in prop_name.lower():
                    found_supplier = True
                elif "stock status" in prop_name.lower():
                    found_stock_status = True
                elif prop_name.lower() == "in stock":
                    found_old_in_stock = True
    
        if not found_reorder_level:
            errors.append("[X] Reorder Level property not found in Inventory Tracker")
        else:
            checks_passed.append("[C] Reorder Level property found in Inventory Tracker")
        if not found_supplier:
            errors.append("[X] Supplier property not found in Inventory Tracker")
        else:
            checks_passed.append("[C] Supplier property found in Inventory Tracker")
        if not found_stock_status:
            errors.append("[X] Stock Status property not found (should be renamed from 'In Stock')")
        else:
            checks_passed.append("[C] Stock Status property found (should be renamed from 'In Stock')")
        if found_old_in_stock:
            errors.append("[X] 'In Stock' property should be renamed to 'Stock Status'")
        else:
            checks_passed.append("[C] 'In Stock' property should be renamed to 'Stock Status'")
    
        # Query inventory rows
        inventory_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_id, "type": "row"}
        })
    
        if not isinstance(inventory_rows, list):
            inventory_rows = []
    
        # Build property name to ID map for UUID support
        prop_id_map = {}
        for prop_data in db_properties:
            if isinstance(prop_data, dict):
                prop_name = prop_data.get("name", "").lower()
                prop_id = prop_data.get("_id", "")
                if prop_name and prop_id:
                    prop_id_map[prop_name] = prop_id
    
        # Verify reorder levels by category and stock status updates
        low_stock_items = 0
        for row in inventory_rows:
            cell_values = row.get("cellValues", {})
            category = None
            quantity = None
            reorder_level = None
            stock_status = None
        
            # Use property ID map to handle UUID-based property IDs
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "category" in key_lower:
                    category = val
                elif "quantity" in key_lower and isinstance(val, (int, float)):
                    quantity = val
        
            # Get values by property ID (handles UUIDs)
            reorder_level_id = prop_id_map.get("reorder level")
            if reorder_level_id:
                reorder_level = cell_values.get(reorder_level_id)
        
            stock_status_id = prop_id_map.get("stock status")
            if stock_status_id:
                stock_status = cell_values.get(stock_status_id)
        
            # Verify reorder level by category
            if category:
                if category == "Electronics" and reorder_level != 5:
                    errors.append(f"[X] Electronics should have Reorder Level=5, found {reorder_level}")
                elif category == "Office Supplies" and reorder_level != 10:
                    errors.append(f"[X] Office Supplies should have Reorder Level=10, found {reorder_level}")
                elif category == "Furniture" and reorder_level != 2:
                    errors.append(f"[X] Furniture should have Reorder Level=2, found {reorder_level}")
                elif category == "Software" and reorder_level != 3:
                    errors.append(f"[X] Software should have Reorder Level=3, found {reorder_level}")
                else:
                    checks_passed.append(f"[C] Software should have Reorder Level=3")
        
            # Verify Stock Status updated if Quantity <= Reorder Level
            if quantity is not None and reorder_level is not None:
                if quantity <= reorder_level:
                    low_stock_items += 1
                    if stock_status:
                        errors.append(f"[X] Item with Quantity {quantity} <= Reorder Level {reorder_level} should have Stock Status=false")
                    else:
                        checks_passed.append(f"[C] Item with Quantity <= Reorder Level should have Stock Status=false")
    
        # Find Bug Tracker page
        bug_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Bug Tracker"}}
        })
    
        if not isinstance(bug_tracker_pages, list) or len(bug_tracker_pages) == 0:
            errors.append("[X] Bug Tracker page not found")
        else:
            checks_passed.append("[C] Bug Tracker page")
    
        bug_tracker_id = bug_tracker_pages[0].get("_id")
    
        # Find alert page
        alert_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": bug_tracker_id, "title": {"$regex": "Inventory Alert System"}}
        })
    
        if not isinstance(alert_pages, list) or len(alert_pages) == 0:
            errors.append("[X] Inventory Alert System page not found under Bug Tracker")
        else:
            checks_passed.append("[C] Inventory Alert System page found under Bug Tracker")
    
        alert_page_id = alert_pages[0].get("_id")
    
        # Get blocks for alert page
        alert_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": alert_page_id}
        })
    
        if not isinstance(alert_blocks, list):
            alert_blocks = []
    
        alert_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_critical_h2 = False
        found_red_callout = False
        found_numbered_list = False
    
        for block in alert_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "inventory alert system" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "critical stock" in content.lower():
                found_critical_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "red" in bg_color.lower() and "urgent" in content.lower():
                    found_red_callout = True
        
            if block_type == "numberedList":
                found_numbered_list = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Inventory Alert System'")
        else:
            checks_passed.append("[C] has heading_1 'Inventory Alert System'")
        if not found_critical_h2:
            errors.append("[X] Missing heading_2 'Critical Stock Items'")
        else:
            checks_passed.append("[C] has heading_2 'Critical Stock Items'")
        if not found_red_callout:
            errors.append("[X] Missing red callout with urgent reorder count")
        else:
            checks_passed.append("[C] has red callout with urgent reorder count")
        if not found_numbered_list:
            errors.append("[X] Missing numbered list with low-stock items")
        else:
            checks_passed.append("[C] has numbered list with low-stock items")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 29: Workspace Analytics System
# =============================================================================
def _validate_agent_workspace_analytics_system(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate comprehensive Workspace Analytics system.
    
    Checks:
    1. Analytics Metrics database exists under Research Notes
    2. 5 calculated metrics with correct formulas
    3. All trends set to 'Stable'
    4. Inline summary database in Company Handbook filtering metrics < 50
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Research Notes page
    try:
        research_notes_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Research Notes"}}
        })
    
        if not isinstance(research_notes_pages, list) or len(research_notes_pages) == 0:
            errors.append("[X] Research Notes page not found")
        else:
            checks_passed.append("[C] Research Notes page")
    
        research_notes_id = research_notes_pages[0].get("_id")
    
        # Find Analytics Metrics database
        metrics_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": research_notes_id, "title": {"$regex": "Analytics Metrics"}}
        })
    
        if not isinstance(metrics_db_pages, list) or len(metrics_db_pages) == 0:
            errors.append("[X] Analytics Metrics database not found under Research Notes")
        else:
            checks_passed.append("[C] Analytics Metrics database found under Research Notes")
    
        metrics_db_id = metrics_db_pages[0].get("_id")
        metrics_db_page = metrics_db_pages[0]
    
        # Verify schema
        db_metadata = metrics_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Metric Name", "Category", "Value", "Percentage", "Trend", "Last Updated"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query metrics rows
        metrics_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": metrics_db_id, "type": "row"}
        })
    
        if not isinstance(metrics_rows, list):
            metrics_rows = []
    
        # Create property ID to name mapping
        prop_id_to_name = {
            prop_data.get("_id"): prop_data.get("name")
            for prop_data in db_properties
            if isinstance(prop_data, dict)
        }
    
        # Check for 5 specific metrics
        required_metrics = {
            "task completion rate": False,
            "project budget utilization": False,
            "vip contact ratio": False,
            "stock availability": False,
            "critical inventory items": False
        }
    
        metrics_below_50 = 0
    
        for row in metrics_rows:
            metric_name = row.get("title", "").lower()
            cell_values = row.get("cellValues", {})
            trend = None
            value = None
        
            for key, val in cell_values.items():
                prop_name = prop_id_to_name.get(key, "").lower()
                if "trend" in prop_name:
                    trend = val
                elif "value" in prop_name and isinstance(val, (int, float)):
                    value = val
        
            # Mark found metrics
            for req_metric in required_metrics.keys():
                if req_metric in metric_name:
                    required_metrics[req_metric] = True
                    break
        
            # Verify trend
            if trend != "Stable":
                errors.append(f"[X] Metric '{metric_name}' should have Trend='Stable', found '{trend}'")
            else:
                checks_passed.append(f"[C] Metric should have Trend='Stable'")
        
            # Count metrics < 50
            if value is not None and value < 50:
                metrics_below_50 += 1
    
        # Check all required metrics found
        missing_metrics = [m for m, found in required_metrics.items() if not found]
        if missing_metrics:
            errors.append(f"[X] Missing metrics: {', '.join(missing_metrics)}")
        else:
            checks_passed.append(f"[C] has metrics")
    
        # Find Company Handbook page
        handbook_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Company Handbook"}}
        })
    
        if not isinstance(handbook_pages, list) or len(handbook_pages) == 0:
            errors.append("[X] Company Handbook page not found")
        else:
            checks_passed.append("[C] Company Handbook page")
    
        handbook_page_id = handbook_pages[0].get("_id")
    
        # Get blocks to find inline database
        handbook_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": handbook_page_id}
        })
    
        if not isinstance(handbook_blocks, list):
            handbook_blocks = []
    
        # Find Areas Needing Attention inline database
        found_attention_db = False
        for block in handbook_blocks:
            if block.get("type") == "database":
                db_id = block.get("properties", {}).get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "areas needing attention" in db_title.lower():
                            found_attention_db = True
                        
                            # Verify it shows only metrics < 50
                            attention_rows = backend.query({
                                "collection": "pages",
                                "filter": {"databaseId": db_id, "type": "row"}
                            })
                            if isinstance(attention_rows, list):
                                # Should show metrics with value < 50
                                if len(attention_rows) != metrics_below_50:
                                    errors.append(f"[X] Areas Needing Attention should show {metrics_below_50} metrics, found {len(attention_rows)}")
                                else:
                                    checks_passed.append(f"[C] Areas Needing Attention should show metrics")
                            break
    
        if not found_attention_db:
            errors.append("[X] Areas Needing Attention inline database not found in Company Handbook")
        else:
            checks_passed.append("[C] Areas Needing Attention inline database found in Company Handbook")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 30: Resource Allocation Dashboard with Dependencies
# =============================================================================
def _validate_agent_resource_allocation_dashboard_dependencies(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Resource Allocation Dashboard with cross-project dependencies.
    
    Checks:
    1. Page exists under Engineering Wiki
    2. Team Workload Analysis section with assignee breakdowns
    3. Dependency Matrix inline database with blocked tasks
    4. Impact Level logic correct
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Engineering Wiki page
    try:
        eng_wiki_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Engineering Wiki"}}
        })
    
        if not isinstance(eng_wiki_pages, list) or len(eng_wiki_pages) == 0:
            errors.append("[X] Engineering Wiki page not found")
        else:
            checks_passed.append("[C] Engineering Wiki page")
    
        eng_wiki_id = eng_wiki_pages[0].get("_id")
    
        # Find dashboard page
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": eng_wiki_id, "title": {"$regex": "Resource Allocation Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Resource Allocation Dashboard page not found under Engineering Wiki")
        else:
            checks_passed.append("[C] Resource Allocation Dashboard page found under Engineering Wiki")
    
        dashboard_page_id = dashboard_pages[0].get("_id")
    
        # Query Task Tracker for assignees
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Get unique assignees and count blocked tasks
        assignees = set()
        blocked_tasks = []
    
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            assignee = None
            status = None
            priority = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "assignee" in key_lower:
                    assignee = val
                elif "status" in key_lower:
                    status = val
                elif "priority" in key_lower:
                    priority = val
        
            if assignee:
                assignees.add(assignee)
        
            if status == "Blocked":
                blocked_tasks.append((row, priority))
    
        # Get blocks for dashboard
        dashboard_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page_id}
        })
    
        if not isinstance(dashboard_blocks, list):
            dashboard_blocks = []
    
        dashboard_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_workload_h2 = False
        found_dependencies_h2 = False
        found_h3_count = 0
        database_block = None
    
        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "heading1" and "resource allocation dashboard" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "team workload" in content.lower():
                found_workload_h2 = True
        
            if block_type == "heading2" and "cross-project dependencies" in content.lower():
                found_dependencies_h2 = True
        
            if block_type == "heading3" and found_workload_h2:
                found_h3_count += 1
        
            if block_type == "database":
                db_id = block.get("properties", {}).get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "dependency matrix" in db_title.lower():
                            database_block = block
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Resource Allocation Dashboard'")
        else:
            checks_passed.append("[C] has heading_1 'Resource Allocation Dashboard'")
        if not found_workload_h2:
            errors.append("[X] Missing heading_2 'Team Workload Analysis'")
        else:
            checks_passed.append("[C] has heading_2 'Team Workload Analysis'")
        if found_h3_count < len(assignees):
            errors.append(f"[X] Expected {len(assignees)} heading_3 for assignees, found {found_h3_count}")
        else:
            checks_passed.append(f"[C] Expected heading_3 for assignees")
        if not found_dependencies_h2:
            errors.append("[X] Missing heading_2 'Cross-Project Dependencies'")
        else:
            checks_passed.append("[C] has heading_2 'Cross-Project Dependencies'")
        if not database_block:
            errors.append("[X] Dependency Matrix inline database not found")
        else:
            checks_passed.append("[C] Dependency Matrix inline database")
    
        # Verify Dependency Matrix
        matrix_db_id = database_block.get("properties", {}).get("databaseId")
        matrix_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": matrix_db_id}
        })
    
        if not isinstance(matrix_db_pages, list) or len(matrix_db_pages) == 0:
            errors.append("[X] Dependency Matrix database page not found")
        else:
            checks_passed.append("[C] Dependency Matrix database page")
    
        matrix_db_page = matrix_db_pages[0]
    
        # Verify schema
        db_metadata = matrix_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Task", "Project", "Blocking", "Impact Level"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query matrix rows
        matrix_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": matrix_db_id, "type": "row"}
        })
    
        if not isinstance(matrix_rows, list):
            matrix_rows = []
    
        # Verify rows for blocked tasks
        if len(matrix_rows) < len(blocked_tasks):
            errors.append(f"[X] Expected {len(blocked_tasks)} rows for blocked tasks, found {len(matrix_rows)}")
        else:
            checks_passed.append(f"[C] Expected rows for blocked tasks")
    
        # Create property ID to name mapping
        prop_id_to_name = {
            prop_data.get("_id"): prop_data.get("name")
            for prop_data in db_properties
            if isinstance(prop_data, dict)
        }
    
        # Verify Impact Level logic (Critical for Urgent, High for High priority, Medium otherwise)
        for row in matrix_rows:
            cell_values = row.get("cellValues", {})
            impact_level = None
        
            for key, val in cell_values.items():
                prop_name = prop_id_to_name.get(key, "").lower()
                if "impact level" in prop_name:
                    impact_level = val
                    break
        
            # Find corresponding task priority
            task_title = row.get("title", "")
            task_priority = None
            for blocked_task, priority in blocked_tasks:
                if blocked_task.get("title", "") in task_title:
                    task_priority = priority
                    break
        
            # Verify Impact Level logic
            if task_priority == "Urgent" and impact_level != "Critical":
                errors.append(f"[X] Urgent priority task should have Impact Level='Critical', found '{impact_level}'")
            elif task_priority == "High" and impact_level != "High":
                errors.append(f"[X] High priority task should have Impact Level='High', found '{impact_level}'")
            else:
                checks_passed.append(f"[C] High priority task should have Impact Level='High'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 31: High-Value Active Projects Report
# =============================================================================
def _validate_agent_high_value_active_projects_report(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate High-Value Active Projects Report with Inventory Tracker entries.
    
    Checks:
    1. Inventory Tracker entries for Active projects with Budget > 50000
    2. Correct naming pattern and values
    3. Report page under Project Portfolio with structure
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Portfolio database
    try:
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        # Query project rows
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        # Find high-value active projects
        high_value_projects = []
        total_investment = 0
    
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
            budget = 0
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "status" in key_lower:
                    status = val
                elif "budget" in key_lower and isinstance(val, (int, float)):
                    budget = val
        
            if status == "Active" and budget > 50000:
                high_value_projects.append(row)
                total_investment += budget
    
        if len(high_value_projects) == 0:
            errors.append("[X] No Active projects with budget > 50000 found")
        else:
            checks_passed.append("[C] Active projects with budget > 50000")
    
        # Find Inventory Tracker database
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_id = inventory_pages[0].get("_id")
    
        # Query inventory rows for budget allocations
        inventory_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_id, "type": "row"}
        })
    
        if not isinstance(inventory_rows, list):
            inventory_rows = []
    
        # Check for budget allocation entries
        budget_entries = []
        for row in inventory_rows:
            item_name = row.get("title", "")
            if "budget allocation" in item_name.lower():
                budget_entries.append(row)
            
                cell_values = row.get("cellValues", {})
                # Verify properties
                sku = None
                category = None
                quantity = None
                unit_price = None
                in_stock = None
            
                for key, val in cell_values.items():
                    key_lower = key.lower()
                    if "sku" in key_lower:
                        sku = val
                    elif "category" in key_lower:
                        category = val
                    elif "quantity" in key_lower:
                        quantity = val
                    elif "price" in key_lower:
                        unit_price = val
                    elif "stock" in key_lower and isinstance(val, bool):
                        in_stock = val
            
                # Verify values
                if sku and not sku.startswith("PROJ-"):
                    errors.append(f"[X] Budget allocation SKU should start with 'PROJ-', found '{sku}'")
                else:
                    checks_passed.append(f"[C] Budget allocation SKU should start with 'PROJ-'")
                if category != "Software":
                    errors.append(f"[X] Budget allocation should have Category='Software', found '{category}'")
                else:
                    checks_passed.append(f"[C] Budget allocation should have Category='Software'")
                if quantity != 1:
                    errors.append(f"[X] Budget allocation should have Quantity=1, found {quantity}")
                else:
                    checks_passed.append(f"[C] Budget allocation should have Quantity=1")
                if not in_stock:
                    errors.append("[X] Budget allocation should have In Stock=true")
                else:
                    checks_passed.append("[C] Budget allocation should have In Stock=true")
    
        if len(budget_entries) < len(high_value_projects):
            errors.append(f"[X] Expected {len(high_value_projects)} budget allocation entries, found {len(budget_entries)}")
        else:
            checks_passed.append(f"[C] Expected budget allocation entries")
    
        # Find report page
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": portfolio_id, "title": {"$regex": "High-Value Active Projects Report"}}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] High-Value Active Projects Report page not found under Project Portfolio")
        else:
            checks_passed.append("[C] High-Value Active Projects Report page found under Project Portfolio")
    
        report_page_id = report_pages[0].get("_id")
    
        # Get blocks for report
        report_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page_id}
        })
    
        if not isinstance(report_blocks, list):
            report_blocks = []
    
        report_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_yellow_callout = False
        found_details_h2 = False
        found_bulleted_list = False
    
        for block in report_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "high-value active projects" in content.lower():
                found_h1 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "yellow" in bg_color.lower() and str(int(total_investment)) in content:
                    found_yellow_callout = True
        
            if block_type == "heading2" and "project details" in content.lower():
                found_details_h2 = True
        
            if block_type == "bulletedList" and found_details_h2:
                found_bulleted_list = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'High-Value Active Projects Report'")
        else:
            checks_passed.append("[C] has heading_1 'High-Value Active Projects Report'")
        if not found_yellow_callout:
            errors.append("[X] Missing yellow callout with total investment")
        else:
            checks_passed.append("[C] has yellow callout with total investment")
        if not found_details_h2:
            errors.append("[X] Missing heading_2 'Project Details'")
        else:
            checks_passed.append("[C] has heading_2 'Project Details'")
        if not found_bulleted_list:
            errors.append("[X] Missing bulleted list with project details")
        else:
            checks_passed.append("[C] has bulleted list with project details")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 32: Resource Planning Dashboard Workload Analysis
# =============================================================================
def _validate_agent_resource_planning_dashboard_workload_analysis(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Resource Planning Dashboard with workload analysis.
    
    Checks:
    1. Page exists under Meeting Notes
    2. Team workload analysis with brown callout
    3. Bulleted list for each assignee with status counts
    4. Project Assignments section for dual-role members
    5. Capacity Warnings for overloaded members
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Meeting Notes page
    try:
        meeting_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Meeting Notes"}}
        })
    
        if not isinstance(meeting_pages, list) or len(meeting_pages) == 0:
            errors.append("[X] Meeting Notes page not found")
        else:
            checks_passed.append("[C] Meeting Notes page")
    
        meeting_id = meeting_pages[0].get("_id")
    
        # Find dashboard page
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": meeting_id, "title": {"$regex": "Resource Planning Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Resource Planning Dashboard page not found under Meeting Notes")
        else:
            checks_passed.append("[C] Resource Planning Dashboard page found under Meeting Notes")
    
        dashboard_page_id = dashboard_pages[0].get("_id")
    
        # Query Task Tracker
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Count tasks by assignee and status
        assignee_workload = {}
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            assignee = None
            status = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "assignee" in key_lower:
                    assignee = val
                elif "status" in key_lower:
                    status = val
        
            if assignee:
                if assignee not in assignee_workload:
                    assignee_workload[assignee] = {"Not Started": 0, "In Progress": 0, "In Review": 0, "Done": 0, "Blocked": 0}
                if status in assignee_workload[assignee]:
                    assignee_workload[assignee][status] += 1
    
        # Find most loaded (most non-Done tasks)
        max_active = 0
        most_loaded = None
        for assignee, counts in assignee_workload.items():
            active_tasks = counts["Not Started"] + counts["In Progress"] + counts["In Review"]
            if active_tasks > max_active:
                max_active = active_tasks
                most_loaded = assignee
    
        # Get blocks for dashboard
        dashboard_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page_id}
        })
    
        if not isinstance(dashboard_blocks, list):
            dashboard_blocks = []
    
        dashboard_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_workload_h2 = False
        found_brown_callout = False
        found_bulleted_lists = 0
        found_assignments_h2 = False
        found_divider = False
        found_warnings_h3 = False
    
        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "resource allocation matrix" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "team workload" in content.lower():
                found_workload_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "brown" in bg_color.lower() and most_loaded and most_loaded.lower() in content.lower():
                    found_brown_callout = True
        
            if block_type == "bulletedList" and found_workload_h2:
                found_bulleted_lists += 1
        
            if block_type == "heading2" and "project assignments" in content.lower():
                found_assignments_h2 = True
        
            if block_type == "divider":
                found_divider = True
        
            if block_type == "heading3" and "capacity warnings" in content.lower():
                found_warnings_h3 = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Resource Allocation Matrix'")
        else:
            checks_passed.append("[C] has heading_1 'Resource Allocation Matrix'")
        if not found_workload_h2:
            errors.append("[X] Missing heading_2 'Team Workload Analysis'")
        else:
            checks_passed.append("[C] has heading_2 'Team Workload Analysis'")
        if not found_brown_callout:
            errors.append("[X] Missing brown callout with most loaded assignee")
        else:
            checks_passed.append("[C] has brown callout with most loaded assignee")
        if found_bulleted_lists < len(assignee_workload):
            errors.append(f"[X] Expected {len(assignee_workload)} bulleted list items, found {found_bulleted_lists}")
        else:
            checks_passed.append(f"[C] Expected bulleted list items")
        if not found_assignments_h2:
            errors.append("[X] Missing heading_2 'Project Assignments'")
        else:
            checks_passed.append("[C] has heading_2 'Project Assignments'")
        if not found_divider:
            errors.append("[X] Missing divider before capacity warnings")
        else:
            checks_passed.append("[C] has divider before capacity warnings")
        if not found_warnings_h3:
            errors.append("[X] Missing heading_3 'Capacity Warnings'")
        else:
            checks_passed.append("[C] has heading_3 'Capacity Warnings'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 33: Expense Tracking Database
# =============================================================================
def _validate_agent_expense_tracking_database(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Expense Tracking database creation.
    
    Checks:
    1. Database exists under Project Portfolio with correct schema
    2. Project select options match all projects with colors
    3. Expense rows for each Active project
    4. Amounts are 10% of project budgets
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Portfolio database
    try:
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        # Find Expense Tracking database
        expense_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": portfolio_id, "title": {"$regex": "Expense Tracking"}}
        })
    
        if not isinstance(expense_db_pages, list) or len(expense_db_pages) == 0:
            errors.append("[X] Expense Tracking database not found under Project Portfolio")
        else:
            checks_passed.append("[C] Expense Tracking database found under Project Portfolio")
    
        expense_db_id = expense_db_pages[0].get("_id")
        expense_db_page = expense_db_pages[0]
    
        # Verify schema
        db_metadata = expense_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Expense Name", "Project", "Amount", "Category", "Paid", "Payment Date", "Vendor"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query project rows
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        # Get active projects
        active_projects = {}
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
            budget = 0
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "status" in key_lower:
                    status = val
                elif "budget" in key_lower and isinstance(val, (int, float)):
                    budget = val
        
            if status == "Active":
                project_name = row.get("title", "")
                active_projects[project_name] = budget
    
        # Query expense rows
        expense_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": expense_db_id, "type": "row"}
        })
    
        if not isinstance(expense_rows, list):
            expense_rows = []
    
        # Check for initial budget allocation rows
        initial_allocation_rows = [r for r in expense_rows if "initial budget allocation" in r.get("title", "").lower()]
    
        if len(initial_allocation_rows) < len(active_projects):
            errors.append(f"[X] Expected {len(active_projects)} expense rows for Active projects, found {len(initial_allocation_rows)}")
        else:
            checks_passed.append(f"[C] Expected expense rows for Active projects")
    
        # Create property ID to name mapping
        prop_id_to_name = {
            prop_data.get("_id"): prop_data.get("name")
            for prop_data in db_properties
            if isinstance(prop_data, dict)
        }
    
        # Verify amounts are 10% of budgets
        for row in initial_allocation_rows:
            cell_values = row.get("cellValues", {})
            amount = None
            category = None
            paid = None
            vendor = None
        
            for key, val in cell_values.items():
                prop_name = prop_id_to_name.get(key, "").lower()
                if "amount" in prop_name and isinstance(val, (int, float)):
                    amount = val
                elif "category" in prop_name:
                    category = val
                elif "paid" in prop_name:
                    paid = val
                elif "vendor" in prop_name:
                    vendor = val
        
            if category != "Consulting":
                errors.append(f"[X] Expense should have Category='Consulting', found '{category}'")
            else:
                checks_passed.append(f"[C] Expense should have Category='Consulting'")
            if paid:
                errors.append("[X] Initial allocation should have Paid=false")
            else:
                checks_passed.append("[C] Initial allocation should have Paid=false")
            if vendor != "TBD":
                errors.append(f"[X] Initial allocation should have Vendor='TBD', found '{vendor}'")
            else:
                checks_passed.append(f"[C] Initial allocation should have Vendor='TBD'")
        
            # Verify amount is ~10% of a project budget
            found_match = False
            for project_name, budget in active_projects.items():
                expected_amount = budget * 0.1
                if abs(amount - expected_amount) < 1:
                    found_match = True
                    break
        
            if not found_match:
                errors.append(f"[X] Expense amount {amount} doesn't match 10% of any active project budget")
            else:
                checks_passed.append(f"[C] Expense amount doesn't match 10% of any active project budget")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 34: Project Portfolio Risk Level Property
# =============================================================================
def _validate_agent_project_portfolio_risk_level_property(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Risk Level property addition and Risk Matrix.
    
    Checks:
    1. Risk Level property added to Project Portfolio after Department
    2. Risk Level set correctly for all projects
    3. Risk Matrix inline database in Project Roadmap
    4. Callout color based on Critical risks
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Portfolio database
    try:
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
        portfolio_page = portfolio_pages[0]
    
        # Check Risk Level property
        db_metadata = portfolio_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        risk_level_prop = None
        for prop_data in db_properties:
            if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == "risk level":
                risk_level_prop = prop_data
                break
    
        if not risk_level_prop:
            errors.append("[X] Risk Level property not found in Project Portfolio")
        else:
            checks_passed.append("[C] Risk Level property found in Project Portfolio")
    
        # Verify options
        options = risk_level_prop.get("options", [])
        required_options = {"Low", "Medium", "High", "Critical"}
        found_options = set()
        for opt in options:
            if isinstance(opt, dict):
                found_options.add(opt.get("name", ""))
    
        if required_options != found_options:
            errors.append(f"[X] Risk Level options incorrect. Expected {required_options}, found {found_options}")
        else:
            checks_passed.append(f"[C] Risk Level options correct")
    
        # Query project rows
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        # Count by risk level
        risk_counts = {"Low": 0, "Medium": 0, "High": 0, "Critical": 0}
        has_critical = False
    
        risk_level_prop_id = risk_level_prop.get("_id")
    
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            risk_level = cell_values.get(risk_level_prop_id)
        
            if risk_level in risk_counts:
                risk_counts[risk_level] += 1
                if risk_level == "Critical":
                    has_critical = True
    
        # Find Project Roadmap page
        roadmap_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Roadmap"}}
        })
    
        if not isinstance(roadmap_pages, list) or len(roadmap_pages) == 0:
            errors.append("[X] Project Roadmap page not found")
        else:
            checks_passed.append("[C] Project Roadmap page")
    
        roadmap_page_id = roadmap_pages[0].get("_id")
    
        # Get blocks to find Risk Matrix
        roadmap_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": roadmap_page_id}
        })
    
        if not isinstance(roadmap_blocks, list):
            roadmap_blocks = []
    
        roadmap_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find Risk Matrix database and heading/callout
        found_h2 = False
        found_callout = False
        callout_color = None
        found_matrix_db = False
    
        for block in roadmap_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading2" and "risk assessment matrix" in content.lower():
                found_h2 = True
        
            if block_type == "callout":
                found_callout = True
                callout_color = properties.get("backgroundColor", "")
        
            if block_type == "database":
                db_id = properties.get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "risk matrix" in db_title.lower():
                            found_matrix_db = True
    
        if not found_h2:
            errors.append("[X] Missing heading_2 'Project Risk Assessment Matrix'")
        else:
            checks_passed.append("[C] has heading_2 'Project Risk Assessment Matrix'")
        if not found_callout:
            errors.append("[X] Missing callout with risk summary")
        else:
            checks_passed.append("[C] has callout with risk summary")
        if not found_matrix_db:
            errors.append("[X] Risk Matrix inline database not found in Project Roadmap")
        else:
            checks_passed.append("[C] Risk Matrix inline database found in Project Roadmap")
    
        # Verify callout color (red if Critical risks exist)
        expected_color = "red" if has_critical else "yellow"
        if expected_color not in callout_color.lower():
            errors.append(f"[X] Expected {expected_color} callout for risk status, found {callout_color}")
        else:
            checks_passed.append(f"[C] Expected callout for risk status")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 35: Project Portfolio Analytics Dashboard
# =============================================================================
def _validate_agent_project_portfolio_analytics_dashboard(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate comprehensive Project Portfolio Analytics dashboard.
    
    Checks:
    1. Page exists under Engineering Wiki
    2. Callout with total projects and budget
    3. Status breakdown with counts and budgets
    4. Department distribution
    5. Active projects timeline
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Engineering Wiki page
    try:
        eng_wiki_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Engineering Wiki"}}
        })
    
        if not isinstance(eng_wiki_pages, list) or len(eng_wiki_pages) == 0:
            errors.append("[X] Engineering Wiki page not found")
        else:
            checks_passed.append("[C] Engineering Wiki page")
    
        eng_wiki_id = eng_wiki_pages[0].get("_id")
    
        # Find analytics page
        analytics_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": eng_wiki_id, "title": {"$regex": "Project Portfolio Analytics"}}
        })
    
        if not isinstance(analytics_pages, list) or len(analytics_pages) == 0:
            errors.append("[X] Project Portfolio Analytics page not found under Engineering Wiki")
        else:
            checks_passed.append("[C] Project Portfolio Analytics page found under Engineering Wiki")
    
        analytics_page_id = analytics_pages[0].get("_id")
    
        # Query Project Portfolio
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        # Calculate statistics
        total_projects = len(project_rows)
        total_budget = 0
        status_counts = {}
        department_counts = {}
        active_projects = []
    
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
            budget = 0
            department = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "status" in key_lower:
                    status = val
                elif "budget" in key_lower and isinstance(val, (int, float)):
                    budget = val
                elif "department" in key_lower:
                    department = val
        
            total_budget += budget
        
            if status:
                if status not in status_counts:
                    status_counts[status] = {"count": 0, "budget": 0}
                status_counts[status]["count"] += 1
                status_counts[status]["budget"] += budget
            
                if status == "Active":
                    active_projects.append(row)
        
            if department:
                department_counts[department] = department_counts.get(department, 0) + 1
    
        # Get blocks for analytics page
        analytics_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": analytics_page_id}
        })
    
        if not isinstance(analytics_blocks, list):
            analytics_blocks = []
    
        analytics_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_blue_callout = False
        found_status_h2 = False
        found_status_list = False
        found_department_h2 = False
        found_department_list = False
        found_timeline_h2 = False
        found_timeline_list = False
    
        for block in analytics_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "portfolio analytics" in content.lower():
                found_h1 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "blue" in bg_color.lower() and str(total_projects) in content:
                    found_blue_callout = True
        
            if block_type == "heading2" and "status breakdown" in content.lower():
                found_status_h2 = True
        
            if block_type == "bulletedList" and found_status_h2:
                found_status_list = True
        
            if block_type == "heading2" and "department distribution" in content.lower():
                found_department_h2 = True
        
            if block_type == "numberedList" and found_department_h2:
                found_department_list = True
        
            if block_type == "heading2" and "active projects timeline" in content.lower():
                found_timeline_h2 = True
        
            if block_type == "numberedList" and found_timeline_h2:
                found_timeline_list = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Project Portfolio Analytics'")
        else:
            checks_passed.append("[C] has heading_1 'Project Portfolio Analytics'")
        if not found_blue_callout:
            errors.append("[X] Missing blue callout with portfolio overview")
        else:
            checks_passed.append("[C] has blue callout with portfolio overview")
        if not found_status_h2:
            errors.append("[X] Missing heading_2 'Status Breakdown'")
        else:
            checks_passed.append("[C] has heading_2 'Status Breakdown'")
        if not found_status_list:
            errors.append("[X] Missing bulleted list with status counts")
        else:
            checks_passed.append("[C] has bulleted list with status counts")
        if not found_department_h2:
            errors.append("[X] Missing heading_2 'Department Distribution'")
        else:
            checks_passed.append("[C] has heading_2 'Department Distribution'")
        if not found_department_list:
            errors.append("[X] Missing numbered list with department counts")
        else:
            checks_passed.append("[C] has numbered list with department counts")
        if not found_timeline_h2:
            errors.append("[X] Missing heading_2 'Active Projects Timeline'")
        else:
            checks_passed.append("[C] has heading_2 'Active Projects Timeline'")
        if not found_timeline_list:
            errors.append("[X] Missing numbered list with active project timelines")
        else:
            checks_passed.append("[C] has numbered list with active project timelines")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 36: Onboarding Progress Tracker
# =============================================================================
def _validate_agent_onboarding_progress_tracker(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Onboarding Progress Tracker inline database.
    
    Checks:
    1. New heading after First Week Goals section
    2. Inline database with correct schema
    3. 8 specific rows with exact values
    4. Callout below database with tip text
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Onboarding Guide page
    try:
        onboarding_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Onboarding Guide"}}
        })
    
        if not isinstance(onboarding_pages, list) or len(onboarding_pages) == 0:
            errors.append("[X] Onboarding Guide page not found")
        else:
            checks_passed.append("[C] Onboarding Guide page")
    
        onboarding_page_id = onboarding_pages[0].get("_id")
    
        # Get blocks
        onboarding_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": onboarding_page_id}
        })
    
        if not isinstance(onboarding_blocks, list):
            onboarding_blocks = []
    
        onboarding_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find First Week Goals section (last numbered list item)
        last_numbered_idx = -1
        for idx, block in enumerate(onboarding_blocks):
            if block.get("type") == "numberedList":
                last_numbered_idx = idx
    
        if last_numbered_idx == -1:
            errors.append("[X] First Week Goals section not found in Onboarding Guide")
        else:
            checks_passed.append("[C] First Week Goals section found in Onboarding Guide")
    
        # Find new content after last numbered list
        found_tracker_h2 = False
        tracker_db_block = None
        found_callout = False
    
        for idx, block in enumerate(onboarding_blocks):
            if idx <= last_numbered_idx:
                continue
        
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading2" and "onboarding progress tracker" in content.lower():
                found_tracker_h2 = True
        
            if block_type == "database":
                db_id = properties.get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "new hire checklist" in db_title.lower():
                            tracker_db_block = block
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "blue" in bg_color.lower() and "tip" in content.lower():
                    found_callout = True
    
        if not found_tracker_h2:
            errors.append("[X] Missing heading_2 'Onboarding Progress Tracker' after First Week Goals")
        else:
            checks_passed.append("[C] has heading_2 'Onboarding Progress Tracker' after First Week Goals")
        if not tracker_db_block:
            errors.append("[X] New Hire Checklist inline database not found")
        else:
            checks_passed.append("[C] New Hire Checklist inline database")
        if not found_callout:
            errors.append("[X] Missing blue callout with tip text below database")
        else:
            checks_passed.append("[C] has blue callout with tip text below database")
    
        # Verify database schema and rows
        tracker_db_id = tracker_db_block.get("properties", {}).get("databaseId")
        tracker_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": tracker_db_id}
        })
    
        if not isinstance(tracker_db_pages, list) or len(tracker_db_pages) == 0:
            errors.append("[X] Tracker database page not found")
        else:
            checks_passed.append("[C] Tracker database page")
    
        tracker_db_page = tracker_db_pages[0]
    
        # Verify schema
        db_metadata = tracker_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Task", "Category", "Due By", "Completed", "Notes"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query tracker rows
        tracker_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": tracker_db_id, "type": "row"}
        })
    
        if not isinstance(tracker_rows, list):
            tracker_rows = []
    
        # Check for 8 specific tasks
        required_tasks = [
            "complete it setup",
            "read company handbook",
            "meet team members",
            "complete security training",
            "set up development environment",
            "shadow team standup",
            "complete first code review",
            "present learnings to team"
        ]
    
        found_tasks = set()
        for row in tracker_rows:
            task_name = row.get("title", "").lower()
            for req_task in required_tasks:
                if req_task in task_name:
                    found_tasks.add(req_task)
                    break
    
        missing_tasks = set(required_tasks) - found_tasks
        if missing_tasks:
            errors.append(f"[X] Missing required tasks: {', '.join(missing_tasks)}")
        else:
            checks_passed.append(f"[C] has required tasks")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 37: Workspace Databases Quick Links
# =============================================================================
def _validate_agent_workspace_databases_quick_links(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Workspace Databases Quick Links inline database.
    
    Checks:
    1. Section appears after Key Features heading, before bulleted lists
    2. Heading_2 and paragraph with exact text
    3. Inline database Quick Links with correct schema
    4. 3 rows for Task Tracker, Project Portfolio, Contacts Directory
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Getting Started page
    try:
        getting_started_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Getting Started"}}
        })
    
        if not isinstance(getting_started_pages, list) or len(getting_started_pages) == 0:
            errors.append("[X] Getting Started page not found")
        else:
            checks_passed.append("[C] Getting Started page")
    
        getting_started_page_id = getting_started_pages[0].get("_id")
    
        # Get blocks
        getting_started_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": getting_started_page_id}
        })
    
        if not isinstance(getting_started_blocks, list):
            getting_started_blocks = []
    
        getting_started_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find Key Features heading
        key_features_idx = -1
        for idx, block in enumerate(getting_started_blocks):
            if "key features" in block.get("content", "").lower():
                key_features_idx = idx
                break
    
        if key_features_idx == -1:
            errors.append("[X] Key Features heading not found in Getting Started")
        else:
            checks_passed.append("[C] Key Features heading found in Getting Started")
    
        # Find new section after Key Features, before bulleted lists
        found_databases_h2 = False
        found_paragraph = False
        quick_links_db = None
    
        for idx, block in enumerate(getting_started_blocks):
            if idx <= key_features_idx:
                continue
        
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            # Stop if we reach bulleted lists
            if block_type == "bulletedList":
                break
        
            if block_type == "heading2" and "workspace databases" in content.lower():
                found_databases_h2 = True
        
            if block_type == "paragraph" and "access your team" in content.lower():
                found_paragraph = True
        
            if block_type == "database":
                db_id = block.get("properties", {}).get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "quick links" in db_title.lower():
                            quick_links_db = block
    
        if not found_databases_h2:
            errors.append("[X] Missing heading_2 'Workspace Databases' after Key Features")
        else:
            checks_passed.append("[C] has heading_2 'Workspace Databases' after Key Features")
        if not found_paragraph:
            errors.append("[X] Missing paragraph with 'Access your team's databases' text")
        else:
            checks_passed.append("[C] has paragraph with 'Access your team's databases' text")
        if not quick_links_db:
            errors.append("[X] Quick Links inline database not found")
        else:
            checks_passed.append("[C] Quick Links inline database")
    
        # Verify database
        quick_links_db_id = quick_links_db.get("properties", {}).get("databaseId")
        quick_links_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": quick_links_db_id}
        })
    
        if not isinstance(quick_links_db_pages, list) or len(quick_links_db_pages) == 0:
            errors.append("[X] Quick Links database page not found")
        else:
            checks_passed.append("[C] Quick Links database page")
    
        quick_links_db_page = quick_links_db_pages[0]
    
        # Verify schema
        db_metadata = quick_links_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Database", "Description", "Last Updated", "Primary Use"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query rows
        quick_links_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": quick_links_db_id, "type": "row"}
        })
    
        if not isinstance(quick_links_rows, list):
            quick_links_rows = []
    
        # Check for 3 specific databases
        required_databases = ["task tracker", "project portfolio", "contacts directory"]
        found_databases = set()
    
        for row in quick_links_rows:
            db_name = row.get("title", "").lower()
            for req_db in required_databases:
                if req_db in db_name:
                    found_databases.add(req_db)
                    break
    
        missing_databases = set(required_databases) - found_databases
        if missing_databases:
            errors.append(f"[X] Missing database rows: {', '.join(missing_databases)}")
        else:
            checks_passed.append(f"[C] has database rows")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 39: Weekly Task Burndown Report
# =============================================================================
def _validate_agent_weekly_task_burndown_report(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Weekly Task Burndown Report creation.
    
    Checks:
    1. Page exists under Sprint Planning - Jan 15
    2. Priority breakdown sections (Urgent, High, Medium, Low)
    3. Code blocks with completion rates
    4. To-do lists with correct checked/unchecked status
    5. Time-Sensitive Items section with tasks due within 5 days
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Sprint Planning - Jan 15 page
    try:
        sprint_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Sprint Planning.*Jan 15"}}
        })
    
        if not isinstance(sprint_pages, list) or len(sprint_pages) == 0:
            errors.append("[X] Sprint Planning - Jan 15 page not found")
        else:
            checks_passed.append("[C] Sprint Planning - Jan 15 page")
    
        sprint_page_id = sprint_pages[0].get("_id")
    
        # Find burndown report page
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": sprint_page_id, "title": {"$regex": "Weekly Task Burndown Report"}}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] Weekly Task Burndown Report page not found under Sprint Planning - Jan 15")
        else:
            checks_passed.append("[C] Weekly Task Burndown Report page found under Sprint Planning - Jan 15")
    
        report_page_id = report_pages[0].get("_id")
    
        # Query Task Tracker for priority breakdown
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Count by priority and due dates
        time_sensitive_tasks = 0
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            due_date = None
        
            for key, val in cell_values.items():
                if "due" in key.lower() and isinstance(val, dict):
                    due_date = val.get("start")
                    break
        
            # Count tasks due within 5 days from 2026-01-12
            if due_date and due_date <= "2026-01-17":
                time_sensitive_tasks += 1
    
        # Get blocks for report page
        report_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page_id}
        })
    
        if not isinstance(report_blocks, list):
            report_blocks = []
    
        report_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_priority_h2 = False
        priority_h3_count = 0
        code_block_count = 0
        found_divider = False
        found_time_sensitive_h2 = False
        found_numbered_list = False
    
        for block in report_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "heading1" and "burndown report" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "priority breakdown" in content.lower():
                found_priority_h2 = True
        
            if block_type == "heading3" and "priority tasks" in content.lower():
                priority_h3_count += 1
        
            if block_type == "code":
                code_block_count += 1
        
            if block_type == "divider":
                found_divider = True
        
            if block_type == "heading2" and "time-sensitive" in content.lower():
                found_time_sensitive_h2 = True
        
            if block_type == "numberedList" and found_time_sensitive_h2:
                found_numbered_list = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Weekly Task Burndown Report'")
        else:
            checks_passed.append("[C] has heading_1 'Weekly Task Burndown Report'")
        if not found_priority_h2:
            errors.append("[X] Missing heading_2 'Priority Breakdown'")
        else:
            checks_passed.append("[C] has heading_2 'Priority Breakdown'")
        if priority_h3_count < 4:
            errors.append(f"[X] Expected 4 priority heading_3 sections (Urgent, High, Medium, Low), found {priority_h3_count}")
        else:
            checks_passed.append(f"[C] Expected 4 priority heading_3 sections (Urgent, High, Medium, Low)")
        if code_block_count < 4:
            errors.append(f"[X] Expected 4 code blocks with completion rates, found {code_block_count}")
        else:
            checks_passed.append(f"[C] Found 4 code blocks with completion rates as expected")
        if not found_divider:
            errors.append("[X] Missing divider after priority sections")
        else:
            checks_passed.append("[C] has divider after priority sections")
        if not found_time_sensitive_h2:
            errors.append("[X] Missing heading_2 'Time-Sensitive Items'")
        else:
            checks_passed.append("[C] has heading_2 'Time-Sensitive Items'")
        if not found_numbered_list:
            errors.append("[X] Missing numbered list with time-sensitive tasks")
        else:
            checks_passed.append("[C] has numbered list with time-sensitive tasks")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 40: CRM Analytics Dashboard with Lead Pipeline
# =============================================================================
def _validate_agent_crm_analytics_dashboard_lead_pipeline(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate CRM Analytics Dashboard with Lead Pipeline.
    
    Checks:
    1. New properties added to Contacts Directory (4 properties)
    2. CRM Analytics Dashboard page at root
    3. Lead Pipeline inline database with correct schema
    4. Rows for each contact Type with calculated statistics
    5. Hot Leads section with VIP/Follow Up contacts
    6. Orange callout with total pipeline value
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Contacts Directory database
    try:
        contacts_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Contacts Directory"}}
        })
    
        if not isinstance(contacts_pages, list) or len(contacts_pages) == 0:
            errors.append("[X] Contacts Directory database not found")
        else:
            checks_passed.append("[C] Contacts Directory database")
    
        contacts_page = contacts_pages[0]
        contacts_id = contacts_page.get("_id")
    
        # Check for new properties
        db_metadata = contacts_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        found_last_contact = False
        found_next_followup = False
        found_deal_value = False
        found_lead_score = False
    
        for prop_data in db_properties:
            if isinstance(prop_data, dict):
                prop_name = prop_data.get("name", "")
                if "last contact date" in prop_name.lower():
                    found_last_contact = True
                elif "next follow up" in prop_name.lower():
                    found_next_followup = True
                elif "deal value" in prop_name.lower():
                    found_deal_value = True
                elif "lead score" in prop_name.lower():
                    found_lead_score = True
    
        if not found_last_contact:
            errors.append("[X] Missing 'Last Contact Date' property in Contacts Directory")
        else:
            checks_passed.append("[C] has 'Last Contact Date' property in Contacts Directory")
        if not found_next_followup:
            errors.append("[X] Missing 'Next Follow Up' property in Contacts Directory")
        else:
            checks_passed.append("[C] has 'Next Follow Up' property in Contacts Directory")
        if not found_deal_value:
            errors.append("[X] Missing 'Deal Value' property in Contacts Directory")
        else:
            checks_passed.append("[C] has 'Deal Value' property in Contacts Directory")
        if not found_lead_score:
            errors.append("[X] Missing 'Lead Score' property in Contacts Directory")
        else:
            checks_passed.append("[C] has 'Lead Score' property in Contacts Directory")
    
        # Query contacts for hot leads and total value
        contact_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": contacts_id, "type": "row"}
        })
    
        if not isinstance(contact_rows, list):
            contact_rows = []
    
        hot_leads_count = 0
        total_pipeline_value = 0
        contact_types = set()
    
        for row in contact_rows:
            cell_values = row.get("cellValues", {})
            tags = []
            deal_value = 0
            contact_type = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "tag" in key_lower and isinstance(val, list):
                    tags = val
                elif "deal value" in key_lower and isinstance(val, (int, float)):
                    deal_value = val
                elif "type" in key_lower:
                    contact_type = val
        
            if "VIP" in tags or "Follow Up" in tags:
                hot_leads_count += 1
        
            total_pipeline_value += deal_value
        
            if contact_type:
                contact_types.add(contact_type)
    
        # Find CRM Analytics Dashboard at root
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "CRM Analytics Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] CRM Analytics Dashboard page not found")
        else:
            checks_passed.append("[C] CRM Analytics Dashboard page")
    
        # Find root-level dashboard
        dashboard_page = None
        for page in dashboard_pages:
            parent_id = page.get("parentId")
            if not parent_id or parent_id == "":
                dashboard_page = page
                break
    
        if not dashboard_page:
            errors.append("[X] CRM Analytics Dashboard not found at root level")
        else:
            checks_passed.append("[C] CRM Analytics Dashboard found at root level")
    
        dashboard_page_id = dashboard_page.get("_id")
    
        # Get blocks
        dashboard_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page_id}
        })
    
        if not isinstance(dashboard_blocks, list):
            dashboard_blocks = []
    
        dashboard_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_lead_pipeline_db = None
        found_hot_leads_h2 = False
        found_orange_callout = False
    
        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "customer relationship" in content.lower():
                found_h1 = True
        
            if block_type == "database":
                db_id = properties.get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "lead pipeline" in db_title.lower():
                            found_lead_pipeline_db = block
        
            if block_type == "heading2" and "hot leads" in content.lower():
                found_hot_leads_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "orange" in bg_color.lower() and "pipeline value" in content.lower():
                    found_orange_callout = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Customer Relationship Analytics'")
        else:
            checks_passed.append("[C] has heading_1 'Customer Relationship Analytics'")
        if not found_lead_pipeline_db:
            errors.append("[X] Lead Pipeline inline database not found")
        else:
            checks_passed.append("[C] Lead Pipeline inline database")
        if not found_hot_leads_h2:
            errors.append("[X] Missing heading_2 'Hot Leads'")
        else:
            checks_passed.append("[C] has heading_2 'Hot Leads'")
        if not found_orange_callout:
            errors.append("[X] Missing orange callout with total pipeline value")
        else:
            checks_passed.append("[C] has orange callout with total pipeline value")
    
        # Verify Lead Pipeline database
        pipeline_db_id = found_lead_pipeline_db.get("properties", {}).get("databaseId")
        pipeline_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": pipeline_db_id}
        })
    
        if not isinstance(pipeline_db_pages, list) or len(pipeline_db_pages) == 0:
            errors.append("[X] Lead Pipeline database page not found")
        else:
            checks_passed.append("[C] Lead Pipeline database page")
    
        pipeline_db_page = pipeline_db_pages[0]
    
        # Verify schema
        db_metadata = pipeline_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Stage", "Count", "Total Value", "Avg Lead Score", "Conversion Target"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query pipeline rows
        pipeline_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": pipeline_db_id, "type": "row"}
        })
    
        if not isinstance(pipeline_rows, list):
            pipeline_rows = []
    
        # Check for rows for each contact type
        if len(pipeline_rows) < len(contact_types):
            errors.append(f"[X] Expected at least {len(contact_types)} rows for contact types, found {len(pipeline_rows)}")
        else:
            checks_passed.append(f"[C] Expected at least rows for contact types")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 41: Team Workload Analytics Dashboard
# =============================================================================
def _validate_agent_team_workload_analytics_dashboard(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Team Workload Analytics dashboard creation.
    
    Checks:
    1. Estimated Hours and Actual Hours properties added to Task Tracker
    2. Team Workload Analytics page at root
    3. Purple callout with workload distribution
    4. Team Capacity inline database with calculations
    5. Rebalancing Recommendations section
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Task Tracker database
    try:
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_page = task_tracker_pages[0]
        task_tracker_id = task_tracker_page.get("_id")
    
        # Check for new properties
        db_metadata = task_tracker_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        found_estimated_hours = False
        found_actual_hours = False
    
        for prop_data in db_properties:
            if isinstance(prop_data, dict):
                prop_name = prop_data.get("name", "")
                if "estimated hours" in prop_name.lower():
                    found_estimated_hours = True
                elif "actual hours" in prop_name.lower():
                    found_actual_hours = True
    
        if not found_estimated_hours:
            errors.append("[X] Missing 'Estimated Hours' property in Task Tracker")
        else:
            checks_passed.append("[C] has 'Estimated Hours' property in Task Tracker")
        if not found_actual_hours:
            errors.append("[X] Missing 'Actual Hours' property in Task Tracker")
        else:
            checks_passed.append("[C] has 'Actual Hours' property in Task Tracker")
    
        # Query tasks
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Get unique assignees
        assignees = set()
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            for key, val in cell_values.items():
                if "assignee" in key.lower() and val:
                    assignees.add(val)
                    break
    
        # Find Team Workload Analytics page at root
        analytics_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Workload Analytics"}}
        })
    
        if not isinstance(analytics_pages, list) or len(analytics_pages) == 0:
            errors.append("[X] Team Workload Analytics page not found")
        else:
            checks_passed.append("[C] Team Workload Analytics page")
    
        # Find root-level page
        analytics_page = None
        for page in analytics_pages:
            parent_id = page.get("parentId")
            if not parent_id or parent_id == "":
                analytics_page = page
                break
    
        if not analytics_page:
            errors.append("[X] Team Workload Analytics not found at root level")
        else:
            checks_passed.append("[C] Team Workload Analytics found at root level")
    
        analytics_page_id = analytics_page.get("_id")
    
        # Get blocks
        analytics_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": analytics_page_id}
        })
    
        if not isinstance(analytics_blocks, list):
            analytics_blocks = []
    
        analytics_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_purple_callout = False
        found_capacity_db = None
        found_recommendations_h2 = False
        found_numbered_list = False
    
        for block in analytics_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "team capacity dashboard" in content.lower():
                found_h1 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "purple" in bg_color.lower() and "workload" in content.lower():
                    found_purple_callout = True
        
            if block_type == "database":
                db_id = properties.get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "team capacity" in db_title.lower():
                            found_capacity_db = block
        
            if block_type == "heading2" and "rebalancing recommendations" in content.lower():
                found_recommendations_h2 = True
        
            if block_type == "numberedList" and found_recommendations_h2:
                found_numbered_list = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Team Capacity Dashboard'")
        else:
            checks_passed.append("[C] has heading_1 'Team Capacity Dashboard'")
        if not found_purple_callout:
            errors.append("[X] Missing purple callout with workload distribution")
        else:
            checks_passed.append("[C] has purple callout with workload distribution")
        if not found_capacity_db:
            errors.append("[X] Team Capacity inline database not found")
        else:
            checks_passed.append("[C] Team Capacity inline database")
        if not found_recommendations_h2:
            errors.append("[X] Missing heading_2 'Rebalancing Recommendations'")
        else:
            checks_passed.append("[C] has heading_2 'Rebalancing Recommendations'")
        if not found_numbered_list:
            errors.append("[X] Missing numbered list with rebalancing suggestions")
        else:
            checks_passed.append("[C] has numbered list with rebalancing suggestions")
    
        # Verify Team Capacity database
        capacity_db_id = found_capacity_db.get("properties", {}).get("databaseId")
        capacity_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": capacity_db_id}
        })
    
        if not isinstance(capacity_db_pages, list) or len(capacity_db_pages) == 0:
            errors.append("[X] Team Capacity database page not found")
        else:
            checks_passed.append("[C] Team Capacity database page")
    
        capacity_db_page = capacity_db_pages[0]
    
        # Verify schema
        db_metadata = capacity_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Team Member", "Active Tasks", "Completed This Week", "Total Estimated Hours", "Utilization %", "Status"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query capacity rows
        capacity_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": capacity_db_id, "type": "row"}
        })
    
        if not isinstance(capacity_rows, list):
            capacity_rows = []
    
        # Check for rows for each assignee
        if len(capacity_rows) < len(assignees):
            errors.append(f"[X] Expected at least {len(assignees)} rows for team members, found {len(capacity_rows)}")
        else:
            checks_passed.append(f"[C] Expected at least rows for team members")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 42: Budget Analysis Report with Categorization
# =============================================================================
def _validate_agent_budget_analysis_report_categorization(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Budget Analysis Report with project categorization.
    
    Checks:
    1. Page exists under Product Specs
    2. Total budget calculation and display
    3. Budget by Department breakdown
    4. High-Value Projects list (>$50k)
    5. Yellow callout for on-hold budget
    6. Budget Category property added to Project Portfolio with correct values
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Product Specs page
    try:
        product_specs_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Product Specs"}}
        })
    
        if not isinstance(product_specs_pages, list) or len(product_specs_pages) == 0:
            errors.append("[X] Product Specs page not found")
        else:
            checks_passed.append("[C] Product Specs page")
    
        product_specs_id = product_specs_pages[0].get("_id")
    
        # Find report page
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": product_specs_id, "title": {"$regex": "Budget Analysis Report"}}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] Budget Analysis Report page not found under Product Specs")
        else:
            checks_passed.append("[C] Budget Analysis Report page found under Product Specs")
    
        report_page_id = report_pages[0].get("_id")
    
        # Query Project Portfolio
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
        portfolio_page = portfolio_pages[0]
    
        # Check Budget Category property
        db_metadata = portfolio_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        budget_category_prop = None
        for prop_data in db_properties:
            if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == "budget category":
                budget_category_prop = prop_data
                break
    
        if not budget_category_prop:
            errors.append("[X] Budget Category property not found in Project Portfolio")
        else:
            checks_passed.append("[C] Budget Category property found in Project Portfolio")
    
        # Verify options
        options = budget_category_prop.get("options", [])
        required_options = {"Small (<25k)", "Medium (25-75k)", "Large (>75k)"}
        found_options = set()
        for opt in options:
            if isinstance(opt, dict):
                found_options.add(opt.get("name", ""))
    
        if not required_options.issubset(found_options):
            errors.append(f"[X] Budget Category options incomplete. Expected {required_options} in {found_options}")
        else:
            checks_passed.append(f"[C] Budget Category options incomplete. Expected in")
    
        # Query project rows
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        # Calculate statistics
        total_budget = 0
        high_value_count = 0
        on_hold_budget = 0
    
        budget_category_prop_id = budget_category_prop.get("_id")
    
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            budget = 0
            status = None
            budget_category = cell_values.get(budget_category_prop_id)
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "budget" in key_lower and not "category" in key_lower and isinstance(val, (int, float)):
                    budget = val
                elif "status" in key_lower:
                    status = val
        
            total_budget += budget
        
            if budget > 50000:
                high_value_count += 1
        
            if status == "On Hold":
                on_hold_budget += budget
        
            # Verify Budget Category logic
            if budget > 0:
                if budget < 25000 and budget_category != "Small (<25k)":
                    errors.append(f"[X] Project with budget ${budget} should have Budget Category='Small (<25k)', found '{budget_category}'")
                elif 25000 <= budget <= 75000 and budget_category != "Medium (25-75k)":
                    errors.append(f"[X] Project with budget ${budget} should have Budget Category='Medium (25-75k)', found '{budget_category}'")
                elif budget > 75000 and budget_category != "Large (>75k)":
                    errors.append(f"[X] Project with budget ${budget} should have Budget Category='Large (>75k)', found '{budget_category}'")
                else:
                    checks_passed.append(f"[C] Project with budget $ should have Budget Category='Large (>75k)'")
    
        # Get blocks for report page
        report_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page_id}
        })
    
        if not isinstance(report_blocks, list):
            report_blocks = []
    
        report_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_total_h2 = False
        found_total_paragraph = False
        found_department_h2 = False
        found_high_value_h2 = False
        found_yellow_callout = False
    
        for block in report_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "budget analysis" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "total budget" in content.lower():
                found_total_h2 = True
        
            if block_type == "paragraph" and str(int(total_budget)) in content:
                found_total_paragraph = True
        
            if block_type == "heading2" and "budget by department" in content.lower():
                found_department_h2 = True
        
            if block_type == "heading2" and "high-value projects" in content.lower():
                found_high_value_h2 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "yellow" in bg_color.lower() and str(int(on_hold_budget)) in content:
                    found_yellow_callout = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Q1 2026 Budget Analysis'")
        else:
            checks_passed.append("[C] has heading_1 'Q1 2026 Budget Analysis'")
        if not found_total_h2:
            errors.append("[X] Missing heading_2 'Total Budget Allocation'")
        else:
            checks_passed.append("[C] has heading_2 'Total Budget Allocation'")
        if not found_total_paragraph:
            errors.append("[X] Missing paragraph with total budget")
        else:
            checks_passed.append("[C] has paragraph with total budget")
        if not found_department_h2:
            errors.append("[X] Missing heading_2 'Budget by Department'")
        else:
            checks_passed.append("[C] has heading_2 'Budget by Department'")
        if not found_high_value_h2:
            errors.append("[X] Missing heading_2 'High-Value Projects'")
        else:
            checks_passed.append("[C] has heading_2 'High-Value Projects'")
        if not found_yellow_callout:
            errors.append("[X] Missing yellow callout with on-hold budget")
        else:
            checks_passed.append("[C] has yellow callout with on-hold budget")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 43: Inventory Reorder Report Critical Alerts
# =============================================================================
def _validate_agent_inventory_reorder_report_critical_alerts(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Inventory Reorder Report with critical alerts.
    
    Checks:
    1. Page exists under Company Handbook
    2. Critical alert count and orange callout
    3. Out of Stock Items section with bulleted list
    4. Low Stock Items section with numbered list
    5. Reorder Cost Analysis section
    6. Emergency Supplies Kit row in Inventory Tracker
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Company Handbook page
    try:
        handbook_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Company Handbook"}}
        })
    
        if not isinstance(handbook_pages, list) or len(handbook_pages) == 0:
            errors.append("[X] Company Handbook page not found")
        else:
            checks_passed.append("[C] Company Handbook page")
    
        handbook_id = handbook_pages[0].get("_id")
    
        # Find report page
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": handbook_id, "title": {"$regex": "Inventory Reorder Report"}}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] Inventory Reorder Report page not found under Company Handbook")
        else:
            checks_passed.append("[C] Inventory Reorder Report page found under Company Handbook")
    
        report_page_id = report_pages[0].get("_id")
    
        # Query Inventory Tracker
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_id = inventory_pages[0].get("_id")
    
        inventory_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_id, "type": "row"}
        })
    
        if not isinstance(inventory_rows, list):
            inventory_rows = []
    
        # Count items needing reorder
        out_of_stock_count = 0
        low_stock_count = 0
    
        for row in inventory_rows:
            cell_values = row.get("cellValues", {})
            in_stock = True
            quantity = 0
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "stock" in key_lower and isinstance(val, bool):
                    in_stock = val
                elif "quantity" in key_lower and isinstance(val, (int, float)):
                    quantity = val
        
            if not in_stock:
                out_of_stock_count += 1
            elif quantity < 5:
                low_stock_count += 1
    
        total_reorder_needed = out_of_stock_count + low_stock_count
    
        # Check for Emergency Supplies Kit
        found_emergency_kit = False
        for row in inventory_rows:
            item_name = row.get("title", "")
            if "emergency supplies kit" in item_name.lower():
                found_emergency_kit = True
            
                # Verify properties
                cell_values = row.get("cellValues", {})
                sku = None
                quantity = None
                in_stock = None
                category = None
            
                for key, val in cell_values.items():
                    key_lower = key.lower()
                    if "sku" in key_lower:
                        sku = val
                    elif "quantity" in key_lower:
                        quantity = val
                    elif "stock" in key_lower and isinstance(val, bool):
                        in_stock = val
                    elif "category" in key_lower:
                        category = val
            
                if sku != "EMER-001":
                    errors.append(f"[X] Emergency Supplies Kit should have SKU='EMER-001', found '{sku}'")
                else:
                    checks_passed.append(f"[C] Emergency Supplies Kit should have SKU='EMER-001'")
                if quantity != 10:
                    errors.append(f"[X] Emergency Supplies Kit should have Quantity=10, found {quantity}")
                else:
                    checks_passed.append(f"[C] Emergency Supplies Kit should have Quantity=10")
                if category != "Office Supplies":
                    errors.append(f"[X] Emergency Supplies Kit should have Category='Office Supplies', found '{category}'")
                else:
                    checks_passed.append(f"[C] Emergency Supplies Kit should have Category='Office Supplies'")
                if not in_stock:
                    errors.append("[X] Emergency Supplies Kit should have In Stock=true")
                else:
                    checks_passed.append("[C] Emergency Supplies Kit should have In Stock=true")
                break
    
        if not found_emergency_kit:
            errors.append("[X] Emergency Supplies Kit row not found in Inventory Tracker")
        else:
            checks_passed.append("[C] Emergency Supplies Kit row found in Inventory Tracker")
    
        # Get blocks for report page
        report_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page_id}
        })
    
        if not isinstance(report_blocks, list):
            report_blocks = []
    
        report_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_orange_callout = False
        found_out_of_stock_h2 = False
        found_bulleted_list = False
        found_low_stock_h2 = False
        found_numbered_list = False
        found_cost_analysis_h2 = False
    
        for block in report_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "critical inventory alert" in content.lower():
                found_h1 = True
        
            if block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "orange" in bg_color.lower() and str(total_reorder_needed) in content:
                    found_orange_callout = True
        
            if block_type == "heading2" and "out of stock" in content.lower():
                found_out_of_stock_h2 = True
        
            if block_type == "bulletedList" and found_out_of_stock_h2:
                found_bulleted_list = True
        
            if block_type == "heading2" and "low stock" in content.lower():
                found_low_stock_h2 = True
        
            if block_type == "numberedList" and found_low_stock_h2:
                found_numbered_list = True
        
            if block_type == "heading2" and "reorder cost analysis" in content.lower():
                found_cost_analysis_h2 = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Critical Inventory Alert'")
        else:
            checks_passed.append("[C] has heading_1 'Critical Inventory Alert'")
        if not found_orange_callout:
            errors.append("[X] Missing orange callout with reorder count")
        else:
            checks_passed.append("[C] has orange callout with reorder count")
        if not found_out_of_stock_h2:
            errors.append("[X] Missing heading_2 'Out of Stock Items'")
        else:
            checks_passed.append("[C] has heading_2 'Out of Stock Items'")
        if not found_bulleted_list:
            errors.append("[X] Missing bulleted list with out-of-stock items")
        else:
            checks_passed.append("[C] has bulleted list with out-of-stock items")
        if not found_low_stock_h2:
            errors.append("[X] Missing heading_2 'Low Stock Items'")
        else:
            checks_passed.append("[C] has heading_2 'Low Stock Items'")
        if not found_numbered_list:
            errors.append("[X] Missing numbered list with low-stock items")
        else:
            checks_passed.append("[C] has numbered list with low-stock items")
        if not found_cost_analysis_h2:
            errors.append("[X] Missing heading_2 'Reorder Cost Analysis'")
        else:
            checks_passed.append("[C] has heading_2 'Reorder Cost Analysis'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 44: Multi-Database Employee Allocation System
# =============================================================================
def _validate_agent_multi_database_employee_allocation_system(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate multi-database employee allocation system.
    
    Checks:
    1. Resource Allocation database exists under Onboarding Guide
    2. Correct schema with Skills multi_select
    3. Rows for assignees with Status='In Progress'
    4. Project Portfolio URLs updated for Active projects
    5. Team Capacity Report page with overallocation warnings
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Onboarding Guide page
    try:
        onboarding_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Onboarding Guide"}}
        })
    
        if not isinstance(onboarding_pages, list) or len(onboarding_pages) == 0:
            errors.append("[X] Onboarding Guide page not found")
        else:
            checks_passed.append("[C] Onboarding Guide page")
    
        onboarding_id = onboarding_pages[0].get("_id")
    
        # Find Resource Allocation database
        resource_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": onboarding_id, "title": {"$regex": "Resource Allocation"}}
        })
    
        if not isinstance(resource_db_pages, list) or len(resource_db_pages) == 0:
            errors.append("[X] Resource Allocation database not found under Onboarding Guide")
        else:
            checks_passed.append("[C] Resource Allocation database found under Onboarding Guide")
    
        resource_db_id = resource_db_pages[0].get("_id")
        resource_db_page = resource_db_pages[0]
    
        # Verify schema
        db_metadata = resource_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Employee", "Current Project", "Allocation %", "Start Date", "End Date", "Skills"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query Task Tracker for assignees with In Progress tasks
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        # Get assignees with In Progress tasks
        in_progress_assignees = set()
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            assignee = None
            status = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "assignee" in key_lower:
                    assignee = val
                elif "status" in key_lower:
                    status = val
        
            if status == "In Progress" and assignee:
                in_progress_assignees.add(assignee)
    
        # Query resource rows
        resource_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": resource_db_id, "type": "row"}
        })
    
        if not isinstance(resource_rows, list):
            resource_rows = []
    
        # Check for rows for in-progress assignees
        if len(resource_rows) < len(in_progress_assignees):
            errors.append(f"[X] Expected at least {len(in_progress_assignees)} resource rows, found {len(resource_rows)}")
        else:
            checks_passed.append(f"[C] Expected at least resource rows")
    
        # Build property name to ID map for UUID support
        prop_id_map = {}
        for prop_data in db_properties:
            if isinstance(prop_data, dict):
                prop_name = prop_data.get("name", "").lower()
                prop_id = prop_data.get("_id", "")
                if prop_name and prop_id:
                    prop_id_map[prop_name] = prop_id
    
        # Verify Allocation=100, Skills=['Backend', 'Testing'] for all
        for row in resource_rows:
            cell_values = row.get("cellValues", {})
            allocation = None
            skills = []
        
            # Get values by property ID (handles UUIDs)
            allocation_id = prop_id_map.get("allocation %")
            if allocation_id:
                val = cell_values.get(allocation_id)
                if isinstance(val, (int, float)):
                    allocation = val
        
            skills_id = prop_id_map.get("skills")
            if skills_id:
                val = cell_values.get(skills_id)
                if isinstance(val, list):
                    skills = val
        
            if allocation != 100:
                errors.append(f"[X] Resource allocation should be 100%, found {allocation}%")
            else:
                checks_passed.append(f"[C] Resource allocation should be 100%%")
        
            if not ("Backend" in skills and "Testing" in skills):
                errors.append(f"[X] Resource should have skills ['Backend', 'Testing'], found {skills}")
            else:
                checks_passed.append(f"[C] Resource should have skills ['Backend', 'Testing']")
    
        # Check Project Portfolio URLs updated
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        # Verify Active projects have URLs
        active_without_url_count = 0
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
            url = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "status" in key_lower:
                    status = val
                elif "url" in key_lower:
                    url = val
        
            if status == "Active" and not url:
                active_without_url_count += 1
    
        if active_without_url_count > 0:
            errors.append(f"[X] Found {active_without_url_count} Active projects without URLs (should be updated)")
        else:
            checks_passed.append(f"[C] Found Active projects without URLs (should be updated)")
    
        # Find Team Directory page
        team_dir_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Directory"}}
        })
    
        if not isinstance(team_dir_pages, list) or len(team_dir_pages) == 0:
            errors.append("[X] Team Directory page not found")
        else:
            checks_passed.append("[C] Team Directory page")
    
        team_dir_id = team_dir_pages[0].get("_id")
    
        # Find Team Capacity Report page
        capacity_report_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": team_dir_id, "title": {"$regex": "Team Capacity Report"}}
        })
    
        if not isinstance(capacity_report_pages, list) or len(capacity_report_pages) == 0:
            errors.append("[X] Team Capacity Report page not found under Team Directory")
        else:
            checks_passed.append("[C] Team Capacity Report page found under Team Directory")
    
        capacity_report_page_id = capacity_report_pages[0].get("_id")
    
        # Get blocks for capacity report
        capacity_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": capacity_report_page_id}
        })
    
        if not isinstance(capacity_blocks, list):
            capacity_blocks = []
    
        capacity_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_overallocation_h2 = False
    
        for block in capacity_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "heading1" and "current team allocations" in content.lower():
                found_h1 = True
        
            if block_type == "heading2" and "overallocated resources" in content.lower():
                found_overallocation_h2 = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Current Team Allocations' in capacity report")
        else:
            checks_passed.append("[C] has heading_1 'Current Team Allocations' in capacity report")
        if not found_overallocation_h2:
            errors.append("[X] Missing heading_2 'Overallocated Resources'")
        else:
            checks_passed.append("[C] has heading_2 'Overallocated Resources'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task 45: VIP Client Projects Cross-Reference
# =============================================================================
def _validate_agent_vip_client_projects_cross_reference(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate VIP Client Projects cross-reference database.
    
    Checks:
    1. VIP Client Projects database exists as child of Contacts Directory (inline=true)
    2. Correct schema with 8 properties
    3. Rows for each VIP Client from Contacts Directory
    4. Exact values: Active Projects=2, Total Project Value=125000, etc.
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Contacts Directory database
    try:
        contacts_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Contacts Directory"}}
        })
    
        if not isinstance(contacts_pages, list) or len(contacts_pages) == 0:
            errors.append("[X] Contacts Directory database not found")
        else:
            checks_passed.append("[C] Contacts Directory database")
    
        contacts_id = contacts_pages[0].get("_id")
    
        # Query contacts to find VIP Clients
        contact_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": contacts_id, "type": "row"}
        })
    
        if not isinstance(contact_rows, list):
            contact_rows = []
    
        # Find VIP Clients
        vip_clients = []
        for row in contact_rows:
            cell_values = row.get("cellValues", {})
            contact_type = None
            tags = []
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "type" in key_lower:
                    contact_type = val
                elif "tag" in key_lower and isinstance(val, list):
                    tags = val
        
            if contact_type == "Client" and "VIP" in tags:
                vip_clients.append(row)
    
        if len(vip_clients) == 0:
            errors.append("[X] No VIP Clients found in Contacts Directory")
        else:
            checks_passed.append("[C] VIP Clients found in Contacts Directory")
    
        # Find VIP Client Projects database under Contacts Directory
        vip_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": contacts_id, "title": {"$regex": "VIP Client Projects"}}
        })
    
        if not isinstance(vip_db_pages, list) or len(vip_db_pages) == 0:
            errors.append("[X] VIP Client Projects database not found as child of Contacts Directory")
        else:
            checks_passed.append("[C] VIP Client Projects database found as child of Contacts Directory")
    
        vip_db_id = vip_db_pages[0].get("_id")
        vip_db_page = vip_db_pages[0]
    
        # Verify it's inline (check if it appears in Contacts Directory page blocks)
        contacts_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": contacts_id}
        })
    
        found_inline = False
        if isinstance(contacts_blocks, list):
            for block in contacts_blocks:
                if block.get("type") == "database":
                    db_id = block.get("properties", {}).get("databaseId")
                    if db_id == vip_db_id:
                        found_inline = True
                        break
    
        if not found_inline:
            errors.append("[X] VIP Client Projects should be an inline database (not found in page blocks)")
        else:
            checks_passed.append("[C] VIP Client Projects should be an inline database (found in page blocks)")
    
        # Verify schema
        db_metadata = vip_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
        required_props = ["Client Name", "Company", "Email", "Active Projects", "Total Project Value", "Primary Contact", "Engagement Level", "Last Contact Date"]
    
        for prop_name in required_props:
            found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "").lower() == prop_name.lower():
                    found = True
                    break
            if not found:
                errors.append(f"[X] Missing database property: {prop_name}")
            else:
                checks_passed.append(f"[C] has database property")
    
        # Query VIP Client Projects rows
        vip_project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": vip_db_id, "type": "row"}
        })
    
        if not isinstance(vip_project_rows, list):
            vip_project_rows = []
    
        if len(vip_project_rows) < len(vip_clients):
            errors.append(f"[X] Expected {len(vip_clients)} VIP Client Project rows, found {len(vip_project_rows)}")
        else:
            checks_passed.append(f"[C] Expected VIP Client Project rows")
    
        # Build property name to ID map for UUID support
        prop_id_map = {}
        for prop_data in db_properties:
            if isinstance(prop_data, dict):
                prop_name = prop_data.get("name", "").lower()
                prop_id = prop_data.get("_id", "")
                if prop_name and prop_id:
                    prop_id_map[prop_name] = prop_id
    
        # Verify values
        for row in vip_project_rows:
            cell_values = row.get("cellValues", {})
            active_projects = None
            total_value = None
            primary_contact = None
            engagement_level = None
        
            # Get values by property ID (handles UUIDs)
            active_projects_id = prop_id_map.get("active projects")
            if active_projects_id:
                active_projects = cell_values.get(active_projects_id)
        
            total_value_id = prop_id_map.get("total project value")
            if total_value_id:
                total_value = cell_values.get(total_value_id)
        
            primary_contact_id = prop_id_map.get("primary contact")
            if primary_contact_id:
                primary_contact = cell_values.get(primary_contact_id)
        
            engagement_level_id = prop_id_map.get("engagement level")
            if engagement_level_id:
                engagement_level = cell_values.get(engagement_level_id)
        
            # Verify exact values
            if active_projects != 2:
                errors.append(f"[X] VIP Client row should have Active Projects=2, found {active_projects}")
            else:
                checks_passed.append(f"[C] VIP Client row should have Active Projects=2")
            if total_value != 125000:
                errors.append(f"[X] VIP Client row should have Total Project Value=125000, found {total_value}")
            else:
                checks_passed.append(f"[C] VIP Client row should have Total Project Value=125000")
            if primary_contact != "Account Manager":
                errors.append(f"[X] VIP Client row should have Primary Contact='Account Manager', found '{primary_contact}'")
            else:
                checks_passed.append(f"[C] VIP Client row should have Primary Contact='Account Manager'")
            if engagement_level != "High":
                errors.append(f"[X] VIP Client row should have Engagement Level='High', found '{engagement_level}'")
            else:
                checks_passed.append(f"[C] VIP Client row should have Engagement Level='High'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_lead_conversion_contact_enrichment(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate lead conversion contact enrichment workflow.
    
    Checks:
    1. Query Contacts Directory for Type='Lead' with 'New' tag
    2. Match email domains with Project Portfolio companies
    3. Verify converted contacts have Type='Client' and 'VIP' tag
    4. Check Lead Conversion Report page exists under Meeting Notes
    5. Validate page structure with headings, lists, callout, and todos
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Contacts Directory
    try:
        contacts_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Contacts Directory"}}
        })
    
        if not isinstance(contacts_pages, list) or len(contacts_pages) == 0:
            errors.append("[X] Contacts Directory database not found")
        else:
            checks_passed.append("[C] Contacts Directory database")
    
        contacts_id = contacts_pages[0].get("_id")
    
        # Query all contact rows
        contact_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": contacts_id, "type": "row"}
        })
    
        if not isinstance(contact_rows, list):
            contact_rows = []
    
        # Find Project Portfolio
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        # Get project companies
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        # Extract company names from projects
        project_companies = set()
        for project in project_rows:
            cell_values = project.get("cellValues", {})
            for key, val in cell_values.items():
                if "company" in key.lower() and isinstance(val, str):
                    project_companies.add(val.lower())
    
        # Verify converted leads
        converted_count = 0
        remaining_leads = []
    
        for row in contact_rows:
            cell_values = row.get("cellValues", {})
            contact_type = None
            tags = []
            email = None
            name = row.get("title", "")
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "type" in key_lower and "outreach" not in key_lower:
                    contact_type = val
                elif "tag" in key_lower and isinstance(val, list):
                    tags = val
                elif "email" in key_lower:
                    email = val
        
            # Check if this was originally a Lead with New tag
            # If it's now a Client with VIP tag, it was converted
            if contact_type == "Client" and "VIP" in tags:
                if email:
                    domain = email.split("@")[1].lower() if "@" in email else ""
                    # Check if domain matches any company
                    if any(company in domain or domain in company for company in project_companies):
                        converted_count += 1
            elif contact_type == "Lead" and "New" in tags:
                remaining_leads.append(name)
    
        # Find Meeting Notes page
        meeting_notes_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Meeting Notes"}}
        })
    
        if not isinstance(meeting_notes_pages, list) or len(meeting_notes_pages) == 0:
            errors.append("[X] Meeting Notes page not found")
        else:
            checks_passed.append("[C] Meeting Notes page")
    
        meeting_notes_id = meeting_notes_pages[0].get("_id")
    
        # Find Lead Conversion Report page
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": meeting_notes_id, "title": {"$regex": "Lead Conversion Report"}}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] Lead Conversion Report page not found under Meeting Notes")
        else:
            checks_passed.append("[C] Lead Conversion Report page found under Meeting Notes")
    
        report_page_id = report_pages[0].get("_id")
    
        # Get blocks for report page
        report_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page_id}
        })
    
        if not isinstance(report_blocks, list):
            report_blocks = []
    
        report_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_converted_h2 = False
        found_remaining_h2 = False
        found_purple_callout = False
        found_todos = 0
        found_numbered_list = False
        found_bulleted_list = False
    
        for block in report_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if block_type == "heading1" and "Lead Conversion Report" in content:
                found_h1 = True
            elif block_type == "heading2":
                if "Converted Leads" in content:
                    found_converted_h2 = True
                elif "Remaining" in content and "Leads" in content:
                    found_remaining_h2 = True
            elif block_type == "callout":
                bg_color = properties.get("backgroundColor", "")
                if "purple" in bg_color.lower() and "Conversion Rate" in content:
                    found_purple_callout = True
            elif block_type == "todo":
                if not properties.get("checked", True):
                    found_todos += 1
            elif block_type == "numberedList":
                found_numbered_list = True
            elif block_type == "bulletedList":
                found_bulleted_list = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Lead Conversion Report'")
        else:
            checks_passed.append("[C] has heading_1 'Lead Conversion Report'")
        if not found_converted_h2:
            errors.append("[X] Missing heading_2 'Converted Leads'")
        else:
            checks_passed.append("[C] has heading_2 'Converted Leads'")
        if not found_remaining_h2:
            errors.append("[X] Missing heading_2 'Remaining New Leads'")
        else:
            checks_passed.append("[C] has heading_2 'Remaining New Leads'")
        if not found_purple_callout:
            errors.append("[X] Missing purple callout with conversion rate")
        else:
            checks_passed.append("[C] has purple callout with conversion rate")
        if found_todos < converted_count and converted_count > 0:
            errors.append(f"[X] Expected {converted_count} todo items for converted leads, found {found_todos}")
        else:
            checks_passed.append(f"[C] Expected todo items for converted leads")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_team_skills_matrix_system(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate team skills matrix system creation.
    
    Checks:
    1. Team Skills Matrix database exists under Team Directory
    2. Correct schema with all required properties
    3. Rows created from Task Tracker tags
    4. Tag-to-category mapping is correct
    5. Onboarding Guide updated with skills section
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Team Directory
    try:
        team_dir_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Directory"}}
        })
    
        if not isinstance(team_dir_pages, list) or len(team_dir_pages) == 0:
            errors.append("[X] Team Directory page not found")
        else:
            checks_passed.append("[C] Team Directory page")
    
        team_dir_id = team_dir_pages[0].get("_id")
    
        # Find Team Skills Matrix database
        skills_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": team_dir_id, "title": {"$regex": "Team Skills Matrix"}}
        })
    
        if not isinstance(skills_db_pages, list) or len(skills_db_pages) == 0:
            errors.append("[X] Team Skills Matrix database not found under Team Directory")
        else:
            checks_passed.append("[C] Team Skills Matrix database found under Team Directory")
    
        skills_db_id = skills_db_pages[0].get("_id")
        skills_db_page = skills_db_pages[0]
    
        # Verify schema
        db_metadata = skills_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        required_props = {
            "Skill": "title",
            "Category": "select",
            "Level Required": "select",
            "Team Members": "multi_select",
            "Projects Using": "multi_select",
            "Training Available": "checkbox"
        }
    
        for prop_name, prop_type in required_props.items():
            prop_found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "") == prop_name:
                    if prop_data.get("type", "") == prop_type:
                        prop_found = True
                    break
            if not prop_found:
                errors.append(f"[X] Property '{prop_name}' ({prop_type}) not found in Team Skills Matrix")
            else:
                checks_passed.append(f"[C] Property found in Team Skills Matrix")
    
        # Query skill rows
        skill_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": skills_db_id, "type": "row"}
        })
    
        if not isinstance(skill_rows, list):
            skill_rows = []
    
        if len(skill_rows) == 0:
            errors.append("[X] No skill rows found in Team Skills Matrix")
        else:
            checks_passed.append("[C] skill rows found in Team Skills Matrix")
    
        # Find Onboarding Guide
        onboarding_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Onboarding Guide"}}
        })
    
        if not isinstance(onboarding_pages, list) or len(onboarding_pages) == 0:
            errors.append("[X] Onboarding Guide page not found")
        else:
            checks_passed.append("[C] Onboarding Guide page")
    
        onboarding_page_id = onboarding_pages[0].get("_id")
    
        # Get blocks for Onboarding Guide
        onboarding_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": onboarding_page_id}
        })
    
        if not isinstance(onboarding_blocks, list):
            onboarding_blocks = []
    
        onboarding_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify Required Skills section exists after First Day Tasks
        found_first_day = False
        found_required_skills_h2 = False
        found_yellow_callout = False
        found_numbered_list = False
    
        for i, block in enumerate(onboarding_blocks):
            block_type = block.get("type", "")
            content = block.get("content", "")
            properties = block.get("properties", {})
        
            if "First Day Tasks" in content:
                found_first_day = True
            elif found_first_day:
                if block_type == "heading2" and "Required Skills" in content:
                    found_required_skills_h2 = True
                elif found_required_skills_h2:
                    if block_type == "callout":
                        bg_color = properties.get("backgroundColor", "")
                        if "yellow" in bg_color.lower() and "Team Skills Matrix" in content:
                            found_yellow_callout = True
                    elif block_type == "numberedList":
                        found_numbered_list = True
    
        if not found_required_skills_h2:
            errors.append("[X] Missing heading_2 'Required Skills' in Onboarding Guide after First Day Tasks")
        else:
            checks_passed.append("[C] has heading_2 'Required Skills' in Onboarding Guide after First Day Tasks")
        if not found_yellow_callout:
            errors.append("[X] Missing yellow callout mentioning Team Skills Matrix")
        else:
            checks_passed.append("[C] has yellow callout mentioning Team Skills Matrix")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_sprint_retrospective_dashboard_creation(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate sprint retrospective dashboard creation.
    
    Checks:
    1. Page exists as child of Team Retrospective
    2. Correct structure with headings and callouts
    3. Task completion by priority calculations
    4. Department performance metrics from Project Portfolio
    5. Risk analysis with blocked tasks
    6. Action Items inline database
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Team Retrospective page
    try:
        retro_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Retrospective"}}
        })
    
        if not isinstance(retro_pages, list) or len(retro_pages) == 0:
            errors.append("[X] Team Retrospective page not found")
        else:
            checks_passed.append("[C] Team Retrospective page")
    
        retro_id = retro_pages[0].get("_id")
    
        # Find Sprint Retrospective Dashboard page
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": retro_id, "title": {"$regex": "Sprint Retrospective Dashboard"}}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Sprint Retrospective Dashboard page not found under Team Retrospective")
        else:
            checks_passed.append("[C] Sprint Retrospective Dashboard page found under Team Retrospective")
    
        dashboard_page_id = dashboard_pages[0].get("_id")
    
        # Get blocks for dashboard
        dashboard_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page_id}
        })
    
        if not isinstance(dashboard_blocks, list):
            dashboard_blocks = []
    
        dashboard_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_green_callout = False
        found_task_completion_h2 = False
        found_dept_performance_h2 = False
        found_risk_analysis_h2 = False
        found_red_callout = False
        found_action_items_db = False
    
        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "heading1" and "Sprint Retrospective Dashboard" in content:
                found_h1 = True
            elif block_type == "heading2":
                if "Task Completion" in content and "Priority" in content:
                    found_task_completion_h2 = True
                elif "Department Performance" in content:
                    found_dept_performance_h2 = True
                elif "Risk Analysis" in content:
                    found_risk_analysis_h2 = True
            elif block_type == "callout":
                bg_color = block.get("properties", {}).get("backgroundColor", "")
                if "green" in bg_color.lower() and "Dashboard generated" in content:
                    found_green_callout = True
                elif "red" in bg_color.lower() and "blocked" in content.lower():
                    found_red_callout = True
            elif block_type == "database":
                db_id = block.get("properties", {}).get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "Action Items" in db_title:
                            found_action_items_db = True
                            # Check for 3 rows
                            action_rows = backend.query({
                                "collection": "pages",
                                "filter": {"databaseId": db_id, "type": "row"}
                            })
                            if not isinstance(action_rows, list) or len(action_rows) < 3:
                                errors.append(f"[X] Action Items database should have at least 3 rows, found {len(action_rows) if isinstance(action_rows, list) else 0}")
                            else:
                                checks_passed.append(f"[C] Action Items database should have at least 3 rows")
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Sprint Retrospective Dashboard'")
        else:
            checks_passed.append("[C] has heading_1 'Sprint Retrospective Dashboard'")
        if not found_green_callout:
            errors.append("[X] Missing green callout with dashboard generation date")
        else:
            checks_passed.append("[C] has green callout with dashboard generation date")
        if not found_task_completion_h2:
            errors.append("[X] Missing heading_2 'Task Completion by Priority'")
        else:
            checks_passed.append("[C] has heading_2 'Task Completion by Priority'")
        if not found_dept_performance_h2:
            errors.append("[X] Missing heading_2 'Department Performance'")
        else:
            checks_passed.append("[C] has heading_2 'Department Performance'")
        if not found_risk_analysis_h2:
            errors.append("[X] Missing heading_2 'Risk Analysis'")
        else:
            checks_passed.append("[C] has heading_2 'Risk Analysis'")
        if not found_red_callout:
            errors.append("[X] Missing red callout with blocked tasks count")
        else:
            checks_passed.append("[C] has red callout with blocked tasks count")
        if not found_action_items_db:
            errors.append("[X] Missing Action Items inline database")
        else:
            checks_passed.append("[C] has Action Items inline database")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_sprint_retrospective_tracker_inline(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate sprint retrospective tracker inline database.
    
    Checks:
    1. Inline database exists in Team Retrospective page
    2. Exact schema with properties in order
    3. Select options with correct colors
    4. 5 specific rows with exact values
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Team Retrospective page (page-3-2)
    try:
        retro_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Retrospective"}}
        })
    
        if not isinstance(retro_pages, list) or len(retro_pages) == 0:
            errors.append("[X] Team Retrospective page not found")
        else:
            checks_passed.append("[C] Team Retrospective page")
    
        retro_page = retro_pages[0]
        retro_id = retro_page.get("_id")
    
        # Get blocks for Team Retrospective to find inline database
        retro_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": retro_id}
        })
    
        if not isinstance(retro_blocks, list):
            retro_blocks = []
    
        # Find Sprint Retrospective Tracker database block
        tracker_db_id = None
        for block in retro_blocks:
            if block.get("type") == "database":
                db_id = block.get("properties", {}).get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "Sprint Retrospective Tracker" in db_title:
                            tracker_db_id = db_id
                            break
    
        if not tracker_db_id:
            errors.append("[X] Sprint Retrospective Tracker inline database not found in Team Retrospective page")
        else:
            checks_passed.append("[C] Sprint Retrospective Tracker inline database found in Team Retrospective page")
    
        # Get database page
        tracker_db_pages = backend.query({
            "collection": "pages",
            "filter": {"_id": tracker_db_id}
        })
    
        if not isinstance(tracker_db_pages, list) or len(tracker_db_pages) == 0:
            errors.append("[X] Sprint Retrospective Tracker database not found")
        else:
            checks_passed.append("[C] Sprint Retrospective Tracker database")
    
        tracker_db_page = tracker_db_pages[0]
    
        # Verify schema
        db_metadata = tracker_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        required_props = [
            ("Item", "title"),
            ("Category", "select"),
            ("Priority", "select"),
            ("Owner", "text"),
            ("Sprint Date", "date"),
            ("Status", "select")
        ]
    
        for prop_name, prop_type in required_props:
            prop_found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "") == prop_name:
                    if prop_data.get("type", "") == prop_type:
                        prop_found = True
                    
                        # Verify select options
                        if prop_type == "select":
                            options = prop_data.get("options", [])
                            if prop_name == "Category":
                                required_options = {"What Went Well", "What Didn't Go Well", "Action Items", "Ideas"}
                                found_options = {opt.get("name", "") for opt in options if isinstance(opt, dict)}
                                if required_options != found_options:
                                    errors.append(f"[X] Category options incorrect. Expected {required_options}, found {found_options}")
                            elif prop_name == "Priority":
                                required_options = {"Low", "Medium", "High"}
                                found_options = {opt.get("name", "") for opt in options if isinstance(opt, dict)}
                                if required_options != found_options:
                                    errors.append(f"[X] Priority options incorrect. Expected {required_options}, found {found_options}")
                            elif prop_name == "Status":
                                required_options = {"New", "In Progress", "Done"}
                                found_options = {opt.get("name", "") for opt in options if isinstance(opt, dict)}
                                if required_options != found_options:
                                    errors.append(f"[X] Status options incorrect. Expected {required_options}, found {found_options}")
                                else:
                                    checks_passed.append(f"[C] Status options correct")
                    break
            if not prop_found:
                errors.append(f"[X] Property '{prop_name}' ({prop_type}) not found in Sprint Retrospective Tracker")
            else:
                checks_passed.append(f"[C] Property found in Sprint Retrospective Tracker")
    
        # Query rows
        tracker_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": tracker_db_id, "type": "row"}
        })
    
        if not isinstance(tracker_rows, list):
            tracker_rows = []
    
        if len(tracker_rows) < 5:
            errors.append(f"[X] Expected at least 5 rows in Sprint Retrospective Tracker, found {len(tracker_rows)}")
        else:
            checks_passed.append(f"[C] Found at least 5 rows in Sprint Retrospective Tracker")
    
        # Verify specific row values
        expected_rows = [
            {"title": "Team communication improved", "Category": "What Went Well", "Priority": "Low", "Status": "Done"},
            {"title": "Deployment pipeline issues", "Category": "What Didn't Go Well", "Priority": "High", "Status": "In Progress"},
            {"title": "Automate testing process", "Category": "Action Items", "Priority": "High", "Status": "New"},
            {"title": "Daily standup format change", "Category": "Ideas", "Priority": "Medium", "Status": "New"},
            {"title": "Code review turnaround time", "Category": "What Didn't Go Well", "Priority": "Medium", "Status": "In Progress"}
        ]
    
        matched_rows = 0
        for expected in expected_rows:
            for row in tracker_rows:
                if expected["title"].lower() in row.get("title", "").lower():
                    matched_rows += 1
                    break
    
        if matched_rows < 5:
            errors.append(f"[X] Not all expected rows found. Matched {matched_rows} out of 5")
        else:
            checks_passed.append(f"[C] all expected rows found. Matched out of 5")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_sprint_backlog_task_prioritization(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate sprint backlog task prioritization system.
    
    Checks:
    1. Query Task Tracker for High/Urgent non-Done tasks
    2. Verify unassigned tasks trigger Contacts Directory entries
    3. Sprint Backlog inline database exists in Sprint Planning page
    4. Correct schema and task imports
    5. Sprint Priority and Story Points calculations
    6. Sprint Velocity Metrics section
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Task Tracker
    try:
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Find Sprint Planning page
        sprint_planning_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Sprint Planning"}}
        })
    
        if not isinstance(sprint_planning_pages, list) or len(sprint_planning_pages) == 0:
            errors.append("[X] Sprint Planning page not found")
        else:
            checks_passed.append("[C] Sprint Planning page")
    
        sprint_planning_id = sprint_planning_pages[0].get("_id")
    
        # Get blocks for Sprint Planning to find inline database
        sprint_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": sprint_planning_id}
        })
    
        if not isinstance(sprint_blocks, list):
            sprint_blocks = []
    
        sprint_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Find Sprint Backlog database block
        backlog_db_id = None
        found_velocity_h2 = False
        found_velocity_callout = False
    
        for block in sprint_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "database":
                db_id = block.get("properties", {}).get("databaseId")
                if db_id:
                    db_pages = backend.query({
                        "collection": "pages",
                        "filter": {"_id": db_id}
                    })
                    if isinstance(db_pages, list) and len(db_pages) > 0:
                        db_title = db_pages[0].get("title", "")
                        if "Sprint Backlog" in db_title:
                            backlog_db_id = db_id
            elif block_type == "heading2" and "Sprint Velocity Metrics" in content:
                found_velocity_h2 = True
            elif block_type == "callout" and "Story Points" in content:
                found_velocity_callout = True
    
        if not backlog_db_id:
            errors.append("[X] Sprint Backlog inline database not found in Sprint Planning page")
        else:
            checks_passed.append("[C] Sprint Backlog inline database found in Sprint Planning page")
    
        if not found_velocity_h2:
            errors.append("[X] Missing heading_2 'Sprint Velocity Metrics'")
        else:
            checks_passed.append("[C] has heading_2 'Sprint Velocity Metrics'")
    
        # Query Sprint Backlog rows
        backlog_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": backlog_db_id, "type": "row"}
        })
    
        if not isinstance(backlog_rows, list):
            backlog_rows = []
    
        if len(backlog_rows) == 0:
            errors.append("[X] No tasks found in Sprint Backlog")
        else:
            checks_passed.append("[C] tasks found in Sprint Backlog")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_project_deadline_alerts(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate project deadline alerts system.
    
    Checks:
    1. Projects with Timeline end date within 30 days
    2. Each project row page has deadline alert heading and callout
    3. Outstanding tasks section with department tasks
    4. Upcoming Project Deadlines summary page under Project Roadmap
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        from datetime import datetime, timedelta
    
        # Find Project Portfolio
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_id = portfolio_pages[0].get("_id")
    
        # Get today's date and 30 days from now
        today = datetime.now()
        thirty_days = today + timedelta(days=30)
        today_str = today.strftime("%Y-%m-%d")
        thirty_days_str = thirty_days.strftime("%Y-%m-%d")
    
        # Query project rows
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_id, "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            project_rows = []
    
        # Find projects ending soon
        ending_soon_count = 0
        for project in project_rows:
            cell_values = project.get("cellValues", {})
            timeline = None
        
            for key, val in cell_values.items():
                if "timeline" in key.lower() and isinstance(val, dict):
                    timeline = val.get("end")
        
            if timeline and today_str <= timeline <= thirty_days_str:
                ending_soon_count += 1
    
        # Find Project Roadmap page
        roadmap_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Roadmap"}}
        })
    
        if not isinstance(roadmap_pages, list) or len(roadmap_pages) == 0:
            errors.append("[X] Project Roadmap page not found")
        else:
            checks_passed.append("[C] Project Roadmap page")
    
        roadmap_id = roadmap_pages[0].get("_id")
    
        # Find Upcoming Project Deadlines page
        deadline_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": roadmap_id, "title": {"$regex": "Upcoming Project Deadlines"}}
        })
    
        if not isinstance(deadline_pages, list) or len(deadline_pages) == 0:
            errors.append("[X] Upcoming Project Deadlines page not found under Project Roadmap")
        else:
            checks_passed.append("[C] Upcoming Project Deadlines page found under Project Roadmap")
    
        deadline_page_id = deadline_pages[0].get("_id")
    
        # Get blocks for deadline page
        deadline_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": deadline_page_id}
        })
    
        if not isinstance(deadline_blocks, list):
            deadline_blocks = []
    
        # Verify structure
        found_h1 = False
        for block in deadline_blocks:
            content = block.get("content", "")
            if block.get("type") == "heading1" and "Projects Ending Soon" in content:
                found_h1 = True
                break
    
        if not found_h1:
            errors.append("[X] Missing heading1 'Projects Ending Soon' in summary page")
        else:
            checks_passed.append("[C] has heading1 'Projects Ending Soon' in summary page")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_crm_engagement_tracking(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate CRM engagement tracking system.
    
    Checks:
    1. Contacts Directory has new properties: Last Contact Date, Next Follow-up, Deal Value, Engagement Score
    2. Date calculations and Deal Value assignments
    3. Engagement Score logic based on Tags
    4. CRM Dashboard page under Team Directory
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Contacts Directory
    try:
        contacts_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Contacts Directory"}}
        })
    
        if not isinstance(contacts_pages, list) or len(contacts_pages) == 0:
            errors.append("[X] Contacts Directory database not found")
        else:
            checks_passed.append("[C] Contacts Directory database")
    
        contacts_page = contacts_pages[0]
        contacts_id = contacts_page.get("_id")
    
        # Verify new properties
        db_metadata = contacts_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        required_props = {
            "Last Contact Date": "date",
            "Next Follow-up": "date",
            "Deal Value": "number",
            "Engagement Score": "select"
        }
    
        for prop_name, prop_type in required_props.items():
            prop_found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "") == prop_name:
                    if prop_data.get("type", "") == prop_type:
                        prop_found = True
                    
                        # Verify Engagement Score options
                        if prop_name == "Engagement Score":
                            options = prop_data.get("options", [])
                            required_options = {"Hot", "Warm", "Cold", "Inactive"}
                            found_options = {opt.get("name", "") for opt in options if isinstance(opt, dict)}
                            if required_options != found_options:
                                errors.append(f"[X] Engagement Score options incorrect. Expected {required_options}, found {found_options}")
                            else:
                                checks_passed.append(f"[C] Engagement Score options correct")
                    break
            if not prop_found:
                errors.append(f"[X] Property '{prop_name}' ({prop_type}) not found in Contacts Directory")
            else:
                checks_passed.append(f"[C] Property found in Contacts Directory")
    
        # Find Team Directory
        team_dir_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Directory"}}
        })
    
        if not isinstance(team_dir_pages, list) or len(team_dir_pages) == 0:
            errors.append("[X] Team Directory page not found")
        else:
            checks_passed.append("[C] Team Directory page")
    
        team_dir_id = team_dir_pages[0].get("_id")
    
        # Find CRM Dashboard page
        crm_dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": team_dir_id, "title": {"$regex": "CRM Dashboard"}}
        })
    
        if not isinstance(crm_dashboard_pages, list) or len(crm_dashboard_pages) == 0:
            errors.append("[X] CRM Dashboard page not found under Team Directory")
        else:
            checks_passed.append("[C] CRM Dashboard page found under Team Directory")
    
        crm_dashboard_id = crm_dashboard_pages[0].get("_id")
    
        # Get blocks for CRM Dashboard
        crm_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": crm_dashboard_id}
        })
    
        if not isinstance(crm_blocks, list):
            crm_blocks = []
    
        # Verify structure
        found_h1 = False
        found_pipeline_callout = False
    
        for block in crm_blocks:
            content = block.get("content", "")
            block_type = block.get("type", "")
        
            if block_type == "heading1" and "Contact Engagement Dashboard" in content:
                found_h1 = True
            elif block_type == "callout" and "Pipeline Value" in content:
                found_pipeline_callout = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Contact Engagement Dashboard'")
        else:
            checks_passed.append("[C] has heading_1 'Contact Engagement Dashboard'")
        if not found_pipeline_callout:
            errors.append("[X] Missing callout with Total Pipeline Value")
        else:
            checks_passed.append("[C] has callout with Total Pipeline Value")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_critical_tasks_summary_report(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate critical tasks summary report.
    
    Checks:
    1. Query Task Tracker for Priority='Urgent' or 'High'
    2. Critical Tasks Summary page exists under Meeting Notes
    3. Structure with headings, callouts, and lists
    4. Tasks by Assignee section with heading_3 per assignee
    5. Tag Distribution section
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Task Tracker
    try:
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Query critical tasks
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        critical_tasks = []
        urgent_count = 0
    
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            priority = None
        
            for key, val in cell_values.items():
                if "priority" in key.lower():
                    priority = val
                    break
        
            if priority in ["High", "Urgent"]:
                critical_tasks.append(row)
                if priority == "Urgent":
                    urgent_count += 1
    
        # Find Meeting Notes page
        meeting_notes_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Meeting Notes"}}
        })
    
        if not isinstance(meeting_notes_pages, list) or len(meeting_notes_pages) == 0:
            errors.append("[X] Meeting Notes page not found")
        else:
            checks_passed.append("[C] Meeting Notes page")
    
        meeting_notes_id = meeting_notes_pages[0].get("_id")
    
        # Find Critical Tasks Summary page
        summary_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": meeting_notes_id, "title": {"$regex": "Critical Tasks Summary"}}
        })
    
        if not isinstance(summary_pages, list) or len(summary_pages) == 0:
            errors.append("[X] Critical Tasks Summary page not found under Meeting Notes")
        else:
            checks_passed.append("[C] Critical Tasks Summary page found under Meeting Notes")
    
        summary_page_id = summary_pages[0].get("_id")
    
        # Get blocks for summary page
        summary_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": summary_page_id}
        })
    
        if not isinstance(summary_blocks, list):
            summary_blocks = []
    
        summary_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_red_callout = False
        found_tasks_by_assignee_h2 = False
        found_tag_distribution_h2 = False
    
        for block in summary_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "heading1" and "High Priority Tasks Review" in content:
                found_h1 = True
            elif block_type == "heading2":
                if "Tasks by Assignee" in content:
                    found_tasks_by_assignee_h2 = True
                elif "Tag Distribution" in content:
                    found_tag_distribution_h2 = True
            elif block_type == "callout":
                bg_color = block.get("properties", {}).get("backgroundColor", "")
                if "red" in bg_color.lower() and str(urgent_count) in content:
                    found_red_callout = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'High Priority Tasks Review'")
        else:
            checks_passed.append("[C] has heading_1 'High Priority Tasks Review'")
        if not found_red_callout:
            errors.append(f"[X] Missing red callout with urgent count ({urgent_count})")
        else:
            checks_passed.append(f"[C] has red callout with urgent count")
        if not found_tasks_by_assignee_h2:
            errors.append("[X] Missing heading_2 'Tasks by Assignee'")
        else:
            checks_passed.append("[C] has heading_2 'Tasks by Assignee'")
        if not found_tag_distribution_h2:
            errors.append("[X] Missing heading_2 'Tag Distribution'")
        else:
            checks_passed.append("[C] has heading_2 'Tag Distribution'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_vendor_performance_procurement(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate vendor performance procurement system.
    
    Checks:
    1. Vendor Performance database exists as child of Company Handbook
    2. Correct schema with 10 properties
    3. Vendor rows created for each Inventory category
    4. Blue callout added to Inventory Tracker page
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Company Handbook
    try:
        handbook_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Company Handbook"}}
        })
    
        if not isinstance(handbook_pages, list) or len(handbook_pages) == 0:
            errors.append("[X] Company Handbook page not found")
        else:
            checks_passed.append("[C] Company Handbook page")
    
        handbook_id = handbook_pages[0].get("_id")
    
        # Find Vendor Performance database
        vendor_db_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": handbook_id, "title": {"$regex": "Vendor Performance"}}
        })
    
        if not isinstance(vendor_db_pages, list) or len(vendor_db_pages) == 0:
            errors.append("[X] Vendor Performance database not found under Company Handbook")
        else:
            checks_passed.append("[C] Vendor Performance database found under Company Handbook")
    
        vendor_db_id = vendor_db_pages[0].get("_id")
        vendor_db_page = vendor_db_pages[0]
    
        # Verify schema
        db_metadata = vendor_db_page.get("databaseMetadata", {})
        db_properties = db_metadata.get("properties", [])
    
        required_props = {
            "Vendor Name": "title",
            "Category": "multi_select",
            "Contact Email": "email",
            "Phone": "phone_number",
            "Average Lead Time (days)": "number",
            "Quality Score": "select",
            "Total Orders": "number",
            "On-Time Delivery %": "number",
            "Primary Contact": "text",
            "Contract Expiry": "date",
            "Preferred": "checkbox"
        }
    
        for prop_name, prop_type in required_props.items():
            prop_found = False
            for prop_data in db_properties:
                if isinstance(prop_data, dict) and prop_data.get("name", "") == prop_name:
                    if prop_data.get("type", "") == prop_type:
                        prop_found = True
                    break
            if not prop_found:
                errors.append(f"[X] Property '{prop_name}' ({prop_type}) not found in Vendor Performance")
            else:
                checks_passed.append(f"[C] Property found in Vendor Performance")
    
        # Query vendor rows
        vendor_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": vendor_db_id, "type": "row"}
        })
    
        if not isinstance(vendor_rows, list):
            vendor_rows = []
    
        if len(vendor_rows) == 0:
            errors.append("[X] No vendor rows found in Vendor Performance database")
        else:
            checks_passed.append("[C] vendor rows found in Vendor Performance database")
    
        # Find Inventory Tracker page
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker page not found")
        else:
            checks_passed.append("[C] Inventory Tracker page")
    
        inventory_page_id = inventory_pages[0].get("_id")
    
        # Get blocks for Inventory Tracker to verify callout
        inventory_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": inventory_page_id}
        })
    
        if not isinstance(inventory_blocks, list):
            inventory_blocks = []
    
        # Verify blue callout exists
        found_blue_callout = False
        for block in inventory_blocks:
            if block.get("type") == "callout":
                bg_color = block.get("properties", {}).get("backgroundColor", "")
                content = block.get("content", "")
                if "blue" in bg_color.lower() and "Vendor Management" in content:
                    found_blue_callout = True
                    break
    
        if not found_blue_callout:
            errors.append("[X] Missing blue callout with Vendor Management text in Inventory Tracker page")
        else:
            checks_passed.append("[C] has blue callout with Vendor Management text in Inventory Tracker page")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_overdue_tasks_impact_analysis(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate overdue tasks impact analysis.
    
    Checks:
    1. Query Task Tracker for overdue tasks (Due Date < today, Status != 'Done'/'Blocked')
    2. Match task names against Project Portfolio project names
    3. Overdue Tasks Impact Analysis page exists under Bug Tracker
    4. Structure with headings, callout, and groupings
    5. Priority updates to 'Urgent' for overdue Low/Medium tasks
    6. Summary statistics in code block
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        from datetime import datetime
    
        # Find Task Tracker
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")
    
        task_tracker_id = task_tracker_pages[0].get("_id")
    
        # Query tasks
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            task_rows = []
    
        today = datetime.now().strftime("%Y-%m-%d")
    
        # Find overdue tasks
        overdue_tasks = []
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            due_date = None
            status = None
        
            for key, val in cell_values.items():
                key_lower = key.lower()
                if "due" in key_lower and isinstance(val, dict):
                    due_date = val.get("start")
                elif "status" in key_lower:
                    status = val
        
            if due_date and due_date < today and status not in ["Done", "Blocked"]:
                overdue_tasks.append(row)
    
        # Find Bug Tracker page
        bug_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Bug Tracker"}}
        })
    
        if not isinstance(bug_tracker_pages, list) or len(bug_tracker_pages) == 0:
            errors.append("[X] Bug Tracker page not found")
        else:
            checks_passed.append("[C] Bug Tracker page")
    
        bug_tracker_id = bug_tracker_pages[0].get("_id")
    
        # Find Overdue Tasks Impact Analysis page
        analysis_pages = backend.query({
            "collection": "pages",
            "filter": {"parentId": bug_tracker_id, "title": {"$regex": "Overdue Tasks Impact Analysis"}}
        })
    
        if not isinstance(analysis_pages, list) or len(analysis_pages) == 0:
            errors.append("[X] Overdue Tasks Impact Analysis page not found under Bug Tracker")
        else:
            checks_passed.append("[C] Overdue Tasks Impact Analysis page found under Bug Tracker")
    
        analysis_page_id = analysis_pages[0].get("_id")
    
        # Get blocks for analysis page
        analysis_blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": analysis_page_id}
        })
    
        if not isinstance(analysis_blocks, list):
            analysis_blocks = []
    
        analysis_blocks.sort(key=lambda b: b.get("order", 0))
    
        # Verify structure
        found_h1 = False
        found_red_callout = False
        found_project_impact_h2 = False
        found_summary_h2 = False
        found_code_block = False
    
        for block in analysis_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
        
            if block_type == "heading1" and "Overdue Tasks Impact Analysis" in content:
                found_h1 = True
            elif block_type == "heading2":
                if "Overdue Tasks by Project Impact" in content:
                    found_project_impact_h2 = True
                elif "Overdue Summary Statistics" in content or "Summary Statistics" in content:
                    found_summary_h2 = True
            elif block_type == "callout":
                bg_color = block.get("properties", {}).get("backgroundColor", "")
                if "red" in bg_color.lower() and str(len(overdue_tasks)) in content:
                    found_red_callout = True
            elif block_type == "code":
                if "Total Overdue" in content:
                    found_code_block = True
    
        if not found_h1:
            errors.append("[X] Missing heading_1 'Overdue Tasks Impact Analysis'")
        else:
            checks_passed.append("[C] has heading_1 'Overdue Tasks Impact Analysis'")
        if not found_red_callout:
            errors.append(f"[X] Missing red callout with overdue count ({len(overdue_tasks)})")
        else:
            checks_passed.append(f"[C] has red callout with overdue count")
        if not found_project_impact_h2:
            errors.append("[X] Missing heading_2 'Overdue Tasks by Project Impact'")
        else:
            checks_passed.append("[C] has heading_2 'Overdue Tasks by Project Impact'")
        if not found_summary_h2:
            errors.append("[X] Missing heading_2 'Overdue Summary Statistics'")
        else:
            checks_passed.append("[C] has heading_2 'Overdue Summary Statistics'")
        if not found_code_block:
            errors.append("[X] Missing code block with summary statistics")
        else:
            checks_passed.append("[C] has code block with summary statistics")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Reward Function Registry
# =============================================================================
def _validate_agent_inventory_reorder_management_system(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate inventory reorder management system.
    
    Checks:
    1. Low stock items in Inventory Tracker have Reorder Date updated to 2026-01-19
    2. Reorder Schedule database created with correct schema
    3. Rows created with accurate calculations
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Inventory Tracker
    try:
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")
    
        inventory_db = inventory_pages[0]
    
        # Check rows with Quantity < 10 have Reorder Date = 2026-01-19
        rows = backend.query({
            "collection": "pages",
            "filter": {
                "databaseId": inventory_db.get("_id"),
                "type": "row"
            }
        })
    
        if not isinstance(rows, list):
            errors.append("[X] Could not query inventory rows")
        else:
            checks_passed.append("[C] Could query inventory rows")
    
        updated_count = 0
        for row in rows:
            cell_values = row.get("cellValues", {})
            quantity = None
            in_stock = None
            reorder_date = None
        
            for prop_id, value in cell_values.items():
                props = inventory_db.get("databaseMetadata", {}).get("properties", [])
                for prop in props:
                    if prop.get("_id") == prop_id:
                        if prop.get("name") == "Quantity" and isinstance(value, (int, float)):
                            quantity = value
                        elif prop.get("name") == "In Stock" and isinstance(value, bool):
                            in_stock = value
                        elif prop.get("name") == "Reorder Date" and isinstance(value, dict):
                            reorder_date = value.get("start")
        
            if quantity is not None and quantity < 10 and in_stock:
                if reorder_date != "2026-01-19":
                    errors.append(f"[X] Row {row.get('title')} with Quantity={quantity} should have Reorder Date=2026-01-19, found {reorder_date}")
                else:
                    checks_passed.append(f"[C] Row with Quantity= should have Reorder Date=2026-01-19")
                updated_count += 1
    
        # Find Reorder Schedule database
        reorder_pages = backend.query({
            "collection": "pages",
            "filter": {"title": "Reorder Schedule", "type": "database"}
        })
    
        if not isinstance(reorder_pages, list) or len(reorder_pages) == 0:
            errors.append("[X] Reorder Schedule database not found")
        else:
            checks_passed.append("[C] Reorder Schedule database")
    
        reorder_db = reorder_pages[0]
    
        # Validate schema
        props = reorder_db.get("databaseMetadata", {}).get("properties", [])
        required_props = {
            "Item": "title",
            "Current Stock": "number",
            "Reorder Quantity": "number",
            "Total Cost": "number",
            "Supplier": "select",
            "Priority": "select",
            "Reorder Date": "date"
        }
    
        for prop_name, prop_type in required_props.items():
            found = False
            for prop in props:
                if prop.get("name") == prop_name and prop.get("type") == prop_type:
                    found = True
                    # Check select options for Supplier and Priority
                    if prop_name == "Supplier":
                        options = {opt.get("name"): opt.get("color") for opt in prop.get("options", [])}
                        expected = {"TechSupply Co": "blue", "OfficeMax": "orange", "FurnitureHub": "brown", "DirectVendor": "green"}
                        if options != expected:
                            errors.append(f"[X] Supplier options incorrect: {options}")
                    elif prop_name == "Priority":
                        options = {opt.get("name"): opt.get("color") for opt in prop.get("options", [])}
                        expected = {"Critical": "red", "High": "orange", "Normal": "yellow"}
                        if options != expected:
                            errors.append(f"[X] Priority options incorrect: {options}")
                        else:
                            checks_passed.append(f"[C] Priority options correct")
                    break
            if not found:
                errors.append(f"[X] Missing property: {prop_name} ({prop_type})")
            else:
                checks_passed.append(f"[C] has property")
    
        # Check reorder rows exist with correct calculations
        reorder_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": reorder_db.get("_id"), "type": "row"}
        })
    
        if not isinstance(reorder_rows, list) or len(reorder_rows) == 0:
            errors.append("[X] No reorder rows created")
        else:
            checks_passed.append("[C] reorder rows created")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_sprint_velocity_tracker_analysis(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate sprint velocity tracker analysis.
    
    Checks:
    1. Sprint Velocity Analysis page under Engineering Wiki
    2. Weekly Velocity inline database with correct schema
    3. 4 rows with accurate task counts and velocity calculations
    4. Blue callout with average velocity
    5. Recommendations section
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Engineering Wiki
    try:
        wiki_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Engineering Wiki"}}
        })
    
        if not isinstance(wiki_pages, list) or len(wiki_pages) == 0:
            errors.append("[X] Engineering Wiki not found")
        else:
            checks_passed.append("[C] Engineering Wiki")
    
        wiki_id = wiki_pages[0].get("_id")
    
        # Find Sprint Velocity Analysis page
        analysis_pages = backend.query({
            "collection": "pages",
            "filter": {
                "title": "Sprint Velocity Analysis",
                "parentId": wiki_id
            }
        })
    
        if not isinstance(analysis_pages, list) or len(analysis_pages) == 0:
            errors.append("[X] Sprint Velocity Analysis page not found under Engineering Wiki")
        else:
            checks_passed.append("[C] Sprint Velocity Analysis page found under Engineering Wiki")
    
        analysis_page = analysis_pages[0]
    
        # Check heading_1
        blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": analysis_page.get("_id")}
        })
    
        if not isinstance(blocks, list):
            errors.append("[X] Could not query blocks")
        else:
            checks_passed.append("[C] Could query blocks")
    
        blocks_sorted = sorted(blocks, key=lambda b: b.get("order", 0))
    
        found_h1 = False
        for block in blocks_sorted:
            if block.get("type") == "heading1" and "Sprint Velocity Analysis" in block.get("content", ""):
                found_h1 = True
                break
    
        if not found_h1:
            errors.append("[X] Missing heading1 'Sprint Velocity Analysis'")
        else:
            checks_passed.append("[C] has heading1 'Sprint Velocity Analysis'")
    
        # Find Weekly Velocity database
        velocity_dbs = backend.query({
            "collection": "pages",
            "filter": {
                "title": "Weekly Velocity",
                "type": "database",
                "parentId": analysis_page.get("_id")
            }
        })
    
        if not isinstance(velocity_dbs, list) or len(velocity_dbs) == 0:
            errors.append("[X] Weekly Velocity inline database not found")
        else:
            checks_passed.append("[C] Weekly Velocity inline database")
    
        velocity_db = velocity_dbs[0]
    
        # Validate schema
        props = velocity_db.get("databaseMetadata", {}).get("properties", [])
        required_props = ["Week", "Sprint Tasks", "Completed Tasks", "Bug Fixes", "Features", "Velocity Points", "Team Size"]
    
        for prop_name in required_props:
            found = any(prop.get("name") == prop_name for prop in props)
            if not found:
                errors.append(f"[X] Missing property: {prop_name}")
            else:
                checks_passed.append(f"[C] has property")
    
        # Check 4 rows exist
        velocity_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": velocity_db.get("_id"), "type": "row"}
        })
    
        if not isinstance(velocity_rows, list) or len(velocity_rows) != 4:
            errors.append(f"[X] Expected 4 weekly rows, found {len(velocity_rows) if isinstance(velocity_rows, list) else 0}")
        else:
            checks_passed.append(f"[C] Found 4 weekly rows as expected")
    
        # Check for blue callout with velocity trends
        found_blue_callout = False
        found_trends_h2 = False
        found_recommendations_h2 = False
    
        for block in blocks_sorted:
            if block.get("type") == "heading2" and "Velocity Trends" in block.get("content", ""):
                found_trends_h2 = True
            elif block.get("type") == "heading2" and "Sprint Planning Recommendations" in block.get("content", ""):
                found_recommendations_h2 = True
            elif block.get("type") == "callout" and "Average Weekly Velocity" in block.get("content", ""):
                found_blue_callout = True
    
        if not found_trends_h2:
            errors.append("[X] Missing heading2 'Velocity Trends'")
        else:
            checks_passed.append("[C] has heading2 'Velocity Trends'")
    
        if not found_blue_callout:
            errors.append("[X] Missing blue callout with Average Weekly Velocity")
        else:
            checks_passed.append("[C] has blue callout with Average Weekly Velocity")
    
        if not found_recommendations_h2:
            errors.append("[X] Missing heading2 'Sprint Planning Recommendations'")
        else:
            checks_passed.append("[C] has heading2 'Sprint Planning Recommendations'")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_code_repository_java_backend_references(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate code repository Java section and backend task references.
    
    Checks:
    1. Java Examples section after Rust Examples
    2. Code block with correct Java code
    3. Backend Task References at end of page
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Code Examples Repository page
    try:
        repo_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Code Examples Repository"}}
        })
    
        if not isinstance(repo_pages, list) or len(repo_pages) == 0:
            errors.append("[X] Code Examples Repository page not found")
        else:
            checks_passed.append("[C] Code Examples Repository page")
    
        repo_page = repo_pages[0]
    
        # Get blocks
        blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": repo_page.get("_id")}
        })
    
        if not isinstance(blocks, list):
            errors.append("[X] Could not query blocks")
        else:
            checks_passed.append("[C] Could query blocks")
    
        blocks_sorted = sorted(blocks, key=lambda b: b.get("order", 0))
    
        # Find Rust Examples heading
        rust_index = -1
        java_index = -1
        backend_ref_index = -1
    
        for i, block in enumerate(blocks_sorted):
            if block.get("type") == "heading2" and "Rust Examples" in block.get("content", ""):
                rust_index = i
            elif block.get("type") == "heading2" and "Java Examples" in block.get("content", ""):
                java_index = i
            elif block.get("type") == "heading2" and "Backend Task References" in block.get("content", ""):
                backend_ref_index = i
    
        if rust_index == -1:
            errors.append("[X] Rust Examples heading not found")
        else:
            checks_passed.append("[C] Rust Examples heading")
    
        if java_index == -1:
            errors.append("[X] Java Examples heading not found")
        else:
            checks_passed.append("[C] Java Examples heading")
    
        # Java should be after Rust
        if java_index <= rust_index:
            errors.append(f"[X] Java Examples (index {java_index}) should be after Rust Examples (index {rust_index})")
        else:
            checks_passed.append(f"[C] Java Examples (index ) should be after Rust Examples (index )")
    
        # Check code block exists after Java heading
        found_java_code = False
        found_java_paragraph = False
    
        for i in range(java_index + 1, min(java_index + 5, len(blocks_sorted))):
            block = blocks_sorted[i]
            if block.get("type") == "code":
                content = block.get("content", "")
                if "public class HelloWorld" in content and "System.out.println" in content:
                    found_java_code = True
                    if block.get("properties", {}).get("language") != "java":
                        errors.append(f"[X] Code block language should be 'java', found {block.get('properties', {}).get('language')}")
            elif block.get("type") == "paragraph" and "java pattern" in block.get("content", "").lower():
                found_java_paragraph = True
    
        if not found_java_code:
            errors.append("[X] Java code block not found or incorrect")
        else:
            checks_passed.append("[C] Java code block found or correct")
    
        if not found_java_paragraph:
            errors.append("[X] Java pattern paragraph not found")
        else:
            checks_passed.append("[C] Java pattern paragraph")
    
        # Check Backend Task References at end
        if backend_ref_index == -1:
            errors.append("[X] Backend Task References heading not found")
        else:
            checks_passed.append("[C] Backend Task References heading")
    
        # Check yellow callout exists
        found_yellow_callout = False
        found_numbered_list = False
    
        for i in range(backend_ref_index + 1, len(blocks_sorted)):
            block = blocks_sorted[i]
            if block.get("type") == "callout" and "Backend Development" in block.get("content", ""):
                found_yellow_callout = True
            elif block.get("type") == "numberedList":
                found_numbered_list = True
    
        if not found_yellow_callout:
            errors.append("[X] Yellow callout with 'Related Backend Development Tasks' not found")
        else:
            checks_passed.append("[C] Yellow callout with 'Related Backend Development Tasks'")
    
        if not found_numbered_list:
            errors.append("[X] Numbered list with backend tasks not found")
        else:
            checks_passed.append("[C] Numbered list with backend tasks")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_project_budget_expense_tracking(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate project budget expense tracking system.
    
    Checks:
    1. Inventory Tracker has budget allocation rows for active projects
    2. Project Portfolio has Budget Tracked property
    3. Active projects have Budget Tracked=true
    4. Summary page with correct total
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Portfolio
    try:
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_db = portfolio_pages[0]
    
        # Check Budget Tracked property exists
        props = portfolio_db.get("databaseMetadata", {}).get("properties", [])
        found_budget_tracked = False
        budget_tracked_order = -1
        budget_prop_order = -1
    
        for prop in props:
            if prop.get("name") == "Budget Tracked" and prop.get("type") == "checkbox":
                found_budget_tracked = True
                budget_tracked_order = prop.get("order", -1)
            elif prop.get("name") == "Budget ($)" or prop.get("name") == "Budget":
                budget_prop_order = prop.get("order", -1)
    
        if not found_budget_tracked:
            errors.append("[X] Budget Tracked property not found in Project Portfolio")
        else:
            checks_passed.append("[C] Budget Tracked property found in Project Portfolio")
    
        # Budget Tracked should be after Budget
        if budget_prop_order != -1 and budget_tracked_order != -1:
            if budget_tracked_order <= budget_prop_order:
                errors.append(f"[X] Budget Tracked (order {budget_tracked_order}) should be after Budget (order {budget_prop_order})")
            else:
                checks_passed.append(f"[C] Budget Tracked (order ) should be after Budget (order )")
    
        # Find active projects
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_db.get("_id"), "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            errors.append("[X] Could not query project rows")
        else:
            checks_passed.append("[C] Could query project rows")
    
        active_projects = []
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = None
            budget_tracked = None
        
            for prop_id, value in cell_values.items():
                for prop in props:
                    if prop.get("_id") == prop_id:
                        if prop.get("name") == "Status" and isinstance(value, str):
                            status = value
                        elif prop.get("name") == "Budget Tracked" and isinstance(value, bool):
                            budget_tracked = value
        
            if status == "Active":
                active_projects.append(row)
                if budget_tracked != True:
                    errors.append(f"[X] Active project {row.get('title')} should have Budget Tracked=true")
                else:
                    checks_passed.append(f"[C] Active project should have Budget Tracked=true")
    
        if len(active_projects) == 0:
            errors.append("[X] No active projects found")
        else:
            checks_passed.append("[C] active projects")
    
        # Check Inventory Tracker has budget allocation rows
        inventory_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Inventory Tracker"}}
        })
    
        if not isinstance(inventory_pages, list) or len(inventory_pages) == 0:
            errors.append("[X] Inventory Tracker not found")
        else:
            checks_passed.append("[C] Inventory Tracker")
    
        inventory_db = inventory_pages[0]
        inventory_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_db.get("_id"), "type": "row"}
        })
    
        expected_items = ["Website Redesign - Budget Allocation", "Customer Portal - Budget Allocation", "Sales Pipeline Automation - Budget Allocation"]
        found_items = {}
    
        if isinstance(inventory_rows, list):
            for row in inventory_rows:
                title = row.get("title", "")
                if "Budget Allocation" in title:
                    found_items[title] = True
    
        for item in expected_items:
            if item not in found_items:
                errors.append(f"[X] Missing inventory row: {item}")
            else:
                checks_passed.append(f"[C] has inventory row")
    
        # Find summary page
        summary_pages = backend.query({
            "collection": "pages",
            "filter": {"title": "Active Projects Budget Report"}
        })
    
        if not isinstance(summary_pages, list) or len(summary_pages) == 0:
            errors.append("[X] Active Projects Budget Report page not found")
        else:
            checks_passed.append("[C] Active Projects Budget Report page")
    
        summary_page = summary_pages[0]
        blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": summary_page.get("_id")}
        })
    
        if not isinstance(blocks, list):
            errors.append("[X] Could not query summary page blocks")
        else:
            checks_passed.append("[C] Could query summary page blocks")
    
        found_h1 = False
        found_total_callout = False
    
        for block in blocks:
            if block.get("type") == "heading1" and "Active Projects Budget Report" in block.get("content", ""):
                found_h1 = True
            elif block.get("type") == "callout" and "160" in block.get("content", ""):
                found_total_callout = True
    
        if not found_h1:
            errors.append("[X] Missing heading1 in summary page")
        else:
            checks_passed.append("[C] has heading1 in summary page")
    
        if not found_total_callout:
            errors.append("[X] Missing callout with total budget ($160,000)")
        else:
            checks_passed.append("[C] has callout with total budget ($160,000)")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_contact_cleanup_engagement_reporting(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate contact cleanup and engagement reporting.
    
    Checks:
    1. Follow-up tasks created for leads
    2. Tags property renamed to Engagement Status
    3. Contact values updated correctly
    4. Summary page created
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Contacts Directory
    try:
        contacts_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Contacts Directory"}}
        })
    
        if not isinstance(contacts_pages, list) or len(contacts_pages) == 0:
            errors.append("[X] Contacts Directory database not found")
        else:
            checks_passed.append("[C] Contacts Directory database")
    
        contacts_db = contacts_pages[0]
    
        # Check Engagement Status property exists (Tags renamed)
        props = contacts_db.get("databaseMetadata", {}).get("properties", [])
        found_engagement_status = False
    
        for prop in props:
            if prop.get("name") == "Engagement Status" and prop.get("type") == "multi_select":
                found_engagement_status = True
                # Check options
                options = {opt.get("name"): opt.get("color") for opt in prop.get("options", [])}
                expected = {"Active": "green", "Inactive": "gray", "Hot Lead": "red", "Nurturing": "yellow"}
                if options != expected:
                    errors.append(f"[X] Engagement Status options incorrect: {options}")
                else:
                    checks_passed.append(f"[C] Engagement Status options correct")
                break
            elif prop.get("name") == "Tags":
                errors.append("[X] Tags property still exists (should be renamed to Engagement Status)")
            else:
                checks_passed.append("[C] Tags property still exists (should be renamed to Engagement Status)")
    
        if not found_engagement_status:
            errors.append("[X] Engagement Status property not found")
        else:
            checks_passed.append("[C] Engagement Status property")
    
        # Check Task Tracker has follow-up task
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker not found")
        else:
            checks_passed.append("[C] Task Tracker")
    
        task_db = task_tracker_pages[0]
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_db.get("_id"), "type": "row"}
        })
    
        found_follow_up = False
        if isinstance(task_rows, list):
            for row in task_rows:
                if "Follow up with" in row.get("title", "") and "James Wilson" in row.get("title", ""):
                    found_follow_up = True
                    break
    
        if not found_follow_up:
            errors.append("[X] Follow-up task for James Wilson not found")
        else:
            checks_passed.append("[C] Follow-up task for James Wilson")
    
        # Find Contact Engagement Report
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"title": "Contact Engagement Report"}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] Contact Engagement Report page not found")
        else:
            checks_passed.append("[C] Contact Engagement Report page")
    
        report_page = report_pages[0]
        blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page.get("_id")}
        })
    
        if not isinstance(blocks, list):
            errors.append("[X] Could not query report blocks")
        else:
            checks_passed.append("[C] Could query report blocks")
    
        found_h1 = False
        found_total_callout = False
    
        for block in blocks:
            if block.get("type") == "heading1" and "Contact Engagement Analysis" in block.get("content", ""):
                found_h1 = True
            elif block.get("type") == "callout" and "5" in block.get("content", ""):
                found_total_callout = True
    
        if not found_h1:
            errors.append("[X] Missing heading1 in report")
        else:
            checks_passed.append("[C] has heading1 in report")
    
        if not found_total_callout:
            errors.append("[X] Missing blue callout with total contacts (5)")
        else:
            checks_passed.append("[C] has blue callout with total contacts (5)")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_employee_directory_vip_account_managers(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate employee directory with VIP account managers.
    
    Checks:
    1. Employee Directory inline database in Team Directory page
    2. Correct schema with 8 properties
    3. Account manager entries for VIP clients
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Team Directory page
    try:
        team_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Directory"}}
        })
    
        if not isinstance(team_pages, list) or len(team_pages) == 0:
            errors.append("[X] Team Directory page not found")
        else:
            checks_passed.append("[C] Team Directory page")
    
        team_page = team_pages[0]
    
        # Find Employee Directory database
        employee_dbs = backend.query({
            "collection": "pages",
            "filter": {
                "title": "Employee Directory",
                "type": "database",
                "parentId": team_page.get("_id")
            }
        })
    
        if not isinstance(employee_dbs, list) or len(employee_dbs) == 0:
            errors.append("[X] Employee Directory inline database not found in Team Directory page")
        else:
            checks_passed.append("[C] Employee Directory inline database found in Team Directory page")
    
        employee_db = employee_dbs[0]
    
        # Validate schema (8 properties total including title)
        props = employee_db.get("databaseMetadata", {}).get("properties", [])
    
        required_props = {
            "Full Name": "title",
            "Email": "email",
            "Department": "select",
            "Role": "text",
            "Start Date": "date",
            "Manager": "text",
            "Location": "select",
            "Active": "checkbox"
        }
    
        for prop_name, prop_type in required_props.items():
            found = False
            for prop in props:
                if prop.get("name") == prop_name and prop.get("type") == prop_type:
                    found = True
                    # Check select options
                    if prop_name == "Department":
                        options = {opt.get("name"): opt.get("color") for opt in prop.get("options", [])}
                        expected_colors = {"Engineering": "blue", "Product": "green", "Design": "purple", "Marketing": "orange", "Sales": "yellow", "HR": "pink"}
                        for dept, color in expected_colors.items():
                            if dept not in options or options[dept] != color:
                                errors.append(f"[X] Department {dept} should have color {color}")
                    elif prop_name == "Location":
                        options = {opt.get("name"): opt.get("color") for opt in prop.get("options", [])}
                        expected_colors = {"Remote": "gray", "NYC Office": "blue", "SF Office": "green", "London Office": "red"}
                        for loc, color in expected_colors.items():
                            if loc not in options or options[loc] != color:
                                errors.append(f"[X] Location {loc} should have color {color}")
                            else:
                                checks_passed.append(f"[C] Location should have color")
                    break
            if not found:
                errors.append(f"[X] Missing property: {prop_name} ({prop_type})")
            else:
                checks_passed.append(f"[C] has property")
    
        # Check account manager entries exist
        employee_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": employee_db.get("_id"), "type": "row"}
        })
    
        if not isinstance(employee_rows, list):
            errors.append("[X] Could not query employee rows")
        else:
            checks_passed.append("[C] Could query employee rows")
    
        account_manager_count = 0
        for row in employee_rows:
            if "Account Manager" in row.get("title", ""):
                account_manager_count += 1
    
        if account_manager_count == 0:
            errors.append("[X] No account manager entries found")
        else:
            checks_passed.append("[C] account manager entries")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_sprint_retrospective_analysis_database(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate sprint retrospective analysis database.
    
    Checks:
    1. Sprint Retrospective database under Team Retrospective
    2. Correct properties and select options
    3. Week 1 and Week 2 retrospective entries
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Team Retrospective page
    try:
        retro_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Team Retrospective"}}
        })
    
        if not isinstance(retro_pages, list) or len(retro_pages) == 0:
            errors.append("[X] Team Retrospective page not found")
        else:
            checks_passed.append("[C] Team Retrospective page")
    
        retro_page = retro_pages[0]
    
        # Find Sprint Retrospective database
        sprint_dbs = backend.query({
            "collection": "pages",
            "filter": {
                "title": "Sprint Retrospective",
                "type": "database",
                "parentId": retro_page.get("_id")
            }
        })
    
        if not isinstance(sprint_dbs, list) or len(sprint_dbs) == 0:
            errors.append("[X] Sprint Retrospective database not found under Team Retrospective")
        else:
            checks_passed.append("[C] Sprint Retrospective database found under Team Retrospective")
    
        sprint_db = sprint_dbs[0]
    
        # Validate schema
        props = sprint_db.get("databaseMetadata", {}).get("properties", [])
        required_props = {
            "Sprint": "title",
            "Date": "date",
            "Team": "multi_select",
            "What Went Well": "text",
            "Improvements": "text",
            "Action Items": "text",
            "Velocity Achieved": "number",
            "Team Mood": "select"
        }
    
        for prop_name, prop_type in required_props.items():
            found = False
            for prop in props:
                if prop.get("name") == prop_name and prop.get("type") == prop_type:
                    found = True
                    if prop_name == "Team Mood":
                        options = {opt.get("name"): opt.get("color") for opt in prop.get("options", [])}
                        expected = {"Great": "green", "Good": "blue", "Neutral": "gray", "Frustrated": "red"}
                        if options != expected:
                            errors.append(f"[X] Team Mood options incorrect: {options}")
                    elif prop_name == "Team":
                        option_names = {opt.get("name") for opt in prop.get("options", [])}
                        expected_names = {"Frontend", "Backend", "Mobile", "Full Stack"}
                        if not expected_names.issubset(option_names):
                            errors.append(f"[X] Team options missing: {expected_names - option_names}")
                        else:
                            checks_passed.append(f"[C] Team options has")
                    break
            if not found:
                errors.append(f"[X] Missing property: {prop_name} ({prop_type})")
            else:
                checks_passed.append(f"[C] has property")
    
        # Check for Week 1 and Week 2 retrospective entries
        sprint_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": sprint_db.get("_id"), "type": "row"}
        })
    
        if not isinstance(sprint_rows, list):
            errors.append("[X] Could not query sprint rows")
        else:
            checks_passed.append("[C] Could query sprint rows")
    
        found_week1 = False
        found_week2 = False
    
        for row in sprint_rows:
            title = row.get("title", "")
            if "Week 1" in title:
                found_week1 = True
            elif "Week 2" in title:
                found_week2 = True
    
        if not found_week1:
            errors.append("[X] Week 1 Retro entry not found")
        else:
            checks_passed.append("[C] Week 1 Retro entry")
    
        if not found_week2:
            errors.append("[X] Week 2 Retro entry not found")
        else:
            checks_passed.append("[C] Week 2 Retro entry")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_project_deadline_risk_matrix(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate project deadline risk matrix.
    
    Checks:
    1. Active projects with approaching deadlines have ' - DEADLINE APPROACHING' suffix
    2. Project Health Report page with inline database
    3. Risk matrix rows with correct calculations
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Project Portfolio
    try:
        portfolio_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Project Portfolio"}}
        })
    
        if not isinstance(portfolio_pages, list) or len(portfolio_pages) == 0:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")
    
        portfolio_db = portfolio_pages[0]
    
        # Check for projects with DEADLINE APPROACHING suffix
        project_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": portfolio_db.get("_id"), "type": "row"}
        })
    
        if not isinstance(project_rows, list):
            errors.append("[X] Could not query project rows")
        else:
            checks_passed.append("[C] Could query project rows")
    
        deadline_projects = 0
        for row in project_rows:
            if "DEADLINE APPROACHING" in row.get("title", ""):
                deadline_projects += 1
    
        # Check for Project Health Report page
        report_pages = backend.query({
            "collection": "pages",
            "filter": {"title": "Project Health Report"}
        })
    
        if not isinstance(report_pages, list) or len(report_pages) == 0:
            errors.append("[X] Project Health Report page not found")
        else:
            checks_passed.append("[C] Project Health Report page")
    
        report_page = report_pages[0]
    
        # Find Project Risk Matrix inline database
        risk_dbs = backend.query({
            "collection": "pages",
            "filter": {
                "title": "Project Risk Matrix",
                "type": "database",
                "parentId": report_page.get("_id")
            }
        })
    
        if not isinstance(risk_dbs, list) or len(risk_dbs) == 0:
            errors.append("[X] Project Risk Matrix inline database not found")
        else:
            checks_passed.append("[C] Project Risk Matrix inline database")
    
        risk_db = risk_dbs[0]
    
        # Validate schema
        props = risk_db.get("databaseMetadata", {}).get("properties", [])
        required_props = {
            "Project": "title",
            "Original Budget": "number",
            "Risk Level": "select",
            "Days Until Deadline": "number",
            "Mitigation Plan": "text"
        }
    
        for prop_name, prop_type in required_props.items():
            found = False
            for prop in props:
                if prop.get("name") == prop_name and prop.get("type") == prop_type:
                    found = True
                    if prop_name == "Risk Level":
                        options = {opt.get("name"): opt.get("color") for opt in prop.get("options", [])}
                        expected = {"Low": "green", "Medium": "yellow", "High": "orange", "Critical": "red"}
                        if options != expected:
                            errors.append(f"[X] Risk Level options incorrect: {options}")
                        else:
                            checks_passed.append(f"[C] Risk Level options correct")
                    break
            if not found:
                errors.append(f"[X] Missing property: {prop_name} ({prop_type})")
            else:
                checks_passed.append(f"[C] has property")
    
        # Check risk matrix rows exist
        risk_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": risk_db.get("_id"), "type": "row"}
        })
    
        if not isinstance(risk_rows, list) or len(risk_rows) == 0:
            errors.append("[X] No risk matrix rows created")
        else:
            checks_passed.append("[C] risk matrix rows created")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_team_workload_overdue_analysis(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate team workload and overdue task analysis.
    
    Checks:
    1. Overdue tasks updated to Urgent priority
    2. Team Workload Dashboard page created
    3. Workload analysis by assignee
    4. Rebalancing recommendations
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Task Tracker
    try:
        task_tracker_pages = backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Task Tracker"}}
        })
    
        if not isinstance(task_tracker_pages, list) or len(task_tracker_pages) == 0:
            errors.append("[X] Task Tracker not found")
        else:
            checks_passed.append("[C] Task Tracker")
    
        task_db = task_tracker_pages[0]
    
        # Check overdue tasks have Urgent priority
        task_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_db.get("_id"), "type": "row"}
        })
    
        if not isinstance(task_rows, list):
            errors.append("[X] Could not query task rows")
        else:
            checks_passed.append("[C] Could query task rows")
    
        # Find Team Workload Dashboard
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"title": "Team Workload Dashboard"}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Team Workload Dashboard page not found")
        else:
            checks_passed.append("[C] Team Workload Dashboard page")
    
        dashboard_page = dashboard_pages[0]
    
        # Check blocks
        blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page.get("_id")}
        })
    
        if not isinstance(blocks, list):
            errors.append("[X] Could not query dashboard blocks")
        else:
            checks_passed.append("[C] Could query dashboard blocks")
    
        found_h1 = False
        found_workload_h3 = False
        found_recommendations_h2 = False
        found_red_circle = False
    
        for block in blocks:
            content = block.get("content", "")
            if block.get("type") == "heading1" and "Team Capacity Analysis" in content:
                found_h1 = True
            elif block.get("type") == "heading3" and "Workload" in content:
                found_workload_h3 = True
            elif block.get("type") == "heading2" and "Rebalancing Recommendations" in content:
                found_recommendations_h2 = True
            elif block.get("type") == "bulletedList" and "🔴" in content:
                found_red_circle = True
    
        if not found_h1:
            errors.append("[X] Missing heading1 'Team Capacity Analysis'")
        else:
            checks_passed.append("[C] has heading1 'Team Capacity Analysis'")
    
        if not found_workload_h3:
            errors.append("[X] Missing heading3 with assignee workload")
        else:
            checks_passed.append("[C] has heading3 with assignee workload")
    
        if not found_recommendations_h2:
            errors.append("[X] Missing heading2 'Rebalancing Recommendations'")
        else:
            checks_passed.append("[C] has heading2 'Rebalancing Recommendations'")
    
        if not found_red_circle:
            errors.append("[X] Missing red circle emoji for overdue tasks")
        else:
            checks_passed.append("[C] has red circle emoji for overdue tasks")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_agent_procurement_dashboard_purchase_orders(
    backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate procurement dashboard and purchase orders.
    
    Checks:
    1. Procurement Dashboard page under Product Specs
    2. Red callout with count of items needing attention
    3. Critical stock levels and out-of-stock sections
    4. Purchase Orders inline database with correct schema
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Find Procurement Dashboard page
    try:
        dashboard_pages = backend.query({
            "collection": "pages",
            "filter": {"title": "Procurement Dashboard"}
        })
    
        if not isinstance(dashboard_pages, list) or len(dashboard_pages) == 0:
            errors.append("[X] Procurement Dashboard page not found")
        else:
            checks_passed.append("[C] Procurement Dashboard page")
    
        dashboard_page = dashboard_pages[0]
    
        # Check blocks
        blocks = backend.query({
            "collection": "blocks",
            "filter": {"pageId": dashboard_page.get("_id")}
        })
    
        if not isinstance(blocks, list):
            errors.append("[X] Could not query dashboard blocks")
        else:
            checks_passed.append("[C] Could query dashboard blocks")
    
        blocks_sorted = sorted(blocks, key=lambda b: b.get("order", 0))
    
        found_h1 = False
        found_red_callout = False
        found_critical_h2 = False
        found_out_of_stock_h2 = False
        found_todo = False
    
        for block in blocks_sorted:
            content = block.get("content", "")
            block_type = block.get("type", "")
        
            if block_type == "heading1" and "Procurement Dashboard" in content:
                found_h1 = True
            elif block_type == "callout" and "URGENT" in content and block.get("properties", {}).get("backgroundColor") == "red":
                found_red_callout = True
            elif block_type == "heading2" and "Critical Stock Levels" in content:
                found_critical_h2 = True
            elif block_type == "heading2" and "Out of Stock Items" in content:
                found_out_of_stock_h2 = True
            elif block_type == "todo":
                found_todo = True
    
        if not found_h1:
            errors.append("[X] Missing heading1 'Procurement Dashboard'")
        else:
            checks_passed.append("[C] has heading1 'Procurement Dashboard'")
    
        if not found_red_callout:
            errors.append("[X] Missing red callout with URGENT message")
        else:
            checks_passed.append("[C] has red callout with URGENT message")
    
        if not found_critical_h2:
            errors.append("[X] Missing heading2 'Critical Stock Levels'")
        else:
            checks_passed.append("[C] has heading2 'Critical Stock Levels'")
    
        if not found_out_of_stock_h2:
            errors.append("[X] Missing heading2 'Out of Stock Items'")
        else:
            checks_passed.append("[C] has heading2 'Out of Stock Items'")
    
        if not found_todo:
            errors.append("[X] Missing to-do list for critical items")
        else:
            checks_passed.append("[C] has to-do list for critical items")
    
        # Find Purchase Orders inline database
        po_dbs = backend.query({
            "collection": "pages",
            "filter": {
                "title": "Purchase Orders",
                "type": "database",
                "parentId": dashboard_page.get("_id")
            }
        })
    
        if not isinstance(po_dbs, list) or len(po_dbs) == 0:
            errors.append("[X] Purchase Orders inline database not found")
        else:
            checks_passed.append("[C] Purchase Orders inline database")
    
        po_db = po_dbs[0]
    
        # Validate schema
        props = po_db.get("databaseMetadata", {}).get("properties", [])
        required_props = {
            "PO Number": "title",
            "Item Name": "text",
            "SKU": "text",
            "Quantity to Order": "number",
            "Unit Price": "number",
            "Total Cost": "number",
            "Vendor": "select",
            "Order Status": "select",
            "Order Date": "date",
            "Expected Delivery": "date"
        }
    
        for prop_name, prop_type in required_props.items():
            found = False
            for prop in props:
                if prop.get("name") == prop_name and prop.get("type") == prop_type:
                    found = True
                    if prop_name == "Vendor":
                        options = {opt.get("name"): opt.get("color") for opt in prop.get("options", [])}
                        expected = {"TechSupplier": "blue", "OfficeWorld": "green", "FurnitureHub": "brown", "DirectHardware": "gray"}
                        if options != expected:
                            errors.append(f"[X] Vendor options incorrect: {options}")
                    elif prop_name == "Order Status":
                        option_names = {opt.get("name") for opt in prop.get("options", [])}
                        expected_names = {"Draft", "Submitted", "Confirmed", "Shipped", "Received"}
                        if not expected_names.issubset(option_names):
                            errors.append(f"[X] Order Status options missing: {expected_names - option_names}")
                        else:
                            checks_passed.append(f"[C] Order Status options has")
                    break
            if not found:
                errors.append(f"[X] Missing property: {prop_name} ({prop_type})")
            else:
                checks_passed.append(f"[C] has property")
    
        # Check purchase order rows exist
        po_rows = backend.query({
            "collection": "pages",
            "filter": {"databaseId": po_db.get("_id"), "type": "row"}
        })
    
        if not isinstance(po_rows, list) or len(po_rows) == 0:
            errors.append("[X] No purchase order rows created")
        else:
            checks_passed.append("[C] purchase order rows created")
    
        # Check PO number format
        for row in po_rows:
            title = row.get("title", "")
            if not title.startswith("PO-2026-"):
                errors.append(f"[X] Invalid PO number format: {title} (should be PO-2026-XXX)")
            else:
                checks_passed.append(f"[C] valid PO number format (should be PO-2026-XXX)")
    
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Reward Functions
# =============================================================================

# =============================================================================
# Task 1: Overdue Task Analysis
# =============================================================================
def _validate_overdue_task_analysis(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate overdue task analysis with database updates and report page."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Task Tracker database
        task_tracker = _find_page_by_title(final_state_backend, "Task Tracker")
        if not task_tracker:
            errors.append("[X] Task Tracker database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Task Tracker database found")

        task_tracker_id = task_tracker.get("_id")

        # Check for 'Overdue' select option with red color
        db_metadata = task_tracker.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        status_prop = next((p for p in properties if p.get("name") == "Status"), None)

        if status_prop:
            options = status_prop.get("options", [])
            overdue_option = next((o for o in options if o.get("name") == "Overdue"), None)
            if overdue_option:
                if overdue_option.get("color") == "red":
                    checks_passed.append("[C] 'Overdue' select option exists with red color")
                else:
                    errors.append("[X] 'Overdue' select option exists but not with red color")
            else:
                errors.append("[X] 'Overdue' select option not found in Status property")
        else:
            errors.append("[X] Status property not found in Task Tracker")

        # Get all task rows
        task_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
        task_rows = task_rows if isinstance(task_rows, list) else []

        # Check for rows that should be updated (originally Not Started with Due Date < 2026-01-12)
        overdue_updated_count = 0
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            status = None
            priority = None
            for key, value in cell_values.items():
                if "status" in key.lower():
                    status = value
                if "priority" in key.lower():
                    priority = value

            if status == "Overdue" and priority == "Urgent":
                overdue_updated_count += 1

        if overdue_updated_count > 0:
            checks_passed.append(f"[C] Found {overdue_updated_count} tasks updated to Overdue status with Urgent priority")
        else:
            errors.append("[X] No tasks found with Status='Overdue' and Priority='Urgent'")

        # Check for report page
        report_page = _find_page_by_title(final_state_backend, "Overdue Tasks Alert")
        if not report_page:
            errors.append("[X] '⚠️ Overdue Tasks Alert' report page not found")
        else:
            checks_passed.append("[C] Overdue Tasks Alert report page found")

            # Check if it's a child of Task Tracker
            if report_page.get("parentId") == task_tracker_id:
                checks_passed.append("[C] Report page is a child of Task Tracker")
            else:
                errors.append("[X] Report page is not a child of Task Tracker")

            # Check report structure
            report_blocks = _get_blocks_for_page(final_state_backend, report_page.get("_id"))

            # Check heading_1
            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "critical overdue tasks report" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_h1:
                checks_passed.append("[C] Report has heading_1 '⚠️ Critical Overdue Tasks Report'")
            else:
                errors.append("[X] Missing heading_1 '⚠️ Critical Overdue Tasks Report'")

            # Check heading_2 Summary Statistics
            has_summary_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "summary statistics" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_summary_h2:
                checks_passed.append("[C] Report has heading_2 '📊 Summary Statistics'")
            else:
                errors.append("[X] Missing heading_2 '📊 Summary Statistics'")

            # Check red callout
            callout_blocks = [b for b in report_blocks if b.get("type") == "callout"]
            has_red_callout = any(
                ("red" in b.get("properties", {}).get("backgroundColor", "").lower() or
                 "red" in b.get("backgroundColor", "").lower()) and
                "action required" in b.get("content", "").lower()
                for b in callout_blocks
            )
            if has_red_callout:
                checks_passed.append("[C] Report has red callout with action required message")
            else:
                errors.append("[X] Missing red callout with action required message")

            # Check heading_2 for Overdue Tasks by Assignee
            has_assignee_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "overdue tasks by assignee" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_assignee_h2:
                checks_passed.append("[C] Report has heading_2 '📋 Overdue Tasks by Assignee'")
            else:
                errors.append("[X] Missing heading_2 '📋 Overdue Tasks by Assignee'")

            # Check for bulleted lists
            bulleted_blocks = [b for b in report_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
            if len(bulleted_blocks) > 0:
                checks_passed.append(f"[C] Report has {len(bulleted_blocks)} bulleted list items")
            else:
                errors.append("[X] Missing bulleted lists for assignee grouping")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 2: Project Resource Allocation Analysis
# =============================================================================
def _validate_project_resource_allocation(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate project resource allocation analysis with cross-database operations."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Project Portfolio database
        project_portfolio = _find_page_by_title(final_state_backend, "Project Portfolio")
        if not project_portfolio:
            errors.append("[X] Project Portfolio database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Project Portfolio database found")

        # Check for Task Count property
        db_metadata = project_portfolio.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        task_count_prop = next((p for p in properties if p.get("name") == "Task Count"), None)

        if task_count_prop:
            if task_count_prop.get("type") == "number":
                checks_passed.append("[C] 'Task Count' property exists with number type")
            else:
                errors.append("[X] 'Task Count' property exists but not number type")

            # Check position (after Department)
            dept_prop = next((p for p in properties if p.get("name") == "Department"), None)
            if dept_prop and task_count_prop:
                dept_order = dept_prop.get("order", 0)
                task_count_order = task_count_prop.get("order", 0)
                if task_count_order > dept_order:
                    checks_passed.append("[C] Task Count property is positioned after Department")
                else:
                    errors.append("[X] Task Count property is not positioned after Department")
        else:
            errors.append("[X] 'Task Count' property not found in Project Portfolio")

        # Find Project Roadmap page
        project_roadmap = _find_page_by_title(final_state_backend, "Project Roadmap")
        if not project_roadmap:
            errors.append("[X] Project Roadmap page not found")
        else:
            checks_passed.append("[C] Project Roadmap page found")

        # Check for report page
        report_page = _find_page_by_title(final_state_backend, "Resource Optimization Report")
        if not report_page:
            errors.append("[X] '🎯 Resource Optimization Report' page not found")
        else:
            checks_passed.append("[C] Resource Optimization Report page found")

            # Check if it's under Project Roadmap
            if project_roadmap and report_page.get("parentId") == project_roadmap.get("_id"):
                checks_passed.append("[C] Report page is under Project Roadmap")
            else:
                errors.append("[X] Report page is not under Project Roadmap")

            # Check report structure
            report_blocks = _get_blocks_for_page(final_state_backend, report_page.get("_id"))

            # Check headings
            has_h1 = any(b.get("type") in ["heading1", "heading_1"] and "project resource analysis" in b.get("content", "").lower() for b in report_blocks)
            has_budget_h2 = any(b.get("type") in ["heading2", "heading_2"] and "budget efficiency metrics" in b.get("content", "").lower() for b in report_blocks)
            has_distribution_h2 = any(b.get("type") in ["heading2", "heading_2"] and "project task distribution" in b.get("content", "").lower() for b in report_blocks)
            has_alerts_h2 = any(b.get("type") in ["heading2", "heading_2"] and "resource alerts" in b.get("content", "").lower() for b in report_blocks)

            if has_h1:
                checks_passed.append("[C] Report has heading_1 '🎯 Project Resource Analysis'")
            else:
                errors.append("[X] Missing heading_1 '🎯 Project Resource Analysis'")

            if has_budget_h2:
                checks_passed.append("[C] Report has heading_2 '💰 Budget Efficiency Metrics'")
            else:
                errors.append("[X] Missing heading_2 '💰 Budget Efficiency Metrics'")

            if has_distribution_h2:
                checks_passed.append("[C] Report has heading_2 '📊 Project Task Distribution'")
            else:
                errors.append("[X] Missing heading_2 '📊 Project Task Distribution'")

            if has_alerts_h2:
                checks_passed.append("[C] Report has heading_2 '⚠️ Resource Alerts'")
            else:
                errors.append("[X] Missing heading_2 '⚠️ Resource Alerts'")

            # Check for yellow callouts
            callout_blocks = [b for b in report_blocks if b.get("type") == "callout"]
            has_yellow_callouts = any(
                "yellow" in b.get("properties", {}).get("backgroundColor", "").lower() or
                "yellow" in b.get("backgroundColor", "").lower()
                for b in callout_blocks
            )
            if has_yellow_callouts:
                checks_passed.append("[C] Report has yellow background callouts")
            else:
                errors.append("[X] Missing yellow background callouts for project distribution")

            # Check for divider
            has_divider = any(b.get("type") == "divider" for b in report_blocks)
            if has_divider:
                checks_passed.append("[C] Report has divider")
            else:
                errors.append("[X] Missing divider in report")

            # Check for heading_3 with Generated date
            has_generated = any(
                b.get("type") in ["heading3", "heading_3"] and "generated" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_generated:
                checks_passed.append("[C] Report has heading_3 with Generated date")
            else:
                errors.append("[X] Missing heading_3 with Generated date")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 3: VIP Client Projects Database
# =============================================================================
def _validate_vip_client_projects(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate VIP Client Projects database creation with cross-database matching."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Meeting Notes page
        meeting_notes = _find_page_by_title(final_state_backend, "Meeting Notes")
        if not meeting_notes:
            errors.append("[X] Meeting Notes page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Meeting Notes page found")
        meeting_notes_id = meeting_notes.get("_id")

        # Find VIP Client Projects database
        vip_db = _find_page_by_title(final_state_backend, "VIP Client Projects")
        if not vip_db:
            errors.append("[X] '🌟 VIP Client Projects' database not found")
        else:
            checks_passed.append("[C] VIP Client Projects database found")

            # Check if it's under Meeting Notes
            if vip_db.get("parentId") == meeting_notes_id:
                checks_passed.append("[C] VIP Client Projects is under Meeting Notes")
            else:
                errors.append("[X] VIP Client Projects is not under Meeting Notes")

            # Check database type
            if vip_db.get("type") == "database":
                checks_passed.append("[C] VIP Client Projects is a database")
            else:
                errors.append("[X] VIP Client Projects is not a database")

            # Check properties
            db_metadata = vip_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Client Name", "Company", "Contact Email", "LinkedIn Profile",
                            "Active Projects", "Project Names", "Total Budget", "Relationship Status"]

            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Relationship Status select options
            rel_status_prop = next((p for p in properties if p.get("name") == "Relationship Status"), None)
            if rel_status_prop:
                options = rel_status_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Excellent": "green", "Good": "blue", "Needs Attention": "yellow", "At Risk": "red"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Option '{name}' missing from Relationship Status")

            # Check for rows in the database
            vip_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": vip_db.get("_id"), "type": "row"}
            })
            vip_rows = vip_rows if isinstance(vip_rows, list) else []

            if len(vip_rows) > 0:
                checks_passed.append(f"[C] Found {len(vip_rows)} VIP client rows")
            else:
                errors.append("[X] No VIP client rows found in database")

        # Check for yellow callout at top of Meeting Notes
        meeting_blocks = _get_blocks_for_page(final_state_backend, meeting_notes_id)
        callout_blocks = [b for b in meeting_blocks if b.get("type") == "callout"]

        has_yellow_alert = any(
            ("yellow" in b.get("properties", {}).get("backgroundColor", "").lower() or
             "yellow" in b.get("backgroundColor", "").lower()) and
            "vip client alert" in b.get("content", "").lower()
            for b in callout_blocks
        )

        if has_yellow_alert:
            checks_passed.append("[C] Yellow callout with VIP Client Alert found on Meeting Notes")
        else:
            errors.append("[X] Missing yellow callout with VIP Client Alert on Meeting Notes")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 4: Contact Analytics Dashboard
# =============================================================================
def _validate_contact_analytics_dashboard(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Contact Analytics Dashboard with contact counts and VIP analysis."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Team Directory page
        team_directory = _find_page_by_title(final_state_backend, "Team Directory")
        if not team_directory:
            errors.append("[X] Team Directory page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Team Directory page found")
        team_directory_id = team_directory.get("_id")

        # Find Contact Analytics Dashboard
        dashboard = _find_page_by_title(final_state_backend, "Contact Analytics Dashboard")
        if not dashboard:
            errors.append("[X] '📊 Contact Analytics Dashboard' page not found")
        else:
            checks_passed.append("[C] Contact Analytics Dashboard page found")

            # Check if it's under Team Directory
            if dashboard.get("parentId") == team_directory_id:
                checks_passed.append("[C] Dashboard is under Team Directory")
            else:
                errors.append("[X] Dashboard is not under Team Directory")

            # Check page structure
            dashboard_blocks = _get_blocks_for_page(final_state_backend, dashboard.get("_id"))

            # Check headings
            has_h1 = any(b.get("type") in ["heading1", "heading_1"] and "contact analytics dashboard" in b.get("content", "").lower() for b in dashboard_blocks)
            has_contacts_by_type_h2 = any(b.get("type") in ["heading2", "heading_2"] and "contacts by type" in b.get("content", "").lower() for b in dashboard_blocks)
            has_vip_summary_h2 = any(b.get("type") in ["heading2", "heading_2"] and "vip contact summary" in b.get("content", "").lower() for b in dashboard_blocks)
            has_vip_list_h3 = any(b.get("type") in ["heading3", "heading_3"] and "vip contact list" in b.get("content", "").lower() for b in dashboard_blocks)

            if has_h1:
                checks_passed.append("[C] Dashboard has heading_1 '📊 Contact Analytics Dashboard'")
            else:
                errors.append("[X] Missing heading_1 '📊 Contact Analytics Dashboard'")

            if has_contacts_by_type_h2:
                checks_passed.append("[C] Dashboard has heading_2 '🏢 Contacts by Type'")
            else:
                errors.append("[X] Missing heading_2 '🏢 Contacts by Type'")

            if has_vip_summary_h2:
                checks_passed.append("[C] Dashboard has heading_2 '⭐ VIP Contact Summary'")
            else:
                errors.append("[X] Missing heading_2 '⭐ VIP Contact Summary'")

            if has_vip_list_h3:
                checks_passed.append("[C] Dashboard has heading_3 '🌟 VIP Contact List'")
            else:
                errors.append("[X] Missing heading_3 '🌟 VIP Contact List'")

            # Check for numbered list with contact counts
            numbered_blocks = [b for b in dashboard_blocks if b.get("type") in ["numberedList", "numbered_list", "numbered_list_item"]]
            if len(numbered_blocks) >= 4:
                checks_passed.append("[C] Dashboard has numbered list with contact type counts")
            else:
                errors.append("[X] Missing or incomplete numbered list with contact type counts")

            # Check for bulleted list with VIP contacts
            bulleted_blocks = [b for b in dashboard_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
            if len(bulleted_blocks) > 0:
                checks_passed.append("[C] Dashboard has bulleted list with VIP contacts")
            else:
                errors.append("[X] Missing bulleted list with VIP contacts")

        # Check Contacts Directory for Last Contact Date property
        contacts_db = _find_page_by_title(final_state_backend, "Contacts Directory")
        if contacts_db:
            db_metadata = contacts_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])

            last_contact_prop = next((p for p in properties if p.get("name") == "Last Contact Date"), None)
            if last_contact_prop:
                if last_contact_prop.get("type") == "date":
                    checks_passed.append("[C] 'Last Contact Date' property exists with date type")
                else:
                    errors.append("[X] 'Last Contact Date' property exists but not date type")

                # Check position (after LinkedIn, before Added)
                prop_orders = {p.get("name"): p.get("order", 0) for p in properties}
                linkedin_order = prop_orders.get("LinkedIn", 0)
                added_order = prop_orders.get("Added", 999)
                last_contact_order = prop_orders.get("Last Contact Date", 0)

                if linkedin_order < last_contact_order < added_order:
                    checks_passed.append("[C] Last Contact Date is at correct position (after LinkedIn, before Added)")
                else:
                    errors.append("[X] Last Contact Date is not at correct position")
            else:
                errors.append("[X] 'Last Contact Date' property not found in Contacts Directory")
        else:
            errors.append("[X] Contacts Directory database not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 5: Engineering Resource Allocation 2
# =============================================================================
def _validate_engineering_resource_allocation_2(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Engineering Resource Allocation database with multi-database operations."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Bug Tracker page
        bug_tracker = _find_page_by_title(final_state_backend, "Bug Tracker")
        if not bug_tracker:
            errors.append("[X] Bug Tracker page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Bug Tracker page found")
        bug_tracker_id = bug_tracker.get("_id")

        # Find Engineering Resource Allocation database
        eng_db = _find_page_by_title(final_state_backend, "Engineering Resource Allocation")
        if not eng_db:
            errors.append("[X] 'Engineering Resource Allocation' database not found")
        else:
            checks_passed.append("[C] Engineering Resource Allocation database found")

            # Check if it's under Bug Tracker
            if eng_db.get("parentId") == bug_tracker_id:
                checks_passed.append("[C] Database is under Bug Tracker")
            else:
                errors.append("[X] Database is not under Bug Tracker")

            # Check database type
            if eng_db.get("type") == "database":
                checks_passed.append("[C] Engineering Resource Allocation is a database")
            else:
                errors.append("[X] Engineering Resource Allocation is not a database")

            # Check properties
            db_metadata = eng_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Project", "Task Count", "Assigned Resources", "Budget Allocated",
                            "Deadline Pressure", "Contact Person", "Notes"]

            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Deadline Pressure select options
            deadline_prop = next((p for p in properties if p.get("name") == "Deadline Pressure"), None)
            if deadline_prop:
                options = deadline_prop.get("options", [])
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected = {"Low": "green", "Medium": "yellow", "High": "orange", "Critical": "red"}
                for name, color in expected.items():
                    if name in option_colors:
                        if option_colors[name] == color:
                            checks_passed.append(f"[C] Deadline Pressure option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Deadline Pressure option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Deadline Pressure option '{name}' missing")

            # Check for rows
            eng_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": eng_db.get("_id"), "type": "row"}
            })
            eng_rows = eng_rows if isinstance(eng_rows, list) else []

            if len(eng_rows) > 0:
                checks_passed.append(f"[C] Found {len(eng_rows)} engineering project rows")
            else:
                errors.append("[X] No engineering project rows found")

        # Check Bug Tracker page for heading and paragraph
        bug_blocks = _get_blocks_for_page(final_state_backend, bug_tracker_id)

        has_overview_h2 = any(
            b.get("type") in ["heading2", "heading_2"] and "resource allocation overview" in b.get("content", "").lower()
            for b in bug_blocks
        )

        if has_overview_h2:
            checks_passed.append("[C] Bug Tracker has heading_2 '📊 Resource Allocation Overview'")
        else:
            errors.append("[X] Missing heading_2 '📊 Resource Allocation Overview' on Bug Tracker")

        # Check for paragraph with stats
        has_stats_para = any(
            b.get("type") in ["paragraph", "text"] and
            "last updated" in b.get("content", "").lower() and
            "total engineering projects" in b.get("content", "").lower()
            for b in bug_blocks
        )

        if has_stats_para:
            checks_passed.append("[C] Bug Tracker has paragraph with updated stats")
        else:
            errors.append("[X] Missing paragraph with stats on Bug Tracker")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 6: Department Budget Summary
# =============================================================================
def _validate_department_budget_summary(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Department Budget Summary inline database in Company Handbook."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Company Handbook page
        handbook = _find_page_by_title(final_state_backend, "Company Handbook")
        if not handbook:
            errors.append("[X] Company Handbook page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Company Handbook page found")
        handbook_id = handbook.get("_id")

        # Find Department Budget Summary database
        budget_db = _find_page_by_title(final_state_backend, "Department Budget Summary")
        if not budget_db:
            errors.append("[X] 'Department Budget Summary' inline database not found")
        else:
            checks_passed.append("[C] Department Budget Summary database found")

            # Check if it's under Company Handbook or inline
            if budget_db.get("parentId") == handbook_id:
                checks_passed.append("[C] Database is under/inside Company Handbook")
            else:
                errors.append("[X] Database is not inside Company Handbook")

            # Check database type
            if budget_db.get("type") == "database":
                checks_passed.append("[C] Department Budget Summary is a database")
            else:
                errors.append("[X] Department Budget Summary is not a database")

            # Check properties (5 properties)
            db_metadata = budget_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Department", "Total Budget", "Active Projects", "Project Names", "Percentage of Total"]

            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            if len(properties) == 5:
                checks_passed.append("[C] Database has exactly 5 properties")
            else:
                errors.append(f"[X] Database has {len(properties)} properties, expected 5")

            # Check for rows (one per department)
            budget_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": budget_db.get("_id"), "type": "row"}
            })
            budget_rows = budget_rows if isinstance(budget_rows, list) else []

            if len(budget_rows) > 0:
                checks_passed.append(f"[C] Found {len(budget_rows)} department budget rows")
            else:
                errors.append("[X] No department budget rows found")

        # Check for yellow callout with total budget
        handbook_blocks = _get_blocks_for_page(final_state_backend, handbook_id)
        callout_blocks = [b for b in handbook_blocks if b.get("type") == "callout"]

        has_budget_callout = any(
            ("yellow" in b.get("properties", {}).get("backgroundColor", "").lower() or
             "yellow" in b.get("backgroundColor", "").lower()) and
            "total company project budget" in b.get("content", "").lower()
            for b in callout_blocks
        )

        if has_budget_callout:
            checks_passed.append("[C] Yellow callout with Total Company Project Budget found")
        else:
            errors.append("[X] Missing yellow callout with Total Company Project Budget")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 7: Resource Allocation Database 2
# =============================================================================
def _validate_resource_allocation_database_2(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Resource Allocation database with team member availability tracking."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Meeting Notes page
        meeting_notes = _find_page_by_title(final_state_backend, "Meeting Notes")
        if not meeting_notes:
            errors.append("[X] Meeting Notes page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Meeting Notes page found")
        meeting_notes_id = meeting_notes.get("_id")

        # Find Resource Allocation database
        resource_db = _find_page_by_title(final_state_backend, "Resource Allocation")
        if not resource_db:
            errors.append("[X] 'Resource Allocation' database not found")
        else:
            checks_passed.append("[C] Resource Allocation database found")

            # Check if it's under Meeting Notes
            if resource_db.get("parentId") == meeting_notes_id:
                checks_passed.append("[C] Database is under Meeting Notes")
            else:
                errors.append("[X] Database is not under Meeting Notes")

            # Check database type
            if resource_db.get("type") == "database":
                checks_passed.append("[C] Resource Allocation is a database")
            else:
                errors.append("[X] Resource Allocation is not a database")

            # Check initial 6 properties
            db_metadata = resource_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            initial_props = ["Team Member", "Current Project", "Allocation %", "Available From", "Skills", "Billable"]

            for prop in initial_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Skills multi_select options
            skills_prop = next((p for p in properties if p.get("name") == "Skills"), None)
            if skills_prop:
                if skills_prop.get("type") == "multi_select":
                    checks_passed.append("[C] Skills property is multi_select type")
                    options = skills_prop.get("options", [])
                    option_names = [o.get("name") for o in options]
                    expected_skills = ["Python", "JavaScript", "React", "Node.js", "DevOps", "UI/UX"]
                    for skill in expected_skills:
                        if skill in option_names:
                            checks_passed.append(f"[C] Skill '{skill}' option exists")
                        else:
                            errors.append(f"[X] Skill '{skill}' option missing")
                else:
                    errors.append("[X] Skills property is not multi_select type")

            # Check for Utilization Status property (added after row creation)
            util_prop = next((p for p in properties if p.get("name") == "Utilization Status"), None)
            if util_prop:
                checks_passed.append("[C] 'Utilization Status' property was added")
                if util_prop.get("type") == "select":
                    options = util_prop.get("options", [])
                    option_colors = {o.get("name"): o.get("color") for o in options}

                    expected = {"Overloaded": "red", "Optimal": "green", "Available": "blue"}
                    for name, color in expected.items():
                        if name in option_colors:
                            if option_colors[name] == color:
                                checks_passed.append(f"[C] Utilization Status option '{name}' has {color} color")
                            else:
                                errors.append(f"[X] Utilization Status option '{name}' does not have {color} color")
                        else:
                            errors.append(f"[X] Utilization Status option '{name}' missing")
            else:
                errors.append("[X] 'Utilization Status' property not found (should be added after rows)")

            # Check for rows (one per unique assignee)
            resource_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": resource_db.get("_id"), "type": "row"}
            })
            resource_rows = resource_rows if isinstance(resource_rows, list) else []

            if len(resource_rows) > 0:
                checks_passed.append(f"[C] Found {len(resource_rows)} team member rows")
            else:
                errors.append("[X] No team member rows found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 8: Budget vs Progress Analysis
# =============================================================================
def _validate_budget_vs_progress_analysis(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Budget vs Progress Analysis report page."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Engineering Wiki page
        eng_wiki = _find_page_by_title(final_state_backend, "Engineering Wiki")
        if not eng_wiki:
            errors.append("[X] Engineering Wiki page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Engineering Wiki page found")
        eng_wiki_id = eng_wiki.get("_id")

        # Find Budget vs Progress Analysis report
        report = _find_page_by_title(final_state_backend, "Budget vs Progress Analysis")
        if not report:
            errors.append("[X] '💰 Budget vs Progress Analysis' page not found")
        else:
            checks_passed.append("[C] Budget vs Progress Analysis page found")

            # Check if it's under Engineering Wiki
            if report.get("parentId") == eng_wiki_id:
                checks_passed.append("[C] Report is under Engineering Wiki")
            else:
                errors.append("[X] Report is not under Engineering Wiki")

            # Check page structure
            report_blocks = _get_blocks_for_page(final_state_backend, report.get("_id"))

            # Check heading_1
            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "budget vs progress analysis" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_h1:
                checks_passed.append("[C] Report has heading_1 '💰 Budget vs Progress Analysis'")
            else:
                errors.append("[X] Missing heading_1 '💰 Budget vs Progress Analysis'")

            # Check for Generated on paragraph (italic)
            has_generated = any(
                b.get("type") in ["paragraph", "text"] and "generated on" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_generated:
                checks_passed.append("[C] Report has 'Generated on' paragraph")
            else:
                errors.append("[X] Missing 'Generated on' paragraph")

            # Check for heading_2 blocks for projects
            heading2_blocks = [b for b in report_blocks if b.get("type") in ["heading2", "heading_2"]]
            project_headings = [b for b in heading2_blocks if "$" in b.get("content", "")]

            if len(project_headings) > 0:
                checks_passed.append(f"[C] Found {len(project_headings)} project heading_2 blocks with budget")
            else:
                errors.append("[X] Missing project heading_2 blocks with budget")

            # Check for yellow callouts with task summaries
            callout_blocks = [b for b in report_blocks if b.get("type") == "callout"]
            yellow_callouts = [
                b for b in callout_blocks
                if "yellow" in b.get("properties", {}).get("backgroundColor", "").lower() or
                   "yellow" in b.get("backgroundColor", "").lower()
            ]

            if len(yellow_callouts) > 0:
                checks_passed.append(f"[C] Found {len(yellow_callouts)} yellow callouts with task summaries")
            else:
                errors.append("[X] Missing yellow callouts with task summaries")

            # Check for numbered lists with tasks
            numbered_blocks = [b for b in report_blocks if b.get("type") in ["numberedList", "numbered_list", "numbered_list_item"]]
            if len(numbered_blocks) > 0:
                checks_passed.append("[C] Report has numbered lists with tasks")
            else:
                errors.append("[X] Missing numbered lists with tasks")

            # Check for Overall Statistics heading_2
            has_stats_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "overall statistics" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_stats_h2:
                checks_passed.append("[C] Report has heading_2 '📈 Overall Statistics'")
            else:
                errors.append("[X] Missing heading_2 '📈 Overall Statistics'")

            # Check for bulleted list with statistics
            bulleted_blocks = [b for b in report_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
            if len(bulleted_blocks) >= 3:
                checks_passed.append("[C] Report has bulleted list with overall statistics")
            else:
                errors.append("[X] Missing or incomplete bulleted list with overall statistics")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 9: Team Velocity Tracker
# =============================================================================
def _validate_team_velocity_tracker(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Team Velocity Tracker inline database in Weekly Standup Template."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Weekly Standup Template page
        standup = _find_page_by_title(final_state_backend, "Weekly Standup Template")
        if not standup:
            errors.append("[X] Weekly Standup Template page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Weekly Standup Template page found")
        standup_id = standup.get("_id")

        # Find Team Velocity Tracker database
        velocity_db = _find_page_by_title(final_state_backend, "Team Velocity Tracker")
        if not velocity_db:
            errors.append("[X] 'Team Velocity Tracker' inline database not found")
        else:
            checks_passed.append("[C] Team Velocity Tracker database found")

            # Check if it's under Weekly Standup Template
            if velocity_db.get("parentId") == standup_id:
                checks_passed.append("[C] Database is under Weekly Standup Template")
            else:
                errors.append("[X] Database is not under Weekly Standup Template")

            # Check database type
            if velocity_db.get("type") == "database":
                checks_passed.append("[C] Team Velocity Tracker is a database")
            else:
                errors.append("[X] Team Velocity Tracker is not a database")

            # Check properties
            db_metadata = velocity_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Week Ending", "Completed Story Points", "Team Member", "Blockers Resolved", "Velocity Trend"]

            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Velocity Trend select options
            trend_prop = next((p for p in properties if p.get("name") == "Velocity Trend"), None)
            if trend_prop:
                options = trend_prop.get("options", [])
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected = {"Increasing": "green", "Stable": "blue", "Decreasing": "red"}
                for name, color in expected.items():
                    if name in option_colors:
                        if option_colors[name] == color:
                            checks_passed.append(f"[C] Velocity Trend option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Velocity Trend option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Velocity Trend option '{name}' missing")

            # Check for rows
            velocity_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": velocity_db.get("_id"), "type": "row"}
            })
            velocity_rows = velocity_rows if isinstance(velocity_rows, list) else []

            if len(velocity_rows) > 0:
                checks_passed.append(f"[C] Found {len(velocity_rows)} velocity tracking rows")
            else:
                errors.append("[X] No velocity tracking rows found")

        # Check for green callout above database
        standup_blocks = _get_blocks_for_page(final_state_backend, standup_id)
        callout_blocks = [b for b in standup_blocks if b.get("type") == "callout"]

        has_green_callout = any(
            ("green" in b.get("properties", {}).get("backgroundColor", "").lower() or
             "green" in b.get("backgroundColor", "").lower()) and
            "team performance" in b.get("content", "").lower()
            for b in callout_blocks
        )

        if has_green_callout:
            checks_passed.append("[C] Green callout with Team Performance found")
        else:
            errors.append("[X] Missing green callout with Team Performance")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 10: Sprint Retrospective Report
# =============================================================================
def _validate_sprint_retrospective(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Sprint Retrospective report with task analysis."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Meeting Notes page
        meeting_notes = _find_page_by_title(final_state_backend, "Meeting Notes")
        if not meeting_notes:
            errors.append("[X] Meeting Notes page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Meeting Notes page found")
        meeting_notes_id = meeting_notes.get("_id")

        # Find Sprint Retrospective page
        retro = _find_page_by_title(final_state_backend, "Sprint Retrospective - January 2026")
        if not retro:
            errors.append("[X] 'Sprint Retrospective - January 2026' page not found")
        else:
            checks_passed.append("[C] Sprint Retrospective - January 2026 page found")

            # Check if it's under Meeting Notes
            if retro.get("parentId") == meeting_notes_id:
                checks_passed.append("[C] Report is under Meeting Notes")
            else:
                errors.append("[X] Report is not under Meeting Notes")

            # Check page structure
            retro_blocks = _get_blocks_for_page(final_state_backend, retro.get("_id"))

            # Check heading_1
            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "sprint retrospective" in b.get("content", "").lower()
                for b in retro_blocks
            )
            if has_h1:
                checks_passed.append("[C] Report has heading_1 '📊 Sprint Retrospective - January 2026'")
            else:
                errors.append("[X] Missing heading_1 '📊 Sprint Retrospective - January 2026'")

            # Check for blue callout with Sprint Health Score
            callout_blocks = [b for b in retro_blocks if b.get("type") == "callout"]
            has_blue_callout = any(
                ("blue" in b.get("properties", {}).get("backgroundColor", "").lower() or
                 "blue" in b.get("backgroundColor", "").lower()) and
                "sprint health score" in b.get("content", "").lower()
                for b in callout_blocks
            )
            if has_blue_callout:
                checks_passed.append("[C] Report has blue callout with Sprint Health Score")
            else:
                errors.append("[X] Missing blue callout with Sprint Health Score")

            # Check heading_2 Task Status Distribution
            has_status_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "task status distribution" in b.get("content", "").lower()
                for b in retro_blocks
            )
            if has_status_h2:
                checks_passed.append("[C] Report has heading_2 '📋 Task Status Distribution'")
            else:
                errors.append("[X] Missing heading_2 '📋 Task Status Distribution'")

            # Check for numbered list with status counts
            numbered_blocks = [b for b in retro_blocks if b.get("type") in ["numberedList", "numbered_list", "numbered_list_item"]]
            if len(numbered_blocks) >= 5:
                checks_passed.append("[C] Report has numbered list with status counts (5 statuses)")
            else:
                errors.append(f"[X] Expected 5+ numbered list items for status distribution, found {len(numbered_blocks)}")

            # Check heading_2 Priority Analysis
            has_priority_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "priority analysis" in b.get("content", "").lower()
                for b in retro_blocks
            )
            if has_priority_h2:
                checks_passed.append("[C] Report has heading_2 '🚨 Priority Analysis'")
            else:
                errors.append("[X] Missing heading_2 '🚨 Priority Analysis'")

            # Check for bulleted list with priority counts
            bulleted_blocks = [b for b in retro_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
            if len(bulleted_blocks) >= 4:
                checks_passed.append("[C] Report has bulleted list with priority counts (4 priority levels)")
            else:
                errors.append(f"[X] Expected 4+ bulleted list items for priority analysis, found {len(bulleted_blocks)}")

            # Check heading_2 Overdue Tasks
            has_overdue_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "overdue tasks" in b.get("content", "").lower()
                for b in retro_blocks
            )
            if has_overdue_h2:
                checks_passed.append("[C] Report has heading_2 '⚠️ Overdue Tasks'")
            else:
                errors.append("[X] Missing heading_2 '⚠️ Overdue Tasks'")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 11: Task Tracker Schema Updates
# =============================================================================
def _validate_task_tracker_schema_updates(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Task Tracker schema updates and bulk row updates."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Task Tracker database
        task_tracker = _find_page_by_title(final_state_backend, "Task Tracker")
        if not task_tracker:
            errors.append("[X] Task Tracker database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Task Tracker database found")
        task_tracker_id = task_tracker.get("_id")

        # Check for new properties
        db_metadata = task_tracker.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        prop_names = [p.get("name") for p in properties]

        # Check Effort Points (number)
        effort_prop = next((p for p in properties if p.get("name") == "Effort Points"), None)
        if effort_prop:
            if effort_prop.get("type") == "number":
                checks_passed.append("[C] 'Effort Points' property exists with number type")
            else:
                errors.append("[X] 'Effort Points' property exists but not number type")
        else:
            errors.append("[X] 'Effort Points' property not found")

        # Check Sprint (select with options)
        sprint_prop = next((p for p in properties if p.get("name") == "Sprint"), None)
        if sprint_prop:
            if sprint_prop.get("type") == "select":
                options = sprint_prop.get("options", [])
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected = {"Sprint 1": "blue", "Sprint 2": "green", "Sprint 3": "yellow"}
                for name, color in expected.items():
                    if name in option_colors:
                        if option_colors[name] == color:
                            checks_passed.append(f"[C] Sprint option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Sprint option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Sprint option '{name}' missing")
            else:
                errors.append("[X] 'Sprint' property is not select type")
        else:
            errors.append("[X] 'Sprint' property not found")

        # Check Risk Level (select with options)
        risk_prop = next((p for p in properties if p.get("name") == "Risk Level"), None)
        if risk_prop:
            if risk_prop.get("type") == "select":
                options = risk_prop.get("options", [])
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected = {"Low": "green", "Medium": "yellow", "High": "orange", "Critical": "red"}
                for name, color in expected.items():
                    if name in option_colors:
                        if option_colors[name] == color:
                            checks_passed.append(f"[C] Risk Level option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Risk Level option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Risk Level option '{name}' missing")
            else:
                errors.append("[X] 'Risk Level' property is not select type")
        else:
            errors.append("[X] 'Risk Level' property not found")

        # Check row updates
        task_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
        task_rows = task_rows if isinstance(task_rows, list) else []

        high_risk_count = 0
        critical_risk_count = 0
        low_risk_count = 0

        for row in task_rows:
            cell_values = row.get("cellValues", {})
            risk_level = _get_cell_value_by_property_name(cell_values, properties, "risk level")
            effort_points = _get_cell_value_by_property_name(cell_values, properties, "effort points")
            sprint = _get_cell_value_by_property_name(cell_values, properties, "sprint")

            if risk_level == "High":
                high_risk_count += 1
            elif risk_level == "Critical":
                critical_risk_count += 1
            elif risk_level == "Low":
                low_risk_count += 1

        # Verify expected updates based on criteria
        if high_risk_count > 0:
            checks_passed.append(f"[C] Found {high_risk_count} tasks with Risk Level 'High'")
        else:
            errors.append("[X] No tasks with Risk Level 'High' found (expected for High/Urgent priority)")

        if critical_risk_count > 0:
            checks_passed.append(f"[C] Found {critical_risk_count} tasks with Risk Level 'Critical'")
        else:
            errors.append("[X] No tasks with Risk Level 'Critical' found (expected for Blocked status)")

        if low_risk_count > 0:
            checks_passed.append(f"[C] Found {low_risk_count} tasks with Risk Level 'Low'")
        else:
            errors.append("[X] No tasks with Risk Level 'Low' found (expected for Done status)")

        # Check for Task Risk Report page
        report = _find_page_by_title(final_state_backend, "Task Risk Report")
        getting_started = _find_page_by_title(final_state_backend, "Getting Started")

        if not report:
            errors.append("[X] 'Task Risk Report' page not found")
        else:
            checks_passed.append("[C] Task Risk Report page found")

            if getting_started and report.get("parentId") == getting_started.get("_id"):
                checks_passed.append("[C] Task Risk Report is under Getting Started")
            else:
                errors.append("[X] Task Risk Report is not under Getting Started")

            # Check page structure
            report_blocks = _get_blocks_for_page(final_state_backend, report.get("_id"))

            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "task risk analysis" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_h1:
                checks_passed.append("[C] Report has heading_1 '⚠️ Task Risk Analysis'")
            else:
                errors.append("[X] Missing heading_1 '⚠️ Task Risk Analysis'")

            bulleted_blocks = [b for b in report_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
            if len(bulleted_blocks) >= 4:
                checks_passed.append("[C] Report has bulleted list with risk level counts")
            else:
                errors.append("[X] Missing or incomplete bulleted list with risk level counts")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 12: Work Allocation Matrix
# =============================================================================
def _validate_work_allocation_matrix(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Work Allocation Matrix database with multi-database task allocation."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Engineering Wiki page
        eng_wiki = _find_page_by_title(final_state_backend, "Engineering Wiki")
        if not eng_wiki:
            errors.append("[X] Engineering Wiki page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Engineering Wiki page found")
        eng_wiki_id = eng_wiki.get("_id")

        # Find Work Allocation Matrix database
        matrix_db = _find_page_by_title(final_state_backend, "Work Allocation Matrix")
        if not matrix_db:
            errors.append("[X] 'Work Allocation Matrix' database not found")
        else:
            checks_passed.append("[C] Work Allocation Matrix database found")

            # Check if it's under Engineering Wiki
            if matrix_db.get("parentId") == eng_wiki_id:
                checks_passed.append("[C] Database is under Engineering Wiki")
            else:
                errors.append("[X] Database is not under Engineering Wiki")

            # Check database type
            if matrix_db.get("type") == "database":
                checks_passed.append("[C] Work Allocation Matrix is a database")
            else:
                errors.append("[X] Work Allocation Matrix is not a database")

            # Check properties (8 properties)
            db_metadata = matrix_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Assignment ID", "Task", "Project", "Combined Priority",
                            "Estimated Hours", "Resource Type", "Week"]

            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Combined Priority select options
            priority_prop = next((p for p in properties if p.get("name") == "Combined Priority"), None)
            if priority_prop:
                options = priority_prop.get("options", [])
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected = {"Critical": "red", "High": "orange", "Medium": "yellow", "Low": "green"}
                for name, color in expected.items():
                    if name in option_colors:
                        if option_colors[name] == color:
                            checks_passed.append(f"[C] Combined Priority option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Combined Priority option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Combined Priority option '{name}' missing")

            # Check Resource Type multi_select options
            resource_prop = next((p for p in properties if p.get("name") == "Resource Type"), None)
            if resource_prop:
                if resource_prop.get("type") == "multi_select":
                    options = resource_prop.get("options", [])
                    option_names = [o.get("name") for o in options]
                    expected_types = ["Frontend Dev", "Backend Dev", "Designer", "QA"]
                    for rt in expected_types:
                        if rt in option_names:
                            checks_passed.append(f"[C] Resource Type '{rt}' option exists")
                        else:
                            errors.append(f"[X] Resource Type '{rt}' option missing")
                else:
                    errors.append("[X] Resource Type is not multi_select type")

            # Check for rows with WA- prefix
            matrix_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": matrix_db.get("_id"), "type": "row"}
            })
            matrix_rows = matrix_rows if isinstance(matrix_rows, list) else []

            wa_prefix_rows = [r for r in matrix_rows if r.get("title", "").startswith("WA-")]

            if len(wa_prefix_rows) > 0:
                checks_passed.append(f"[C] Found {len(wa_prefix_rows)} allocation entries with WA- prefix")
            else:
                errors.append("[X] No allocation entries with WA- prefix found")

            # Check Week value
            week_values = []
            for row in matrix_rows:
                cell_values = row.get("cellValues", {})
                for key, value in cell_values.items():
                    if "week" in key.lower():
                        week_values.append(value)

            if any("week of jan 13" in str(w).lower() or "jan 13, 2026" in str(w).lower() for w in week_values):
                checks_passed.append("[C] Entries have Week set to 'Week of Jan 13, 2026'")
            else:
                errors.append("[X] Week values do not match 'Week of Jan 13, 2026'")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 13: Contact Analysis Report
# =============================================================================
def _validate_contact_analysis_report(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Contact Analysis Report with contact distribution and VIP analysis."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Company Handbook page
        handbook = _find_page_by_title(final_state_backend, "Company Handbook")
        if not handbook:
            errors.append("[X] Company Handbook page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Company Handbook page found")
        handbook_id = handbook.get("_id")

        # Find Contact Analysis Report
        report = _find_page_by_title(final_state_backend, "Contact Analysis Report")
        if not report:
            errors.append("[X] '📇 Contact Analysis Report' page not found")
        else:
            checks_passed.append("[C] Contact Analysis Report page found")

            # Check if it's under Company Handbook
            if report.get("parentId") == handbook_id:
                checks_passed.append("[C] Report is under Company Handbook")
            else:
                errors.append("[X] Report is not under Company Handbook")

            # Check page structure
            report_blocks = _get_blocks_for_page(final_state_backend, report.get("_id"))

            # Check heading_1
            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "contact analysis report" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_h1:
                checks_passed.append("[C] Report has heading_1 '📇 Contact Analysis Report'")
            else:
                errors.append("[X] Missing heading_1 '📇 Contact Analysis Report'")

            # Check for paragraph with report generated date
            has_generated = any(
                b.get("type") in ["paragraph", "text"] and "report generated on" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_generated:
                checks_passed.append("[C] Report has 'Report generated on' paragraph")
            else:
                errors.append("[X] Missing 'Report generated on' paragraph")

            # Check heading_2 Contact Distribution
            has_distribution_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "contact distribution" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_distribution_h2:
                checks_passed.append("[C] Report has heading_2 '📊 Contact Distribution'")
            else:
                errors.append("[X] Missing heading_2 '📊 Contact Distribution'")

            # Check for yellow callout with Total Contacts
            callout_blocks = [b for b in report_blocks if b.get("type") == "callout"]
            has_yellow_total = any(
                ("yellow" in b.get("properties", {}).get("backgroundColor", "").lower() or
                 "yellow" in b.get("backgroundColor", "").lower()) and
                "total contacts" in b.get("content", "").lower()
                for b in callout_blocks
            )
            if has_yellow_total:
                checks_passed.append("[C] Report has yellow callout with Total Contacts")
            else:
                errors.append("[X] Missing yellow callout with Total Contacts")

            # Check for numbered list with Type counts (4 types)
            numbered_blocks = [b for b in report_blocks if b.get("type") in ["numberedList", "numbered_list", "numbered_list_item"]]
            if len(numbered_blocks) >= 4:
                checks_passed.append("[C] Report has numbered list with Type counts (Client, Vendor, Partner, Lead)")
            else:
                errors.append(f"[X] Expected 4+ numbered list items for Type distribution, found {len(numbered_blocks)}")

            # Check heading_2 Active Engagement
            has_engagement_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "active engagement" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_engagement_h2:
                checks_passed.append("[C] Report has heading_2 '🏷️ Active Engagement'")
            else:
                errors.append("[X] Missing heading_2 '🏷️ Active Engagement'")

            # Check for bulleted list with Active+VIP contacts
            bulleted_blocks = [b for b in report_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
            if len(bulleted_blocks) > 0:
                checks_passed.append("[C] Report has bulleted list with Active+VIP contacts")
            else:
                errors.append("[X] Missing bulleted list with Active+VIP contacts")

            # Check heading_2 Recent Additions
            has_recent_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "recent additions" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_recent_h2:
                checks_passed.append("[C] Report has heading_2 '📅 Recent Additions'")
            else:
                errors.append("[X] Missing heading_2 '📅 Recent Additions'")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 14: Database Integrity Report
# =============================================================================
def _validate_database_integrity_report(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Database Integrity Report with cross-database synchronization checks."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Engineering Wiki page
        eng_wiki = _find_page_by_title(final_state_backend, "Engineering Wiki")
        if not eng_wiki:
            errors.append("[X] Engineering Wiki page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Engineering Wiki page found")
        eng_wiki_id = eng_wiki.get("_id")

        # Find Database Integrity Report
        report = _find_page_by_title(final_state_backend, "Database Integrity Report")
        if not report:
            errors.append("[X] '🔗 Database Integrity Report' page not found")
        else:
            checks_passed.append("[C] Database Integrity Report page found")

            # Check if it's under Engineering Wiki
            if report.get("parentId") == eng_wiki_id:
                checks_passed.append("[C] Report is under Engineering Wiki")
            else:
                errors.append("[X] Report is not under Engineering Wiki")

            # Check page structure
            report_blocks = _get_blocks_for_page(final_state_backend, report.get("_id"))

            # Check heading_1
            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "database integrity report" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_h1:
                checks_passed.append("[C] Report has heading_1 '🔗 Database Integrity Report'")
            else:
                errors.append("[X] Missing heading_1 '🔗 Database Integrity Report'")

            # Check heading_2 Orphaned Tasks
            has_orphaned_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "orphaned tasks" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_orphaned_h2:
                checks_passed.append("[C] Report has heading_2 '⚠️ Orphaned Tasks'")
            else:
                errors.append("[X] Missing heading_2 '⚠️ Orphaned Tasks'")

            # Check for numbered list with orphaned tasks
            numbered_blocks = [b for b in report_blocks if b.get("type") in ["numberedList", "numbered_list", "numbered_list_item"]]
            if len(numbered_blocks) > 0:
                checks_passed.append("[C] Report has numbered list for orphaned tasks")
            else:
                # This may be valid if no orphaned tasks exist
                checks_passed.append("[C] No orphaned tasks to list (or numbered list present)")

            # Check heading_2 Budget Impact Analysis
            has_budget_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "budget impact analysis" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_budget_h2:
                checks_passed.append("[C] Report has heading_2 '💰 Budget Impact Analysis'")
            else:
                errors.append("[X] Missing heading_2 '💰 Budget Impact Analysis'")

            # Check for bulleted list with project costs
            bulleted_blocks = [b for b in report_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
            if len(bulleted_blocks) > 0:
                checks_passed.append("[C] Report has bulleted list for budget impact")
            else:
                errors.append("[X] Missing bulleted list for budget impact")

            # Check for red callout with discrepancy count
            callout_blocks = [b for b in report_blocks if b.get("type") == "callout"]
            has_red_callout = any(
                ("red" in b.get("properties", {}).get("backgroundColor", "").lower() or
                 "red" in b.get("backgroundColor", "").lower()) and
                "discrepancies found" in b.get("content", "").lower()
                for b in callout_blocks
            )
            if has_red_callout:
                checks_passed.append("[C] Report has red callout with Total Cross-Database Discrepancies Found")
            else:
                errors.append("[X] Missing red callout with Total Cross-Database Discrepancies Found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 15: Project Expenses Tracking
# =============================================================================
def _validate_project_expenses_tracking(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Project Expenses database creation with Active project data."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Engineering Wiki page
        eng_wiki = _find_page_by_title(final_state_backend, "Engineering Wiki")
        if not eng_wiki:
            errors.append("[X] Engineering Wiki page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Engineering Wiki page found")
        eng_wiki_id = eng_wiki.get("_id")

        # Find Project Expenses database
        project_expenses = _find_page_by_title(final_state_backend, "Project Expenses")
        if not project_expenses:
            errors.append("[X] 'Project Expenses' database not found")
        else:
            checks_passed.append("[C] Project Expenses database found")

            # Check if it's under Engineering Wiki
            if project_expenses.get("parentId") == eng_wiki_id:
                checks_passed.append("[C] Project Expenses is under Engineering Wiki")
            else:
                errors.append("[X] Project Expenses is not under Engineering Wiki")

            # Check database type
            if project_expenses.get("type") == "database":
                checks_passed.append("[C] Project Expenses is a database")
            else:
                errors.append("[X] Project Expenses is not a database")

            # Check properties
            db_metadata = project_expenses.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Expense Item", "Project", "Amount", "Category", "Date", "Approved"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Category select options
            category_prop = next((p for p in properties if p.get("name") == "Category"), None)
            if category_prop:
                options = category_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Development": "blue", "Infrastructure": "gray", "Marketing": "green", "Operations": "orange"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Category option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Category option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Category option '{name}' missing")

            # Check for rows in the database
            expense_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": project_expenses.get("_id"), "type": "row"}
            })
            expense_rows = expense_rows if isinstance(expense_rows, list) else []

            if len(expense_rows) > 0:
                checks_passed.append(f"[C] Found {len(expense_rows)} expense rows")
            else:
                errors.append("[X] No expense rows found in database")

        # Check for yellow callout at top of Engineering Wiki
        eng_wiki_blocks = _get_blocks_for_page(final_state_backend, eng_wiki_id)
        callout_blocks = [b for b in eng_wiki_blocks if b.get("type") == "callout"]

        has_yellow_alert = any(
            ("yellow" in b.get("properties", {}).get("backgroundColor", "").lower() or
             "yellow" in b.get("backgroundColor", "").lower()) and
            "budget alert" in b.get("content", "").lower()
            for b in callout_blocks
        )

        if has_yellow_alert:
            checks_passed.append("[C] Yellow callout with Budget Alert found on Engineering Wiki")
        else:
            errors.append("[X] Missing yellow callout with Budget Alert on Engineering Wiki")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 16: Sprint Performance Tracker Database
# =============================================================================
def _validate_sprint_performance_tracker_db(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Sprint Performance Tracker database creation with task aggregation."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Engineering Wiki page
        eng_wiki = _find_page_by_title(final_state_backend, "Engineering Wiki")
        if not eng_wiki:
            errors.append("[X] Engineering Wiki page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Engineering Wiki page found")
        eng_wiki_id = eng_wiki.get("_id")

        # Find Sprint Performance Tracker database
        sprint_tracker = _find_page_by_title(final_state_backend, "Sprint Performance Tracker")
        if not sprint_tracker:
            errors.append("[X] 'Sprint Performance Tracker' database not found")
        else:
            checks_passed.append("[C] Sprint Performance Tracker database found")

            # Check if it's under Engineering Wiki
            if sprint_tracker.get("parentId") == eng_wiki_id:
                checks_passed.append("[C] Sprint Performance Tracker is under Engineering Wiki")
            else:
                errors.append("[X] Sprint Performance Tracker is not under Engineering Wiki")

            # Check database type
            if sprint_tracker.get("type") == "database":
                checks_passed.append("[C] Sprint Performance Tracker is a database")
            else:
                errors.append("[X] Sprint Performance Tracker is not a database")

            # Check properties
            db_metadata = sprint_tracker.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Sprint Name", "Start Date", "End Date", "Total Tasks", "Completed Tasks",
                            "Team Size", "Velocity", "Sprint Status", "Sprint Goals"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Sprint Status select options
            status_prop = next((p for p in properties if p.get("name") == "Sprint Status"), None)
            if status_prop:
                options = status_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Planning": "gray", "Active": "blue", "Review": "yellow", "Completed": "green", "Cancelled": "red"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Sprint Status option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Sprint Status option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Sprint Status option '{name}' missing")

            # Check for rows
            sprint_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": sprint_tracker.get("_id"), "type": "row"}
            })
            sprint_rows = sprint_rows if isinstance(sprint_rows, list) else []

            # Should have at least 2 rows (December Sprint and January Sprint)
            if len(sprint_rows) >= 2:
                checks_passed.append(f"[C] Found {len(sprint_rows)} sprint rows (expected at least 2)")
            else:
                errors.append(f"[X] Expected at least 2 sprint rows, found {len(sprint_rows)}")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 17: Database Sync Scripts
# =============================================================================
def _validate_database_sync_scripts(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Database Sync Scripts section with code block and inline database."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Code Examples Repository page
        code_examples = _find_page_by_title(final_state_backend, "Code Examples Repository")
        if not code_examples:
            errors.append("[X] Code Examples Repository page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Code Examples Repository page found")
        code_examples_id = code_examples.get("_id")

        # Get blocks for the page
        blocks = _get_blocks_for_page(final_state_backend, code_examples_id)

        # Check for heading_2 'Database Sync Scripts'
        has_sync_scripts_h2 = any(
            b.get("type") in ["heading2", "heading_2"] and "database sync scripts" in b.get("content", "").lower()
            for b in blocks
        )
        if has_sync_scripts_h2:
            checks_passed.append("[C] heading_2 'Database Sync Scripts' found")
        else:
            errors.append("[X] Missing heading_2 'Database Sync Scripts'")

        # Check for Python code block
        code_blocks = [b for b in blocks if b.get("type") == "code"]
        has_python_code = any(
            "python" in b.get("properties", {}).get("language", "").lower() and
            "sync_databases" in b.get("content", "")
            for b in code_blocks
        )
        if has_python_code:
            checks_passed.append("[C] Python code block with sync_databases function found")
        else:
            errors.append("[X] Missing Python code block with sync_databases function")

        # Check for Sync Log inline database
        sync_log_db = _find_page_by_title(final_state_backend, "Sync Log")
        if sync_log_db:
            checks_passed.append("[C] Sync Log database found")

            # Check if it's a database
            if sync_log_db.get("type") == "database":
                checks_passed.append("[C] Sync Log is a database")
            else:
                errors.append("[X] Sync Log is not a database")

            # Check properties
            db_metadata = sync_log_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Timestamp", "Source", "Target", "Records", "Status"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Sync Log property '{prop}' exists")
                else:
                    errors.append(f"[X] Sync Log property '{prop}' missing")

            # Check Status select options
            status_prop = next((p for p in properties if p.get("name") == "Status"), None)
            if status_prop:
                options = status_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                expected = ["Success", "Failed", "Pending"]
                for name in expected:
                    if name in option_names:
                        checks_passed.append(f"[C] Status option '{name}' exists")
                    else:
                        errors.append(f"[X] Status option '{name}' missing")

            # Check for at least one row
            sync_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": sync_log_db.get("_id"), "type": "row"}
            })
            sync_rows = sync_rows if isinstance(sync_rows, list) else []

            if len(sync_rows) >= 1:
                checks_passed.append(f"[C] Found {len(sync_rows)} sync log row(s)")
            else:
                errors.append("[X] No sync log rows found")
        else:
            errors.append("[X] Sync Log database not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 18: Low Stock Alert
# =============================================================================
def _validate_low_stock_alert(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Low Stock Alert page with inventory updates and report structure."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Inventory Tracker database
        inventory_tracker = _find_page_by_title(final_state_backend, "Inventory Tracker")
        if not inventory_tracker:
            errors.append("[X] Inventory Tracker database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Inventory Tracker database found")

        # Check for Reorder Date property updates
        inventory_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": inventory_tracker.get("_id"), "type": "row"}
        })
        inventory_rows = inventory_rows if isinstance(inventory_rows, list) else []

        # Get database properties for cell value lookup
        inv_metadata = inventory_tracker.get("databaseMetadata", {})
        inv_properties = inv_metadata.get("properties", [])

        rows_with_reorder_date = 0
        for row in inventory_rows:
            cell_values = row.get("cellValues", {})
            reorder_value = _get_cell_value_by_property_name(cell_values, inv_properties, "reorder date")
            if reorder_value:
                rows_with_reorder_date += 1

        if rows_with_reorder_date > 0:
            checks_passed.append(f"[C] Found {rows_with_reorder_date} rows with Reorder Date set")
        else:
            errors.append("[X] No rows have Reorder Date set")

        # Find Company Handbook
        handbook = _find_page_by_title(final_state_backend, "Company Handbook")
        if not handbook:
            errors.append("[X] Company Handbook page not found")
        else:
            checks_passed.append("[C] Company Handbook page found")
            handbook_id = handbook.get("_id")

            # Find Low Stock Alert page
            alert_page = _find_page_by_title(final_state_backend, "Low Stock Alert")
            if not alert_page:
                errors.append("[X] 'Low Stock Alert' page not found")
            else:
                checks_passed.append("[C] Low Stock Alert page found")

                # Check if it's under Company Handbook
                if alert_page.get("parentId") == handbook_id:
                    checks_passed.append("[C] Low Stock Alert is under Company Handbook")
                else:
                    errors.append("[X] Low Stock Alert is not under Company Handbook")

                # Check page structure
                alert_blocks = _get_blocks_for_page(final_state_backend, alert_page.get("_id"))

                # Check heading_1
                has_h1 = any(
                    b.get("type") in ["heading1", "heading_1"] and "low stock alert" in b.get("content", "").lower()
                    for b in alert_blocks
                )
                if has_h1:
                    checks_passed.append("[C] Page has heading_1 'Low Stock Alert Report'")
                else:
                    errors.append("[X] Missing heading_1 'Low Stock Alert Report'")

                # Check heading_2 Summary
                has_summary_h2 = any(
                    b.get("type") in ["heading2", "heading_2"] and "summary" in b.get("content", "").lower()
                    for b in alert_blocks
                )
                if has_summary_h2:
                    checks_passed.append("[C] Page has heading_2 'Summary'")
                else:
                    errors.append("[X] Missing heading_2 'Summary'")

                # Check heading_2 Urgent Reorders
                has_urgent_h2 = any(
                    b.get("type") in ["heading2", "heading_2"] and "urgent reorder" in b.get("content", "").lower()
                    for b in alert_blocks
                )
                if has_urgent_h2:
                    checks_passed.append("[C] Page has heading_2 'Urgent Reorders'")
                else:
                    errors.append("[X] Missing heading_2 'Urgent Reorders'")

                # Check heading_2 Standard Reorders
                has_standard_h2 = any(
                    b.get("type") in ["heading2", "heading_2"] and "standard reorder" in b.get("content", "").lower()
                    for b in alert_blocks
                )
                if has_standard_h2:
                    checks_passed.append("[C] Page has heading_2 'Standard Reorders'")
                else:
                    errors.append("[X] Missing heading_2 'Standard Reorders'")

                # Check for red callout with critical items
                callout_blocks = [b for b in alert_blocks if b.get("type") == "callout"]
                has_red_callout = any(
                    ("red" in b.get("properties", {}).get("backgroundColor", "").lower() or
                     "red" in b.get("backgroundColor", "").lower()) and
                    ("critical" in b.get("content", "").lower() or "out of stock" in b.get("content", "").lower())
                    for b in callout_blocks
                )
                if has_red_callout:
                    checks_passed.append("[C] Page has red callout with critical items count")
                else:
                    errors.append("[X] Missing red callout with critical items count")

                # Check for numbered list
                numbered_blocks = [b for b in alert_blocks if b.get("type") in ["numberedList", "numbered_list", "numbered_list_item"]]
                if len(numbered_blocks) > 0:
                    checks_passed.append(f"[C] Page has {len(numbered_blocks)} numbered list items")
                else:
                    errors.append("[X] Missing numbered list for items")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 19: Quarterly Business Review Database
# =============================================================================
def _validate_quarterly_business_review_db(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Quarterly Business Review database with aggregated metrics."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Meeting Notes page
        meeting_notes = _find_page_by_title(final_state_backend, "Meeting Notes")
        if not meeting_notes:
            errors.append("[X] Meeting Notes page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Meeting Notes page found")
        meeting_notes_id = meeting_notes.get("_id")

        # Find Quarterly Business Review database
        qbr_db = _find_page_by_title(final_state_backend, "Quarterly Business Review")
        if not qbr_db:
            errors.append("[X] 'Quarterly Business Review' database not found")
        else:
            checks_passed.append("[C] Quarterly Business Review database found")

            # Check if it's under Meeting Notes
            if qbr_db.get("parentId") == meeting_notes_id:
                checks_passed.append("[C] Database is under Meeting Notes")
            else:
                errors.append("[X] Database is not under Meeting Notes")

            # Check database type
            if qbr_db.get("type") == "database":
                checks_passed.append("[C] Quarterly Business Review is a database")
            else:
                errors.append("[X] Quarterly Business Review is not a database")

            # Check properties
            db_metadata = qbr_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Quarter", "Total Revenue", "Active Projects", "Completed Tasks",
                            "New Clients", "Team Size", "Health Score", "Notes"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Health Score select options
            health_prop = next((p for p in properties if p.get("name") == "Health Score"), None)
            if health_prop:
                options = health_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Excellent": "green", "Good": "blue", "Needs Attention": "yellow", "Critical": "red"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Health Score option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Health Score option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Health Score option '{name}' missing")

            # Check for Q4 2025 row
            qbr_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": qbr_db.get("_id"), "type": "row"}
            })
            qbr_rows = qbr_rows if isinstance(qbr_rows, list) else []

            if len(qbr_rows) >= 1:
                checks_passed.append(f"[C] Found {len(qbr_rows)} QBR row(s)")
            else:
                errors.append("[X] No QBR rows found")

        # Check for summary section in Getting Started page
        getting_started = _find_page_by_title(final_state_backend, "Getting Started")
        if getting_started:
            gs_blocks = _get_blocks_for_page(final_state_backend, getting_started.get("_id"))
            has_quarter_summary = any(
                b.get("type") in ["heading2", "heading_2"] and "latest quarter summary" in b.get("content", "").lower()
                for b in gs_blocks
            )
            if has_quarter_summary:
                checks_passed.append("[C] Getting Started has 'Latest Quarter Summary' heading")
            else:
                errors.append("[X] Getting Started missing 'Latest Quarter Summary' heading")
        else:
            errors.append("[X] Getting Started page not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 20: Q1 2026 Performance Dashboard
# =============================================================================
def _validate_q1_performance_dashboard(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Q1 2026 Performance Dashboard with multi-database aggregation."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Meeting Notes page
        meeting_notes = _find_page_by_title(final_state_backend, "Meeting Notes")
        if not meeting_notes:
            errors.append("[X] Meeting Notes page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Meeting Notes page found")
        meeting_notes_id = meeting_notes.get("_id")

        # Find Q1 2026 Performance Dashboard page
        dashboard = _find_page_by_title(final_state_backend, "Q1 2026 Performance Dashboard")
        if not dashboard:
            errors.append("[X] 'Q1 2026 Performance Dashboard' page not found")
        else:
            checks_passed.append("[C] Q1 2026 Performance Dashboard page found")

            # Check if it's under Meeting Notes
            if dashboard.get("parentId") == meeting_notes_id:
                checks_passed.append("[C] Dashboard is under Meeting Notes")
            else:
                errors.append("[X] Dashboard is not under Meeting Notes")

            # Check page structure
            dashboard_blocks = _get_blocks_for_page(final_state_backend, dashboard.get("_id"))

            # Check heading_1
            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "q1 2026 performance dashboard" in b.get("content", "").lower()
                for b in dashboard_blocks
            )
            if has_h1:
                checks_passed.append("[C] Dashboard has heading_1 'Q1 2026 Performance Dashboard'")
            else:
                errors.append("[X] Missing heading_1 'Q1 2026 Performance Dashboard'")

            # Check heading_2 Task Completion Metrics
            has_task_metrics_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "task completion metrics" in b.get("content", "").lower()
                for b in dashboard_blocks
            )
            if has_task_metrics_h2:
                checks_passed.append("[C] Dashboard has heading_2 'Task Completion Metrics'")
            else:
                errors.append("[X] Missing heading_2 'Task Completion Metrics'")

            # Check for green callout
            callout_blocks = [b for b in dashboard_blocks if b.get("type") == "callout"]
            has_green_callout = any(
                ("green" in b.get("properties", {}).get("backgroundColor", "").lower() or
                 "green" in b.get("backgroundColor", "").lower()) and
                "completion rate" in b.get("content", "").lower()
                for b in callout_blocks
            )
            if has_green_callout:
                checks_passed.append("[C] Dashboard has green callout with completion rate")
            else:
                errors.append("[X] Missing green callout with completion rate")

            # Check heading_2 Project Status Overview
            has_project_overview_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "project status overview" in b.get("content", "").lower()
                for b in dashboard_blocks
            )
            if has_project_overview_h2:
                checks_passed.append("[C] Dashboard has heading_2 'Project Status Overview'")
            else:
                errors.append("[X] Missing heading_2 'Project Status Overview'")

            # Check heading_2 Cross-Database Insights
            has_insights_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "cross-database insights" in b.get("content", "").lower()
                for b in dashboard_blocks
            )
            if has_insights_h2:
                checks_passed.append("[C] Dashboard has heading_2 'Cross-Database Insights'")
            else:
                errors.append("[X] Missing heading_2 'Cross-Database Insights'")

            # Check for bulleted list
            bulleted_blocks = [b for b in dashboard_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
            if len(bulleted_blocks) > 0:
                checks_passed.append(f"[C] Dashboard has {len(bulleted_blocks)} bulleted list items")
            else:
                errors.append("[X] Missing bulleted list for priority breakdown")

            # Check for numbered list
            numbered_blocks = [b for b in dashboard_blocks if b.get("type") in ["numberedList", "numbered_list", "numbered_list_item"]]
            if len(numbered_blocks) > 0:
                checks_passed.append(f"[C] Dashboard has {len(numbered_blocks)} numbered list items")
            else:
                errors.append("[X] Missing numbered list for department breakdown")

        # Check for Department Efficiency Score inline database
        efficiency_db = _find_page_by_title(final_state_backend, "Department Efficiency Score")
        if efficiency_db:
            checks_passed.append("[C] Department Efficiency Score database found")

            # Check properties
            db_metadata = efficiency_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Department", "Active Projects", "Task Completion %", "Budget Utilization", "Efficiency Rating"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Efficiency Score property '{prop}' exists")
                else:
                    errors.append(f"[X] Efficiency Score property '{prop}' missing")
        else:
            errors.append("[X] Department Efficiency Score database not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 21: Workflow Automation Tracker
# =============================================================================
def _validate_workflow_automation_tracker(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Workflow Automation Tracker section in Sprint Planning."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Sprint Planning page
        sprint_planning = _find_page_by_title(final_state_backend, "Sprint Planning - Jan 15")
        if not sprint_planning:
            sprint_planning = _find_page_by_title(final_state_backend, "Sprint Planning")
        if not sprint_planning:
            errors.append("[X] Sprint Planning page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Sprint Planning page found")
        sprint_planning_id = sprint_planning.get("_id")

        # Get blocks
        blocks = _get_blocks_for_page(final_state_backend, sprint_planning_id)

        # Check heading_2 Automated Workflow Status
        has_workflow_h2 = any(
            b.get("type") in ["heading2", "heading_2"] and "automated workflow status" in b.get("content", "").lower()
            for b in blocks
        )
        if has_workflow_h2:
            checks_passed.append("[C] heading_2 'Automated Workflow Status' found")
        else:
            errors.append("[X] Missing heading_2 'Automated Workflow Status'")

        # Check for divider
        has_divider = any(b.get("type") == "divider" for b in blocks)
        if has_divider:
            checks_passed.append("[C] Divider found in page")
        else:
            errors.append("[X] Missing divider in page")

        # Check for yellow callout with automation health
        callout_blocks = [b for b in blocks if b.get("type") == "callout"]
        has_yellow_automation = any(
            ("yellow" in b.get("properties", {}).get("backgroundColor", "").lower() or
             "yellow" in b.get("backgroundColor", "").lower()) and
            "automation health" in b.get("content", "").lower()
            for b in callout_blocks
        )
        if has_yellow_automation:
            checks_passed.append("[C] Yellow callout with Automation Health found")
        else:
            errors.append("[X] Missing yellow callout with Automation Health")

        # Check for Workflow Automation Log database
        workflow_db = _find_page_by_title(final_state_backend, "Workflow Automation Log")
        if workflow_db:
            checks_passed.append("[C] Workflow Automation Log database found")

            # Check if it's a database
            if workflow_db.get("type") == "database":
                checks_passed.append("[C] Workflow Automation Log is a database")
            else:
                errors.append("[X] Workflow Automation Log is not a database")

            # Check properties
            db_metadata = workflow_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Workflow Name", "Trigger", "Last Run", "Success Rate %", "Items Processed", "Failed Items", "Dependencies"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Trigger select options
            trigger_prop = next((p for p in properties if p.get("name") == "Trigger"), None)
            if trigger_prop:
                options = trigger_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                expected = ["Daily", "Weekly", "On Demand", "Event Based"]
                for name in expected:
                    if name in option_names:
                        checks_passed.append(f"[C] Trigger option '{name}' exists")
                    else:
                        errors.append(f"[X] Trigger option '{name}' missing")

            # Check for rows (at least 3: Task Status Sync, Project Budget Monitor, Inventory Alerts)
            workflow_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": workflow_db.get("_id"), "type": "row"}
            })
            workflow_rows = workflow_rows if isinstance(workflow_rows, list) else []

            if len(workflow_rows) >= 3:
                checks_passed.append(f"[C] Found {len(workflow_rows)} workflow rows")
            else:
                errors.append(f"[X] Expected at least 3 workflow rows, found {len(workflow_rows)}")
        else:
            errors.append("[X] Workflow Automation Log database not found")

        # Check for todo list
        todo_blocks = [b for b in blocks if b.get("type") in ["todo", "to_do", "checkbox"]]
        if len(todo_blocks) >= 3:
            checks_passed.append(f"[C] Found {len(todo_blocks)} todo items")
        else:
            errors.append(f"[X] Expected at least 3 todo items, found {len(todo_blocks)}")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 22: Engineering Sync Report
# =============================================================================
def _validate_engineering_sync_report(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Engineering Sync Report with cross-database task analysis."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Meeting Notes page
        meeting_notes = _find_page_by_title(final_state_backend, "Meeting Notes")
        if not meeting_notes:
            errors.append("[X] Meeting Notes page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Meeting Notes page found")
        meeting_notes_id = meeting_notes.get("_id")

        # Find Engineering Sync Report page
        report = _find_page_by_title(final_state_backend, "Engineering Sync Report")
        if not report:
            errors.append("[X] 'Engineering Sync Report' page not found")
        else:
            checks_passed.append("[C] Engineering Sync Report page found")

            # Check if it's under Meeting Notes
            if report.get("parentId") == meeting_notes_id:
                checks_passed.append("[C] Report is under Meeting Notes")
            else:
                errors.append("[X] Report is not under Meeting Notes")

            # Check page structure
            report_blocks = _get_blocks_for_page(final_state_backend, report.get("_id"))

            # Check heading_1
            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "engineering projects" in b.get("content", "").lower() and "tasks sync report" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_h1:
                checks_passed.append("[C] Report has heading_1 'Engineering Projects & Tasks Sync Report'")
            else:
                errors.append("[X] Missing heading_1 'Engineering Projects & Tasks Sync Report'")

            # Check heading_2 Active Engineering Projects
            has_active_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "active engineering projects" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_active_h2:
                checks_passed.append("[C] Report has heading_2 'Active Engineering Projects'")
            else:
                errors.append("[X] Missing heading_2 'Active Engineering Projects'")

            # Check heading_2 Orphaned Tasks
            has_orphaned_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "orphaned tasks" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_orphaned_h2:
                checks_passed.append("[C] Report has heading_2 'Orphaned Tasks'")
            else:
                errors.append("[X] Missing heading_2 'Orphaned Tasks'")

            # Check for yellow callout with sync status
            callout_blocks = [b for b in report_blocks if b.get("type") == "callout"]
            has_yellow_sync = any(
                ("yellow" in b.get("properties", {}).get("backgroundColor", "").lower() or
                 "yellow" in b.get("backgroundColor", "").lower()) and
                "sync status" in b.get("content", "").lower()
                for b in callout_blocks
            )
            if has_yellow_sync:
                checks_passed.append("[C] Report has yellow callout with Sync Status")
            else:
                errors.append("[X] Missing yellow callout with Sync Status")

            # Check for bulleted list
            bulleted_blocks = [b for b in report_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
            if len(bulleted_blocks) > 0:
                checks_passed.append(f"[C] Report has {len(bulleted_blocks)} bulleted list items")
            else:
                errors.append("[X] Missing bulleted list for tasks")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 23: Sprint Velocity Tracking System
# =============================================================================
def _validate_sprint_velocity_tracking(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Sprint Velocity tracking with schema updates and velocity page."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Task Tracker database
        task_tracker = _find_page_by_title(final_state_backend, "Task Tracker")
        if not task_tracker:
            errors.append("[X] Task Tracker database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Task Tracker database found")
        task_tracker_id = task_tracker.get("_id")

        # Check for Sprint property
        db_metadata = task_tracker.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        sprint_prop = next((p for p in properties if p.get("name") == "Sprint"), None)

        if sprint_prop:
            checks_passed.append("[C] Sprint property exists in Task Tracker")

            # Check Sprint options
            options = sprint_prop.get("options", [])
            option_names = [o.get("name") for o in options]
            option_colors = {o.get("name"): o.get("color") for o in options}

            expected_options = {"Sprint 1": "blue", "Sprint 2": "green", "Sprint 3": "purple", "Sprint 4": "orange"}
            for name, color in expected_options.items():
                if name in option_names:
                    if option_colors.get(name) == color:
                        checks_passed.append(f"[C] Sprint option '{name}' has {color} color")
                    else:
                        errors.append(f"[X] Sprint option '{name}' does not have {color} color")
                else:
                    errors.append(f"[X] Sprint option '{name}' missing")
        else:
            errors.append("[X] Sprint property not found in Task Tracker")

        # Check for Story Points property
        story_points_prop = next((p for p in properties if p.get("name") == "Story Points"), None)
        if story_points_prop:
            if story_points_prop.get("type") == "number":
                checks_passed.append("[C] Story Points property exists with number type")
            else:
                errors.append("[X] Story Points property is not number type")
        else:
            errors.append("[X] Story Points property not found in Task Tracker")

        # Find Sprint Planning page
        sprint_planning = _find_page_by_title(final_state_backend, "Sprint Planning - Jan 15")
        if not sprint_planning:
            sprint_planning = _find_page_by_title(final_state_backend, "Sprint Planning")
        if not sprint_planning:
            errors.append("[X] Sprint Planning page not found")
        else:
            checks_passed.append("[C] Sprint Planning page found")
            sprint_planning_id = sprint_planning.get("_id")

            # Find Sprint Velocity Tracker page
            velocity_tracker = _find_page_by_title(final_state_backend, "Sprint Velocity Tracker")
            if velocity_tracker:
                checks_passed.append("[C] Sprint Velocity Tracker page found")

                # Check if it's under Sprint Planning
                if velocity_tracker.get("parentId") == sprint_planning_id:
                    checks_passed.append("[C] Sprint Velocity Tracker is under Sprint Planning")
                else:
                    errors.append("[X] Sprint Velocity Tracker is not under Sprint Planning")

                # Check page structure
                velocity_blocks = _get_blocks_for_page(final_state_backend, velocity_tracker.get("_id"))

                # Check heading_1
                has_h1 = any(
                    b.get("type") in ["heading1", "heading_1"] and "sprint velocity tracker" in b.get("content", "").lower()
                    for b in velocity_blocks
                )
                if has_h1:
                    checks_passed.append("[C] Page has heading_1 'Sprint Velocity Tracker'")
                else:
                    errors.append("[X] Missing heading_1 'Sprint Velocity Tracker'")

                # Check for heading_2 with Sprint Metrics
                has_sprint_metrics = any(
                    b.get("type") in ["heading2", "heading_2"] and "metrics" in b.get("content", "").lower()
                    for b in velocity_blocks
                )
                if has_sprint_metrics:
                    checks_passed.append("[C] Page has heading_2 with Sprint Metrics")
                else:
                    errors.append("[X] Missing heading_2 with Sprint Metrics")

                # Check for bulleted list with points
                bulleted_blocks = [b for b in velocity_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
                if len(bulleted_blocks) > 0:
                    checks_passed.append(f"[C] Page has {len(bulleted_blocks)} bulleted list items")
                else:
                    errors.append("[X] Missing bulleted list for sprint metrics")
            else:
                errors.append("[X] Sprint Velocity Tracker page not found")

        # Check for Velocity History inline database
        velocity_history = _find_page_by_title(final_state_backend, "Velocity History")
        if velocity_history:
            checks_passed.append("[C] Velocity History database found")

            # Check properties
            db_metadata = velocity_history.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Sprint", "Planned Points", "Completed Points", "Velocity %"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Velocity History property '{prop}' exists")
                else:
                    errors.append(f"[X] Velocity History property '{prop}' missing")
        else:
            errors.append("[X] Velocity History database not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 24: Project Deadline Alert System
# =============================================================================
def _validate_project_deadline_alert(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Project Deadline Alert System database with deadline analysis."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Company Handbook page
        handbook = _find_page_by_title(final_state_backend, "Company Handbook")
        if not handbook:
            errors.append("[X] Company Handbook page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Company Handbook page found")
        handbook_id = handbook.get("_id")

        # Find Project Deadline Alert System database
        alert_db = _find_page_by_title(final_state_backend, "Project Deadline Alert System")
        if not alert_db:
            errors.append("[X] 'Project Deadline Alert System' database not found")
        else:
            checks_passed.append("[C] Project Deadline Alert System database found")

            # Check if it's under Company Handbook
            if alert_db.get("parentId") == handbook_id:
                checks_passed.append("[C] Database is under Company Handbook")
            else:
                errors.append("[X] Database is not under Company Handbook")

            # Check database type
            if alert_db.get("type") == "database":
                checks_passed.append("[C] Project Deadline Alert System is a database")
            else:
                errors.append("[X] Project Deadline Alert System is not a database")

            # Check properties
            db_metadata = alert_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Alert ID", "Project Name", "Days Until Deadline", "Risk Level",
                            "Department", "Project Lead", "Action Required", "Alert Date"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Risk Level select options
            risk_prop = next((p for p in properties if p.get("name") == "Risk Level"), None)
            if risk_prop:
                options = risk_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Critical": "red", "High": "orange", "Medium": "yellow", "Low": "green"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Risk Level option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Risk Level option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Risk Level option '{name}' missing")

            # Check for Alert ID format in rows
            alert_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": alert_db.get("_id"), "type": "row"}
            })
            alert_rows = alert_rows if isinstance(alert_rows, list) else []

            if len(alert_rows) > 0:
                checks_passed.append(f"[C] Found {len(alert_rows)} alert rows")

                # Check Alert ID format
                has_valid_format = False
                for row in alert_rows:
                    title = row.get("title", "")
                    if "ALERT-" in title and "-2026" in title:
                        has_valid_format = True
                        break
                if has_valid_format:
                    checks_passed.append("[C] Alert ID follows 'ALERT-[PROJECT]-2026' format")
                else:
                    errors.append("[X] Alert ID does not follow expected format")
            else:
                errors.append("[X] No alert rows found in database")

            # Check database description
            description = alert_db.get("description", "") or db_metadata.get("description", "")
            if description and "active alerts" in description.lower():
                checks_passed.append("[C] Database description includes active alerts count")
            else:
                errors.append("[X] Database description missing active alerts count")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 25: Cross-Project Dependencies
# =============================================================================
def _validate_cross_project_dependencies(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Cross-Project Dependencies database with dependency analysis."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find 2024 Product Roadmap page
        roadmap = _find_page_by_title(final_state_backend, "2024 Product Roadmap")
        if not roadmap:
            roadmap = _find_page_by_title(final_state_backend, "Product Roadmap")
        if not roadmap:
            errors.append("[X] 2024 Product Roadmap page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] 2024 Product Roadmap page found")
        roadmap_id = roadmap.get("_id")

        # Find Cross-Project Dependencies database
        deps_db = _find_page_by_title(final_state_backend, "Cross-Project Dependencies")
        if not deps_db:
            errors.append("[X] 'Cross-Project Dependencies' database not found")
        else:
            checks_passed.append("[C] Cross-Project Dependencies database found")

            # Check if it's under Product Roadmap
            if deps_db.get("parentId") == roadmap_id:
                checks_passed.append("[C] Database is under Product Roadmap")
            else:
                errors.append("[X] Database is not under Product Roadmap")

            # Check database type
            if deps_db.get("type") == "database":
                checks_passed.append("[C] Cross-Project Dependencies is a database")
            else:
                errors.append("[X] Cross-Project Dependencies is not a database")

            # Check properties
            db_metadata = deps_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Dependency ID", "Source Project", "Target Project", "Dependency Type",
                            "Impact Level", "Resolution Date", "Blocked Tasks", "Notes"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Dependency Type select options
            dep_type_prop = next((p for p in properties if p.get("name") == "Dependency Type"), None)
            if dep_type_prop:
                options = dep_type_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                expected = ["Technical", "Resource", "Timeline", "Budget"]
                for name in expected:
                    if name in option_names:
                        checks_passed.append(f"[C] Dependency Type option '{name}' exists")
                    else:
                        errors.append(f"[X] Dependency Type option '{name}' missing")

            # Check Impact Level select options
            impact_prop = next((p for p in properties if p.get("name") == "Impact Level"), None)
            if impact_prop:
                options = impact_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Low": "green", "Medium": "yellow", "High": "orange", "Critical": "red"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Impact Level option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Impact Level option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Impact Level option '{name}' missing")

        # Check for page section above database
        roadmap_blocks = _get_blocks_for_page(final_state_backend, roadmap_id)

        # Check heading_1 Dependency Analysis Report
        has_analysis_h1 = any(
            b.get("type") in ["heading1", "heading_1"] and "dependency analysis report" in b.get("content", "").lower()
            for b in roadmap_blocks
        )
        if has_analysis_h1:
            checks_passed.append("[C] heading_1 'Dependency Analysis Report' found")
        else:
            errors.append("[X] Missing heading_1 'Dependency Analysis Report'")

        # Check for orange callout with action required
        callout_blocks = [b for b in roadmap_blocks if b.get("type") == "callout"]
        has_orange_action = any(
            ("orange" in b.get("properties", {}).get("backgroundColor", "").lower() or
             "orange" in b.get("backgroundColor", "").lower()) and
            "action required" in b.get("content", "").lower()
            for b in callout_blocks
        )
        if has_orange_action:
            checks_passed.append("[C] Orange callout with Action Required found")
        else:
            errors.append("[X] Missing orange callout with Action Required")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 26: Task Tracker Sprint Burndown
# =============================================================================
def _validate_task_tracker_sprint_burndown(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Task Tracker Sprint property and Sprint Burndown database."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Task Tracker database
        task_tracker = _find_page_by_title(final_state_backend, "Task Tracker")
        if not task_tracker:
            errors.append("[X] Task Tracker database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Task Tracker database found")
        task_tracker_id = task_tracker.get("_id")

        # Check for Sprint property
        db_metadata = task_tracker.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        sprint_prop = next((p for p in properties if p.get("name") == "Sprint"), None)

        if sprint_prop:
            checks_passed.append("[C] Sprint property exists in Task Tracker")

            # Check Sprint options
            options = sprint_prop.get("options", [])
            option_names = [o.get("name") for o in options]
            option_colors = {o.get("name"): o.get("color") for o in options}

            expected_options = {"Sprint Alpha": "purple", "Sprint Beta": "blue", "Sprint Gamma": "green", "Backlog": "gray"}
            for name, color in expected_options.items():
                if name in option_names:
                    if option_colors.get(name) == color:
                        checks_passed.append(f"[C] Sprint option '{name}' has {color} color")
                    else:
                        errors.append(f"[X] Sprint option '{name}' does not have {color} color")
                else:
                    errors.append(f"[X] Sprint option '{name}' missing")

            # Check property position (after Completed, before Created)
            sprint_order = sprint_prop.get("order", 0)
            completed_prop = next((p for p in properties if p.get("name") == "Completed"), None)
            created_prop = next((p for p in properties if p.get("name") == "Created"), None)
            if completed_prop and created_prop:
                completed_order = completed_prop.get("order", 0)
                created_order = created_prop.get("order", 0)
                if completed_order < sprint_order < created_order:
                    checks_passed.append("[C] Sprint property is positioned after Completed and before Created")
                else:
                    # Relaxed check - just verify it exists
                    checks_passed.append("[C] Sprint property position check (flexible)")
        else:
            errors.append("[X] Sprint property not found in Task Tracker")

        # Check for Sprint Burndown database as child of Task Tracker
        burndown_db = _find_page_by_title(final_state_backend, "Sprint Burndown")
        if burndown_db:
            checks_passed.append("[C] Sprint Burndown database found")

            # Check if it's under Task Tracker
            if burndown_db.get("parentId") == task_tracker_id:
                checks_passed.append("[C] Sprint Burndown is under Task Tracker")
            else:
                errors.append("[X] Sprint Burndown is not under Task Tracker")

            # Check database type
            if burndown_db.get("type") == "database":
                checks_passed.append("[C] Sprint Burndown is a database")
            else:
                errors.append("[X] Sprint Burndown is not a database")

            # Check properties
            bd_metadata = burndown_db.get("databaseMetadata", {})
            bd_properties = bd_metadata.get("properties", [])
            bd_prop_names = [p.get("name") for p in bd_properties]

            required_props = ["Date", "Sprint", "Total Tasks", "Remaining Tasks", "Completion Rate"]
            for prop in required_props:
                if prop in bd_prop_names:
                    checks_passed.append(f"[C] Sprint Burndown property '{prop}' exists")
                else:
                    errors.append(f"[X] Sprint Burndown property '{prop}' missing")

            # Check for rows (6 rows: 3 per sprint for Alpha and Beta)
            burndown_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": burndown_db.get("_id"), "type": "row"}
            })
            burndown_rows = burndown_rows if isinstance(burndown_rows, list) else []

            if len(burndown_rows) >= 6:
                checks_passed.append(f"[C] Found {len(burndown_rows)} burndown rows (expected 6)")
            else:
                errors.append(f"[X] Expected at least 6 burndown rows, found {len(burndown_rows)}")
        else:
            errors.append("[X] Sprint Burndown database not found")

        # Check that tasks have Sprint values assigned
        task_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
        task_rows = task_rows if isinstance(task_rows, list) else []

        tasks_with_sprint = 0
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            for key, value in cell_values.items():
                if "sprint" in key.lower() and value:
                    tasks_with_sprint += 1
                    break

        if tasks_with_sprint > 0:
            checks_passed.append(f"[C] Found {tasks_with_sprint} tasks with Sprint values assigned")
        else:
            errors.append("[X] No tasks have Sprint values assigned")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 27: Employee Performance Review
# =============================================================================
def _validate_employee_performance_review(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Employee Performance Review database with task-based metrics."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Team Directory page
        team_directory = _find_page_by_title(final_state_backend, "Team Directory")
        if not team_directory:
            errors.append("[X] Team Directory page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Team Directory page found")
        team_directory_id = team_directory.get("_id")

        # Find Performance Reviews database
        reviews_db = _find_page_by_title(final_state_backend, "Performance Reviews")
        if not reviews_db:
            errors.append("[X] 'Performance Reviews' database not found")
        else:
            checks_passed.append("[C] Performance Reviews database found")

            # Check if it's under Team Directory
            if reviews_db.get("parentId") == team_directory_id:
                checks_passed.append("[C] Database is under Team Directory")
            else:
                errors.append("[X] Database is not under Team Directory")

            # Check database type
            if reviews_db.get("type") == "database":
                checks_passed.append("[C] Performance Reviews is a database")
            else:
                errors.append("[X] Performance Reviews is not a database")

            # Check layout
            db_metadata = reviews_db.get("databaseMetadata", {})
            layout = db_metadata.get("layout", "")
            if layout == "gallery":
                checks_passed.append("[C] Database has gallery layout")
            else:
                errors.append(f"[X] Database layout is '{layout}', expected 'gallery'")

            # Check properties
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Employee Name", "Review Period", "Tasks Completed", "Tasks On Time",
                            "Quality Score", "Collaboration", "Manager Notes", "Overall Rating"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Quality Score select options
            quality_prop = next((p for p in properties if p.get("name") == "Quality Score"), None)
            if quality_prop:
                options = quality_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                expected = ["1-Needs Improvement", "2-Satisfactory", "3-Good", "4-Excellent", "5-Outstanding"]
                for name in expected:
                    if name in option_names:
                        checks_passed.append(f"[C] Quality Score option '{name}' exists")
                    else:
                        errors.append(f"[X] Quality Score option '{name}' missing")

            # Check Collaboration multi_select options
            collab_prop = next((p for p in properties if p.get("name") == "Collaboration"), None)
            if collab_prop:
                if collab_prop.get("type") == "multi_select":
                    checks_passed.append("[C] Collaboration is multi_select type")
                    options = collab_prop.get("options", [])
                    option_names = [o.get("name") for o in options]
                    expected = ["Team Player", "Mentors Others", "Cross-functional", "Leadership"]
                    for name in expected:
                        if name in option_names:
                            checks_passed.append(f"[C] Collaboration option '{name}' exists")
                        else:
                            errors.append(f"[X] Collaboration option '{name}' missing")
                else:
                    errors.append("[X] Collaboration is not multi_select type")

            # Check for review rows
            review_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": reviews_db.get("_id"), "type": "row"}
            })
            review_rows = review_rows if isinstance(review_rows, list) else []

            if len(review_rows) > 0:
                checks_passed.append(f"[C] Found {len(review_rows)} performance review rows")
            else:
                errors.append("[X] No performance review rows found")

        # Check for Performance Summary section in Team Directory
        td_blocks = _get_blocks_for_page(final_state_backend, team_directory_id)
        has_summary_h2 = any(
            b.get("type") in ["heading2", "heading_2"] and "performance summary" in b.get("content", "").lower()
            for b in td_blocks
        )
        if has_summary_h2:
            checks_passed.append("[C] Team Directory has 'Performance Summary' heading")
        else:
            errors.append("[X] Team Directory missing 'Performance Summary' heading")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 28: Lead Scoring Matrix
# =============================================================================
def _validate_lead_scoring_matrix(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Lead Scoring Matrix with contact enrichment."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Contacts Directory database
        contacts = _find_page_by_title(final_state_backend, "Contacts Directory")
        if not contacts:
            errors.append("[X] Contacts Directory database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Contacts Directory database found")

        # Check for contact updates (Company field populated, Follow Up tag)
        contacts_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": contacts.get("_id"), "type": "row"}
        })
        contacts_rows = contacts_rows if isinstance(contacts_rows, list) else []

        leads_with_company = 0
        leads_with_followup = 0
        for row in contacts_rows:
            cell_values = row.get("cellValues", {})
            contact_type = None
            has_company = False
            has_followup = False

            for key, value in cell_values.items():
                if "type" in key.lower():
                    contact_type = value
                if "company" in key.lower() and value:
                    has_company = True
                if "tags" in key.lower() and value:
                    tags = value if isinstance(value, list) else [value]
                    if any("follow" in str(t).lower() for t in tags):
                        has_followup = True

            if contact_type == "Lead":
                if has_company:
                    leads_with_company += 1
                if has_followup:
                    leads_with_followup += 1

        if leads_with_company > 0:
            checks_passed.append(f"[C] Found {leads_with_company} leads with Company field populated")
        else:
            errors.append("[X] No leads have Company field populated")

        # Find Marketing Ideas page
        marketing = _find_page_by_title(final_state_backend, "Marketing Ideas")
        if not marketing:
            errors.append("[X] Marketing Ideas page not found")
        else:
            checks_passed.append("[C] Marketing Ideas page found")
            marketing_id = marketing.get("_id")

            # Find Lead Scoring Matrix database
            scoring_db = _find_page_by_title(final_state_backend, "Lead Scoring Matrix")
            if scoring_db:
                checks_passed.append("[C] Lead Scoring Matrix database found")

                # Check if it's under Marketing Ideas
                if scoring_db.get("parentId") == marketing_id:
                    checks_passed.append("[C] Database is under Marketing Ideas")
                else:
                    errors.append("[X] Database is not under Marketing Ideas")

                # Check database type
                if scoring_db.get("type") == "database":
                    checks_passed.append("[C] Lead Scoring Matrix is a database")
                else:
                    errors.append("[X] Lead Scoring Matrix is not a database")

                # Check properties
                db_metadata = scoring_db.get("databaseMetadata", {})
                properties = db_metadata.get("properties", [])
                prop_names = [p.get("name") for p in properties]

                required_props = ["Lead Name", "Company Size", "Engagement Level", "Score", "Next Action"]
                for prop in required_props:
                    if prop in prop_names:
                        checks_passed.append(f"[C] Property '{prop}' exists")
                    else:
                        errors.append(f"[X] Property '{prop}' missing")

                # Check Company Size select options
                size_prop = next((p for p in properties if p.get("name") == "Company Size"), None)
                if size_prop:
                    options = size_prop.get("options", [])
                    option_names = [o.get("name") for o in options]
                    expected = ["Startup", "SMB", "Enterprise"]
                    for name in expected:
                        if name in option_names:
                            checks_passed.append(f"[C] Company Size option '{name}' exists")
                        else:
                            errors.append(f"[X] Company Size option '{name}' missing")

                # Check for lead rows
                scoring_rows = final_state_backend.query({
                    "collection": "pages",
                    "filter": {"databaseId": scoring_db.get("_id"), "type": "row"}
                })
                scoring_rows = scoring_rows if isinstance(scoring_rows, list) else []

                if len(scoring_rows) > 0:
                    checks_passed.append(f"[C] Found {len(scoring_rows)} lead scoring rows")
                else:
                    errors.append("[X] No lead scoring rows found")
            else:
                errors.append("[X] Lead Scoring Matrix database not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 29: Quarterly Business Review Page
# =============================================================================
def _validate_quarterly_business_review_page(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Quarterly Business Review page with department budget analysis."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Quarterly Business Review page at root level
        qbr_page = _find_page_by_title(final_state_backend, "Quarterly Business Review")
        if not qbr_page:
            qbr_page = _find_page_by_title(final_state_backend, "Q4 2025 Business Review")
        if not qbr_page:
            errors.append("[X] 'Quarterly Business Review' page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Quarterly Business Review page found")

        # Check page structure
        qbr_blocks = _get_blocks_for_page(final_state_backend, qbr_page.get("_id"))

        # Check heading_1
        has_h1 = any(
            b.get("type") in ["heading1", "heading_1"] and ("q4 2025 business review" in b.get("content", "").lower() or "quarterly business review" in b.get("content", "").lower())
            for b in qbr_blocks
        )
        if has_h1:
            checks_passed.append("[C] Page has heading_1 'Q4 2025 Business Review'")
        else:
            errors.append("[X] Missing heading_1 'Q4 2025 Business Review'")

        # Check heading_2 Department Budget Allocation
        has_budget_h2 = any(
            b.get("type") in ["heading2", "heading_2"] and "department budget allocation" in b.get("content", "").lower()
            for b in qbr_blocks
        )
        if has_budget_h2:
            checks_passed.append("[C] Page has heading_2 'Department Budget Allocation'")
        else:
            errors.append("[X] Missing heading_2 'Department Budget Allocation'")

        # Check heading_2 Project Status Distribution
        has_status_h2 = any(
            b.get("type") in ["heading2", "heading_2"] and "project status distribution" in b.get("content", "").lower()
            for b in qbr_blocks
        )
        if has_status_h2:
            checks_passed.append("[C] Page has heading_2 'Project Status Distribution'")
        else:
            errors.append("[X] Missing heading_2 'Project Status Distribution'")

        # Check for blue callout with status distribution
        callout_blocks = [b for b in qbr_blocks if b.get("type") == "callout"]
        has_blue_status = any(
            ("blue" in b.get("properties", {}).get("backgroundColor", "").lower() or
             "blue" in b.get("backgroundColor", "").lower()) and
            ("active" in b.get("content", "").lower() or "projects" in b.get("content", "").lower())
            for b in callout_blocks
        )
        if has_blue_status:
            checks_passed.append("[C] Page has blue callout with status distribution")
        else:
            errors.append("[X] Missing blue callout with status distribution")

        # Check for heading_3 blocks (department names)
        heading3_blocks = [b for b in qbr_blocks if b.get("type") in ["heading3", "heading_3"]]
        if len(heading3_blocks) > 0:
            checks_passed.append(f"[C] Page has {len(heading3_blocks)} heading_3 blocks for departments")
        else:
            errors.append("[X] Missing heading_3 blocks for department names")

        # Check for Action Items inline database under QBR page
        qbr_page_id = qbr_page.get("_id")
        action_items_db = _find_page_by_title_pattern_and_parent(final_state_backend, "Action Items", qbr_page_id)
        if not action_items_db:
            # Fallback: search globally but filter for database type
            all_action_items = _find_pages_by_title(final_state_backend, "Action Items")
            action_items_db = next((p for p in all_action_items if p.get("type") == "database"), None)
        if action_items_db:
            checks_passed.append("[C] Action Items inline database found")

            # Check database type
            if action_items_db.get("type") == "database":
                checks_passed.append("[C] Action Items is a database")
            else:
                errors.append(f"[X] Action Items is not a database (type: {action_items_db.get('type')})")

            # Check properties
            db_metadata = action_items_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Action", "Owner", "Department", "Due Date", "Priority"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Action Items property '{prop}' exists")
                else:
                    errors.append(f"[X] Action Items property '{prop}' missing")

            # Check for 5 action item rows
            action_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": action_items_db.get("_id"), "type": "row"}
            })
            action_rows = action_rows if isinstance(action_rows, list) else []

            if len(action_rows) >= 5:
                checks_passed.append(f"[C] Found {len(action_rows)} action item rows")
            else:
                errors.append(f"[X] Expected at least 5 action item rows, found {len(action_rows)}")
        else:
            errors.append("[X] Action Items inline database not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 30: Financial Overview Page
# =============================================================================
def _validate_financial_overview(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Financial Overview page with budget analysis."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Company Handbook page
        handbook = _find_page_by_title(final_state_backend, "Company Handbook")
        if not handbook:
            errors.append("[X] Company Handbook page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Company Handbook page found")
        handbook_id = handbook.get("_id")

        # Find Financial Overview page
        financial = _find_page_by_title(final_state_backend, "Financial Overview")
        if not financial:
            errors.append("[X] 'Financial Overview' page not found")
        else:
            checks_passed.append("[C] Financial Overview page found")

            # Check if it's under Company Handbook
            if financial.get("parentId") == handbook_id:
                checks_passed.append("[C] Page is under Company Handbook")
            else:
                errors.append("[X] Page is not under Company Handbook")

            # Check page structure
            financial_blocks = _get_blocks_for_page(final_state_backend, financial.get("_id"))

            # Check heading_1
            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "financial overview" in b.get("content", "").lower()
                for b in financial_blocks
            )
            if has_h1:
                checks_passed.append("[C] Page has heading_1 'Financial Overview'")
            else:
                errors.append("[X] Missing heading_1 'Financial Overview'")

            # Check heading_2 Project Investments
            has_investments_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "project investments" in b.get("content", "").lower()
                for b in financial_blocks
            )
            if has_investments_h2:
                checks_passed.append("[C] Page has heading_2 'Project Investments'")
            else:
                errors.append("[X] Missing heading_2 'Project Investments'")

            # Check for paragraph with total investments
            has_total_para = any(
                b.get("type") in ["paragraph", "text"] and "total project investments" in b.get("content", "").lower()
                for b in financial_blocks
            )
            if has_total_para:
                checks_passed.append("[C] Page has paragraph with total project investments")
            else:
                errors.append("[X] Missing paragraph with total project investments")

            # Check heading_2 Financial Alerts
            has_alerts_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "financial alerts" in b.get("content", "").lower()
                for b in financial_blocks
            )
            if has_alerts_h2:
                checks_passed.append("[C] Page has heading_2 'Financial Alerts'")
            else:
                errors.append("[X] Missing heading_2 'Financial Alerts'")

            # Check for numbered list
            numbered_blocks = [b for b in financial_blocks if b.get("type") in ["numberedList", "numbered_list", "numbered_list_item"]]
            if len(numbered_blocks) >= 3:
                checks_passed.append(f"[C] Page has {len(numbered_blocks)} numbered list items for alerts")
            else:
                errors.append(f"[X] Expected at least 3 numbered list items, found {len(numbered_blocks)}")

        # Check for Budget Utilization Tracker inline database
        budget_db = _find_page_by_title(final_state_backend, "Budget Utilization Tracker")
        if budget_db:
            checks_passed.append("[C] Budget Utilization Tracker database found")

            # Check properties
            db_metadata = budget_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Category", "Allocated Budget", "Items/Projects", "Status"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Budget Tracker property '{prop}' exists")
                else:
                    errors.append(f"[X] Budget Tracker property '{prop}' missing")

            # Check Status select options
            status_prop = next((p for p in properties if p.get("name") == "Status"), None)
            if status_prop:
                options = status_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"On Track": "green", "Review Needed": "yellow", "Over Budget": "red"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Status option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Status option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Status option '{name}' missing")

            # Check for rows
            budget_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": budget_db.get("_id"), "type": "row"}
            })
            budget_rows = budget_rows if isinstance(budget_rows, list) else []

            if len(budget_rows) >= 2:
                checks_passed.append(f"[C] Found {len(budget_rows)} budget tracker rows")
            else:
                errors.append(f"[X] Expected at least 2 budget tracker rows, found {len(budget_rows)}")
        else:
            errors.append("[X] Budget Utilization Tracker database not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 31: Development Time Tracking
# =============================================================================
def _validate_development_time_tracking(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Development Time Tracking with schema updates and inline database."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Task Tracker database
        task_tracker = _find_page_by_title(final_state_backend, "Task Tracker")
        if not task_tracker:
            errors.append("[X] Task Tracker database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Task Tracker database found")
        task_tracker_id = task_tracker.get("_id")

        # Check for Estimated Hours property
        db_metadata = task_tracker.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        est_hours_prop = next((p for p in properties if p.get("name") == "Estimated Hours"), None)

        if est_hours_prop:
            if est_hours_prop.get("type") == "number":
                checks_passed.append("[C] Estimated Hours property exists with number type")
            else:
                errors.append("[X] Estimated Hours property is not number type")

            # Check position (after Priority)
            priority_prop = next((p for p in properties if p.get("name") == "Priority"), None)
            if priority_prop and est_hours_prop:
                priority_order = priority_prop.get("order", 0)
                est_hours_order = est_hours_prop.get("order", 0)
                if est_hours_order > priority_order:
                    checks_passed.append("[C] Estimated Hours is positioned after Priority")
                else:
                    # Relaxed check
                    checks_passed.append("[C] Estimated Hours position check (flexible)")
        else:
            errors.append("[X] Estimated Hours property not found in Task Tracker")

        # Check that Backend+Feature tasks have Estimated Hours set
        task_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
        task_rows = task_rows if isinstance(task_rows, list) else []

        tasks_with_est_hours = 0
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            has_backend_feature = False
            has_est_hours = False

            for key, value in cell_values.items():
                if "tags" in key.lower() and value:
                    tags = value if isinstance(value, list) else [value]
                    has_backend = any("backend" in str(t).lower() for t in tags)
                    has_feature = any("feature" in str(t).lower() for t in tags)
                    if has_backend and has_feature:
                        has_backend_feature = True
                if "estimated" in key.lower() and value:
                    has_est_hours = True

            if has_backend_feature and has_est_hours:
                tasks_with_est_hours += 1

        if tasks_with_est_hours > 0:
            checks_passed.append(f"[C] Found {tasks_with_est_hours} Backend+Feature tasks with Estimated Hours set")
        else:
            # May not have any Backend+Feature tasks, so this is a soft check
            checks_passed.append("[C] Estimated Hours assignment check (no matching tasks or values set)")

        # Find Engineering Wiki page
        eng_wiki = _find_page_by_title(final_state_backend, "Engineering Wiki")
        if not eng_wiki:
            errors.append("[X] Engineering Wiki page not found")
        else:
            checks_passed.append("[C] Engineering Wiki page found")

            # Find Development Time Tracking inline database
            time_tracking_db = _find_page_by_title(final_state_backend, "Development Time Tracking")
            if time_tracking_db:
                checks_passed.append("[C] Development Time Tracking database found")

                # Check database type
                if time_tracking_db.get("type") == "database":
                    checks_passed.append("[C] Development Time Tracking is a database")
                else:
                    errors.append("[X] Development Time Tracking is not a database")

                # Check properties
                tt_metadata = time_tracking_db.get("databaseMetadata", {})
                tt_properties = tt_metadata.get("properties", [])
                tt_prop_names = [p.get("name") for p in tt_properties]

                required_props = ["Developer", "Task Reference", "Hours Logged", "Date", "Status"]
                for prop in required_props:
                    if prop in tt_prop_names:
                        checks_passed.append(f"[C] Time Tracking property '{prop}' exists")
                    else:
                        errors.append(f"[X] Time Tracking property '{prop}' missing")

                # Check Status select options
                status_prop = next((p for p in tt_properties if p.get("name") == "Status"), None)
                if status_prop:
                    options = status_prop.get("options", [])
                    option_names = [o.get("name") for o in options]
                    option_colors = {o.get("name"): o.get("color") for o in options}

                    expected_options = {"In Progress": "yellow", "Completed": "green", "Blocked": "red"}
                    for name, color in expected_options.items():
                        if name in option_names:
                            if option_colors.get(name) == color:
                                checks_passed.append(f"[C] Status option '{name}' has {color} color")
                            else:
                                errors.append(f"[X] Status option '{name}' does not have {color} color")
                        else:
                            errors.append(f"[X] Status option '{name}' missing")

                # Check for time tracking rows
                tracking_rows = final_state_backend.query({
                    "collection": "pages",
                    "filter": {"databaseId": time_tracking_db.get("_id"), "type": "row"}
                })
                tracking_rows = tracking_rows if isinstance(tracking_rows, list) else []

                if len(tracking_rows) > 0:
                    checks_passed.append(f"[C] Found {len(tracking_rows)} time tracking rows")
                else:
                    errors.append("[X] No time tracking rows found")
            else:
                errors.append("[X] Development Time Tracking database not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 32: Sprint Retrospective System
# =============================================================================
def _validate_sprint_retrospective_system(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Sprint Retrospective System with Task Tracker updates and new database."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Task Tracker database
        task_tracker = _find_page_by_title(final_state_backend, "Task Tracker")
        if not task_tracker:
            errors.append("[X] Task Tracker database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Task Tracker database found")
        task_tracker_id = task_tracker.get("_id")

        # Check Task Tracker rows for Q1-2026 tag updates
        task_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
        task_rows = task_rows if isinstance(task_rows, list) else []

        done_feature_with_q1_count = 0
        done_feature_count = 0
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            status = None
            tags = []

            for key, value in cell_values.items():
                if "status" in key.lower():
                    status = value
                if "tags" in key.lower() and value:
                    tags = value if isinstance(value, list) else [value]

            has_feature = any("feature" in str(t).lower() for t in tags)
            has_q1_2026 = any("q1-2026" in str(t).lower() for t in tags)

            if status == "Done" and has_feature:
                done_feature_count += 1
                if has_q1_2026:
                    done_feature_with_q1_count += 1

        if done_feature_count > 0 and done_feature_with_q1_count == done_feature_count:
            checks_passed.append(f"[C] All {done_feature_count} Done+Feature tasks have Q1-2026 tag")
        elif done_feature_with_q1_count > 0:
            checks_passed.append(f"[C] {done_feature_with_q1_count}/{done_feature_count} Done+Feature tasks have Q1-2026 tag")
        else:
            errors.append("[X] No Done+Feature tasks found with Q1-2026 tag")

        # Find Team Retrospective page
        team_retro = _find_page_by_title(final_state_backend, "Team Retrospective")
        if not team_retro:
            errors.append("[X] Team Retrospective page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Team Retrospective page found")
        team_retro_id = team_retro.get("_id")

        # Find Sprint Retrospectives database
        sprint_retros = _find_page_by_title(final_state_backend, "Sprint Retrospectives")
        if not sprint_retros:
            errors.append("[X] 'Sprint Retrospectives' database not found")
        else:
            checks_passed.append("[C] Sprint Retrospectives database found")

            # Check if it's under Team Retrospective
            if sprint_retros.get("parentId") == team_retro_id:
                checks_passed.append("[C] Database is under Team Retrospective")
            else:
                errors.append("[X] Database is not under Team Retrospective")

            # Check database properties
            db_metadata = sprint_retros.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Sprint", "Start Date", "End Date", "Completed Features",
                            "Total Story Points", "Team Sentiment", "Key Achievements", "Improvement Areas"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Team Sentiment select options
            sentiment_prop = next((p for p in properties if p.get("name") == "Team Sentiment"), None)
            if sentiment_prop:
                options = sentiment_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Very Positive": "green", "Positive": "blue", "Neutral": "yellow",
                                  "Needs Improvement": "orange", "Critical": "red"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Team Sentiment option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Team Sentiment option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Team Sentiment option '{name}' missing")

            # Check Improvement Areas multi_select options
            improvement_prop = next((p for p in properties if p.get("name") == "Improvement Areas"), None)
            if improvement_prop:
                options = improvement_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                expected_areas = ["Communication", "Planning", "Technical Debt", "Testing", "Documentation"]
                for name in expected_areas:
                    if name in option_names:
                        checks_passed.append(f"[C] Improvement Areas option '{name}' exists")
                    else:
                        errors.append(f"[X] Improvement Areas option '{name}' missing")

            # Check for sprint row
            sprint_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": sprint_retros.get("_id"), "type": "row"}
            })
            sprint_rows = sprint_rows if isinstance(sprint_rows, list) else []

            has_sprint_2026_01 = any("sprint 2026-01" in row.get("title", "").lower() for row in sprint_rows)
            if has_sprint_2026_01:
                checks_passed.append("[C] Sprint 2026-01 row exists")
            else:
                errors.append("[X] Sprint 2026-01 row not found")

        # Check for green callout on Team Retrospective
        retro_blocks = _get_blocks_for_page(final_state_backend, team_retro_id)
        callout_blocks = [b for b in retro_blocks if b.get("type") == "callout"]

        has_green_callout = any(
            ("green" in b.get("properties", {}).get("backgroundColor", "").lower() or
             "green" in b.get("backgroundColor", "").lower()) and
            "latest sprint" in b.get("content", "").lower()
            for b in callout_blocks
        )
        if has_green_callout:
            checks_passed.append("[C] Green callout with 'Latest Sprint' found on Team Retrospective")
        else:
            errors.append("[X] Missing green callout with 'Latest Sprint' on Team Retrospective")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 33: Client Project Assignments
# =============================================================================
def _validate_client_project_assignments(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Client Project Assignments database with cross-database operations."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Team Directory page
        team_directory = _find_page_by_title(final_state_backend, "Team Directory")
        if not team_directory:
            errors.append("[X] Team Directory page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Team Directory page found")
        team_directory_id = team_directory.get("_id")

        # Find Client Project Assignments database
        assignments_db = _find_page_by_title(final_state_backend, "Client Project Assignments")
        if not assignments_db:
            errors.append("[X] 'Client Project Assignments' database not found")
        else:
            checks_passed.append("[C] Client Project Assignments database found")

            # Check if it's under Team Directory
            if assignments_db.get("parentId") == team_directory_id:
                checks_passed.append("[C] Database is under Team Directory")
            else:
                errors.append("[X] Database is not under Team Directory")

            # Check database properties
            db_metadata = assignments_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Assignment ID", "Client Name", "Client Company", "Project",
                            "Relationship Manager", "Last Contact", "Status"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Status select options
            status_prop = next((p for p in properties if p.get("name") == "Status"), None)
            if status_prop:
                options = status_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Active": "green", "Pending": "yellow", "On Hold": "orange"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Status option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Status option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Status option '{name}' missing")

            # Check for client rows (Sarah Johnson and Lisa Thompson)
            assignment_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": assignments_db.get("_id"), "type": "row"}
            })
            assignment_rows = assignment_rows if isinstance(assignment_rows, list) else []

            has_sarah = any("sarah johnson" in row.get("title", "").lower() for row in assignment_rows)
            has_lisa = any("lisa thompson" in row.get("title", "").lower() for row in assignment_rows)

            if len(assignment_rows) >= 2:
                checks_passed.append(f"[C] Found {len(assignment_rows)} client assignment rows")
            else:
                errors.append(f"[X] Expected at least 2 client rows, found {len(assignment_rows)}")

            if has_sarah:
                checks_passed.append("[C] Sarah Johnson assignment row exists")
            else:
                errors.append("[X] Sarah Johnson assignment row not found")

            if has_lisa:
                checks_passed.append("[C] Lisa Thompson assignment row exists")
            else:
                errors.append("[X] Lisa Thompson assignment row not found")

        # Check for summary section on Team Directory
        directory_blocks = _get_blocks_for_page(final_state_backend, team_directory_id)

        has_summary_h2 = any(
            b.get("type") in ["heading2", "heading_2"] and "client assignment summary" in b.get("content", "").lower()
            for b in directory_blocks
        )
        if has_summary_h2:
            checks_passed.append("[C] heading_2 'Client Assignment Summary' found")
        else:
            errors.append("[X] Missing heading_2 'Client Assignment Summary'")

        has_summary_paragraph = any(
            b.get("type") in ["paragraph", "text"] and
            "total client assignments" in b.get("content", "").lower() and
            "pending" in b.get("content", "").lower()
            for b in directory_blocks
        )
        if has_summary_paragraph:
            checks_passed.append("[C] Summary paragraph with assignment counts found")
        else:
            errors.append("[X] Missing summary paragraph with assignment counts")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 34: Cross-Functional Dependencies (Task Dependencies)
# =============================================================================
def _validate_cross_functional_dependencies(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Task Dependencies database with cross-database analysis."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Product Specs page
        product_specs = _find_page_by_title(final_state_backend, "Product Specs")
        if not product_specs:
            errors.append("[X] Product Specs page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Product Specs page found")
        product_specs_id = product_specs.get("_id")

        # Find Task Dependencies database
        deps_db = _find_page_by_title(final_state_backend, "Task Dependencies")
        if not deps_db:
            errors.append("[X] 'Task Dependencies' database not found")
        else:
            checks_passed.append("[C] Task Dependencies database found")

            # Check if it's under Product Specs
            if deps_db.get("parentId") == product_specs_id:
                checks_passed.append("[C] Database is under Product Specs")
            else:
                errors.append("[X] Database is not under Product Specs")

            # Check database properties
            db_metadata = deps_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Dependency ID", "Task", "Depends On", "Project Impact",
                            "Risk Score", "Mitigation Plan", "Status"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Status select options
            status_prop = next((p for p in properties if p.get("name") == "Status"), None)
            if status_prop:
                options = status_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Identified": "gray", "Mitigated": "green", "Escalated": "red"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Status option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Status option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Status option '{name}' missing")

            # Check for dependency rows
            dep_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": deps_db.get("_id"), "type": "row"}
            })
            dep_rows = dep_rows if isinstance(dep_rows, list) else []

            if len(dep_rows) >= 2:
                checks_passed.append(f"[C] Found {len(dep_rows)} dependency rows")
            else:
                errors.append(f"[X] Expected at least 2 dependency rows (Blocked + In Review), found {len(dep_rows)}")

        # Check dashboard on Product Specs
        specs_blocks = _get_blocks_for_page(final_state_backend, product_specs_id)

        has_dashboard_h1 = any(
            b.get("type") in ["heading1", "heading_1"] and "dependency management dashboard" in b.get("content", "").lower()
            for b in specs_blocks
        )
        if has_dashboard_h1:
            checks_passed.append("[C] heading_1 'Dependency Management Dashboard' found")
        else:
            errors.append("[X] Missing heading_1 'Dependency Management Dashboard'")

        # Check for orange callout with active dependencies
        callout_blocks = [b for b in specs_blocks if b.get("type") == "callout"]
        has_orange_callout = any(
            ("orange" in b.get("properties", {}).get("backgroundColor", "").lower() or
             "orange" in b.get("backgroundColor", "").lower()) and
            "active dependencies" in b.get("content", "").lower()
            for b in callout_blocks
        )
        if has_orange_callout:
            checks_passed.append("[C] Orange callout with active dependencies found")
        else:
            errors.append("[X] Missing orange callout with active dependencies")

        has_risk_h2 = any(
            b.get("type") in ["heading2", "heading_2"] and "risk distribution" in b.get("content", "").lower()
            for b in specs_blocks
        )
        if has_risk_h2:
            checks_passed.append("[C] heading_2 'Risk Distribution' found")
        else:
            errors.append("[X] Missing heading_2 'Risk Distribution'")

        # Check for bulleted list with risk scores
        bulleted_blocks = [b for b in specs_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
        if len(bulleted_blocks) >= 3:
            checks_passed.append(f"[C] Found {len(bulleted_blocks)} bulleted list items for risk distribution")
        else:
            errors.append("[X] Missing bulleted list for risk distribution")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 35: Sprint Performance Tracker Database (v2)
# =============================================================================
def _validate_sprint_performance_tracker_v2(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate comprehensive Sprint Performance Tracker with task aggregation."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Engineering Wiki page
        eng_wiki = _find_page_by_title(final_state_backend, "Engineering Wiki")
        if not eng_wiki:
            errors.append("[X] Engineering Wiki page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Engineering Wiki page found")
        eng_wiki_id = eng_wiki.get("_id")

        # Find Sprint Performance Tracker database
        tracker_db = _find_page_by_title(final_state_backend, "Sprint Performance Tracker")
        if not tracker_db:
            errors.append("[X] 'Sprint Performance Tracker' database not found")
        else:
            checks_passed.append("[C] Sprint Performance Tracker database found")

            # Check if it's under Engineering Wiki
            if tracker_db.get("parentId") == eng_wiki_id:
                checks_passed.append("[C] Database is under Engineering Wiki")
            else:
                errors.append("[X] Database is not under Engineering Wiki")

            # Check database properties
            db_metadata = tracker_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Sprint Name", "Start Date", "End Date", "Total Tasks",
                            "Completed Tasks", "Team Size", "Velocity", "Sprint Status", "Sprint Goals"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Sprint Status select options
            status_prop = next((p for p in properties if p.get("name") == "Sprint Status"), None)
            if status_prop:
                options = status_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Planning": "gray", "Active": "blue", "Review": "yellow",
                                  "Completed": "green", "Cancelled": "red"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Sprint Status option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Sprint Status option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Sprint Status option '{name}' missing")

            # Check for sprint rows
            tracker_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": tracker_db.get("_id"), "type": "row"}
            })
            tracker_rows = tracker_rows if isinstance(tracker_rows, list) else []

            has_december_sprint = any("december sprint" in row.get("title", "").lower() for row in tracker_rows)
            has_january_sprint = any("january sprint" in row.get("title", "").lower() for row in tracker_rows)

            if has_december_sprint:
                checks_passed.append("[C] December Sprint row exists")
            else:
                errors.append("[X] December Sprint row not found")

            if has_january_sprint:
                checks_passed.append("[C] January Sprint row exists")
            else:
                errors.append("[X] January Sprint row not found")

            if len(tracker_rows) >= 2:
                checks_passed.append(f"[C] Found {len(tracker_rows)} sprint rows")
            else:
                errors.append(f"[X] Expected at least 2 sprint rows, found {len(tracker_rows)}")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 36: Resource Allocation Matrix
# =============================================================================
def _validate_resource_allocation_matrix(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Resource Allocation Matrix with assignee calculations."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Engineering Wiki page
        eng_wiki = _find_page_by_title(final_state_backend, "Engineering Wiki")
        if not eng_wiki:
            errors.append("[X] Engineering Wiki page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Engineering Wiki page found")
        eng_wiki_id = eng_wiki.get("_id")

        # Find Resource Allocation Matrix database
        matrix_db = _find_page_by_title(final_state_backend, "Resource Allocation Matrix")
        if not matrix_db:
            errors.append("[X] 'Resource Allocation Matrix' database not found")
        else:
            checks_passed.append("[C] Resource Allocation Matrix database found")

            # Check if it's under Engineering Wiki
            if matrix_db.get("parentId") == eng_wiki_id:
                checks_passed.append("[C] Database is under Engineering Wiki")
            else:
                errors.append("[X] Database is not under Engineering Wiki")

            # Check database properties
            db_metadata = matrix_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Resource", "Type", "Allocated To", "Allocation %",
                            "Cost per Month", "Status"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Type select options
            type_prop = next((p for p in properties if p.get("name") == "Type"), None)
            if type_prop:
                options = type_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Human": "blue", "Infrastructure": "gray", "Software": "purple", "Budget": "green"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Type option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Type option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Type option '{name}' missing")

            # Check Status select options
            status_prop = next((p for p in properties if p.get("name") == "Status"), None)
            if status_prop:
                options = status_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Fully Allocated": "red", "Available": "green", "Partially Allocated": "yellow"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Status option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Status option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Status option '{name}' missing")

            # Check for rows (human resources + infrastructure)
            matrix_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": matrix_db.get("_id"), "type": "row"}
            })
            matrix_rows = matrix_rows if isinstance(matrix_rows, list) else []

            # Check for infrastructure rows
            has_aws = any("aws" in row.get("title", "").lower() for row in matrix_rows)
            has_licenses = any("license" in row.get("title", "").lower() for row in matrix_rows)

            if has_aws:
                checks_passed.append("[C] AWS Cloud Services row exists")
            else:
                errors.append("[X] AWS Cloud Services row not found")

            if has_licenses:
                checks_passed.append("[C] Development Licenses row exists")
            else:
                errors.append("[X] Development Licenses row not found")

            if len(matrix_rows) >= 5:
                checks_passed.append(f"[C] Found {len(matrix_rows)} resource rows (including assignees)")
            else:
                errors.append(f"[X] Expected at least 5 resource rows (3 assignees + 2 infrastructure), found {len(matrix_rows)}")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 37: Sprint Capacity Planning with Effort Points 2
# =============================================================================
def _validate_sprint_capacity_planning_2(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Sprint Capacity Planning with Effort Points property updates."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Task Tracker database
        task_tracker = _find_page_by_title(final_state_backend, "Task Tracker")
        if not task_tracker:
            errors.append("[X] Task Tracker database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Task Tracker database found")
        task_tracker_id = task_tracker.get("_id")

        # Check for Effort Points property
        db_metadata = task_tracker.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        effort_prop = next((p for p in properties if p.get("name") == "Effort Points"), None)

        if effort_prop:
            if effort_prop.get("type") == "number":
                checks_passed.append("[C] Effort Points property exists with number type")
            else:
                errors.append("[X] Effort Points property is not number type")

            # Check position (after Tags)
            tags_prop = next((p for p in properties if p.get("name") == "Tags"), None)
            if tags_prop and effort_prop:
                tags_order = tags_prop.get("order", 0)
                effort_order = effort_prop.get("order", 0)
                if effort_order > tags_order:
                    checks_passed.append("[C] Effort Points is positioned after Tags")
                else:
                    checks_passed.append("[C] Effort Points position check (flexible)")
        else:
            errors.append("[X] Effort Points property not found in Task Tracker")

        # Check that rows have Effort Points based on Priority
        task_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
        task_rows = task_rows if isinstance(task_rows, list) else []

        effort_points_set = 0
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            effort_value = _get_cell_value_by_property_name(cell_values, properties, "effort points")
            if effort_value is not None:
                effort_points_set += 1

        if effort_points_set > 0:
            checks_passed.append(f"[C] {effort_points_set} tasks have Effort Points set")
        else:
            errors.append("[X] No tasks have Effort Points set")

        # Find Engineering Wiki page
        eng_wiki = _find_page_by_title(final_state_backend, "Engineering Wiki")
        if not eng_wiki:
            errors.append("[X] Engineering Wiki page not found")
        else:
            checks_passed.append("[C] Engineering Wiki page found")
            eng_wiki_id = eng_wiki.get("_id")

            # Find Sprint Capacity Planning database
            capacity_db = _find_page_by_title(final_state_backend, "Sprint Capacity Planning")
            if capacity_db:
                checks_passed.append("[C] Sprint Capacity Planning database found")

                # Check if it's inline in Engineering Wiki
                if capacity_db.get("parentId") == eng_wiki_id:
                    checks_passed.append("[C] Database is under Engineering Wiki")
                else:
                    errors.append("[X] Database is not under Engineering Wiki")

                # Check database properties
                cap_metadata = capacity_db.get("databaseMetadata", {})
                cap_properties = cap_metadata.get("properties", [])
                cap_prop_names = [p.get("name") for p in cap_properties]

                required_props = ["Developer", "Available Points", "Assigned Points", "Utilization %", "Sprint"]
                for prop in required_props:
                    if prop in cap_prop_names:
                        checks_passed.append(f"[C] Capacity property '{prop}' exists")
                    else:
                        errors.append(f"[X] Capacity property '{prop}' missing")

                # Check Sprint select options
                sprint_prop = next((p for p in cap_properties if p.get("name") == "Sprint"), None)
                if sprint_prop:
                    options = sprint_prop.get("options", [])
                    option_names = [o.get("name") for o in options]
                    option_colors = {o.get("name"): o.get("color") for o in options}

                    expected_options = {"Current": "green", "Next": "blue", "Future": "gray"}
                    for name, color in expected_options.items():
                        if name in option_names:
                            if option_colors.get(name) == color:
                                checks_passed.append(f"[C] Sprint option '{name}' has {color} color")
                            else:
                                errors.append(f"[X] Sprint option '{name}' does not have {color} color")
                        else:
                            errors.append(f"[X] Sprint option '{name}' missing")

                # Check for developer rows
                capacity_rows = final_state_backend.query({
                    "collection": "pages",
                    "filter": {"databaseId": capacity_db.get("_id"), "type": "row"}
                })
                capacity_rows = capacity_rows if isinstance(capacity_rows, list) else []

                if len(capacity_rows) >= 3:
                    checks_passed.append(f"[C] Found {len(capacity_rows)} developer capacity rows")
                else:
                    errors.append(f"[X] Expected at least 3 developer rows, found {len(capacity_rows)}")
            else:
                errors.append("[X] Sprint Capacity Planning database not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 38: Department Budget Analysis
# =============================================================================
def _validate_department_budget_analysis(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Department Budget Analysis page with calculated statistics."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Project Documentation page
        project_docs = _find_page_by_title(final_state_backend, "Project Documentation")
        if not project_docs:
            errors.append("[X] Project Documentation page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Project Documentation page found")
        project_docs_id = project_docs.get("_id")

        # Find Department Budget Analysis page
        budget_page = _find_page_by_title(final_state_backend, "Department Budget Analysis")
        if not budget_page:
            errors.append("[X] 'Department Budget Analysis' page not found")
        else:
            checks_passed.append("[C] Department Budget Analysis page found")

            # Check if it's under Project Documentation
            if budget_page.get("parentId") == project_docs_id:
                checks_passed.append("[C] Page is under Project Documentation")
            else:
                errors.append("[X] Page is not under Project Documentation")

            # Check page structure
            budget_blocks = _get_blocks_for_page(final_state_backend, budget_page.get("_id"))

            # Check heading_1
            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "department budget analysis" in b.get("content", "").lower()
                for b in budget_blocks
            )
            if has_h1:
                checks_passed.append("[C] heading_1 'Department Budget Analysis' found")
            else:
                errors.append("[X] Missing heading_1 'Department Budget Analysis'")

            # Check heading_2 Budget Distribution
            has_distribution_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "budget distribution" in b.get("content", "").lower()
                for b in budget_blocks
            )
            if has_distribution_h2:
                checks_passed.append("[C] heading_2 'Budget Distribution by Department' found")
            else:
                errors.append("[X] Missing heading_2 'Budget Distribution by Department'")

            # Check for blue callout with total budget
            callout_blocks = [b for b in budget_blocks if b.get("type") == "callout"]
            has_blue_callout = any(
                ("blue" in b.get("properties", {}).get("backgroundColor", "").lower() or
                 "blue" in b.get("backgroundColor", "").lower()) and
                "total budget" in b.get("content", "").lower()
                for b in callout_blocks
            )
            if has_blue_callout:
                checks_passed.append("[C] Blue callout with total budget found")
            else:
                errors.append("[X] Missing blue callout with total budget")

            # Check for department heading_3 sections
            departments = ["Engineering", "Marketing", "Sales", "Operations", "HR"]
            for dept in departments:
                has_dept_h3 = any(
                    b.get("type") in ["heading3", "heading_3"] and dept.lower() in b.get("content", "").lower()
                    for b in budget_blocks
                )
                if has_dept_h3:
                    checks_passed.append(f"[C] heading_3 for '{dept}' found")
                else:
                    errors.append(f"[X] Missing heading_3 for '{dept}'")

            # Check for bulleted lists under each department
            bulleted_blocks = [b for b in budget_blocks if b.get("type") in ["bulletedList", "bulleted_list", "bulleted_list_item"]]
            if len(bulleted_blocks) >= 15:  # At least 3 bullets per department * 5 departments
                checks_passed.append(f"[C] Found {len(bulleted_blocks)} bulleted list items for department stats")
            else:
                errors.append(f"[X] Expected at least 15 bulleted items (3 per department), found {len(bulleted_blocks)}")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 39: Team Workload Balance
# =============================================================================
def _validate_team_workload_balance(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Team Workload Balance inline database with task calculations."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Team Retrospective page
        team_retro = _find_page_by_title(final_state_backend, "Team Retrospective")
        if not team_retro:
            errors.append("[X] Team Retrospective page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Team Retrospective page found")
        team_retro_id = team_retro.get("_id")

        # Check for heading_2 Workload Distribution Analysis
        retro_blocks = _get_blocks_for_page(final_state_backend, team_retro_id)

        has_workload_h2 = any(
            b.get("type") in ["heading2", "heading_2"] and "workload distribution analysis" in b.get("content", "").lower()
            for b in retro_blocks
        )
        if has_workload_h2:
            checks_passed.append("[C] heading_2 'Workload Distribution Analysis' found")
        else:
            errors.append("[X] Missing heading_2 'Workload Distribution Analysis'")

        # Find Team Workload Balance database
        workload_db = _find_page_by_title(final_state_backend, "Team Workload Balance")
        if not workload_db:
            errors.append("[X] 'Team Workload Balance' database not found")
        else:
            checks_passed.append("[C] Team Workload Balance database found")

            # Check if it's under Team Retrospective
            if workload_db.get("parentId") == team_retro_id:
                checks_passed.append("[C] Database is under Team Retrospective")
            else:
                errors.append("[X] Database is not under Team Retrospective")

            # Check database properties
            db_metadata = workload_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Team Member", "Active Tasks", "Completed Tasks", "Blocked Tasks",
                            "Avg Priority Score", "Overdue Items"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check for team member rows
            workload_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": workload_db.get("_id"), "type": "row"}
            })
            workload_rows = workload_rows if isinstance(workload_rows, list) else []

            if len(workload_rows) >= 3:
                checks_passed.append(f"[C] Found {len(workload_rows)} team member rows")
            else:
                errors.append(f"[X] Expected at least 3 team member rows, found {len(workload_rows)}")

        # Check for red callout if workload imbalance detected
        callout_blocks = [b for b in retro_blocks if b.get("type") == "callout"]
        has_red_callout = any(
            ("red" in b.get("properties", {}).get("backgroundColor", "").lower() or
             "red" in b.get("backgroundColor", "").lower()) and
            "workload imbalance" in b.get("content", "").lower()
            for b in callout_blocks
        )
        if has_red_callout:
            checks_passed.append("[C] Red callout for workload imbalance found")
        else:
            # This is conditional - only required if someone has >2 active tasks
            checks_passed.append("[C] Red callout check (conditional based on workload)")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 40: Inventory Command Center
# =============================================================================
def _validate_inventory_command_center(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Inventory Command Center with reorder levels and categorized analysis."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Inventory Tracker database
        inventory_tracker = _find_page_by_title(final_state_backend, "Inventory Tracker")
        if not inventory_tracker:
            errors.append("[X] Inventory Tracker database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Inventory Tracker database found")
        inventory_tracker_id = inventory_tracker.get("_id")

        # Check for Reorder Level property
        db_metadata = inventory_tracker.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        reorder_prop = next((p for p in properties if p.get("name") == "Reorder Level"), None)

        if reorder_prop:
            if reorder_prop.get("type") == "number":
                checks_passed.append("[C] Reorder Level property exists with number type")
            else:
                errors.append("[X] Reorder Level property is not number type")
        else:
            errors.append("[X] Reorder Level property not found in Inventory Tracker")

        # Find Inventory Command Center page
        command_center = _find_page_by_title(final_state_backend, "Inventory Command Center")
        if not command_center:
            errors.append("[X] 'Inventory Command Center' page not found")
        else:
            checks_passed.append("[C] Inventory Command Center page found")
            command_center_id = command_center.get("_id")

            # Check if Inventory Tracker is under Inventory Command Center
            if inventory_tracker.get("parentId") == command_center_id:
                checks_passed.append("[C] Inventory Tracker is under Inventory Command Center")
            else:
                errors.append("[X] Inventory Tracker is not under Inventory Command Center")

            # Check page structure
            center_blocks = _get_blocks_for_page(final_state_backend, command_center_id)

            # Check heading_1
            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "inventory management hub" in b.get("content", "").lower()
                for b in center_blocks
            )
            if has_h1:
                checks_passed.append("[C] heading_1 'Inventory Management Hub' found")
            else:
                errors.append("[X] Missing heading_1 'Inventory Management Hub'")

            # Check for category headings with emojis
            categories = [
                ("Electronics", "💻"),
                ("Furniture", "🪑"),
                ("Office Supplies", "📎"),
                ("Software", "💿"),
                ("Hardware", "🔧")
            ]
            for cat_name, emoji in categories:
                has_cat_h2 = any(
                    b.get("type") in ["heading2", "heading_2"] and cat_name.lower() in b.get("content", "").lower()
                    for b in center_blocks
                )
                if has_cat_h2:
                    checks_passed.append(f"[C] heading_2 for '{cat_name}' found")
                else:
                    errors.append(f"[X] Missing heading_2 for '{cat_name}'")

            # Check for todo items for low stock
            todo_blocks = [b for b in center_blocks if b.get("type") in ["todo", "to_do", "checkbox"]]
            if len(todo_blocks) > 0:
                checks_passed.append(f"[C] Found {len(todo_blocks)} todo items for low stock alerts")
            else:
                errors.append("[X] Missing todo items for low stock items")

            # Check for Inventory Valuation section
            has_valuation_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "inventory valuation" in b.get("content", "").lower()
                for b in center_blocks
            )
            if has_valuation_h2:
                checks_passed.append("[C] heading_2 'Inventory Valuation' found")
            else:
                errors.append("[X] Missing heading_2 'Inventory Valuation'")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 41: Project Resource Allocation Tasks
# =============================================================================
def _validate_project_resource_allocation_tasks(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate cross-database project resource allocation with Task Tracker updates."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Task Tracker database
        task_tracker = _find_page_by_title(final_state_backend, "Task Tracker")
        if not task_tracker:
            errors.append("[X] Task Tracker database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Task Tracker database found")
        task_tracker_id = task_tracker.get("_id")

        # Find Project Portfolio database
        project_portfolio = _find_page_by_title(final_state_backend, "Project Portfolio")
        if not project_portfolio:
            errors.append("[X] Project Portfolio database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Project Portfolio database found")
        project_portfolio_id = project_portfolio.get("_id")

        # Check for 'Task Created' checkbox property in Project Portfolio
        db_metadata = project_portfolio.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        task_created_prop = next((p for p in properties if p.get("name") == "Task Created"), None)

        if task_created_prop:
            if task_created_prop.get("type") == "checkbox":
                checks_passed.append("[C] Task Created property exists with checkbox type")
            else:
                errors.append("[X] Task Created property is not checkbox type")
        else:
            errors.append("[X] Task Created property not found in Project Portfolio")

        # Check for Resource Review tasks in Task Tracker
        task_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
        task_rows = task_rows if isinstance(task_rows, list) else []

        resource_review_count = sum(
            1 for row in task_rows
            if "resource review" in row.get("title", "").lower()
        )

        if resource_review_count >= 3:  # At least 3 active projects
            checks_passed.append(f"[C] Found {resource_review_count} Resource Review tasks")
        else:
            errors.append(f"[X] Expected at least 3 Resource Review tasks, found {resource_review_count}")

        # Check that active projects have Task Created checked
        project_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": project_portfolio_id, "type": "row"}
        })
        project_rows = project_rows if isinstance(project_rows, list) else []

        active_with_task_created = 0
        for row in project_rows:
            cell_values = row.get("cellValues", {})
            status = _get_cell_value_by_property_name(cell_values, properties, "status")
            task_created = _get_cell_value_by_property_name(cell_values, properties, "task created")

            if status == "Active" and task_created is True:
                active_with_task_created += 1

        if active_with_task_created > 0:
            checks_passed.append(f"[C] {active_with_task_created} active projects have Task Created checked")
        else:
            errors.append("[X] No active projects have Task Created checked")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 42: Contact Relationship Mapper
# =============================================================================
def _validate_contact_relationship_mapper(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Contact Relationship Mapper with schema updates and hub page."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Contacts Directory database
        contacts_dir = _find_page_by_title(final_state_backend, "Contacts Directory")
        if not contacts_dir:
            errors.append("[X] Contacts Directory database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Contacts Directory database found")

        # Check for Last Contact Date and Relationship Strength properties
        db_metadata = contacts_dir.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        prop_names = [p.get("name") for p in properties]

        if "Last Contact Date" in prop_names:
            checks_passed.append("[C] Last Contact Date property exists")
        else:
            errors.append("[X] Last Contact Date property not found")

        if "Relationship Strength" in prop_names:
            checks_passed.append("[C] Relationship Strength property exists")

            # Check Relationship Strength select options
            rel_prop = next((p for p in properties if p.get("name") == "Relationship Strength"), None)
            if rel_prop:
                options = rel_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Strong": "green", "Growing": "blue", "Needs Attention": "yellow", "At Risk": "red"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Relationship Strength option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Relationship Strength option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Relationship Strength option '{name}' missing")
        else:
            errors.append("[X] Relationship Strength property not found")

        # Find Team Retrospective page
        team_retro = _find_page_by_title(final_state_backend, "Team Retrospective")
        if not team_retro:
            errors.append("[X] Team Retrospective page not found")
        else:
            checks_passed.append("[C] Team Retrospective page found")
            team_retro_id = team_retro.get("_id")

            # Find Relationship Management Hub page
            rel_hub = _find_page_by_title(final_state_backend, "Relationship Management Hub")
            if rel_hub:
                checks_passed.append("[C] Relationship Management Hub page found")

                # Check if it's under Team Retrospective
                if rel_hub.get("parentId") == team_retro_id:
                    checks_passed.append("[C] Hub is under Team Retrospective")
                else:
                    errors.append("[X] Hub is not under Team Retrospective")

                # Check page structure
                hub_blocks = _get_blocks_for_page(final_state_backend, rel_hub.get("_id"))

                has_h1 = any(
                    b.get("type") in ["heading1", "heading_1"] and "client relationship tracker" in b.get("content", "").lower()
                    for b in hub_blocks
                )
                if has_h1:
                    checks_passed.append("[C] heading_1 'Client Relationship Tracker' found")
                else:
                    errors.append("[X] Missing heading_1 'Client Relationship Tracker'")

                # Check for blue callout with VIP count
                callout_blocks = [b for b in hub_blocks if b.get("type") == "callout"]
                has_blue_callout = any(
                    ("blue" in b.get("properties", {}).get("backgroundColor", "").lower() or
                     "blue" in b.get("backgroundColor", "").lower()) and
                    "active relationships" in b.get("content", "").lower()
                    for b in callout_blocks
                )
                if has_blue_callout:
                    checks_passed.append("[C] Blue callout with active relationships found")
                else:
                    errors.append("[X] Missing blue callout with active relationships")

                has_at_risk_h2 = any(
                    b.get("type") in ["heading2", "heading_2"] and "at risk relationships" in b.get("content", "").lower()
                    for b in hub_blocks
                )
                if has_at_risk_h2:
                    checks_passed.append("[C] heading_2 'At Risk Relationships' found")
                else:
                    errors.append("[X] Missing heading_2 'At Risk Relationships'")

                # Check for Outreach Schedule inline database
                outreach_db = _find_page_by_title(final_state_backend, "Outreach Schedule")
                if outreach_db:
                    checks_passed.append("[C] Outreach Schedule database found")

                    # Check properties
                    outreach_metadata = outreach_db.get("databaseMetadata", {})
                    outreach_properties = outreach_metadata.get("properties", [])
                    outreach_prop_names = [p.get("name") for p in outreach_properties]

                    required_props = ["Contact", "Scheduled Date", "Outreach Type", "Notes"]
                    for prop in required_props:
                        if prop in outreach_prop_names:
                            checks_passed.append(f"[C] Outreach property '{prop}' exists")
                        else:
                            errors.append(f"[X] Outreach property '{prop}' missing")

                    # Check for rows
                    outreach_rows = final_state_backend.query({
                        "collection": "pages",
                        "filter": {"databaseId": outreach_db.get("_id"), "type": "row"}
                    })
                    outreach_rows = outreach_rows if isinstance(outreach_rows, list) else []

                    if len(outreach_rows) >= 5:
                        checks_passed.append(f"[C] Found {len(outreach_rows)} outreach schedule rows")
                    else:
                        errors.append(f"[X] Expected at least 5 outreach rows, found {len(outreach_rows)}")
                else:
                    errors.append("[X] Outreach Schedule database not found")
            else:
                errors.append("[X] Relationship Management Hub page not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 43: Task Dependency Tracking System
# =============================================================================
def _validate_task_dependency_tracking(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Task Dependency Tracking with schema updates and report page."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Task Tracker database
        task_tracker = _find_page_by_title(final_state_backend, "Task Tracker")
        if not task_tracker:
            errors.append("[X] Task Tracker database not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Task Tracker database found")
        task_tracker_id = task_tracker.get("_id")

        # Check for new properties in Task Tracker
        db_metadata = task_tracker.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        prop_names = [p.get("name") for p in properties]

        new_props = ["Dependencies", "Blocked By", "Risk Level"]
        for prop in new_props:
            if prop in prop_names:
                checks_passed.append(f"[C] Task Tracker property '{prop}' exists")
            else:
                errors.append(f"[X] Task Tracker property '{prop}' missing")

        # Check Risk Level select options
        risk_prop = next((p for p in properties if p.get("name") == "Risk Level"), None)
        if risk_prop:
            options = risk_prop.get("options", [])
            option_names = [o.get("name") for o in options]
            option_colors = {o.get("name"): o.get("color") for o in options}

            expected_options = {"Low": "green", "Medium": "yellow", "High": "orange", "Critical": "red"}
            for name, color in expected_options.items():
                if name in option_names:
                    if option_colors.get(name) == color:
                        checks_passed.append(f"[C] Risk Level option '{name}' has {color} color")
                    else:
                        errors.append(f"[X] Risk Level option '{name}' does not have {color} color")
                else:
                    errors.append(f"[X] Risk Level option '{name}' missing")

        # Check that Blocked tasks have Critical risk level
        task_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": task_tracker_id, "type": "row"}
        })
        task_rows = task_rows if isinstance(task_rows, list) else []

        blocked_with_critical = 0
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            status = _get_cell_value_by_property_name(cell_values, properties, "status")
            risk_level = _get_cell_value_by_property_name(cell_values, properties, "risk level")

            if status == "Blocked" and risk_level == "Critical":
                blocked_with_critical += 1

        if blocked_with_critical > 0:
            checks_passed.append(f"[C] {blocked_with_critical} Blocked tasks have Critical risk level")
        else:
            errors.append("[X] No Blocked tasks found with Critical risk level")

        # Find Bug Tracker page
        bug_tracker = _find_page_by_title(final_state_backend, "Bug Tracker")
        if not bug_tracker:
            errors.append("[X] Bug Tracker page not found")
        else:
            checks_passed.append("[C] Bug Tracker page found")
            bug_tracker_id = bug_tracker.get("_id")

            # Find Dependency Matrix database
            dep_matrix = _find_page_by_title(final_state_backend, "Dependency Matrix")
            if dep_matrix:
                checks_passed.append("[C] Dependency Matrix database found")

                # Check if it's under Bug Tracker
                if dep_matrix.get("parentId") == bug_tracker_id:
                    checks_passed.append("[C] Database is under Bug Tracker")
                else:
                    errors.append("[X] Database is not under Bug Tracker")

                # Check properties
                matrix_metadata = dep_matrix.get("databaseMetadata", {})
                matrix_properties = matrix_metadata.get("properties", [])
                matrix_prop_names = [p.get("name") for p in matrix_properties]

                required_props = ["Task", "Depends On", "Impact Score", "Resolution Priority"]
                for prop in required_props:
                    if prop in matrix_prop_names:
                        checks_passed.append(f"[C] Matrix property '{prop}' exists")
                    else:
                        errors.append(f"[X] Matrix property '{prop}' missing")

                # Check Resolution Priority select options
                res_prop = next((p for p in matrix_properties if p.get("name") == "Resolution Priority"), None)
                if res_prop:
                    options = res_prop.get("options", [])
                    option_names = [o.get("name") for o in options]
                    expected = ["P0", "P1", "P2", "P3"]
                    for name in expected:
                        if name in option_names:
                            checks_passed.append(f"[C] Resolution Priority option '{name}' exists")
                        else:
                            errors.append(f"[X] Resolution Priority option '{name}' missing")
            else:
                errors.append("[X] Dependency Matrix database not found")

        # Find Critical Dependencies Report page
        report = _find_page_by_title(final_state_backend, "Critical Dependencies Report")
        if report:
            checks_passed.append("[C] Critical Dependencies Report page found")

            # Check page structure
            report_blocks = _get_blocks_for_page(final_state_backend, report.get("_id"))

            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "blocking issues analysis" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_h1:
                checks_passed.append("[C] heading_1 'Blocking Issues Analysis' found")
            else:
                errors.append("[X] Missing heading_1 'Blocking Issues Analysis'")

            # Check for red callout
            callout_blocks = [b for b in report_blocks if b.get("type") == "callout"]
            has_red_callout = any(
                ("red" in b.get("properties", {}).get("backgroundColor", "").lower() or
                 "red" in b.get("backgroundColor", "").lower()) and
                "critical" in b.get("content", "").lower()
                for b in callout_blocks
            )
            if has_red_callout:
                checks_passed.append("[C] Red callout with critical count found")
            else:
                errors.append("[X] Missing red callout with critical count")

            # Check for todos
            todo_blocks = [b for b in report_blocks if b.get("type") in ["todo", "to_do", "checkbox"]]
            if len(todo_blocks) > 0:
                checks_passed.append(f"[C] Found {len(todo_blocks)} action todos")
            else:
                errors.append("[X] Missing action todos for critical tasks")
        else:
            errors.append("[X] Critical Dependencies Report page not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Task 44: Project Health Monitoring System
# =============================================================================
def _validate_project_health_monitoring(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """Validate Project Health Monitoring with inline database and calculated metrics."""
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # Find Project Guidelines & Best Practices page
        guidelines = _find_page_by_title(final_state_backend, "Project Guidelines")
        if not guidelines:
            guidelines = _find_page_by_title(final_state_backend, "Best Practices")
        if not guidelines:
            errors.append("[X] Project Guidelines & Best Practices page not found")
            return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))
        checks_passed.append("[C] Project Guidelines & Best Practices page found")
        guidelines_id = guidelines.get("_id")

        # Find Project Health Metrics database
        health_db = _find_page_by_title(final_state_backend, "Project Health Metrics")
        if not health_db:
            errors.append("[X] 'Project Health Metrics' database not found")
        else:
            checks_passed.append("[C] Project Health Metrics database found")

            # Check if it's inline in Guidelines page
            if health_db.get("parentId") == guidelines_id:
                checks_passed.append("[C] Database is under Project Guidelines")
            else:
                errors.append("[X] Database is not under Project Guidelines")

            # Check database properties
            db_metadata = health_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name") for p in properties]

            required_props = ["Project", "Health Score", "On-Time Tasks %", "Budget Utilization %",
                            "Team Velocity", "Risk Factors", "Status Color"]
            for prop in required_props:
                if prop in prop_names:
                    checks_passed.append(f"[C] Property '{prop}' exists")
                else:
                    errors.append(f"[X] Property '{prop}' missing")

            # Check Risk Factors multi_select options
            risk_prop = next((p for p in properties if p.get("name") == "Risk Factors"), None)
            if risk_prop:
                options = risk_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                expected_risks = ["Timeline", "Budget", "Resources", "Scope", "Quality"]
                for name in expected_risks:
                    if name in option_names:
                        checks_passed.append(f"[C] Risk Factors option '{name}' exists")
                    else:
                        errors.append(f"[X] Risk Factors option '{name}' missing")

            # Check Status Color select options
            status_prop = next((p for p in properties if p.get("name") == "Status Color"), None)
            if status_prop:
                options = status_prop.get("options", [])
                option_names = [o.get("name") for o in options]
                option_colors = {o.get("name"): o.get("color") for o in options}

                expected_options = {"Green": "green", "Yellow": "yellow", "Red": "red"}
                for name, color in expected_options.items():
                    if name in option_names:
                        if option_colors.get(name) == color:
                            checks_passed.append(f"[C] Status Color option '{name}' has {color} color")
                        else:
                            errors.append(f"[X] Status Color option '{name}' does not have {color} color")
                    else:
                        errors.append(f"[X] Status Color option '{name}' missing")

            # Check for project rows
            health_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": health_db.get("_id"), "type": "row"}
            })
            health_rows = health_rows if isinstance(health_rows, list) else []

            if len(health_rows) >= 3:  # Non-cancelled projects
                checks_passed.append(f"[C] Found {len(health_rows)} project health rows")
            else:
                errors.append(f"[X] Expected at least 3 project rows, found {len(health_rows)}")

        # Find Project Health Report page
        report = _find_page_by_title(final_state_backend, "Project Health Report")
        if report:
            checks_passed.append("[C] Project Health Report page found")

            # Check page structure
            report_blocks = _get_blocks_for_page(final_state_backend, report.get("_id"))

            has_h1 = any(
                b.get("type") in ["heading1", "heading_1"] and "weekly health check" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_h1:
                checks_passed.append("[C] heading_1 'Weekly Health Check' found")
            else:
                errors.append("[X] Missing heading_1 'Weekly Health Check'")

            has_metrics_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "health metrics by project" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_metrics_h2:
                checks_passed.append("[C] heading_2 'Health Metrics by Project' found")
            else:
                errors.append("[X] Missing heading_2 'Health Metrics by Project'")

            has_risk_h2 = any(
                b.get("type") in ["heading2", "heading_2"] and "risk mitigation actions" in b.get("content", "").lower()
                for b in report_blocks
            )
            if has_risk_h2:
                checks_passed.append("[C] heading_2 'Risk Mitigation Actions' found")
            else:
                errors.append("[X] Missing heading_2 'Risk Mitigation Actions'")

            # Check for todos for risk mitigation
            todo_blocks = [b for b in report_blocks if b.get("type") in ["todo", "to_do", "checkbox"]]
            if len(todo_blocks) > 0:
                checks_passed.append(f"[C] Found {len(todo_blocks)} risk mitigation todos")
            else:
                errors.append("[X] Missing risk mitigation todos")
        else:
            errors.append("[X] Project Health Report page not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(score=0.0, metadata=ScoreMetadata(success_accumulator=checks_passed, error_accumulator=errors))

# =============================================================================
# Sprint Performance Tracker Task
# =============================================================================
def _validate_agent_sprint_performance_tracker(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a comprehensive 'Sprint Performance Tracker' database as a child page of the
    'Engineering Wiki' page. Configure it with table layout and these exact properties in order:
    'Sprint Name' (title), 'Start Date' (date), 'End Date' (date), 'Total Tasks' (number),
    'Completed Tasks' (number), 'Team Size' (number), 'Velocity' (number), 'Sprint Status'
    (select with options: 'Planning' gray, 'Active' blue, 'Review' yellow, 'Completed' green,
    'Cancelled' red), 'Sprint Goals' (text). After creating the database, query the 'Task Tracker'
    database to aggregate data from completed tasks (Status='Done') with Due Date between
    2025-12-01 and 2026-01-12. Create a Sprint Performance Tracker row titled 'December Sprint'
    with: Start Date='2025-12-01', End Date='2026-01-12', Total Tasks=[count all tasks in date range],
    Completed Tasks=[count of Done tasks], Team Size=3, Velocity=[Completed Tasks * 2],
    Sprint Status='Completed', Sprint Goals='Complete authentication module, fix critical bugs'.
    Then add a second row for 'January Sprint' with Start Date='2026-01-13', End Date='2026-02-12',
    Sprint Status='Active', and other fields empty.

    Expected values from initial data:
    - Tasks in date range (2025-12-01 to 2026-01-12): 2
    - Done tasks in that range: 2
    - Total Tasks = 2, Completed Tasks = 2, Velocity = 4
    """
    checks_passed: list[str] = []

    errors = []

    # Step 1: Find the Sprint Performance Tracker database with correct parent
    # Engineering Wiki is page-4
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        sprint_tracker_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "sprint performance tracker" in title:
                    sprint_tracker_db = page
                    break

        if not sprint_tracker_db:
            errors.append("[X] Database 'Sprint Performance Tracker' not found")
        else:
            checks_passed.append("[C] Database 'Sprint Performance Tracker'")

        # Check parent is Engineering Wiki (page-4)
        parent_id = sprint_tracker_db.get("parentId")
        if parent_id != "page-4":
            # Also check if parent has title "Engineering Wiki"
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "engineering wiki" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Engineering Wiki' (page-4), got parentId: {parent_id}")

        # Step 2: Validate database layout is table
        db_metadata = sprint_tracker_db.get("databaseMetadata", {})
        layout = db_metadata.get("layout", "")
        if layout != "table":
            errors.append(f"Database layout should be 'table', got '{layout}'")

        # Step 3: Validate database properties - types and order
        properties = db_metadata.get("properties", [])
        expected_properties = [
            ("Sprint Name", "title", 0),
            ("Start Date", "date", 1),
            ("End Date", "date", 2),
            ("Total Tasks", "number", 3),
            ("Completed Tasks", "number", 4),
            ("Team Size", "number", 5),
            ("Velocity", "number", 6),
            ("Sprint Status", "select", 7),
            ("Sprint Goals", "text", 8),
        ]

        if len(properties) < len(expected_properties):
            errors.append(f"Expected at least {len(expected_properties)} properties, got {len(properties)}")
        else:
            for expected_name, expected_type, expected_order in expected_properties:
                found = False
                for prop in properties:
                    prop_name = prop.get("name", "")
                    # Case-insensitive comparison
                    if prop_name.lower() == expected_name.lower():
                        found = True
                        # Check type
                        if prop.get("type") != expected_type:
                            errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{prop.get('type')}'")
                        # Check order
                        if prop.get("order") != expected_order:
                            errors.append(f"Property '{expected_name}' should have order {expected_order}, got {prop.get('order')}")
                        break
                if not found:
                    errors.append(f"Property '{expected_name}' not found in database")

        # Step 4: Validate Sprint Status select options
        sprint_status_prop = None
        for prop in properties:
            if prop.get("name", "").lower() == "sprint status":
                sprint_status_prop = prop
                break

        if sprint_status_prop:
            options = sprint_status_prop.get("options", [])
            expected_options = {
                "planning": "gray",
                "active": "blue",
                "review": "yellow",
                "completed": "green",
                "cancelled": "red",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Sprint Status missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Sprint Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")
        else:
            errors.append("Sprint Status property not found")

        # Step 5: Find and validate rows in the Sprint Performance Tracker database
        db_id = sprint_tracker_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        # Find December Sprint row
        december_sprint = None
        january_sprint = None
        for row in rows:
            title = row.get("title", "").lower()
            if "december sprint" in title:
                december_sprint = row
            elif "january sprint" in title:
                january_sprint = row

        # Validate December Sprint row
        if not december_sprint:
            errors.append("Row 'December Sprint' not found in Sprint Performance Tracker")
        else:
            cell_values = december_sprint.get("cellValues", {})

            # We need to find property IDs by name to check cell values
            prop_id_map = {}
            for prop in properties:
                prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

            # Helper to get cell value by property name
            def get_cell_value(prop_name_lower):
                prop_id = prop_id_map.get(prop_name_lower)
                if prop_id:
                    return cell_values.get(prop_id)
                # Also try matching by property name directly in cell_values
                for key, val in cell_values.items():
                    if prop_name_lower in key.lower():
                        return val
                return None

            # Check Start Date = 2025-12-01
            start_date = get_cell_value("start date")
            if start_date:
                start_val = start_date.get("start", "") if isinstance(start_date, dict) else str(start_date)
                if "2025-12-01" not in str(start_val):
                    errors.append(f"December Sprint Start Date should be '2025-12-01', got '{start_val}'")
            else:
                errors.append("December Sprint missing Start Date")

            # Check End Date = 2026-01-12
            end_date = get_cell_value("end date")
            if end_date:
                end_val = end_date.get("start", "") if isinstance(end_date, dict) else str(end_date)
                if "2026-01-12" not in str(end_val):
                    errors.append(f"December Sprint End Date should be '2026-01-12', got '{end_val}'")
            else:
                errors.append("December Sprint missing End Date")

            # Check Total Tasks = 2
            total_tasks = get_cell_value("total tasks")
            if total_tasks is not None:
                if int(total_tasks) != 2:
                    errors.append(f"December Sprint Total Tasks should be 2, got {total_tasks}")
            else:
                errors.append("December Sprint missing Total Tasks")

            # Check Completed Tasks = 2
            completed_tasks = get_cell_value("completed tasks")
            if completed_tasks is not None:
                if int(completed_tasks) != 2:
                    errors.append(f"December Sprint Completed Tasks should be 2, got {completed_tasks}")
            else:
                errors.append("December Sprint missing Completed Tasks")

            # Check Team Size = 3
            team_size = get_cell_value("team size")
            if team_size is not None:
                if int(team_size) != 3:
                    errors.append(f"December Sprint Team Size should be 3, got {team_size}")
            else:
                errors.append("December Sprint missing Team Size")

            # Check Velocity = 4 (Completed Tasks * 2 = 2 * 2 = 4)
            velocity = get_cell_value("velocity")
            if velocity is not None:
                if int(velocity) != 4:
                    errors.append(f"December Sprint Velocity should be 4, got {velocity}")
            else:
                errors.append("December Sprint missing Velocity")

            # Check Sprint Status = Completed
            sprint_status = get_cell_value("sprint status")
            if sprint_status:
                if "completed" not in str(sprint_status).lower():
                    errors.append(f"December Sprint Status should be 'Completed', got '{sprint_status}'")
            else:
                errors.append("December Sprint missing Sprint Status")

            # Check Sprint Goals contains required text
            sprint_goals = get_cell_value("sprint goals")
            if sprint_goals:
                goals_lower = str(sprint_goals).lower()
                if "authentication" not in goals_lower or "module" not in goals_lower:
                    errors.append("December Sprint Goals should mention 'authentication module'")
                if "critical" not in goals_lower or "bugs" not in goals_lower:
                    errors.append("December Sprint Goals should mention 'critical bugs'")
            else:
                errors.append("December Sprint missing Sprint Goals")

        # Step 6: Validate January Sprint row
        if not january_sprint:
            errors.append("Row 'January Sprint' not found in Sprint Performance Tracker")
        else:
            cell_values = january_sprint.get("cellValues", {})

            # Helper to get cell value by property name
            def get_cell_value_jan(prop_name_lower):
                prop_id = prop_id_map.get(prop_name_lower) if 'prop_id_map' in dir() else None
                if prop_id:
                    return cell_values.get(prop_id)
                # Also try matching by property name directly in cell_values
                for key, val in cell_values.items():
                    if prop_name_lower in key.lower():
                        return val
                return None

            # Rebuild prop_id_map if needed
            prop_id_map = {}
            for prop in properties:
                prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

            def get_jan_cell(prop_name_lower):
                prop_id = prop_id_map.get(prop_name_lower)
                if prop_id:
                    return cell_values.get(prop_id)
                for key, val in cell_values.items():
                    if prop_name_lower in key.lower():
                        return val
                return None

            # Check Start Date = 2026-01-13
            start_date = get_jan_cell("start date")
            if start_date:
                start_val = start_date.get("start", "") if isinstance(start_date, dict) else str(start_date)
                if "2026-01-13" not in str(start_val):
                    errors.append(f"January Sprint Start Date should be '2026-01-13', got '{start_val}'")
            else:
                errors.append("January Sprint missing Start Date")

            # Check End Date = 2026-02-12
            end_date = get_jan_cell("end date")
            if end_date:
                end_val = end_date.get("start", "") if isinstance(end_date, dict) else str(end_date)
                if "2026-02-12" not in str(end_val):
                    errors.append(f"January Sprint End Date should be '2026-02-12', got '{end_val}'")
            else:
                errors.append("January Sprint missing End Date")

            # Check Sprint Status = Active
            sprint_status = get_jan_cell("sprint status")
            if sprint_status:
                if "active" not in str(sprint_status).lower():
                    errors.append(f"January Sprint Status should be 'Active', got '{sprint_status}'")
            else:
                errors.append("January Sprint missing Sprint Status")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# High Priority Task Report
# =============================================================================
def _validate_agent_high_priority_task_report(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Navigate to the 'Task Tracker' database and analyze all rows where the 'Priority'
    select property is 'Urgent' or 'High' and the 'Status' is NOT 'Done'. For each matching row,
    update the 'Due Date' to 3 days from today if it's currently set to a date more than a week away.
    Then create a new page titled '🚨 High Priority Task Report' as a child of the 'Project Roadmap'
    page. Structure the page with: heading_1 '🚨 High Priority Task Dashboard', followed by a callout
    with red background and text '⚠️ Critical Tasks Requiring Immediate Attention'. Add heading_2
    '📊 Task Statistics' with a paragraph showing 'Total High/Urgent Tasks: [X]' and 'Overdue
    High/Urgent Tasks: [Y]' where Y is the count of tasks with Due Date before today. Under heading_2
    '📋 Task Details by Priority', create heading_3 '🔴 Urgent Tasks' followed by a numbered list of
    urgent task names with assignee in format '1. [Task Name] - Assigned to: [Assignee]'. Then add
    heading_3 '🟠 High Priority Tasks' with the same numbered list format. Finally, add a divider
    and a paragraph with italic text '*Report generated on [today's date]*'.

    Expected values from initial data (assuming today = 2026-01-14):
    - Total High/Urgent Tasks (not Done): 2
      - row-1: "Fix login authentication bug" - High, In Progress, Assignee: Alice Chen (user-1)
      - row-5: "Update database schema" - Urgent, Blocked, Assignee: Bob Martinez (user-2)
    - Overdue Tasks (Due Date < 2026-01-14): 1
      - row-5: Due 2026-01-13 (overdue)
    - Urgent tasks: 1 (Update database schema - Bob Martinez)
    - High Priority tasks: 1 (Fix login authentication bug - Alice Chen)
    - No due date updates needed (none > 7 days away)
    """
    checks_passed: list[str] = []

    errors = []

    # Step 1: Find the High Priority Task Report page
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        report_page = None
        for page in all_pages:
            title = page.get("title", "")
            if "high priority task report" in title.lower():
                report_page = page
                break

        if not report_page:
            errors.append("[X] Page 'High Priority Task Report' not found")
        else:
            checks_passed.append("[C] Page 'High Priority Task Report'")

        # Step 2: Check page is child of Project Roadmap (page-2)
        parent_id = report_page.get("parentId")
        if parent_id != "page-2":
            # Also check if parent has title "Project Roadmap"
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "project roadmap" not in parent_page.get("title", "").lower():
                errors.append(f"Report page should be child of 'Project Roadmap' (page-2), got parentId: {parent_id}")

        # Step 3: Check page title contains emoji
        page_title = report_page.get("title", "")
        if "🚨" not in page_title:
            errors.append("Page title should contain '🚨' emoji")

        # Step 4: Get all blocks for the report page
        report_page_id = report_page.get("_id")
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": report_page_id}})
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0))

        # Step 5: Check heading_1 - Dashboard Title
        heading1_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading1", "heading_1"]:
                content = block.get("content", "")
                if "high priority task dashboard" in content.lower() and "🚨" in content:
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("heading_1 '🚨 High Priority Task Dashboard' not found")

        # Step 6: Check red callout with warning text
        callout_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type == "callout":
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", block.get("backgroundColor", ""))
                content = block.get("content", "")

                has_red_bg = "red" in str(bg_color).lower() if bg_color else False
                has_warning_icon = "⚠️" in content
                has_warning_text = "critical tasks" in content.lower() and "immediate attention" in content.lower()

                if has_red_bg and has_warning_icon and has_warning_text:
                    callout_found = True
                    break

        if not callout_found:
            errors.append("Red callout with '⚠️ Critical Tasks Requiring Immediate Attention' not found")

        # Step 7: Check heading_2 - Task Statistics
        stats_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "task statistics" in content.lower() and "📊" in content:
                    stats_heading_found = True
                    break

        if not stats_heading_found:
            errors.append("heading_2 '📊 Task Statistics' not found")

        # Step 8: Check statistics paragraph - Total Count
        total_count_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["paragraph", "text"]:
                content = block.get("content", "")
                if "total high/urgent tasks" in content.lower() or "total" in content.lower() and ("high" in content.lower() or "urgent" in content.lower()):
                    # Check for number 2
                    if "2" in content:
                        total_count_found = True
                        break

        if not total_count_found:
            errors.append("Statistics paragraph with 'Total High/Urgent Tasks: 2' not found")

        # Step 9: Check statistics paragraph - Overdue Count
        overdue_count_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["paragraph", "text"]:
                content = block.get("content", "")
                if "overdue" in content.lower():
                    # Check for number 1
                    if "1" in content:
                        overdue_count_found = True
                        break

        if not overdue_count_found:
            errors.append("Statistics paragraph with 'Overdue High/Urgent Tasks: 1' not found")

        # Step 10: Check heading_2 - Task Details by Priority
        details_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "task details" in content.lower() and "priority" in content.lower() and "📋" in content:
                    details_heading_found = True
                    break

        if not details_heading_found:
            errors.append("heading_2 '📋 Task Details by Priority' not found")

        # Step 11: Check heading_3 - Urgent Tasks
        urgent_heading_found = False
        urgent_heading_idx = -1
        for i, block in enumerate(sorted_blocks):
            block_type = block.get("type", "")
            if block_type in ["heading3", "heading_3"]:
                content = block.get("content", "")
                if "urgent" in content.lower() and "🔴" in content:
                    urgent_heading_found = True
                    urgent_heading_idx = i
                    break

        if not urgent_heading_found:
            errors.append("heading_3 '🔴 Urgent Tasks' not found")

        # Step 12: Check numbered list for Urgent tasks
        urgent_list_found = False
        for i, block in enumerate(sorted_blocks):
            if i <= urgent_heading_idx:
                continue
            block_type = block.get("type", "")
            if block_type in ["numberedList", "numbered_list", "numbered_list_item", "numbered"]:
                content = block.get("content", "")
                # Should contain "Update database schema" and "Bob Martinez" or "Assigned to"
                if "update database schema" in content.lower():
                    if "bob martinez" in content.lower() or "assigned to" in content.lower():
                        urgent_list_found = True
                        break

        if not urgent_list_found:
            errors.append("Numbered list with 'Update database schema - Assigned to: Bob Martinez' not found under Urgent Tasks")

        # Step 13: Check heading_3 - High Priority Tasks
        high_heading_found = False
        high_heading_idx = -1
        for i, block in enumerate(sorted_blocks):
            block_type = block.get("type", "")
            if block_type in ["heading3", "heading_3"]:
                content = block.get("content", "")
                if "high priority" in content.lower() and "🟠" in content:
                    high_heading_found = True
                    high_heading_idx = i
                    break

        if not high_heading_found:
            errors.append("heading_3 '🟠 High Priority Tasks' not found")

        # Step 14: Check numbered list for High Priority tasks
        high_list_found = False
        for i, block in enumerate(sorted_blocks):
            if i <= high_heading_idx:
                continue
            block_type = block.get("type", "")
            if block_type in ["numberedList", "numbered_list", "numbered_list_item", "numbered"]:
                content = block.get("content", "")
                # Should contain "Fix login authentication bug" and "Alice Chen" or "Assigned to"
                if "fix login authentication bug" in content.lower() or "login" in content.lower():
                    if "alice chen" in content.lower() or "assigned to" in content.lower():
                        high_list_found = True
                        break

        if not high_list_found:
            errors.append("Numbered list with 'Fix login authentication bug - Assigned to: Alice Chen' not found under High Priority Tasks")

        # Step 15: Check divider block
        divider_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type == "divider":
                divider_found = True
                break

        if not divider_found:
            errors.append("Divider block not found")

        # Step 16: Check generated date paragraph with italic
        generated_paragraph_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["paragraph", "text"]:
                content = block.get("content", "")
                properties = block.get("properties", {})

                if "report generated" in content.lower():
                    # Check for italic formatting - either markdown (*text*) or property
                    has_italic_markdown = re.search(r'\*[^*]+\*', content) is not None
                    is_italic_property = properties.get("italic", False)

                    if has_italic_markdown or is_italic_property:
                        generated_paragraph_found = True
                        break
                    else:
                        # Accept even without italic if content is correct
                        generated_paragraph_found = True
                        break

        if not generated_paragraph_found:
            errors.append("Paragraph with 'Report generated on [date]' not found")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Tracker Database Task
# =============================================================================
def _validate_agent_sprint_tracker_database(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a new database titled '🎯 Sprint Tracker' as a child of the 'Engineering Wiki' page
    with table layout. Configure properties: 'Sprint Name' (title), 'Start Date' (date), 'End Date' (date),
    'Status' (select with options 'Planning' gray, 'Active' blue, 'Completed' green, 'Cancelled' red),
    'Team' (multi_select with options 'Frontend' orange, 'Backend' purple, 'Mobile' pink, 'DevOps' brown,
    'QA' yellow), 'Sprint Goal' (text), 'Completion Rate' (number), 'Retrospective Complete' (checkbox).
    Add 5 initial rows: 'Sprint 2024-W1' (Start: 2024-01-01, End: 2024-01-14, Status: 'Completed',
    Team: ['Frontend', 'Backend'], Goal: 'Launch auth system', Rate: 95, Retro: true),
    'Sprint 2024-W3' (Start: 2024-01-15, End: 2024-01-28, Status: 'Completed', Team: ['Frontend', 'Mobile'],
    Goal: 'Mobile MVP release', Rate: 87, Retro: true), 'Sprint 2024-W5' (Start: 2024-01-29, End: 2024-02-11,
    Status: 'Active', Team: ['Backend', 'DevOps'], Goal: 'Performance optimization', Rate: 45, Retro: false),
    'Sprint 2024-W7' (Start: 2024-02-12, End: 2024-02-25, Status: 'Planning', Team: ['Frontend', 'QA'],
    Goal: 'Testing framework setup', Rate: 0, Retro: false), 'Sprint 2024-W9' (Start: 2024-02-26,
    End: 2024-03-10, Status: 'Planning', Team: ['Mobile', 'Backend'], Goal: 'Offline mode support',
    Rate: 0, Retro: false).

    Validation checks:
    1. Database exists with title containing "Sprint Tracker"
    2. Database is child of "Engineering Wiki" (page-4)
    3. Layout is "table"
    4. All 8 properties exist with correct types
    5. Status select has 4 options with correct colors
    6. Team multi_select has 5 options with correct colors
    7. 5 rows exist with correct data
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # Step 1: Find the Sprint Tracker database
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        sprint_tracker_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "")
                if "sprint tracker" in title.lower():
                    sprint_tracker_db = page
                    break

        if not sprint_tracker_db:
            errors.append("[X] Database 'Sprint Tracker' not found")
        else:
            checks_passed.append("[C] Database 'Sprint Tracker'")

        # Step 2: Check parent is Engineering Wiki (page-4)
        parent_id = sprint_tracker_db.get("parentId")
        if parent_id != "page-4":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "engineering wiki" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Engineering Wiki' (page-4), got parentId: {parent_id}")

        # Step 3: Validate database layout is table
        db_metadata = sprint_tracker_db.get("databaseMetadata", {})
        layout = db_metadata.get("layout", "")
        if layout != "table":
            errors.append(f"Database layout should be 'table', got '{layout}'")

        # Step 4: Validate database properties exist with correct types
        properties = db_metadata.get("properties", [])
        expected_properties = {
            "sprint name": "title",
            "start date": "date",
            "end date": "date",
            "status": "select",
            "team": "multi_select",
            "sprint goal": "text",
            "completion rate": "number",
            "retrospective complete": "checkbox",
        }

        prop_name_to_obj = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_name_to_obj[prop_name_lower] = prop

        for expected_name, expected_type in expected_properties.items():
            if expected_name not in prop_name_to_obj:
                errors.append(f"Property '{expected_name}' not found in database")
            else:
                actual_type = prop_name_to_obj[expected_name].get("type", "")
                if actual_type != expected_type:
                    errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{actual_type}'")

        # Step 5: Validate Status select options
        status_prop = prop_name_to_obj.get("status")
        if status_prop:
            options = status_prop.get("options", [])
            expected_status_options = {
                "planning": "gray",
                "active": "blue",
                "completed": "green",
                "cancelled": "red",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_status_options.items():
                if exp_name not in found_options:
                    errors.append(f"Status missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 6: Validate Team multi_select options
        team_prop = prop_name_to_obj.get("team")
        if team_prop:
            options = team_prop.get("options", [])
            expected_team_options = {
                "frontend": "orange",
                "backend": "purple",
                "mobile": "pink",
                "devops": "brown",
                "qa": "yellow",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_team_options.items():
                if exp_name not in found_options:
                    errors.append(f"Team missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Team option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 7: Find and validate rows
        db_id = sprint_tracker_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        if len(rows) < 5:
            errors.append(f"Expected 5 rows, found {len(rows)}")

        # Build property ID map for cell value lookup
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            """Get cell value by property name from a row."""
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Fallback: try matching by property name in cell_values keys
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        def get_date_str(date_val):
            """Extract date string from date value."""
            if isinstance(date_val, dict):
                return date_val.get("start", "")
            return str(date_val) if date_val else ""

        # Expected row data
        expected_rows = [
            {
                "title": "sprint 2024-w1",
                "start_date": "2024-01-01",
                "end_date": "2024-01-14",
                "status": "completed",
                "team": ["frontend", "backend"],
                "goal": "launch auth system",
                "rate": 95,
                "retro": True,
            },
            {
                "title": "sprint 2024-w3",
                "start_date": "2024-01-15",
                "end_date": "2024-01-28",
                "status": "completed",
                "team": ["frontend", "mobile"],
                "goal": "mobile mvp release",
                "rate": 87,
                "retro": True,
            },
            {
                "title": "sprint 2024-w5",
                "start_date": "2024-01-29",
                "end_date": "2024-02-11",
                "status": "active",
                "team": ["backend", "devops"],
                "goal": "performance optimization",
                "rate": 45,
                "retro": False,
            },
            {
                "title": "sprint 2024-w7",
                "start_date": "2024-02-12",
                "end_date": "2024-02-25",
                "status": "planning",
                "team": ["frontend", "qa"],
                "goal": "testing framework setup",
                "rate": 0,
                "retro": False,
            },
            {
                "title": "sprint 2024-w9",
                "start_date": "2024-02-26",
                "end_date": "2024-03-10",
                "status": "planning",
                "team": ["mobile", "backend"],
                "goal": "offline mode support",
                "rate": 0,
                "retro": False,
            },
        ]

        # Validate each expected row
        for expected in expected_rows:
            row_found = None
            for row in rows:
                row_title = row.get("title", "").lower()
                if expected["title"] in row_title:
                    row_found = row
                    break

            if not row_found:
                errors.append(f"Row '{expected['title']}' not found")
                continue

            row_title_display = expected["title"].replace("sprint ", "Sprint ").replace("-w", "-W")

            # Check Start Date
            start_date = get_cell_value(row_found, "start date")
            start_str = get_date_str(start_date)
            if expected["start_date"] not in start_str:
                errors.append(f"{row_title_display}: Start Date should be '{expected['start_date']}', got '{start_str}'")

            # Check End Date
            end_date = get_cell_value(row_found, "end date")
            end_str = get_date_str(end_date)
            if expected["end_date"] not in end_str:
                errors.append(f"{row_title_display}: End Date should be '{expected['end_date']}', got '{end_str}'")

            # Check Status
            status = get_cell_value(row_found, "status")
            if status:
                if expected["status"] not in str(status).lower():
                    errors.append(f"{row_title_display}: Status should be '{expected['status']}', got '{status}'")
            else:
                errors.append(f"{row_title_display}: Status is missing")

            # Check Team (multi_select)
            team = get_cell_value(row_found, "team")
            if team:
                if isinstance(team, list):
                    team_lower = [t.lower() for t in team]
                else:
                    team_lower = [str(team).lower()]
                for expected_team in expected["team"]:
                    if expected_team not in team_lower:
                        errors.append(f"{row_title_display}: Team should include '{expected_team}'")
            else:
                errors.append(f"{row_title_display}: Team is missing")

            # Check Sprint Goal
            goal = get_cell_value(row_found, "sprint goal")
            if goal:
                if expected["goal"] not in str(goal).lower():
                    errors.append(f"{row_title_display}: Sprint Goal should contain '{expected['goal']}', got '{goal}'")
            else:
                errors.append(f"{row_title_display}: Sprint Goal is missing")

            # Check Completion Rate
            rate = get_cell_value(row_found, "completion rate")
            if rate is not None:
                try:
                    if int(rate) != expected["rate"]:
                        errors.append(f"{row_title_display}: Completion Rate should be {expected['rate']}, got {rate}")
                except (ValueError, TypeError):
                    errors.append(f"{row_title_display}: Completion Rate should be {expected['rate']}, got '{rate}'")
            else:
                errors.append(f"{row_title_display}: Completion Rate is missing")

            # Check Retrospective Complete
            retro = get_cell_value(row_found, "retrospective complete")
            if retro is not None:
                retro_bool = retro if isinstance(retro, bool) else str(retro).lower() in ["true", "1", "yes"]
                if retro_bool != expected["retro"]:
                    errors.append(f"{row_title_display}: Retrospective Complete should be {expected['retro']}, got {retro}")
            else:
                errors.append(f"{row_title_display}: Retrospective Complete is missing")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Performance Metrics Database Task
# =============================================================================
def _validate_agent_sprint_performance_metrics(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a new database titled 'Sprint Performance Metrics' as a child page of the 'Project Portfolio' database. Configure with table layout and these exact properties: 'Sprint Name' (title), 'Start Date' (date), 'End Date' (date), 'Team' (select with options 'Frontend' in blue, 'Backend' in green, 'Mobile' in orange, 'QA' in purple, 'DevOps' in gray), 'Story Points Planned' (number), 'Story Points Completed' (number), 'Velocity' (number), 'Sprint Goals Met' (checkbox), 'Blockers' (text), 'Notes' (text). Then query the 'Task Tracker' database and for each unique combination of Assignee and Tags containing 'Backend', create a row in the new database with: Sprint Name='Backend Sprint - [ASSIGNEE NAME]', Start Date='2026-01-13', End Date='2026-01-27', Team='Backend', Story Points Planned=20, Story Points Completed=0, Velocity=0, Sprint Goals Met=false, Blockers='Pending task allocation'. After creating all rows, add a page property description to the database: 'Track sprint performance metrics across all teams to identify bottlenecks and improve velocity.'

    Expected values from initial data:
    - Project Portfolio database: db-projects
    - Task Tracker rows with Backend tag:
      - row-1: user-1 (Alice Chen) - [Bug, Backend]
      - row-3: user-3 (Carol Williams) - [Documentation, Backend]
      - row-4: user-1 (Alice Chen) - [Feature, Backend, Frontend]
      - row-5: user-2 (Bob Martinez) - [Backend]
      - row-8: user-2 (Bob Martinez) - [Backend]
    - Unique assignees with Backend: Alice Chen, Bob Martinez, Carol Williams (3 users)
    - Expected rows: 3 (one per unique assignee)
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # Step 1: Find the Sprint Performance Metrics database
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        sprint_metrics_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "sprint performance metrics" in title:
                    sprint_metrics_db = page
                    break

        if not sprint_metrics_db:
            errors.append("[X] Database 'Sprint Performance Metrics' not found")
        else:
            checks_passed.append("[C] Database 'Sprint Performance Metrics'")

        # Step 2: Check parent is Project Portfolio (db-projects)
        parent_id = sprint_metrics_db.get("parentId")
        if parent_id != "db-projects":
            # Also check if parent has title "Project Portfolio"
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "project portfolio" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Project Portfolio' (db-projects), got parentId: {parent_id}")

        # Step 3: Validate database layout is table
        db_metadata = sprint_metrics_db.get("databaseMetadata", {})
        layout = db_metadata.get("layout", "")
        if layout != "table":
            errors.append(f"Database layout should be 'table', got '{layout}'")

        # Step 4: Validate database properties exist with correct types
        properties = db_metadata.get("properties", [])
        expected_properties = {
            "sprint name": "title",
            "start date": "date",
            "end date": "date",
            "team": "select",
            "story points planned": "number",
            "story points completed": "number",
            "velocity": "number",
            "sprint goals met": "checkbox",
            "blockers": "text",
            "notes": "text",
        }

        prop_name_to_obj = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_name_to_obj[prop_name_lower] = prop

        for expected_name, expected_type in expected_properties.items():
            if expected_name not in prop_name_to_obj:
                errors.append(f"Property '{expected_name}' not found in database")
            else:
                actual_type = prop_name_to_obj[expected_name].get("type", "")
                if actual_type != expected_type:
                    errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{actual_type}'")

        # Step 5: Validate Team select options with correct colors
        team_prop = prop_name_to_obj.get("team")
        if team_prop:
            options = team_prop.get("options", [])
            expected_team_options = {
                "frontend": "blue",
                "backend": "green",
                "mobile": "orange",
                "qa": "purple",
                "devops": "gray",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_team_options.items():
                if exp_name not in found_options:
                    errors.append(f"Team missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Team option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")
        else:
            errors.append("Team property not found")

        # Step 6: Validate database description
        db_description = db_metadata.get("description", "")
        expected_desc_keywords = ["sprint performance metrics", "bottlenecks", "velocity"]
        desc_lower = db_description.lower()
        for keyword in expected_desc_keywords:
            if keyword not in desc_lower:
                errors.append(f"Database description should contain '{keyword}'")
                break

        # Step 7: Find and validate rows - should have 3 rows (one per unique assignee with Backend tag)
        db_id = sprint_metrics_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        # Expected assignees with Backend tasks
        expected_assignees = ["alice chen", "bob martinez", "carol williams"]

        if len(rows) < 3:
            errors.append(f"Expected at least 3 rows (one per unique Backend assignee), found {len(rows)}")

        # Build property ID map for cell value lookup
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            """Get cell value by property name from a row."""
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Fallback: try matching by property name in cell_values keys
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        def get_date_str(date_val):
            """Extract date string from date value."""
            if isinstance(date_val, dict):
                return date_val.get("start", "")
            return str(date_val) if date_val else ""

        # Validate each expected row by assignee name
        for assignee in expected_assignees:
            row_found = None
            for row in rows:
                row_title = row.get("title", "").lower()
                # Should match "Backend Sprint - [Assignee Name]"
                if "backend sprint" in row_title and assignee in row_title:
                    row_found = row
                    break

            if not row_found:
                errors.append(f"Row 'Backend Sprint - {assignee.title()}' not found")
                continue

            assignee_display = assignee.title()

            # Check Start Date = 2026-01-13
            start_date = get_cell_value(row_found, "start date")
            start_str = get_date_str(start_date)
            if "2026-01-13" not in start_str:
                errors.append(f"Backend Sprint - {assignee_display}: Start Date should be '2026-01-13', got '{start_str}'")

            # Check End Date = 2026-01-27
            end_date = get_cell_value(row_found, "end date")
            end_str = get_date_str(end_date)
            if "2026-01-27" not in end_str:
                errors.append(f"Backend Sprint - {assignee_display}: End Date should be '2026-01-27', got '{end_str}'")

            # Check Team = Backend
            team = get_cell_value(row_found, "team")
            if team:
                if "backend" not in str(team).lower():
                    errors.append(f"Backend Sprint - {assignee_display}: Team should be 'Backend', got '{team}'")
            else:
                errors.append(f"Backend Sprint - {assignee_display}: Team is missing")

            # Check Story Points Planned = 20
            story_planned = get_cell_value(row_found, "story points planned")
            if story_planned is not None:
                try:
                    if int(story_planned) != 20:
                        errors.append(f"Backend Sprint - {assignee_display}: Story Points Planned should be 20, got {story_planned}")
                except (ValueError, TypeError):
                    errors.append(f"Backend Sprint - {assignee_display}: Story Points Planned should be 20, got '{story_planned}'")
            else:
                errors.append(f"Backend Sprint - {assignee_display}: Story Points Planned is missing")

            # Check Story Points Completed = 0
            story_completed = get_cell_value(row_found, "story points completed")
            if story_completed is not None:
                try:
                    if int(story_completed) != 0:
                        errors.append(f"Backend Sprint - {assignee_display}: Story Points Completed should be 0, got {story_completed}")
                except (ValueError, TypeError):
                    errors.append(f"Backend Sprint - {assignee_display}: Story Points Completed should be 0, got '{story_completed}'")
            else:
                errors.append(f"Backend Sprint - {assignee_display}: Story Points Completed is missing")

            # Check Velocity = 0
            velocity = get_cell_value(row_found, "velocity")
            if velocity is not None:
                try:
                    if int(velocity) != 0:
                        errors.append(f"Backend Sprint - {assignee_display}: Velocity should be 0, got {velocity}")
                except (ValueError, TypeError):
                    errors.append(f"Backend Sprint - {assignee_display}: Velocity should be 0, got '{velocity}'")
            else:
                errors.append(f"Backend Sprint - {assignee_display}: Velocity is missing")

            # Check Sprint Goals Met = false
            goals_met = get_cell_value(row_found, "sprint goals met")
            if goals_met is not None:
                goals_met_bool = goals_met if isinstance(goals_met, bool) else str(goals_met).lower() in ["true", "1", "yes"]
                if goals_met_bool != False:
                    errors.append(f"Backend Sprint - {assignee_display}: Sprint Goals Met should be false, got {goals_met}")
            else:
                errors.append(f"Backend Sprint - {assignee_display}: Sprint Goals Met is missing")

            # Check Blockers = 'Pending task allocation'
            blockers = get_cell_value(row_found, "blockers")
            if blockers:
                if "pending task allocation" not in str(blockers).lower():
                    errors.append(f"Backend Sprint - {assignee_display}: Blockers should contain 'Pending task allocation', got '{blockers}'")
            else:
                errors.append(f"Backend Sprint - {assignee_display}: Blockers is missing")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Progress Dashboard Task
# =============================================================================
def _validate_agent_sprint_progress_dashboard(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Navigate to the 'Task Tracker' database and analyze all existing tasks. Count tasks
    grouped by their Status property values, and calculate the completion percentage based on rows
    where Completed checkbox is true. Create a new page titled 'Sprint Progress Dashboard' as a
    child of the 'Project Roadmap' page. Structure it exactly as follows: Add a heading_1 block
    with text '🚀 Sprint Progress Dashboard', then add a callout block with green_background and
    text '✅ Overall Completion Rate: [X]%' where [X] is the percentage of rows with Completed=true.
    Add a heading_2 block with text '📊 Task Status Breakdown', followed by a bulleted list showing
    counts: 'Not Started: [count]', 'In Progress: [count]', 'In Review: [count]', 'Done: [count]',
    'Blocked: [count]'. Then add a heading_2 block with text '🔴 High Priority Items' and create
    a numbered list of all tasks where Priority is 'High' or 'Urgent' AND Status is NOT 'Done',
    showing format: '[Task Name] - Status: [status] - Due: [date]'. Finally, add a divider block
    and a paragraph with italic text: '*Dashboard last updated: [today's date and time]*'.

    Expected values from initial data:
    - Task Tracker database: db-tasks with 8 rows
    - Status counts: Not Started=2, In Progress=2, In Review=1, Done=2, Blocked=1
    - Completed checkbox true: 2 rows (row-4, row-8)
    - Completion Rate: 2/8 = 25%
    - High Priority Items (High/Urgent AND Status NOT Done): 2 tasks
      - row-1: "Fix login authentication bug" - In Progress - 2026-01-14 (High)
      - row-5: "Update database schema" - Blocked - 2026-01-13 (Urgent)
    """
    checks_passed: list[str] = []

    errors = []

    # Step 1: Find the Sprint Progress Dashboard page
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        dashboard_page = None
        for page in all_pages:
            title = page.get("title", "")
            if "sprint progress dashboard" in title.lower():
                dashboard_page = page
                break

        if not dashboard_page:
            errors.append("[X] Page 'Sprint Progress Dashboard' not found")
        else:
            checks_passed.append("[C] Page 'Sprint Progress Dashboard'")

        # Step 2: Check page is child of Project Roadmap (page-2)
        parent_id = dashboard_page.get("parentId")
        if parent_id != "page-2":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "project roadmap" not in parent_page.get("title", "").lower():
                errors.append(f"Dashboard page should be child of 'Project Roadmap' (page-2), got parentId: {parent_id}")

        # Step 3: Get all blocks for the dashboard page
        dashboard_page_id = dashboard_page.get("_id")
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": dashboard_page_id}})
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0))

        # Step 4: Check heading_1 - Dashboard Title
        heading1_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading1", "heading_1"]:
                content = block.get("content", "")
                if "sprint progress dashboard" in content.lower() and "🚀" in content:
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("heading_1 '🚀 Sprint Progress Dashboard' not found")

        # Step 5: Check green callout with completion rate
        callout_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type == "callout":
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", block.get("backgroundColor", ""))
                content = block.get("content", "")

                has_green_bg = "green" in str(bg_color).lower() if bg_color else False
                has_checkmark = "✅" in content
                has_completion_text = "overall completion rate" in content.lower()
                # Check for 25% (2/8 = 25%)
                has_correct_percentage = "25" in content

                if has_green_bg and has_checkmark and has_completion_text and has_correct_percentage:
                    callout_found = True
                    break

        if not callout_found:
            errors.append("Green callout with '✅ Overall Completion Rate: 25%' not found")

        # Step 6: Check heading_2 - Task Status Breakdown
        status_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "task status breakdown" in content.lower() and "📊" in content:
                    status_heading_found = True
                    break

        if not status_heading_found:
            errors.append("heading_2 '📊 Task Status Breakdown' not found")

        # Step 7: Check bulleted list with status counts
        # Expected: Not Started: 2, In Progress: 2, In Review: 1, Done: 2, Blocked: 1
        expected_counts = {
            "not started": "2",
            "in progress": "2",
            "in review": "1",
            "done": "2",
            "blocked": "1",
        }

        found_counts = {key: False for key in expected_counts}

        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["bulletedList", "bulleted_list", "bulleted_list_item", "bulleted"]:
                content = block.get("content", "").lower()
                for status_name, count in expected_counts.items():
                    if status_name in content and count in content:
                        found_counts[status_name] = True

        for status_name, found in found_counts.items():
            if not found:
                errors.append(f"Bulleted list item '{status_name.title()}: {expected_counts[status_name]}' not found")

        # Step 8: Check heading_2 - High Priority Items
        priority_heading_found = False
        priority_heading_idx = -1
        for i, block in enumerate(sorted_blocks):
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "high priority items" in content.lower() and "🔴" in content:
                    priority_heading_found = True
                    priority_heading_idx = i
                    break

        if not priority_heading_found:
            errors.append("heading_2 '🔴 High Priority Items' not found")

        # Step 9: Check numbered list with high priority tasks
        # Expected tasks:
        # - "Fix login authentication bug" - Status: In Progress - Due: 2026-01-14
        # - "Update database schema" - Status: Blocked - Due: 2026-01-13
        high_priority_tasks_found = {
            "fix login authentication bug": False,
            "update database schema": False,
        }

        for i, block in enumerate(sorted_blocks):
            block_type = block.get("type", "")
            if block_type in ["numberedList", "numbered_list", "numbered_list_item", "numbered"]:
                content = block.get("content", "").lower()

                # Check for Fix login authentication bug
                if "fix login authentication bug" in content or "login" in content:
                    if "in progress" in content or "status" in content:
                        high_priority_tasks_found["fix login authentication bug"] = True

                # Check for Update database schema
                if "update database schema" in content or "database schema" in content:
                    if "blocked" in content or "status" in content:
                        high_priority_tasks_found["update database schema"] = True

        for task_name, found in high_priority_tasks_found.items():
            if not found:
                errors.append(f"Numbered list item for '{task_name}' with status and due date not found")

        # Step 10: Check divider block
        divider_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type == "divider":
                divider_found = True
                break

        if not divider_found:
            errors.append("Divider block not found")

        # Step 11: Check paragraph with italic text for dashboard last updated
        updated_paragraph_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["paragraph", "text"]:
                content = block.get("content", "")
                properties = block.get("properties", {})

                if "dashboard last updated" in content.lower():
                    # Check for italic formatting - either markdown (*text*) or property
                    has_italic_markdown = re.search(r'\*[^*]+\*', content) is not None
                    is_italic_property = properties.get("italic", False)

                    if has_italic_markdown or is_italic_property:
                        updated_paragraph_found = True
                        break
                    else:
                        # Accept even without italic if content is correct
                        updated_paragraph_found = True
                        break

        if not updated_paragraph_found:
            errors.append("Paragraph with italic text 'Dashboard last updated: [date]' not found")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Cross-Database Task-Project Sync Task
# =============================================================================
def _validate_agent_cross_database_task_project_sync(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a cross-database synchronization between 'Task Tracker' and 'Project Portfolio'.
    First, add a new property 'Related Project' (text) to the Task Tracker database schema. Then for
    each task, determine its related project by matching: tasks with 'Frontend' or 'Design' tags
    belong to 'Website Redesign', tasks with 'Backend' tag belong to 'Customer Portal', tasks with
    'Documentation' tag belong to 'API Documentation' (which should be added as a new project row
    in Project Portfolio with Status='Planning', Department='Engineering', Budget=15000). Update
    each task's 'Related Project' field accordingly. Create a summary page titled '🔗 Task-Project
    Mapping Report' under 'Getting Started' with heading_1 '🔗 Task-Project Mapping Report', then
    for each project add heading_2 '[Project Name]' followed by a numbered list of its related
    tasks showing '[Task Title] ([Status]) - [Priority]'.

    Expected mappings based on initial data (priority: Documentation > Frontend/Design > Backend):
    - db-tasks-row-1: "Fix login authentication bug" [Bug, Backend] → Customer Portal
    - db-tasks-row-2: "Design new dashboard layout" [Design, Frontend] → Website Redesign
    - db-tasks-row-3: "Write API documentation" [Documentation, Backend] → API Documentation
    - db-tasks-row-4: "Implement user notifications" [Feature, Backend, Frontend] → Website Redesign
    - db-tasks-row-5: "Update database schema" [Backend] → Customer Portal
    - db-tasks-row-6: "Create onboarding flow" [Feature, Frontend, Design] → Website Redesign
    - db-tasks-row-7: "Fix mobile responsive issues" [Bug, Frontend] → Website Redesign
    - db-tasks-row-8: "Add unit tests for auth module" [Backend] → Customer Portal

    Summary:
    - Website Redesign: 4 tasks (rows 2, 4, 6, 7)
    - Customer Portal: 3 tasks (rows 1, 5, 8)
    - API Documentation: 1 task (row 3)
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # ==================== CHECK 1: Schema Update ====================
        # Verify Task Tracker database has 'Related Project' property of type 'text'

        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        task_tracker_db = None
        for page in all_pages:
            if page.get("_id") == "db-tasks" or (page.get("type") == "database" and "task tracker" in page.get("title", "").lower()):
                task_tracker_db = page
                break

        if not task_tracker_db:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")

        db_metadata = task_tracker_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        related_project_prop = None
        related_project_prop_id = None
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            if "related project" in prop_name or prop_name == "related project":
                related_project_prop = prop
                related_project_prop_id = prop.get("_id")
                break

        if not related_project_prop:
            errors.append("[X] Task Tracker database missing 'Related Project' property")
        else:
            checks_passed.append("[C] Task Tracker database has 'Related Project' property")

        if related_project_prop.get("type") != "text":
            errors.append(f"'Related Project' property should have type 'text', got '{related_project_prop.get('type')}'")

        # ==================== CHECK 2: New Project Row ====================
        # Verify 'API Documentation' project exists in Project Portfolio with correct values

        project_rows = []
        for page in all_pages:
            if page.get("databaseId") == "db-projects" and page.get("type") == "row":
                project_rows.append(page)

        api_doc_project = None
        for row in project_rows:
            title = row.get("title", "").lower()
            if "api documentation" in title:
                api_doc_project = row
                break

        if not api_doc_project:
            errors.append("[X] Project 'API Documentation' not found in Project Portfolio")
        else:
            checks_passed.append("[C] Project 'API Documentation' found in Project Portfolio")

        # Get Project Portfolio database to find property IDs
        project_portfolio_db = None
        for page in all_pages:
            if page.get("_id") == "db-projects":
                project_portfolio_db = page
                break

        project_props = project_portfolio_db.get("databaseMetadata", {}).get("properties", []) if project_portfolio_db else []
        project_prop_id_map = {}
        for prop in project_props:
            project_prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        cell_values = api_doc_project.get("cellValues", {})

        # Helper to get cell value by property name
        def get_project_cell_value(prop_name_lower):
            prop_id = project_prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            for key, value in cell_values.items():
                if prop_name_lower in key.lower():
                    return value
            return None

        # Check Status = 'Planning'
        status_value = get_project_cell_value("status")
        if not status_value or "planning" not in str(status_value).lower():
            errors.append(f"API Documentation project Status should be 'Planning', got '{status_value}'")

        # Check Department = 'Engineering'
        dept_value = get_project_cell_value("department")
        if not dept_value or "engineering" not in str(dept_value).lower():
            errors.append(f"API Documentation project Department should be 'Engineering', got '{dept_value}'")

        # Check Budget = 15000
        budget_value = get_project_cell_value("budget")
        if budget_value is None:
            errors.append("API Documentation project Budget is missing")
        else:
            try:
                if int(budget_value) != 15000:
                    errors.append(f"API Documentation project Budget should be 15000, got {budget_value}")
            except (ValueError, TypeError):
                errors.append(f"API Documentation project Budget should be 15000, got '{budget_value}'")

        # ==================== CHECK 3: Task Row Updates ====================
        # Verify each task has correct 'Related Project' value based on tag mapping

        task_rows = []
        for page in all_pages:
            if page.get("databaseId") == "db-tasks" and page.get("type") == "row":
                task_rows.append(page)

        # Expected mappings based on priority rules:
        # Documentation > Frontend/Design > Backend
        expected_mappings = {
            "fix login authentication bug": "customer portal",
            "design new dashboard layout": "website redesign",
            "write api documentation": "api documentation",
            "implement user notifications": "website redesign",
            "update database schema": "customer portal",
            "create onboarding flow": "website redesign",
            "fix mobile responsive issues": "website redesign",
            "add unit tests for auth module": "customer portal",
        }

        # Build property ID map for Task Tracker
        task_prop_id_map = {}
        for prop in properties:
            task_prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        tasks_validated = 0
        for task_row in task_rows:
            task_title = task_row.get("title", "").lower()

            if task_title not in expected_mappings:
                continue

            expected_project = expected_mappings[task_title]
            cell_values = task_row.get("cellValues", {})

            # Find the Related Project value
            actual_project = None
            if related_project_prop_id and related_project_prop_id in cell_values:
                actual_project = cell_values.get(related_project_prop_id)
            else:
                for key, value in cell_values.items():
                    if "related" in key.lower() and "project" in key.lower():
                        actual_project = value
                        break

            if actual_project is None:
                errors.append(f"Task '{task_title}' missing 'Related Project' value")
            elif expected_project not in str(actual_project).lower():
                errors.append(f"Task '{task_title}' Related Project should be '{expected_project}', got '{actual_project}'")
            else:
                tasks_validated += 1

        if tasks_validated < 8:
            errors.append(f"Expected 8 tasks with Related Project values, validated {tasks_validated}")

        # ==================== CHECK 4: Report Page Exists ====================
        # Verify page titled '🔗 Task-Project Mapping Report' exists under 'Getting Started' (page-1)

        report_page = None
        for page in all_pages:
            if page.get("type") not in ["database", "row"]:
                title = page.get("title", "")
                if "task-project mapping report" in title.lower() or ("🔗" in title and "mapping" in title.lower()):
                    report_page = page
                    break

        if not report_page:
            errors.append("[X] Page '🔗 Task-Project Mapping Report' not found")
        else:
            checks_passed.append("[C] Page '🔗 Task-Project Mapping Report'")

        # Check parent is 'Getting Started' (page-1)
        parent_id = report_page.get("parentId")
        if parent_id != "page-1":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "getting started" not in parent_page.get("title", "").lower():
                errors.append(f"Report page should be child of 'Getting Started' (page-1), got parentId: {parent_id}")

        # ==================== CHECK 5: Report has heading_1 ====================
        report_page_id = report_page.get("_id")
        report_content = report_page.get("content", [])

        # If content is empty, try to query blocks
        if not report_content:
            all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": report_page_id}})
            report_content = sorted(all_blocks, key=lambda b: b.get("order", 0)) if all_blocks else []

        heading1_found = False
        for block in report_content:
            block_type = block.get("type", "")
            content = block.get("content", "")
            if block_type in ["heading1", "heading_1"]:
                if "task-project mapping report" in content.lower() and "🔗" in content:
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("heading_1 '🔗 Task-Project Mapping Report' not found in report page")

        # ==================== CHECK 6: Report has heading_2 for each project ====================
        required_projects = ["website redesign", "customer portal", "api documentation"]
        found_project_headings = set()

        for block in report_content:
            block_type = block.get("type", "")
            content = block.get("content", "").lower()
            if block_type in ["heading2", "heading_2"]:
                for project in required_projects:
                    if project in content:
                        found_project_headings.add(project)

        for project in required_projects:
            if project not in found_project_headings:
                errors.append(f"heading_2 for '{project.title()}' not found in report page")

        # ==================== CHECK 7: Numbered lists for each project ====================
        # Expected tasks per project in format "[Task Title] ([Status]) - [Priority]"
        website_redesign_tasks = [
            ("design new dashboard layout", "in review", "medium"),
            ("implement user notifications", "done", "high"),
            ("create onboarding flow", "in progress", "medium"),
            ("fix mobile responsive issues", "not started", "medium"),
        ]
        customer_portal_tasks = [
            ("fix login authentication bug", "in progress", "high"),
            ("update database schema", "blocked", "urgent"),
            ("add unit tests for auth module", "done", "high"),
        ]
        api_doc_tasks = [
            ("write api documentation", "not started", "low"),
        ]

        all_expected_tasks = website_redesign_tasks + customer_portal_tasks + api_doc_tasks

        # Collect all numbered list items
        numbered_items = []
        for block in report_content:
            block_type = block.get("type", "")
            content = block.get("content", "").lower()
            if block_type in ["numberedList", "numbered_list", "numbered_list_item", "numbered"]:
                numbered_items.append(content)

        # Check that tasks appear in numbered lists with format pattern
        # Format: "[Task Title] ([Status]) - [Priority]"
        tasks_found_in_list = 0
        for task_name, status, priority in all_expected_tasks:
            for item in numbered_items:
                if task_name in item:
                    # Check for format pattern with status in parens and priority
                    # More lenient: just check if task name is in a numbered list
                    tasks_found_in_list += 1
                    break

        if tasks_found_in_list < 8:
            errors.append(f"Expected 8 tasks in numbered lists, found {tasks_found_in_list}")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Velocity Tracker Task
# =============================================================================
def _validate_agent_sprint_velocity_tracker(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Build a 'Sprint Velocity Tracker' by analyzing task completion patterns. Create a new
    database 'Sprint Metrics' under 'Weekly Standup Template' with properties: 'Week Ending' (date),
    'Tasks Completed' (number), 'Story Points' (number), 'Completion Rate %' (number), 'Top Contributor'
    (text), 'Blockers' (number), 'Week Status' (select: 'Above Target' green, 'On Target' blue,
    'Below Target' red). Analyze the Task Tracker database week by week for the past 4 weeks (counting
    back from today, 2026-01-12). For each week: count tasks where Status changed to 'Done', assign
    5 points for Urgent, 3 for High, 2 for Medium, 1 for Low priority, calculate completion rate as
    (Done / Total tasks that week * 100), identify the assignee who completed most tasks, count tasks
    with Status='Blocked'. Set Week Status based on completion rate (>70% Above Target, 50-70% On
    Target, <50% Below Target). Add a summary page section above the database with heading_2
    '📈 4-Week Velocity Trend' and a paragraph showing 'Average Weekly Completion: [X]% | Total
    Points Delivered: [Y]'.

    Expected values from initial data (analyzing by updatedAt timestamp):
    - Week 1 (2026-01-06 to 2026-01-12): 0 Done, 0 pts, 0%, 1 blocker, Below Target
    - Week 2 (2025-12-30 to 2026-01-05): 1 Done, 3 pts, 33%, 0 blockers, Below Target, Top: Alice Chen
    - Week 3 (2025-12-23 to 2025-12-29): 1 Done, 3 pts, 100%, 0 blockers, Above Target, Top: Bob Martinez
    - Week 4 (2025-12-16 to 2025-12-22): 0 Done, 0 pts, 0%, 0 blockers, Below Target
    - Summary: Average ~33% | Total Points: 6
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # Step 1: Find the Sprint Metrics database
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        sprint_metrics_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "sprint metrics" in title:
                    sprint_metrics_db = page
                    break

        if not sprint_metrics_db:
            errors.append("[X] Database 'Sprint Metrics' not found")
        else:
            checks_passed.append("[C] Database 'Sprint Metrics'")

        # Step 2: Check parent is Weekly Standup Template (page-9)
        parent_id = sprint_metrics_db.get("parentId")
        if parent_id != "page-9":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "weekly standup template" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Weekly Standup Template' (page-9), got parentId: {parent_id}")

        # Step 3: Validate database layout is table
        db_metadata = sprint_metrics_db.get("databaseMetadata", {})
        layout = db_metadata.get("layout", "")
        if layout != "table":
            errors.append(f"Database layout should be 'table', got '{layout}'")

        # Step 4: Validate database properties exist with correct types
        properties = db_metadata.get("properties", [])
        expected_properties = {
            "week ending": "date",
            "tasks completed": "number",
            "story points": "number",
            "completion rate %": "number",
            "top contributor": "text",
            "blockers": "number",
            "week status": "select",
        }

        prop_name_to_obj = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_name_to_obj[prop_name_lower] = prop

        for expected_name, expected_type in expected_properties.items():
            if expected_name not in prop_name_to_obj:
                errors.append(f"Property '{expected_name}' not found in database")
            else:
                actual_type = prop_name_to_obj[expected_name].get("type", "")
                if actual_type != expected_type:
                    errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{actual_type}'")

        # Step 5: Validate Week Status select options with correct colors
        week_status_prop = prop_name_to_obj.get("week status")
        if week_status_prop:
            options = week_status_prop.get("options", [])
            expected_options = {
                "above target": "green",
                "on target": "blue",
                "below target": "red",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Week Status missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Week Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")
        else:
            errors.append("Week Status property not found")

        # Step 6: Find and validate rows in the Sprint Metrics database
        db_id = sprint_metrics_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        if len(rows) < 4:
            errors.append(f"Expected 4 rows (one per week), found {len(rows)}")

        # Build property ID map for cell value lookup
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            """Get cell value by property name from a row."""
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        def get_date_str(date_val):
            """Extract date string from date value."""
            if isinstance(date_val, dict):
                return date_val.get("start", "")
            return str(date_val) if date_val else ""

        # Expected week data
        expected_weeks = [
            {
                "week_ending": "2026-01-12",
                "tasks_completed": 0,
                "story_points": 0,
                "completion_rate": 0,
                "blockers": 1,
                "week_status": "below target",
                "top_contributor": None,
            },
            {
                "week_ending": "2026-01-05",
                "tasks_completed": 1,
                "story_points": 3,
                "completion_rate": 33,
                "blockers": 0,
                "week_status": "below target",
                "top_contributor": "alice chen",
            },
            {
                "week_ending": "2025-12-29",
                "tasks_completed": 1,
                "story_points": 3,
                "completion_rate": 100,
                "blockers": 0,
                "week_status": "above target",
                "top_contributor": "bob martinez",
            },
            {
                "week_ending": "2025-12-22",
                "tasks_completed": 0,
                "story_points": 0,
                "completion_rate": 0,
                "blockers": 0,
                "week_status": "below target",
                "top_contributor": None,
            },
        ]

        # Validate each expected week row
        for expected in expected_weeks:
            row_found = None
            for row in rows:
                week_ending_val = get_cell_value(row, "week ending")
                week_ending_str = get_date_str(week_ending_val)
                if expected["week_ending"] in week_ending_str:
                    row_found = row
                    break

            if not row_found:
                errors.append(f"Row with Week Ending '{expected['week_ending']}' not found")
                continue

            week_display = expected["week_ending"]

            # Check Tasks Completed
            tasks_completed = get_cell_value(row_found, "tasks completed")
            if tasks_completed is not None:
                try:
                    if int(tasks_completed) != expected["tasks_completed"]:
                        errors.append(f"Week {week_display}: Tasks Completed should be {expected['tasks_completed']}, got {tasks_completed}")
                except (ValueError, TypeError):
                    errors.append(f"Week {week_display}: Tasks Completed should be {expected['tasks_completed']}, got '{tasks_completed}'")
            else:
                errors.append(f"Week {week_display}: Tasks Completed is missing")

            # Check Story Points
            story_points = get_cell_value(row_found, "story points")
            if story_points is not None:
                try:
                    if int(story_points) != expected["story_points"]:
                        errors.append(f"Week {week_display}: Story Points should be {expected['story_points']}, got {story_points}")
                except (ValueError, TypeError):
                    errors.append(f"Week {week_display}: Story Points should be {expected['story_points']}, got '{story_points}'")
            else:
                errors.append(f"Week {week_display}: Story Points is missing")

            # Check Completion Rate % (allow some tolerance for rounding)
            completion_rate = get_cell_value(row_found, "completion rate %")
            if completion_rate is not None:
                try:
                    rate_val = float(completion_rate)
                    expected_rate = expected["completion_rate"]
                    if expected_rate == 33:
                        if rate_val < 30 or rate_val > 35:
                            errors.append(f"Week {week_display}: Completion Rate % should be ~33%, got {rate_val}")
                    else:
                        if abs(rate_val - expected_rate) > 1:
                            errors.append(f"Week {week_display}: Completion Rate % should be {expected_rate}, got {rate_val}")
                except (ValueError, TypeError):
                    errors.append(f"Week {week_display}: Completion Rate % should be {expected['completion_rate']}, got '{completion_rate}'")
            else:
                errors.append(f"Week {week_display}: Completion Rate % is missing")

            # Check Blockers
            blockers = get_cell_value(row_found, "blockers")
            if blockers is not None:
                try:
                    if int(blockers) != expected["blockers"]:
                        errors.append(f"Week {week_display}: Blockers should be {expected['blockers']}, got {blockers}")
                except (ValueError, TypeError):
                    errors.append(f"Week {week_display}: Blockers should be {expected['blockers']}, got '{blockers}'")
            else:
                errors.append(f"Week {week_display}: Blockers is missing")

            # Check Week Status
            week_status = get_cell_value(row_found, "week status")
            if week_status:
                if expected["week_status"] not in str(week_status).lower():
                    errors.append(f"Week {week_display}: Week Status should be '{expected['week_status']}', got '{week_status}'")
            else:
                errors.append(f"Week {week_display}: Week Status is missing")

            # Check Top Contributor (only for weeks with completions)
            top_contributor = get_cell_value(row_found, "top contributor")
            if expected["top_contributor"]:
                if top_contributor:
                    if expected["top_contributor"] not in str(top_contributor).lower():
                        errors.append(f"Week {week_display}: Top Contributor should be '{expected['top_contributor']}', got '{top_contributor}'")
                else:
                    errors.append(f"Week {week_display}: Top Contributor is missing")

        # Step 7: Check summary section on page-9 (Weekly Standup Template)
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": "page-9"}})
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0))

        # Check for heading_2 '📈 4-Week Velocity Trend'
        velocity_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "4-week velocity trend" in content.lower() and "📈" in content:
                    velocity_heading_found = True
                    break

        if not velocity_heading_found:
            errors.append("heading_2 '📈 4-Week Velocity Trend' not found on Weekly Standup Template page")

        # Check for paragraph with summary stats
        summary_paragraph_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["paragraph", "text"]:
                content = block.get("content", "")
                content_lower = content.lower()

                has_avg_completion = "average weekly completion" in content_lower or "average" in content_lower
                has_total_points = "total points delivered" in content_lower or "total points" in content_lower
                has_six_points = "6" in content

                if has_avg_completion and has_total_points and has_six_points:
                    summary_paragraph_found = True
                    break

        if not summary_paragraph_found:
            errors.append("Summary paragraph with 'Average Weekly Completion: [X]% | Total Points Delivered: 6' not found")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Inventory Low-Stock Reorder Tracker Task
# =============================================================================
def _validate_agent_inventory_low_stock_reorder_tracker(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: In the 'Inventory Tracker' database, identify all items where Quantity < 5 OR In Stock
    checkbox is false. For these low-stock items: (1) If Reorder Date is null, set it to 7 days
    from today, (2) If Category is 'Electronics' or 'Software', also update Unit Price by increasing
    it 10% (round to nearest dollar). Then create a new database titled '🚨 Reorder Tracker' as
    child of the 'Inventory Tracker' page with properties: 'Item' (title), 'Original Stock' (number),
    'Reorder Status' (select: 'Pending' in yellow, 'Ordered' in blue, 'Received' in green),
    'Vendor' (text), 'Reorder Date' (date), 'Urgency' (select: 'Low' in gray, 'Medium' in orange,
    'High' in red). For each low-stock item identified, create a row with Item=[name],
    Original Stock=[current quantity], Reorder Status='Pending', Reorder Date=[the date you set],
    Urgency='High' if quantity=0, 'Medium' if quantity<3, 'Low' otherwise.

    Expected from initial data:
    Low-stock items (Quantity < 5 OR In Stock = false):
    - Standing Desk (row-4): Quantity=3, In Stock=true, Category=Furniture, Price=$699, Reorder Date=null
      → Reorder Date set to 7 days from today (2026-01-21), Price unchanged (not Electronics/Software)
      → Urgency='Low' (3 is not < 3)
    - Wireless Keyboard (row-5): Quantity=0, In Stock=false, Category=Hardware, Price=$79, Reorder Date=2026-02-07
      → Reorder Date unchanged (already set), Price unchanged (not Electronics/Software)
      → Urgency='High' (quantity=0)
    """
    checks_passed: list[str] = []

    errors = []

    # Get all pages
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # ==========================================================================
        # Phase 1: Verify Low-Stock Item Updates in Inventory Tracker
        # ==========================================================================

        # Find low-stock items in Inventory Tracker database
        standing_desk_row = None
        wireless_keyboard_row = None

        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == "db-inventory":
                title = page.get("title", "").lower()
                if "standing desk" in title:
                    standing_desk_row = page
                elif "wireless keyboard" in title:
                    wireless_keyboard_row = page

        # Check 1: Standing Desk Reorder Date Updated
        if not standing_desk_row:
            errors.append("Standing Desk row not found in Inventory Tracker")
        else:
            cell_values = standing_desk_row.get("cellValues", {})
            reorder_date = cell_values.get("db-inventory-prop-reorder-date")

            if reorder_date is None:
                errors.append("Standing Desk Reorder Date was not updated (still null)")
            else:
                # Extract date string
                date_str = reorder_date.get("start", "") if isinstance(reorder_date, dict) else str(reorder_date)
                # Should be approximately 7 days from 2026-01-14 (2026-01-21 +/- 1 day)
                if date_str:
                    if not any(d in date_str for d in ["2026-01-20", "2026-01-21", "2026-01-22"]):
                        errors.append(f"Standing Desk Reorder Date should be ~7 days from today (2026-01-21), got '{date_str}'")
                else:
                    errors.append("Standing Desk Reorder Date is set but has no date value")

            # Check price unchanged (Furniture - not Electronics/Software)
            price = cell_values.get("db-inventory-prop-price")
            if price is not None and int(price) != 699:
                errors.append(f"Standing Desk price should remain $699 (Furniture category), got ${price}")

        # Check 2: Wireless Keyboard Reorder Date Unchanged
        if not wireless_keyboard_row:
            errors.append("Wireless Keyboard row not found in Inventory Tracker")
        else:
            cell_values = wireless_keyboard_row.get("cellValues", {})
            reorder_date = cell_values.get("db-inventory-prop-reorder-date")

            if reorder_date is None:
                errors.append("Wireless Keyboard Reorder Date was incorrectly cleared (should remain 2026-02-07)")
            else:
                date_str = reorder_date.get("start", "") if isinstance(reorder_date, dict) else str(reorder_date)
                if "2026-02-07" not in str(date_str):
                    errors.append(f"Wireless Keyboard Reorder Date should remain '2026-02-07', got '{date_str}'")

            # Check price unchanged (Hardware - not Electronics/Software)
            price = cell_values.get("db-inventory-prop-price")
            if price is not None and int(price) != 79:
                errors.append(f"Wireless Keyboard price should remain $79 (Hardware category), got ${price}")

        # ==========================================================================
        # Phase 2: Verify New Database Creation
        # ==========================================================================

        # Check 4: Find Reorder Tracker Database
        reorder_tracker_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "")
                if "reorder tracker" in title.lower():
                    reorder_tracker_db = page
                    break

        if not reorder_tracker_db:
            errors.append("Database '🚨 Reorder Tracker' not found")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        # Check title contains emoji
        db_title = reorder_tracker_db.get("title", "")
        if "🚨" not in db_title:
            errors.append("Database title should contain '🚨' emoji")

        # Check 5: Database is Child of Inventory Tracker
        parent_id = reorder_tracker_db.get("parentId")
        if parent_id != "db-inventory":
            # Also check if parent has title "Inventory Tracker"
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "inventory tracker" not in parent_page.get("title", "").lower():
                errors.append(f"Database should be child of 'Inventory Tracker' (db-inventory), got parentId: {parent_id}")

        # Check 6: Database Properties
        db_metadata = reorder_tracker_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Expected properties
        expected_props = [
            ("Item", "title"),
            ("Original Stock", "number"),
            ("Reorder Status", "select"),
            ("Vendor", "text"),
            ("Reorder Date", "date"),
            ("Urgency", "select"),
        ]

        prop_name_map = {}  # Map lowercase name to property
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            prop_name_map[prop_name] = prop

        for exp_name, exp_type in expected_props:
            prop = prop_name_map.get(exp_name.lower())
            if not prop:
                errors.append(f"Property '{exp_name}' not found in Reorder Tracker database")
            elif prop.get("type") != exp_type:
                errors.append(f"Property '{exp_name}' should have type '{exp_type}', got '{prop.get('type')}'")

        # Check Reorder Status select options
        reorder_status_prop = prop_name_map.get("reorder status")
        if reorder_status_prop:
            options = reorder_status_prop.get("options", [])
            expected_status_options = {
                "pending": "yellow",
                "ordered": "blue",
                "received": "green",
            }
            found_options = {}
            for opt in options:
                found_options[opt.get("name", "").lower()] = opt.get("color", "").lower()

            for opt_name, opt_color in expected_status_options.items():
                if opt_name not in found_options:
                    errors.append(f"Reorder Status missing option '{opt_name}'")
                elif found_options[opt_name] != opt_color:
                    errors.append(f"Reorder Status option '{opt_name}' should have color '{opt_color}', got '{found_options[opt_name]}'")

        # Check Urgency select options
        urgency_prop = prop_name_map.get("urgency")
        if urgency_prop:
            options = urgency_prop.get("options", [])
            expected_urgency_options = {
                "low": "gray",
                "medium": "orange",
                "high": "red",
            }
            found_options = {}
            for opt in options:
                found_options[opt.get("name", "").lower()] = opt.get("color", "").lower()

            for opt_name, opt_color in expected_urgency_options.items():
                if opt_name not in found_options:
                    errors.append(f"Urgency missing option '{opt_name}'")
                elif found_options[opt_name] != opt_color:
                    errors.append(f"Urgency option '{opt_name}' should have color '{opt_color}', got '{found_options[opt_name]}'")

        # ==========================================================================
        # Phase 3: Verify Database Rows
        # ==========================================================================

        # Get rows in Reorder Tracker database
        reorder_db_id = reorder_tracker_db.get("_id")
        reorder_rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == reorder_db_id:
                reorder_rows.append(page)

        # Check 9: Correct number of rows
        if len(reorder_rows) != 2:
            errors.append(f"Reorder Tracker should have exactly 2 rows, got {len(reorder_rows)}")

        # Build property ID map for the new database
        new_prop_id_map = {}
        for prop in properties:
            new_prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            cell_values = row.get("cellValues", {})
            prop_id = new_prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values[prop_id]
            # Fallback: search by property name in keys
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        # Find Standing Desk and Wireless Keyboard rows in Reorder Tracker
        standing_desk_reorder_row = None
        wireless_keyboard_reorder_row = None

        for row in reorder_rows:
            title = row.get("title", "").lower()
            if "standing desk" in title:
                standing_desk_reorder_row = row
            elif "wireless keyboard" in title:
                wireless_keyboard_reorder_row = row

        # Check 7: Standing Desk Row in Reorder Tracker
        if not standing_desk_reorder_row:
            errors.append("Row for 'Standing Desk' not found in Reorder Tracker database")
        else:
            # Check Original Stock = 3
            original_stock = get_cell_value(standing_desk_reorder_row, "original stock")
            if original_stock is None:
                errors.append("Standing Desk row missing 'Original Stock' value")
            elif int(original_stock) != 3:
                errors.append(f"Standing Desk Original Stock should be 3, got {original_stock}")

            # Check Reorder Status = Pending
            reorder_status = get_cell_value(standing_desk_reorder_row, "reorder status")
            if reorder_status is None:
                errors.append("Standing Desk row missing 'Reorder Status' value")
            elif "pending" not in str(reorder_status).lower():
                errors.append(f"Standing Desk Reorder Status should be 'Pending', got '{reorder_status}'")

            # Check Urgency = Low (quantity=3, not < 3)
            urgency = get_cell_value(standing_desk_reorder_row, "urgency")
            if urgency is None:
                errors.append("Standing Desk row missing 'Urgency' value")
            elif "low" not in str(urgency).lower():
                errors.append(f"Standing Desk Urgency should be 'Low' (quantity=3), got '{urgency}'")

            # Check Reorder Date is set
            reorder_date = get_cell_value(standing_desk_reorder_row, "reorder date")
            if reorder_date is None:
                errors.append("Standing Desk row missing 'Reorder Date' value")

        # Check 8: Wireless Keyboard Row in Reorder Tracker
        if not wireless_keyboard_reorder_row:
            errors.append("Row for 'Wireless Keyboard' not found in Reorder Tracker database")
        else:
            # Check Original Stock = 0
            original_stock = get_cell_value(wireless_keyboard_reorder_row, "original stock")
            if original_stock is None:
                errors.append("Wireless Keyboard row missing 'Original Stock' value")
            elif int(original_stock) != 0:
                errors.append(f"Wireless Keyboard Original Stock should be 0, got {original_stock}")

            # Check Reorder Status = Pending
            reorder_status = get_cell_value(wireless_keyboard_reorder_row, "reorder status")
            if reorder_status is None:
                errors.append("Wireless Keyboard row missing 'Reorder Status' value")
            elif "pending" not in str(reorder_status).lower():
                errors.append(f"Wireless Keyboard Reorder Status should be 'Pending', got '{reorder_status}'")

            # Check Urgency = High (quantity=0)
            urgency = get_cell_value(wireless_keyboard_reorder_row, "urgency")
            if urgency is None:
                errors.append("Wireless Keyboard row missing 'Urgency' value")
            elif "high" not in str(urgency).lower():
                errors.append(f"Wireless Keyboard Urgency should be 'High' (quantity=0), got '{urgency}'")

            # Check Reorder Date is set
            reorder_date = get_cell_value(wireless_keyboard_reorder_row, "reorder date")
            if reorder_date is None:
                errors.append("Wireless Keyboard row missing 'Reorder Date' value")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Resource Allocation Dashboard Task
# =============================================================================
def _validate_agent_resource_allocation_dashboard(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a comprehensive 'Resource Allocation Dashboard' that combines data from multiple
    databases. First, modify the Task Tracker database by adding a new number property 'Estimated Hours'.
    Then for all existing tasks: set Estimated Hours = 8 for Low priority, 16 for Medium, 24 for High,
    40 for Urgent. Create a new page '📊 Resource Allocation Dashboard' under 'Getting Started'.
    Structure: heading_1 '📊 Resource Allocation Dashboard', heading_2 '⏱️ Hours by Status' with a
    bulleted list showing total estimated hours for each Status value. Add heading_2 '👥 Workload
    Distribution' with a numbered list showing each assignee and their total estimated hours (sum
    across all their tasks). Add a divider, then heading_2 '💰 Budget Impact Analysis'. Query Project
    Portfolio and for each Engineering department project, add a callout with blue background showing
    '[PROJECT]: [X] estimated hours across [Y] related tasks = $[COST]' where COST = hours * 150. End
    with a red callout: '⚠️ Total Engineering Hours Allocated: [TOTAL] hours ($[TOTAL*150] at $150/hour)'.

    Expected values from initial data:
    - Task Tracker has 8 tasks with priorities: Low(1), Medium(3), High(3), Urgent(1)
    - Estimated Hours: Low=8, Medium=16, High=24, Urgent=40
    - Total hours: 8 + 48 + 72 + 40 = 168 hours
    - Hours by Status:
      - Not Started: 24 (row-3: 8 + row-7: 16)
      - In Progress: 40 (row-1: 24 + row-6: 16)
      - In Review: 16 (row-2: 16)
      - Done: 48 (row-4: 24 + row-8: 24)
      - Blocked: 40 (row-5: 40)
    - Workload by Assignee:
      - Alice Chen (user-1): 64 hours (row-1: 24 + row-4: 24 + row-7: 16)
      - Bob Martinez (user-2): 80 hours (row-2: 16 + row-5: 40 + row-8: 24)
      - Carol Williams (user-3): 24 hours (row-3: 8 + row-6: 16)
    - Engineering projects: Website Redesign, Mobile App Development, Customer Portal
    - Total Engineering Hours: 168 (all tasks), Cost: $25,200
    """
    checks_passed: list[str] = []

    errors = []

    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # ==========================================================================
        # Step 1: Validate Task Tracker has "Estimated Hours" property
        # ==========================================================================
        task_tracker_db = None
        for page in all_pages:
            if page.get("type") == "database" and "task tracker" in page.get("title", "").lower():
                task_tracker_db = page
                break

        if not task_tracker_db:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")

        db_metadata = task_tracker_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        estimated_hours_prop = None
        for prop in properties:
            if "estimated hours" in prop.get("name", "").lower():
                estimated_hours_prop = prop
                break

        if not estimated_hours_prop:
            errors.append("'Estimated Hours' property not found in Task Tracker database")
        elif estimated_hours_prop.get("type") != "number":
            errors.append(f"'Estimated Hours' should be type 'number', got '{estimated_hours_prop.get('type')}'")

        # ==========================================================================
        # Step 2: Validate task rows have correct Estimated Hours based on priority
        # ==========================================================================
        task_db_id = task_tracker_db.get("_id")
        task_rows = [p for p in all_pages if p.get("type") == "row" and p.get("databaseId") == task_db_id]

        # Build property ID map
        prop_id_map = {prop.get("name", "").lower(): prop.get("_id") for prop in properties}

        def get_cell_value(row, prop_name_lower):
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        priority_hours_map = {"low": 8, "medium": 16, "high": 24, "urgent": 40}

        for row in task_rows:
            priority = get_cell_value(row, "priority")
            estimated_hours = get_cell_value(row, "estimated hours")

            if priority:
                priority_lower = str(priority).lower()
                expected_hours = priority_hours_map.get(priority_lower)

                if expected_hours is not None:
                    if estimated_hours is None:
                        errors.append(f"Task '{row.get('title')}' missing Estimated Hours")
                    elif int(estimated_hours) != expected_hours:
                        errors.append(f"Task '{row.get('title')}' should have Estimated Hours {expected_hours}, got {estimated_hours}")

        # ==========================================================================
        # Step 3: Find the Resource Allocation Dashboard page
        # ==========================================================================
        dashboard_page = None
        for page in all_pages:
            if page.get("type") in ["normal", None]:
                title = page.get("title", "")
                if "resource allocation dashboard" in title.lower():
                    dashboard_page = page
                    break

        if not dashboard_page:
            errors.append("[X] Page 'Resource Allocation Dashboard' not found")
        else:
            checks_passed.append("[C] Page 'Resource Allocation Dashboard'")

        # Check parent is "Getting Started" (page-1)
        parent_id = dashboard_page.get("parentId")
        if parent_id != "page-1":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "getting started" not in parent_page.get("title", "").lower():
                errors.append(f"Dashboard page should be child of 'Getting Started' (page-1), got parentId: {parent_id}")

        # Check page title contains emoji
        if "📊" not in dashboard_page.get("title", ""):
            errors.append("Dashboard page title should contain '📊' emoji")

        # ==========================================================================
        # Step 4: Get all blocks for the dashboard page
        # ==========================================================================
        dashboard_page_id = dashboard_page.get("_id")
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": dashboard_page_id}})
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0))

        # ==========================================================================
        # Step 5: Check heading_1 - Dashboard Title
        # ==========================================================================
        heading1_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading1", "heading_1"]:
                content = block.get("content", "")
                if "resource allocation dashboard" in content.lower() and "📊" in content:
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("heading_1 '📊 Resource Allocation Dashboard' not found")

        # ==========================================================================
        # Step 6: Check heading_2 - Hours by Status
        # ==========================================================================
        hours_status_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "hours by status" in content.lower() and "⏱️" in content:
                    hours_status_heading_found = True
                    break

        if not hours_status_heading_found:
            errors.append("heading_2 '⏱️ Hours by Status' not found")

        # ==========================================================================
        # Step 7: Check bulleted list with Hours by Status values
        # ==========================================================================
        expected_status_hours = {
            "not started": 24,
            "in progress": 40,
            "in review": 16,
            "done": 48,
            "blocked": 40,
        }

        status_hours_found = {k: False for k in expected_status_hours}

        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["bulletedList", "bulleted_list", "bulleted_list_item", "bulleted"]:
                content = block.get("content", "").lower()
                for status, hours in expected_status_hours.items():
                    if status in content and str(hours) in block.get("content", ""):
                        status_hours_found[status] = True

        for status, found in status_hours_found.items():
            if not found:
                errors.append(f"Bulleted list item for '{status}' with {expected_status_hours[status]} hours not found")

        # ==========================================================================
        # Step 8: Check heading_2 - Workload Distribution
        # ==========================================================================
        workload_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "workload distribution" in content.lower() and "👥" in content:
                    workload_heading_found = True
                    break

        if not workload_heading_found:
            errors.append("heading_2 '👥 Workload Distribution' not found")

        # ==========================================================================
        # Step 9: Check numbered list with Workload Distribution values
        # ==========================================================================
        expected_workloads = {
            "alice chen": 64,
            "bob martinez": 80,
            "carol williams": 24,
        }

        workload_found = {k: False for k in expected_workloads}

        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["numberedList", "numbered_list", "numbered_list_item", "numbered"]:
                content = block.get("content", "").lower()
                for assignee, hours in expected_workloads.items():
                    if assignee in content and str(hours) in block.get("content", ""):
                        workload_found[assignee] = True

        for assignee, found in workload_found.items():
            if not found:
                errors.append(f"Numbered list item for '{assignee}' with {expected_workloads[assignee]} hours not found")

        # ==========================================================================
        # Step 10: Check divider block
        # ==========================================================================
        divider_found = False
        for block in sorted_blocks:
            if block.get("type") == "divider":
                divider_found = True
                break

        if not divider_found:
            errors.append("Divider block not found")

        # ==========================================================================
        # Step 11: Check heading_2 - Budget Impact Analysis
        # ==========================================================================
        budget_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "budget impact analysis" in content.lower() and "💰" in content:
                    budget_heading_found = True
                    break

        if not budget_heading_found:
            errors.append("heading_2 '💰 Budget Impact Analysis' not found")

        # ==========================================================================
        # Step 12: Check blue callouts for Engineering projects
        # ==========================================================================
        engineering_projects = ["website redesign", "mobile app development", "customer portal"]
        project_callouts_found = {p: False for p in engineering_projects}

        for block in sorted_blocks:
            if block.get("type") == "callout":
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", block.get("backgroundColor", ""))
                content = block.get("content", "").lower()

                if "blue" in str(bg_color).lower():
                    for project in engineering_projects:
                        if project in content:
                            project_callouts_found[project] = True

        for project, found in project_callouts_found.items():
            if not found:
                errors.append(f"Blue callout for Engineering project '{project}' not found")

        # ==========================================================================
        # Step 13: Check red callout with total Engineering hours
        # ==========================================================================
        total_callout_found = False
        for block in sorted_blocks:
            if block.get("type") == "callout":
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", block.get("backgroundColor", ""))
                content = block.get("content", "")

                if "red" in str(bg_color).lower():
                    if "⚠️" in content and "total engineering hours" in content.lower():
                        # Check for 168 hours and $25,200 cost
                        content_normalized = content.replace(",", "").replace("$", "")
                        if "168" in content_normalized and "25200" in content_normalized:
                            total_callout_found = True
                            break

        if not total_callout_found:
            errors.append("Red callout with '⚠️ Total Engineering Hours Allocated: 168 hours ($25,200 at $150/hour)' not found")

        # ==========================================================================
        # Return result
        # ==========================================================================
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Employee Performance Review Database Task
# =============================================================================
def _validate_agent_employee_performance_review_database(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a new 'Employee Performance Review' database as a child of the 'Team Directory'
    page with the following exact schema: 'Employee Name' (title), 'Review Period' (select with
    options 'Q1 2026' in blue, 'Q2 2026' in green, 'Q3 2026' in yellow, 'Q4 2026' in orange),
    'Department' (select with options 'Engineering' in purple, 'Marketing' in pink, 'Sales' in blue,
    'HR' in brown, 'Operations' in gray), 'Performance Rating' (select with options 'Exceeds
    Expectations' in green, 'Meets Expectations' in blue, 'Needs Improvement' in yellow, 'Below
    Expectations' in red), 'Goals Met' (number), 'Total Goals' (number), 'Review Date' (date),
    'Manager Comments' (text). Set table layout. Then add 5 initial rows with exact values:
    'John Smith' (Q1 2026, Engineering, Meets Expectations, 8, 10, {start: '2026-01-15'}),
    'Sarah Johnson' (Q1 2026, Marketing, Exceeds Expectations, 12, 10, {start: '2026-01-16'}),
    'Mike Chen' (Q1 2026, Sales, Meets Expectations, 7, 10, {start: '2026-01-14'}),
    'Lisa Garcia' (Q1 2026, HR, Exceeds Expectations, 11, 10, {start: '2026-01-15'}),
    'David Park' (Q1 2026, Operations, Needs Improvement, 5, 10, {start: '2026-01-13'}).

    Validation:
    1. Database exists with title "Employee Performance Review"
    2. Database is child of "Team Directory" (page-8)
    3. Layout is "table"
    4. 8 properties exist with correct types
    5. Review Period select has 4 options with correct colors
    6. Department select has 5 options with correct colors
    7. Performance Rating select has 4 options with correct colors
    8. 5 rows exist with correct employee data
    """
    checks_passed: list[str] = []

    errors = []

    # Step 1: Find the Employee Performance Review database
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        perf_review_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "employee performance review" in title:
                    perf_review_db = page
                    break

        if not perf_review_db:
            errors.append("[X] Database 'Employee Performance Review' not found")
        else:
            checks_passed.append("[C] Database 'Employee Performance Review'")

        # Step 2: Check parent is Team Directory (page-8)
        parent_id = perf_review_db.get("parentId")
        if parent_id != "page-8":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "team directory" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Team Directory' (page-8), got parentId: {parent_id}")

        # Step 3: Validate database layout is table
        db_metadata = perf_review_db.get("databaseMetadata", {})
        layout = db_metadata.get("layout", "")
        if layout != "table":
            errors.append(f"Database layout should be 'table', got '{layout}'")

        # Step 4: Validate database properties exist with correct types
        properties = db_metadata.get("properties", [])
        expected_properties = {
            "employee name": "title",
            "review period": "select",
            "department": "select",
            "performance rating": "select",
            "goals met": "number",
            "total goals": "number",
            "review date": "date",
            "manager comments": "text",
        }

        prop_name_to_obj = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_name_to_obj[prop_name_lower] = prop

        for expected_name, expected_type in expected_properties.items():
            if expected_name not in prop_name_to_obj:
                errors.append(f"Property '{expected_name}' not found in database")
            else:
                actual_type = prop_name_to_obj[expected_name].get("type", "")
                if actual_type != expected_type:
                    errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{actual_type}'")

        # Step 5: Validate Review Period select options
        review_period_prop = prop_name_to_obj.get("review period")
        if review_period_prop:
            options = review_period_prop.get("options", [])
            expected_options = {
                "q1 2026": "blue",
                "q2 2026": "green",
                "q3 2026": "yellow",
                "q4 2026": "orange",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Review Period missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Review Period option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 6: Validate Department select options
        department_prop = prop_name_to_obj.get("department")
        if department_prop:
            options = department_prop.get("options", [])
            expected_options = {
                "engineering": "purple",
                "marketing": "pink",
                "sales": "blue",
                "hr": "brown",
                "operations": "gray",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Department missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Department option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 7: Validate Performance Rating select options
        perf_rating_prop = prop_name_to_obj.get("performance rating")
        if perf_rating_prop:
            options = perf_rating_prop.get("options", [])
            expected_options = {
                "exceeds expectations": "green",
                "meets expectations": "blue",
                "needs improvement": "yellow",
                "below expectations": "red",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Performance Rating missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Performance Rating option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 8: Find and validate rows
        db_id = perf_review_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        if len(rows) < 5:
            errors.append(f"Expected 5 rows, found {len(rows)}")

        # Build property ID map for cell value lookup
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            """Get cell value by property name from a row."""
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Fallback: try matching by property name in cell_values keys
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        def get_date_str(date_val):
            """Extract date string from date value."""
            if isinstance(date_val, dict):
                return date_val.get("start", "")
            return str(date_val) if date_val else ""

        # Expected row data
        expected_rows = [
            {
                "title": "john smith",
                "review_period": "q1 2026",
                "department": "engineering",
                "performance_rating": "meets expectations",
                "goals_met": 8,
                "total_goals": 10,
                "review_date": "2026-01-15",
            },
            {
                "title": "sarah johnson",
                "review_period": "q1 2026",
                "department": "marketing",
                "performance_rating": "exceeds expectations",
                "goals_met": 12,
                "total_goals": 10,
                "review_date": "2026-01-16",
            },
            {
                "title": "mike chen",
                "review_period": "q1 2026",
                "department": "sales",
                "performance_rating": "meets expectations",
                "goals_met": 7,
                "total_goals": 10,
                "review_date": "2026-01-14",
            },
            {
                "title": "lisa garcia",
                "review_period": "q1 2026",
                "department": "hr",
                "performance_rating": "exceeds expectations",
                "goals_met": 11,
                "total_goals": 10,
                "review_date": "2026-01-15",
            },
            {
                "title": "david park",
                "review_period": "q1 2026",
                "department": "operations",
                "performance_rating": "needs improvement",
                "goals_met": 5,
                "total_goals": 10,
                "review_date": "2026-01-13",
            },
        ]

        # Validate each expected row
        for expected in expected_rows:
            row_found = False
            for row in rows:
                row_title = row.get("title", "").lower()
                if expected["title"] in row_title:
                    row_found = True

                    # Validate Review Period
                    review_period = get_cell_value(row, "review period")
                    if review_period:
                        if expected["review_period"] not in str(review_period).lower():
                            errors.append(f"Row '{expected['title']}' Review Period should be '{expected['review_period']}', got '{review_period}'")
                    else:
                        errors.append(f"Row '{expected['title']}' missing Review Period")

                    # Validate Department
                    department = get_cell_value(row, "department")
                    if department:
                        if expected["department"] not in str(department).lower():
                            errors.append(f"Row '{expected['title']}' Department should be '{expected['department']}', got '{department}'")
                    else:
                        errors.append(f"Row '{expected['title']}' missing Department")

                    # Validate Performance Rating
                    perf_rating = get_cell_value(row, "performance rating")
                    if perf_rating:
                        if expected["performance_rating"] not in str(perf_rating).lower():
                            errors.append(f"Row '{expected['title']}' Performance Rating should be '{expected['performance_rating']}', got '{perf_rating}'")
                    else:
                        errors.append(f"Row '{expected['title']}' missing Performance Rating")

                    # Validate Goals Met
                    goals_met = get_cell_value(row, "goals met")
                    if goals_met is not None:
                        if int(goals_met) != expected["goals_met"]:
                            errors.append(f"Row '{expected['title']}' Goals Met should be {expected['goals_met']}, got {goals_met}")
                    else:
                        errors.append(f"Row '{expected['title']}' missing Goals Met")

                    # Validate Total Goals
                    total_goals = get_cell_value(row, "total goals")
                    if total_goals is not None:
                        if int(total_goals) != expected["total_goals"]:
                            errors.append(f"Row '{expected['title']}' Total Goals should be {expected['total_goals']}, got {total_goals}")
                    else:
                        errors.append(f"Row '{expected['title']}' missing Total Goals")

                    # Validate Review Date
                    review_date = get_cell_value(row, "review date")
                    if review_date:
                        date_str = get_date_str(review_date)
                        if expected["review_date"] not in date_str:
                            errors.append(f"Row '{expected['title']}' Review Date should be '{expected['review_date']}', got '{date_str}'")
                    else:
                        errors.append(f"Row '{expected['title']}' missing Review Date")

                    break

            if not row_found:
                errors.append(f"Row '{expected['title']}' not found in database")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Planning Inline Database Task
# =============================================================================
def _validate_agent_sprint_planning_inline_database(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create an inline 'Sprint Planning' database within the 'Sprint Planning - Jan 15' page,
    positioned immediately after the 'Action Items' heading but before any existing to-do blocks.
    Configure the database with properties: 'User Story' (title), 'Story Points' (number),
    'Assignee' (text), 'Priority' (select with 'P0' in red, 'P1' in orange, 'P2' in yellow, 'P3' in gray),
    'Status' (select with 'To Do' in gray, 'In Progress' in blue, 'Done' in green),
    'Sprint Goal' (multi_select with options 'Auth Flow' in purple, 'Drag Drop' in blue, 'Permissions UI' in green).
    Add 4 rows: 'Implement OAuth integration' (5 points, Alice Chen, P0, To Do, ['Auth Flow']),
    'Add drag handle to blocks' (3 points, Bob Martinez, P1, In Progress, ['Drag Drop']),
    'Design permission modal' (2 points, Carol Williams, P1, To Do, ['Permissions UI']),
    'Write auth documentation' (1 point, Alice Chen, P2, To Do, ['Auth Flow']).
    After the database, add a divider block and a callout with yellow_background containing
    'Total Story Points for Sprint: 11'.

    Initial state: page-3-1 is "Sprint Planning - Jan 15" with "Action Items" heading at order 9
    and two todo blocks at orders 10 and 11.
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # Step 1: Find the Sprint Planning database with correct parent
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        sprint_planning_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "sprint planning" in title:
                    sprint_planning_db = page
                    break

        if not sprint_planning_db:
            errors.append("[X] Database 'Sprint Planning' not found")
        else:
            checks_passed.append("[C] Database 'Sprint Planning'")

        # Check parent is Sprint Planning - Jan 15 (page-3-1)
        parent_id = sprint_planning_db.get("parentId")
        if parent_id != "page-3-1":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "sprint planning" not in parent_page.get("title", "").lower() or "jan 15" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Sprint Planning - Jan 15' (page-3-1), got parentId: {parent_id}")

        db_id = sprint_planning_db.get("_id")

        # Step 2: Verify inline database block exists
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": "page-3-1"}})

        inline_db_block = None
        for block in all_blocks:
            if block.get("type") == "database":
                props = block.get("properties", {})
                if props.get("databaseId") == db_id:
                    inline_db_block = block
                    break

        if not inline_db_block:
            errors.append("Inline database block not found on page-3-1")

        db_block_order = inline_db_block.get("order", 0) if inline_db_block else 0

        # Step 3: Validate database properties
        db_metadata = sprint_planning_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        expected_properties = {
            "user story": "title",
            "story points": "number",
            "assignee": "text",
            "priority": "select",
            "status": "select",
            "sprint goal": "multi_select",
        }

        prop_name_to_obj = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_name_to_obj[prop_name_lower] = prop

        for expected_name, expected_type in expected_properties.items():
            if expected_name not in prop_name_to_obj:
                errors.append(f"Property '{expected_name}' not found in database")
            else:
                actual_type = prop_name_to_obj[expected_name].get("type", "")
                if actual_type != expected_type:
                    errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{actual_type}'")

        # Step 4: Validate Priority select options
        priority_prop = prop_name_to_obj.get("priority")
        if priority_prop:
            options = priority_prop.get("options", [])
            expected_priority_options = {
                "p0": "red",
                "p1": "orange",
                "p2": "yellow",
                "p3": "gray",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_priority_options.items():
                if exp_name not in found_options:
                    errors.append(f"Priority missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Priority option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 5: Validate Status select options
        status_prop = prop_name_to_obj.get("status")
        if status_prop:
            options = status_prop.get("options", [])
            expected_status_options = {
                "to do": "gray",
                "in progress": "blue",
                "done": "green",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_status_options.items():
                if exp_name not in found_options:
                    errors.append(f"Status missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 6: Validate Sprint Goal multi_select options
        sprint_goal_prop = prop_name_to_obj.get("sprint goal")
        if sprint_goal_prop:
            options = sprint_goal_prop.get("options", [])
            expected_goal_options = {
                "auth flow": "purple",
                "drag drop": "blue",
                "permissions ui": "green",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_goal_options.items():
                if exp_name not in found_options:
                    errors.append(f"Sprint Goal missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Sprint Goal option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 7: Validate database rows
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        if len(rows) < 4:
            errors.append(f"Expected 4 rows, found {len(rows)}")

        # Build property ID map for cell value lookup
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            """Get cell value by property name from a row."""
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Fallback: try matching by property name in cell_values keys
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        # Expected row data
        expected_rows = [
            {
                "title": "implement oauth integration",
                "story_points": 5,
                "assignee": "alice chen",
                "priority": "p0",
                "status": "to do",
                "sprint_goal": ["auth flow"],
            },
            {
                "title": "add drag handle to blocks",
                "story_points": 3,
                "assignee": "bob martinez",
                "priority": "p1",
                "status": "in progress",
                "sprint_goal": ["drag drop"],
            },
            {
                "title": "design permission modal",
                "story_points": 2,
                "assignee": "carol williams",
                "priority": "p1",
                "status": "to do",
                "sprint_goal": ["permissions ui"],
            },
            {
                "title": "write auth documentation",
                "story_points": 1,
                "assignee": "alice chen",
                "priority": "p2",
                "status": "to do",
                "sprint_goal": ["auth flow"],
            },
        ]

        # Validate each expected row
        for expected in expected_rows:
            row_found = None
            for row in rows:
                row_title = row.get("title", "").lower()
                if expected["title"] in row_title:
                    row_found = row
                    break

            if not row_found:
                errors.append(f"Row '{expected['title']}' not found")
                continue

            row_title_display = expected["title"].title()

            # Check Story Points
            story_points = get_cell_value(row_found, "story points")
            if story_points is not None:
                try:
                    if int(story_points) != expected["story_points"]:
                        errors.append(f"{row_title_display}: Story Points should be {expected['story_points']}, got {story_points}")
                except (ValueError, TypeError):
                    errors.append(f"{row_title_display}: Story Points should be {expected['story_points']}, got '{story_points}'")
            else:
                errors.append(f"{row_title_display}: Story Points is missing")

            # Check Assignee
            assignee = get_cell_value(row_found, "assignee")
            if assignee:
                if expected["assignee"] not in str(assignee).lower():
                    errors.append(f"{row_title_display}: Assignee should contain '{expected['assignee']}', got '{assignee}'")
            else:
                errors.append(f"{row_title_display}: Assignee is missing")

            # Check Priority
            priority = get_cell_value(row_found, "priority")
            if priority:
                if expected["priority"] not in str(priority).lower():
                    errors.append(f"{row_title_display}: Priority should be '{expected['priority']}', got '{priority}'")
            else:
                errors.append(f"{row_title_display}: Priority is missing")

            # Check Status
            status = get_cell_value(row_found, "status")
            if status:
                if expected["status"] not in str(status).lower():
                    errors.append(f"{row_title_display}: Status should be '{expected['status']}', got '{status}'")
            else:
                errors.append(f"{row_title_display}: Status is missing")

            # Check Sprint Goal (multi_select)
            sprint_goal = get_cell_value(row_found, "sprint goal")
            if sprint_goal:
                if isinstance(sprint_goal, list):
                    goal_lower = [g.lower() for g in sprint_goal]
                else:
                    goal_lower = [str(sprint_goal).lower()]
                for expected_goal in expected["sprint_goal"]:
                    if expected_goal not in goal_lower:
                        errors.append(f"{row_title_display}: Sprint Goal should include '{expected_goal}'")
            else:
                errors.append(f"{row_title_display}: Sprint Goal is missing")

        # Step 8: Validate divider block after database
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0))

        divider_found = False
        divider_order = -1

        for block in sorted_blocks:
            if block.get("type") == "divider":
                block_order = block.get("order", 0)
                if block_order > db_block_order:
                    divider_found = True
                    divider_order = block_order
                    break

        if not divider_found:
            errors.append("Divider block not found after database")

        # Step 9: Validate callout block with yellow background
        callout_found = False
        for block in sorted_blocks:
            if block.get("type") == "callout":
                block_order = block.get("order", 0)
                if block_order > divider_order or (not divider_found and block_order > db_block_order):
                    properties = block.get("properties", {})
                    bg_color = properties.get("backgroundColor", "")
                    content = block.get("content", "")

                    has_yellow_bg = "yellow" in str(bg_color).lower()
                    has_correct_text = "total story points for sprint" in content.lower() and "11" in content

                    if has_yellow_bg and has_correct_text:
                        callout_found = True
                        break

        if not callout_found:
            errors.append("Callout with yellow_background containing 'Total Story Points for Sprint: 11' not found after divider")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Bug Management System Task
# =============================================================================
def _validate_agent_bug_management_system(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Implement a comprehensive bug tracking enhancement system that analyzes the
    'Bug Tracker' page content and creates a structured bug database with automatic severity
    calculation. Navigate to the 'Bug Tracker' page and create an inline database titled
    'Bug Management System' immediately after the main heading but before the 'Track and
    manage bugs' paragraph. Configure with properties: 'Bug Description' (title), 'Severity'
    (select with 'Critical' in red, 'High' in orange, 'Medium' in yellow, 'Low' in green),
    'Component' (select with 'Authentication' in purple, 'Data Sync' in blue, 'Upload' in
    green, 'Search' in yellow, 'UI' in pink), 'Resolution Time (hours)' (number), 'Fixed'
    (checkbox), 'Reported Date' (date). Analyze each to-do block in the page: for checked
    items, create entries with 'Fixed' = true, for unchecked create with 'Fixed' = false.
    Map bug descriptions to components (login→Authentication, sync→Data Sync, upload→Upload,
    search→Search, tooltip→UI). Set Severity based on section headers (Critical for items
    under 'Critical Bugs', Medium under 'Medium Priority', Low under 'Low Priority'). Set
    Resolution Time to 24 for Critical, 16 for Medium, 8 for Low. After the database, add
    a callout with red_background showing 'Active Bugs: [X] Critical, [Y] Medium, [Z] Low'
    with actual counts.

    Initial state: page-11 is "Bug Tracker" with:
    - heading1 "Bug Tracker" (order 0)
    - paragraph "Track and manage bugs across our products." (order 1)
    - heading2 "Critical Bugs" (order 2)
    - todo "Fix login timeout issue on mobile" (checked: true, order 3)
    - todo "Resolve data sync conflict in offline mode" (checked: false, order 4)
    - heading2 "Medium Priority" (order 5)
    - todo "Image upload fails for files over 5MB" (checked: true, order 6)
    - todo "Search results not updating in real-time" (checked: false, order 7)
    - heading2 "Low Priority" (order 8)
    - todo "Tooltip text cut off on small screens" (checked: false, order 9)

    Expected rows:
    1. "Fix login timeout issue on mobile" → Authentication, Critical, Fixed=true, Resolution=24
    2. "Resolve data sync conflict in offline mode" → Data Sync, Critical, Fixed=false, Resolution=24
    3. "Image upload fails for files over 5MB" → Upload, Medium, Fixed=true, Resolution=16
    4. "Search results not updating in real-time" → Search, Medium, Fixed=false, Resolution=16
    5. "Tooltip text cut off on small screens" → UI, Low, Fixed=false, Resolution=8

    Active bug counts: 1 Critical, 1 Medium, 1 Low
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # Step 1: Find the Bug Management System database
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        bug_mgmt_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "bug management system" in title:
                    bug_mgmt_db = page
                    break

        if not bug_mgmt_db:
            errors.append("[X] Database 'Bug Management System' not found")
        else:
            checks_passed.append("[C] Database 'Bug Management System'")

        # Step 2: Check parent is Bug Tracker (page-11)
        parent_id = bug_mgmt_db.get("parentId")
        if parent_id != "page-11":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "bug tracker" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Bug Tracker' (page-11), got parentId: {parent_id}")

        db_id = bug_mgmt_db.get("_id")

        # Step 3: Verify inline database block exists on page-11 and check positioning
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": "page-11"}})
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0))

        inline_db_block = None
        for block in all_blocks:
            if block.get("type") == "database":
                props = block.get("properties", {})
                if props.get("databaseId") == db_id:
                    inline_db_block = block
                    break

        if not inline_db_block:
            errors.append("Inline database block not found on Bug Tracker page")

        db_block_order = inline_db_block.get("order", 0) if inline_db_block else 0

        # Step 4: Verify positioning - after heading1 but before paragraph "Track and manage bugs"
        heading1_order = None
        paragraph_order = None
        for block in sorted_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "").lower()
            if block_type in ["heading1", "heading_1"] and "bug tracker" in content:
                heading1_order = block.get("order", 0)
            if block_type == "paragraph" and "track and manage bugs" in content:
                paragraph_order = block.get("order", 0)

        if inline_db_block and heading1_order is not None and paragraph_order is not None:
            if not (heading1_order < db_block_order < paragraph_order):
                errors.append(f"Database should be positioned between heading1 (order {heading1_order}) and paragraph (order {paragraph_order}), but has order {db_block_order}")

        # Step 5: Validate database properties
        db_metadata = bug_mgmt_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        expected_properties = {
            "bug description": "title",
            "severity": "select",
            "component": "select",
            "resolution time (hours)": "number",
            "fixed": "checkbox",
            "reported date": "date",
        }

        prop_name_to_obj = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_name_to_obj[prop_name_lower] = prop

        for expected_name, expected_type in expected_properties.items():
            if expected_name not in prop_name_to_obj:
                errors.append(f"Property '{expected_name}' not found in database")
            else:
                actual_type = prop_name_to_obj[expected_name].get("type", "")
                if actual_type != expected_type:
                    errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{actual_type}'")

        # Step 6: Validate Severity select options
        severity_prop = prop_name_to_obj.get("severity")
        if severity_prop:
            options = severity_prop.get("options", [])
            expected_severity_options = {
                "critical": "red",
                "high": "orange",
                "medium": "yellow",
                "low": "green",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_severity_options.items():
                if exp_name not in found_options:
                    errors.append(f"Severity missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Severity option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 7: Validate Component select options
        component_prop = prop_name_to_obj.get("component")
        if component_prop:
            options = component_prop.get("options", [])
            expected_component_options = {
                "authentication": "purple",
                "data sync": "blue",
                "upload": "green",
                "search": "yellow",
                "ui": "pink",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_component_options.items():
                if exp_name not in found_options:
                    errors.append(f"Component missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Component option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 8: Validate database rows
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        if len(rows) < 5:
            errors.append(f"Expected 5 rows, found {len(rows)}")

        # Build property ID map for cell value lookup
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            """Get cell value by property name from a row."""
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Fallback: try matching by property name in cell_values keys
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        # Expected row data based on Bug Tracker page todos
        expected_rows = [
            {
                "title_contains": "login timeout",
                "component": "authentication",
                "severity": "critical",
                "fixed": True,
                "resolution_time": 24,
            },
            {
                "title_contains": "data sync",
                "component": "data sync",
                "severity": "critical",
                "fixed": False,
                "resolution_time": 24,
            },
            {
                "title_contains": "upload",
                "component": "upload",
                "severity": "medium",
                "fixed": True,
                "resolution_time": 16,
            },
            {
                "title_contains": "search",
                "component": "search",
                "severity": "medium",
                "fixed": False,
                "resolution_time": 16,
            },
            {
                "title_contains": "tooltip",
                "component": "ui",
                "severity": "low",
                "fixed": False,
                "resolution_time": 8,
            },
        ]

        # Validate each expected row
        for expected in expected_rows:
            row_found = None
            for row in rows:
                row_title = row.get("title", "").lower()
                if expected["title_contains"] in row_title:
                    row_found = row
                    break

            if not row_found:
                errors.append(f"Row containing '{expected['title_contains']}' not found")
                continue

            row_display = expected["title_contains"].title()

            # Check Component
            component = get_cell_value(row_found, "component")
            if component:
                if expected["component"] not in str(component).lower():
                    errors.append(f"Row '{row_display}': Component should be '{expected['component']}', got '{component}'")
            else:
                errors.append(f"Row '{row_display}': Component is missing")

            # Check Severity
            severity = get_cell_value(row_found, "severity")
            if severity:
                if expected["severity"] not in str(severity).lower():
                    errors.append(f"Row '{row_display}': Severity should be '{expected['severity']}', got '{severity}'")
            else:
                errors.append(f"Row '{row_display}': Severity is missing")

            # Check Fixed
            fixed = get_cell_value(row_found, "fixed")
            if fixed is not None:
                fixed_bool = fixed if isinstance(fixed, bool) else str(fixed).lower() in ["true", "1", "yes"]
                if fixed_bool != expected["fixed"]:
                    errors.append(f"Row '{row_display}': Fixed should be {expected['fixed']}, got {fixed}")
            else:
                errors.append(f"Row '{row_display}': Fixed is missing")

            # Check Resolution Time
            resolution = get_cell_value(row_found, "resolution time (hours)")
            if resolution is not None:
                try:
                    if int(resolution) != expected["resolution_time"]:
                        errors.append(f"Row '{row_display}': Resolution Time should be {expected['resolution_time']}, got {resolution}")
                except (ValueError, TypeError):
                    errors.append(f"Row '{row_display}': Resolution Time should be {expected['resolution_time']}, got '{resolution}'")
            else:
                errors.append(f"Row '{row_display}': Resolution Time is missing")

        # Step 9: Validate callout block with red background after database
        callout_found = False
        for block in sorted_blocks:
            if block.get("type") == "callout":
                block_order = block.get("order", 0)
                if block_order > db_block_order:
                    properties = block.get("properties", {})
                    bg_color = properties.get("backgroundColor", "")
                    content = block.get("content", "")

                    has_red_bg = "red" in str(bg_color).lower()
                    has_active_bugs = "active bugs" in content.lower()
                    # Check for counts: 1 Critical, 1 Medium, 1 Low
                    has_critical_count = "1" in content and "critical" in content.lower()
                    has_medium_count = "1" in content and "medium" in content.lower()
                    has_low_count = "1" in content and "low" in content.lower()

                    if has_red_bg and has_active_bugs and has_critical_count and has_medium_count and has_low_count:
                        callout_found = True
                        break

        if not callout_found:
            errors.append("Callout with red_background showing 'Active Bugs: 1 Critical, 1 Medium, 1 Low' not found after database")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Effort Analysis Task
# =============================================================================
def _validate_agent_sprint_effort_analysis(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Update the 'Task Tracker' database schema by adding a new property called 'Effort Points'
    (number type) positioned after 'Priority' but before 'Assignee'. Then, for all existing rows,
    set 'Effort Points' based on this logic: if Priority='Urgent' set to 8, if Priority='High' set
    to 5, if Priority='Medium' set to 3, if Priority='Low' set to 1. After updating all rows, create
    a summary page titled '🎯 Sprint Effort Analysis' as a child of 'Sprint Planning - Jan 15'. Add
    heading_1 '🎯 Sprint Effort Analysis', then a callout with blue background showing 'Total Sprint
    Effort: [X] points' (sum of all Effort Points). Add heading_2 '📊 Effort by Status' and create
    a table-like structure using bulleted lists where each item shows 'Status | Total Points | Task
    Count', include all 5 status values even if count is 0.

    Initial data - Task Tracker rows and their priorities:
    - row-1: High (5 points), Status: In Progress
    - row-2: Medium (3 points), Status: In Review
    - row-3: Low (1 point), Status: Not Started
    - row-4: High (5 points), Status: Done
    - row-5: Urgent (8 points), Status: Blocked
    - row-6: Medium (3 points), Status: In Progress
    - row-7: Medium (3 points), Status: Not Started
    - row-8: High (5 points), Status: Done

    Expected totals:
    - Total effort: 33 points
    - Not Started: 4 points, 2 tasks
    - In Progress: 8 points, 2 tasks
    - In Review: 3 points, 1 task
    - Done: 10 points, 2 tasks
    - Blocked: 8 points, 1 task
    """
    checks_passed: list[str] = []

    errors = []

    # Get all pages
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # ==========================================================================
        # Check 1: Database Schema Updated - 'Effort Points' Property Added
        # ==========================================================================
        task_tracker_db = None
        for page in all_pages:
            if page.get("_id") == "db-tasks" or (
                page.get("type") == "database" and "task tracker" in page.get("title", "").lower()
            ):
                task_tracker_db = page
                break

        if not task_tracker_db:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database")

        db_metadata = task_tracker_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Find Effort Points property
        effort_points_prop = None
        effort_points_prop_id = None
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            if "effort" in prop_name and "point" in prop_name:
                effort_points_prop = prop
                effort_points_prop_id = prop.get("_id")
                break

        if not effort_points_prop:
            errors.append("'Effort Points' property not found in Task Tracker database")
        else:
            # Check type is number
            if effort_points_prop.get("type") != "number":
                errors.append(f"'Effort Points' property should be type 'number', got '{effort_points_prop.get('type')}'")

            # Check position: should be after Priority (order 2) and before Assignee
            effort_order = effort_points_prop.get("order")
            priority_order = None
            assignee_order = None

            for prop in properties:
                prop_name = prop.get("name", "").lower()
                if prop_name == "priority":
                    priority_order = prop.get("order")
                elif prop_name == "assignee":
                    assignee_order = prop.get("order")

            if priority_order is not None and effort_order is not None:
                if effort_order <= priority_order:
                    errors.append(f"'Effort Points' (order {effort_order}) should be after 'Priority' (order {priority_order})")

            if assignee_order is not None and effort_order is not None:
                if effort_order >= assignee_order:
                    errors.append(f"'Effort Points' (order {effort_order}) should be before 'Assignee' (order {assignee_order})")

        # ==========================================================================
        # Check 2: All Rows Have Correct Effort Points Values
        # ==========================================================================
        # Expected effort points by priority
        priority_to_effort = {
            "urgent": 8,
            "high": 5,
            "medium": 3,
            "low": 1,
        }

        # Find all rows in Task Tracker
        task_rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == "db-tasks":
                task_rows.append(page)

        if len(task_rows) < 8:
            errors.append(f"Expected at least 8 rows in Task Tracker, found {len(task_rows)}")

        # Build property ID map for looking up cell values
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        # Helper to get cell value by property name or ID
        def get_cell_value(cell_values, prop_name_lower):
            # Try by property ID first
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Try effort points prop ID directly
            if effort_points_prop_id and effort_points_prop_id in cell_values:
                if prop_name_lower in ["effort points", "effort_points"]:
                    return cell_values.get(effort_points_prop_id)
            # Try matching by key containing the property name
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", "").replace("-", "").replace("_", ""):
                    return val
            return None

        rows_with_incorrect_effort = []
        for row in task_rows:
            cell_values = row.get("cellValues", {})
            row_title = row.get("title", "Unknown")

            # Get priority
            priority_val = get_cell_value(cell_values, "priority")
            if priority_val:
                priority_lower = str(priority_val).lower()
                expected_effort = priority_to_effort.get(priority_lower)

                # Get effort points value
                effort_val = get_cell_value(cell_values, "effort points")
                if effort_val is None:
                    # Also try with underscore
                    effort_val = get_cell_value(cell_values, "effort_points")

                if effort_val is None:
                    # Try finding by effort_points_prop_id directly
                    if effort_points_prop_id:
                        effort_val = cell_values.get(effort_points_prop_id)

                if effort_val is None:
                    rows_with_incorrect_effort.append(f"'{row_title}' missing Effort Points")
                elif expected_effort is not None and int(effort_val) != expected_effort:
                    rows_with_incorrect_effort.append(
                        f"'{row_title}' has Effort Points={effort_val}, expected {expected_effort} for Priority={priority_val}"
                    )

        if rows_with_incorrect_effort:
            errors.append("Incorrect Effort Points: " + "; ".join(rows_with_incorrect_effort[:3]))
            if len(rows_with_incorrect_effort) > 3:
                errors.append(f"...and {len(rows_with_incorrect_effort) - 3} more rows with incorrect values")

        # ==========================================================================
        # Check 3: Summary Page Created with Correct Title and Parent
        # ==========================================================================
        summary_page = None
        for page in all_pages:
            if page.get("type") in ["normal", "page"]:
                title = page.get("title", "")
                if "sprint effort analysis" in title.lower() and "🎯" in title:
                    summary_page = page
                    break

        if not summary_page:
            errors.append("Summary page '🎯 Sprint Effort Analysis' not found")
        else:
            # Check parent is 'Sprint Planning - Jan 15' (page-3-1)
            parent_id = summary_page.get("parentId")
            valid_parent = False

            if parent_id == "page-3-1":
                valid_parent = True
            else:
                # Check if parent has the right title
                for page in all_pages:
                    if page.get("_id") == parent_id:
                        parent_title = page.get("title", "").lower()
                        if "sprint planning" in parent_title and "jan 15" in parent_title:
                            valid_parent = True
                        break

            if not valid_parent:
                errors.append(f"Summary page parent should be 'Sprint Planning - Jan 15' (page-3-1), got parentId: {parent_id}")

        # ==========================================================================
        # Checks 4-7: Validate Page Content
        # ==========================================================================
        if summary_page:
            page_id = summary_page.get("_id")

            # Get blocks for this page
            all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})
            page_blocks = [b for b in all_blocks if b.get("parentId") == page_id or b.get("pageId") == page_id]

            # Also check page content array
            page_content = summary_page.get("content", [])
            if isinstance(page_content, list):
                for block in page_content:
                    if isinstance(block, dict) and block not in page_blocks:
                        page_blocks.append(block)

            # Sort blocks by order
            sorted_blocks = sorted(page_blocks, key=lambda x: x.get("order", 0))

            # Check 4: heading_1 '🎯 Sprint Effort Analysis'
            heading1_found = False
            for block in sorted_blocks:
                block_type = block.get("type", "")
                if block_type in ["heading_1", "heading1"]:
                    content = block.get("content", "")
                    if "sprint effort analysis" in content.lower() and "🎯" in content:
                        heading1_found = True
                        break

            if not heading1_found:
                errors.append("heading_1 '🎯 Sprint Effort Analysis' not found in summary page")

            # Check 5: Callout with blue background and total points
            callout_found = False
            for block in sorted_blocks:
                block_type = block.get("type", "")
                if block_type == "callout":
                    properties = block.get("properties", {})
                    bg_color = str(properties.get("backgroundColor", "")).lower()
                    content = block.get("content", "").lower()

                    has_blue_bg = "blue" in bg_color
                    has_total_effort = "total sprint effort" in content or "total effort" in content
                    # Total should be 33 points
                    has_correct_total = "33" in content

                    if has_blue_bg and has_total_effort and has_correct_total:
                        callout_found = True
                        break

            if not callout_found:
                errors.append("Callout with blue background showing 'Total Sprint Effort: 33 points' not found")

            # Check 6: heading_2 '📊 Effort by Status'
            heading2_found = False
            heading2_order = -1
            for block in sorted_blocks:
                block_type = block.get("type", "")
                if block_type in ["heading_2", "heading2"]:
                    content = block.get("content", "")
                    if "effort by status" in content.lower() and "📊" in content:
                        heading2_found = True
                        heading2_order = block.get("order", 0)
                        break

            if not heading2_found:
                errors.append("heading_2 '📊 Effort by Status' not found in summary page")

            # Check 7: Bulleted list with all 5 status values
            # Expected data:
            expected_status_data = {
                "not started": {"points": 4, "count": 2},
                "in progress": {"points": 8, "count": 2},
                "in review": {"points": 3, "count": 1},
                "done": {"points": 10, "count": 2},
                "blocked": {"points": 8, "count": 1},
            }

            found_statuses = set()
            bullet_blocks = []
            for block in sorted_blocks:
                block_type = block.get("type", "")
                if block_type in ["bulletedList", "bulleted_list", "bulleted_list_item"]:
                    content = block.get("content", "").lower()
                    bullet_blocks.append(content)

                    for status in expected_status_data.keys():
                        if status in content:
                            found_statuses.add(status)
                            # Verify the data in the bullet
                            expected = expected_status_data[status]
                            # Check if points and count are present
                            if str(expected["points"]) in content and str(expected["count"]) in content:
                                pass  # Data is correct
                            # Also allow "0" for statuses with 0 tasks (though all have tasks in this case)

            missing_statuses = set(expected_status_data.keys()) - found_statuses
            if missing_statuses:
                errors.append(f"Missing status entries in bulleted list: {', '.join(missing_statuses)}")

        # ==========================================================================
        # Return Result
        # ==========================================================================
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Inventory Procurement Merge Task
# =============================================================================
def _validate_agent_inventory_procurement_merge(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Perform a complex database merge operation. In the 'Inventory Tracker' database,
    add two new properties: 'Last Audit Date' (date) after 'Reorder Date', and 'Audit Status'
    (select: 'Current' green, 'Needs Review' yellow, 'Critical' red) after 'Last Audit Date'.
    For all rows where 'Quantity' < 10, set 'Audit Status' = 'Critical' and 'Last Audit Date' =
    {start: '[today's date]'}. For rows where 'Quantity' between 10-50, set 'Audit Status' =
    'Needs Review'. All others set to 'Current'. Then create a new database 'Procurement Pipeline'
    as child of 'Inventory Tracker' page with properties: 'Item' (title), 'Current Stock' (number),
    'Reorder Quantity' (number), 'Urgency' (select: matching Audit Status options), 'Estimated Cost'
    (number), 'Approval Status' (checkbox). For each 'Critical' item in Inventory, create a
    procurement row with Reorder Quantity = 50, Urgency = 'Critical', Estimated Cost = Unit Price * 50.

    Initial State:
    - Inventory Tracker (db-inventory) has 10 rows with varying quantities
    - Critical items (qty < 10): Office Chair Ergonomic (8, $450), Standing Desk (3, $699), Wireless Keyboard (0, $79)
    - Needs Review items (10 <= qty <= 50): MacBook Pro (12), Dell Monitor (25), USB-C Hub (45), Adobe CC (10), MS 365 (50)
    - Current items (qty > 50): Printer Paper (200), Sticky Notes (150)

    Returns 1.0 if ALL checks pass, otherwise 0.0.
    """
    checks_passed: list[str] = []

    errors = []

    # Get all pages
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # Find Inventory Tracker database (db-inventory)
        inventory_db = None
        for page in all_pages:
            if page.get("_id") == "db-inventory" or (
                page.get("type") == "database" and
                "inventory tracker" in page.get("title", "").lower()
            ):
                inventory_db = page
                break

        if not inventory_db:
            errors.append("[X] Inventory Tracker database not found")
        else:
            checks_passed.append("[C] Inventory Tracker database")

        inventory_db_id = inventory_db.get("_id")
        db_metadata = inventory_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Build property name to ID map
        prop_name_to_id = {}
        prop_id_to_name = {}
        for prop in properties:
            name_lower = prop.get("name", "").lower()
            prop_id = prop.get("_id", "")
            prop_name_to_id[name_lower] = prop_id
            prop_id_to_name[prop_id] = prop.get("name", "")

        # =========================================================================
        # Check 1: Verify 'Last Audit Date' property with order 7
        # =========================================================================
        last_audit_date_prop = None
        for prop in properties:
            if "last audit date" in prop.get("name", "").lower():
                last_audit_date_prop = prop
                break

        if not last_audit_date_prop:
            errors.append("'Last Audit Date' property not found in Inventory Tracker")
        else:
            if last_audit_date_prop.get("type") != "date":
                errors.append(f"'Last Audit Date' should be type 'date', got '{last_audit_date_prop.get('type')}'")
            if last_audit_date_prop.get("order") != 7:
                errors.append(f"'Last Audit Date' should have order 7, got '{last_audit_date_prop.get('order')}'")

        # =========================================================================
        # Check 2: Verify 'Audit Status' property with order 8 and correct options
        # =========================================================================
        audit_status_prop = None
        for prop in properties:
            if "audit status" in prop.get("name", "").lower():
                audit_status_prop = prop
                break

        if not audit_status_prop:
            errors.append("'Audit Status' property not found in Inventory Tracker")
        else:
            if audit_status_prop.get("type") != "select":
                errors.append(f"'Audit Status' should be type 'select', got '{audit_status_prop.get('type')}'")
            if audit_status_prop.get("order") != 8:
                errors.append(f"'Audit Status' should have order 8, got '{audit_status_prop.get('order')}'")

            options = audit_status_prop.get("options", [])
            expected_options = {
                "current": "green",
                "needs review": "yellow",
                "critical": "red"
            }
            found_options = {opt.get("name", "").lower(): opt.get("color", "").lower() for opt in options}

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Audit Status missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Audit Status option '{exp_name}' should be '{exp_color}', got '{found_options[exp_name]}'")

        # Get all inventory rows
        inventory_rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == inventory_db_id:
                inventory_rows.append(page)

        # Helper to get cell value by property name (case-insensitive)
        def get_cell_value(row, prop_name_lower):
            cell_values = row.get("cellValues", {})
            # Try by property ID
            prop_id = prop_name_to_id.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Try by partial match in key
            for key, val in cell_values.items():
                key_name = prop_id_to_name.get(key, key)
                if prop_name_lower in key_name.lower() or prop_name_lower.replace(" ", "") in key.lower():
                    return val
            return None

        # Categorize rows by quantity and store their data
        critical_rows = []  # qty < 10
        needs_review_rows = []  # 10 <= qty <= 50
        current_rows = []  # qty > 50

        # Store critical item data for procurement validation
        critical_item_data = {}

        for row in inventory_rows:
            qty = get_cell_value(row, "quantity")
            if qty is None:
                continue
            qty = int(qty)

            unit_price = get_cell_value(row, "unit price")
            if unit_price is None:
                # Try alternative property name
                unit_price = get_cell_value(row, "unit price ($)")
            unit_price = int(unit_price) if unit_price else 0

            if qty < 10:
                critical_rows.append(row)
                critical_item_data[row.get("title", "").lower()] = {
                    "quantity": qty,
                    "unit_price": unit_price,
                    "estimated_cost": unit_price * 50
                }
            elif qty <= 50:
                needs_review_rows.append(row)
            else:
                current_rows.append(row)

        # =========================================================================
        # Check 3: Critical items have Audit Status = 'Critical' and Last Audit Date set
        # =========================================================================
        for row in critical_rows:
            title = row.get("title", "")
            audit_status = get_cell_value(row, "audit status")
            last_audit = get_cell_value(row, "last audit date")

            if audit_status is None or str(audit_status).lower() != "critical":
                errors.append(f"Critical item '{title}' should have Audit Status = 'Critical', got '{audit_status}'")

            if last_audit is None:
                errors.append(f"Critical item '{title}' should have Last Audit Date set")
            else:
                # Check if it has a start date
                if isinstance(last_audit, dict):
                    if not last_audit.get("start"):
                        errors.append(f"Critical item '{title}' Last Audit Date missing start value")
                elif not last_audit:
                    errors.append(f"Critical item '{title}' Last Audit Date is empty")

        # =========================================================================
        # Check 4: Needs Review items have Audit Status = 'Needs Review'
        # =========================================================================
        for row in needs_review_rows:
            title = row.get("title", "")
            audit_status = get_cell_value(row, "audit status")

            if audit_status is None or str(audit_status).lower() != "needs review":
                errors.append(f"Needs Review item '{title}' should have Audit Status = 'Needs Review', got '{audit_status}'")

        # =========================================================================
        # Check 5: Current items have Audit Status = 'Current'
        # =========================================================================
        for row in current_rows:
            title = row.get("title", "")
            audit_status = get_cell_value(row, "audit status")

            if audit_status is None or str(audit_status).lower() != "current":
                errors.append(f"Current item '{title}' should have Audit Status = 'Current', got '{audit_status}'")

        # =========================================================================
        # Check 6: Procurement Pipeline database exists as child of Inventory Tracker
        # =========================================================================
        procurement_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "procurement pipeline" in title:
                    procurement_db = page
                    break

        if not procurement_db:
            errors.append("'Procurement Pipeline' database not found")
        else:
            # Check parent is Inventory Tracker
            parent_id = procurement_db.get("parentId")
            if parent_id != inventory_db_id:
                # Check if parent title matches
                parent_page = None
                for page in all_pages:
                    if page.get("_id") == parent_id:
                        parent_page = page
                        break
                if not parent_page or "inventory tracker" not in parent_page.get("title", "").lower():
                    errors.append(f"'Procurement Pipeline' parent should be 'Inventory Tracker', got parentId: {parent_id}")

        # =========================================================================
        # Check 7: Procurement Pipeline database properties
        # =========================================================================
        proc_properties = []
        if procurement_db:
            proc_metadata = procurement_db.get("databaseMetadata", {})
            proc_properties = proc_metadata.get("properties", [])

            # Build property map for procurement
            proc_prop_map = {}
            for prop in proc_properties:
                proc_prop_map[prop.get("name", "").lower()] = prop

            # Check required properties
            expected_proc_props = [
                ("item", "title"),
                ("current stock", "number"),
                ("reorder quantity", "number"),
                ("urgency", "select"),
                ("estimated cost", "number"),
                ("approval status", "checkbox")
            ]

            for exp_name, exp_type in expected_proc_props:
                found = False
                for prop_name, prop in proc_prop_map.items():
                    if exp_name in prop_name:
                        found = True
                        if prop.get("type") != exp_type:
                            errors.append(f"Procurement Pipeline property '{exp_name}' should be type '{exp_type}', got '{prop.get('type')}'")
                        break
                if not found:
                    errors.append(f"Procurement Pipeline missing property '{exp_name}'")

            # Check Urgency options
            urgency_prop = proc_prop_map.get("urgency")
            if urgency_prop:
                options = urgency_prop.get("options", [])
                expected_urgency_options = {
                    "current": "green",
                    "needs review": "yellow",
                    "critical": "red"
                }
                found_options = {opt.get("name", "").lower(): opt.get("color", "").lower() for opt in options}

                for exp_name, exp_color in expected_urgency_options.items():
                    if exp_name not in found_options:
                        errors.append(f"Urgency missing option '{exp_name}'")
                    elif found_options[exp_name] != exp_color:
                        errors.append(f"Urgency option '{exp_name}' should be '{exp_color}', got '{found_options[exp_name]}'")

        # =========================================================================
        # Check 8: Procurement rows for Critical items with correct values
        # =========================================================================
        if procurement_db:
            procurement_db_id = procurement_db.get("_id")
            procurement_rows = []
            for page in all_pages:
                if page.get("type") == "row" and page.get("databaseId") == procurement_db_id:
                    procurement_rows.append(page)

            # Build property map for procurement rows
            proc_prop_id_map = {}
            proc_prop_id_to_name = {}
            for prop in proc_properties:
                name_lower = prop.get("name", "").lower()
                prop_id = prop.get("_id", "")
                proc_prop_id_map[name_lower] = prop_id
                proc_prop_id_to_name[prop_id] = prop.get("name", "")

            def get_proc_cell_value(row, prop_name_lower):
                cell_values = row.get("cellValues", {})
                prop_id = proc_prop_id_map.get(prop_name_lower)
                if prop_id and prop_id in cell_values:
                    return cell_values.get(prop_id)
                for key, val in cell_values.items():
                    key_name = proc_prop_id_to_name.get(key, key)
                    if prop_name_lower in key_name.lower():
                        return val
                return None

            # Expected critical items with their expected values
            # Office Chair Ergonomic (qty=8, price=450), Standing Desk (qty=3, price=699), Wireless Keyboard (qty=0, price=79)
            expected_critical = {
                "office chair": {"current_stock": 8, "estimated_cost": 22500},
                "standing desk": {"current_stock": 3, "estimated_cost": 34950},
                "wireless keyboard": {"current_stock": 0, "estimated_cost": 3950}
            }

            found_critical_items = set()

            for row in procurement_rows:
                title = row.get("title", "").lower()

                # Check if this matches any critical item
                matched_item = None
                expected_values = None
                for item_name, values in expected_critical.items():
                    if item_name in title:
                        matched_item = item_name
                        expected_values = values
                        break

                if matched_item:
                    found_critical_items.add(matched_item)

                    # Check Current Stock
                    current_stock = get_proc_cell_value(row, "current stock")
                    if current_stock is None or int(current_stock) != expected_values["current_stock"]:
                        errors.append(f"Procurement row '{title}' should have Current Stock = {expected_values['current_stock']}, got '{current_stock}'")

                    # Check Reorder Quantity = 50
                    reorder_qty = get_proc_cell_value(row, "reorder quantity")
                    if reorder_qty is None or int(reorder_qty) != 50:
                        errors.append(f"Procurement row '{title}' should have Reorder Quantity = 50, got '{reorder_qty}'")

                    # Check Urgency = 'Critical'
                    urgency = get_proc_cell_value(row, "urgency")
                    if urgency is None or str(urgency).lower() != "critical":
                        errors.append(f"Procurement row '{title}' should have Urgency = 'Critical', got '{urgency}'")

                    # Check Estimated Cost
                    est_cost = get_proc_cell_value(row, "estimated cost")
                    if est_cost is None or int(est_cost) != expected_values["estimated_cost"]:
                        errors.append(f"Procurement row '{title}' should have Estimated Cost = {expected_values['estimated_cost']}, got '{est_cost}'")

            # Check all critical items have procurement rows
            for item_name in expected_critical.keys():
                if item_name not in found_critical_items:
                    errors.append(f"Missing procurement row for critical item '{item_name}'")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Planning System (Inline Database with Task Migration)
# =============================================================================
def _validate_agent_sprint_planning_system(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a sprint planning system by establishing a new 'Sprint Tasks' database and
    migrating specific tasks. Create the database as an inline database within the 'Q1 Goals' page,
    positioned immediately after the 'Key Results' heading but before the bulleted list items.
    Configure with properties: 'Task' (title), 'Sprint' (select: 'Sprint 1' in blue, 'Sprint 2'
    in green, 'Sprint 3' in purple), 'Story Points' (number), 'Status' (select: 'To Do' in gray,
    'In Progress' in blue, 'Done' in green), 'Owner' (text). Query the Task Tracker database for
    all tasks with Priority='High' or Priority='Urgent' that are not Status='Done'. For each
    matching task, create a row in Sprint Tasks with: Task=[original task name], Sprint='Sprint 1',
    Story Points=5 for Urgent or 3 for High priority, Status='To Do', Owner=[assignee from original].
    After populating, add a paragraph immediately after the database with italic text:
    '*Sprint 1 capacity: [sum of story points] points across [count] high-priority items*'.

    Initial state analysis from Task Tracker (db-tasks):
    - "Fix login authentication bug" - High priority, In Progress, Assignee: user-1 (Alice Chen) -> Include
    - "Update database schema" - Urgent priority, Blocked, Assignee: user-2 (Bob Martinez) -> Include
    - "Implement user notifications" - High priority, Done -> EXCLUDE (status is Done)
    - "Add unit tests for auth module" - High priority, Done -> EXCLUDE (status is Done)

    Expected migrations:
    1. "Fix login authentication bug" -> Sprint 1, Story Points=3 (High), Status='To Do', Owner='Alice Chen'
    2. "Update database schema" -> Sprint 1, Story Points=5 (Urgent), Status='To Do', Owner='Bob Martinez'

    Expected totals: 8 story points (3 + 5), 2 high-priority items
    Summary text: "*Sprint 1 capacity: 8 points across 2 high-priority items*"
    """
    checks_passed: list[str] = []

    errors = []

    # User ID to name mapping for owner validation
    try:
        user_names = {
            "user-1": "alice",
            "user-2": "bob",
            "user-3": "carol",
        }

        # ===== CHECK 1: Sprint Tasks Database Exists with Correct Parent =====
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        sprint_tasks_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "sprint tasks" in title:
                    sprint_tasks_db = page
                    break

        if not sprint_tasks_db:
            errors.append("[X] Database 'Sprint Tasks' not found")
        else:
            checks_passed.append("[C] Database 'Sprint Tasks'")

        # Check parent is Q1 Goals (page-2-1)
        parent_id = sprint_tasks_db.get("parentId")
        if parent_id != "page-2-1":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "q1 goals" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Q1 Goals' (page-2-1), got parentId: {parent_id}")

        db_id = sprint_tasks_db.get("_id")

        # ===== CHECK 2: Database Properties Configuration =====
        db_metadata = sprint_tasks_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Build property map
        prop_name_to_obj = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_name_to_obj[prop_name_lower] = prop

        # Expected properties with types
        expected_properties = {
            "task": "title",
            "sprint": "select",
            "story points": "number",
            "status": "select",
            "owner": "text",
        }

        for expected_name, expected_type in expected_properties.items():
            if expected_name not in prop_name_to_obj:
                errors.append(f"Property '{expected_name}' not found in database")
            else:
                actual_type = prop_name_to_obj[expected_name].get("type", "")
                if actual_type != expected_type:
                    errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{actual_type}'")

        # Validate Sprint select options
        sprint_prop = prop_name_to_obj.get("sprint")
        if sprint_prop:
            options = sprint_prop.get("options", [])
            expected_sprint_options = {
                "sprint 1": "blue",
                "sprint 2": "green",
                "sprint 3": "purple",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_sprint_options.items():
                if exp_name not in found_options:
                    errors.append(f"Sprint property missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Sprint option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Validate Status select options
        status_prop = prop_name_to_obj.get("status")
        if status_prop:
            options = status_prop.get("options", [])
            expected_status_options = {
                "to do": "gray",
                "in progress": "blue",
                "done": "green",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_status_options.items():
                if exp_name not in found_options:
                    errors.append(f"Status property missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # ===== CHECK 3: Migrated Task Rows Exist with Correct Values =====
        # Get all rows in Sprint Tasks database
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        # Build property ID map for cell value lookup
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            """Get cell value by property name from a row."""
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Fallback: try matching by property name in cell_values keys
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        # Expected migrated tasks:
        # 1. "Fix login authentication bug" - High -> 3 points, Owner: Alice
        # 2. "Update database schema" - Urgent -> 5 points, Owner: Bob
        expected_migrations = [
            {
                "title_contains": "fix login authentication bug",
                "sprint": "sprint 1",
                "story_points": 3,
                "status": "to do",
                "owner_contains": "alice",
            },
            {
                "title_contains": "update database schema",
                "sprint": "sprint 1",
                "story_points": 5,
                "status": "to do",
                "owner_contains": "bob",
            },
        ]

        found_migrations = {exp["title_contains"]: False for exp in expected_migrations}

        for row in rows:
            title = row.get("title", "").lower()

            for expected in expected_migrations:
                if expected["title_contains"] in title:
                    found_migrations[expected["title_contains"]] = True

                    # Check Sprint value
                    sprint_val = get_cell_value(row, "sprint")
                    sprint_str = str(sprint_val).lower() if sprint_val else ""
                    if expected["sprint"] not in sprint_str:
                        errors.append(f"Row '{title}' should have Sprint='{expected['sprint']}', got '{sprint_val}'")

                    # Check Story Points value
                    story_points_val = get_cell_value(row, "story points")
                    if story_points_val is None or int(story_points_val) != expected["story_points"]:
                        errors.append(f"Row '{title}' should have Story Points={expected['story_points']}, got '{story_points_val}'")

                    # Check Status value
                    status_val = get_cell_value(row, "status")
                    status_str = str(status_val).lower() if status_val else ""
                    if expected["status"] not in status_str:
                        errors.append(f"Row '{title}' should have Status='{expected['status']}', got '{status_val}'")

                    # Check Owner contains expected name
                    owner_val = get_cell_value(row, "owner")
                    owner_str = str(owner_val).lower() if owner_val else ""
                    if expected["owner_contains"] not in owner_str:
                        errors.append(f"Row '{title}' should have Owner containing '{expected['owner_contains']}', got '{owner_val}'")

        # Check all expected migrations were found
        for title_key, was_found in found_migrations.items():
            if not was_found:
                errors.append(f"Expected migrated task '{title_key}' not found in Sprint Tasks database")

        # ===== CHECK 4: Database Positioning (inline in Q1 Goals page) =====
        # For inline databases, verify there's a database block on page-2-1
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": "page-2-1"}})
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0))

        inline_db_block = None
        for block in all_blocks:
            if block.get("type") == "database":
                props = block.get("properties", {})
                if props.get("databaseId") == db_id:
                    inline_db_block = block
                    break

        # Note: Inline databases may not always create a separate block - the parent relationship is key
        # The database having parentId=page-2-1 already confirms correct placement

        # ===== CHECK 5: Summary Paragraph with Italic Text =====
        # Look for a paragraph block with italic text about Sprint 1 capacity
        # Expected: "*Sprint 1 capacity: 8 points across 2 high-priority items*"
        summary_found = False

        for block in all_blocks:
            if block.get("type") == "paragraph":
                content = block.get("content", "")
                props = block.get("properties", {})

                # Check for italic text (markdown style *text* or italic property)
                # Pattern: starts with *, contains sprint 1 capacity, ends with *
                has_italic_markdown = bool(re.search(r'^\*.*sprint\s*1\s*capacity.*\*$', content, re.IGNORECASE))
                is_italic_property = props.get("italic", False)

                # Check content contains required info
                content_lower = content.lower()
                has_sprint_capacity = "sprint 1 capacity" in content_lower or ("sprint" in content_lower and "capacity" in content_lower)
                has_points = "8 points" in content_lower or ("8" in content and "point" in content_lower)
                has_count = "2" in content

                if (has_italic_markdown or is_italic_property) and has_sprint_capacity and has_points and has_count:
                    summary_found = True
                    break

        if not summary_found:
            errors.append("Summary paragraph with italic text not found. Expected '*Sprint 1 capacity: 8 points across 2 high-priority items*'")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Executive Project Dashboard Task 2
# =============================================================================
def _validate_agent_executive_project_dashboard_2(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Analyze the 'Project Portfolio' database to create a comprehensive dashboard. First, add a new property 'Health Score' (select: 'At Risk' red, 'Needs Attention' yellow, 'On Track' green) to the database schema. Update all rows: set 'Health Score' to 'At Risk' if Status='On Hold' or Budget>75000, 'Needs Attention' if Status='Planning', else 'On Track'. Create page '📈 Executive Project Dashboard' under 'Getting Started'. Add heading_1 '📈 Executive Project Dashboard', callout (blue background) 'Last updated: [today's date]'. Add heading_2 '💰 Budget Analysis' with paragraph 'Total allocated budget: $[SUM] across [COUNT] projects'. Create heading_3 blocks for each department showing 'Department: [X projects, $Y total budget]'. Finally add heading_2 '⚡ Projects Requiring Attention' with numbered list of all 'At Risk' and 'Needs Attention' projects.

    Initial Data Summary:
    - Project Portfolio database (db-projects) with 6 rows
    - Budget property is named "Budget ($)"
    - Expected Health Scores:
      * Website Redesign (Active, $50000) -> On Track
      * Q1 Marketing Campaign (Completed, $25000) -> On Track
      * Mobile App Development (Planning, $100000) -> At Risk (Budget > 75000)
      * Customer Portal (Active, $75000) -> On Track (not > 75000)
      * Employee Training Program (On Hold, $15000) -> At Risk (Status = On Hold)
      * Sales Pipeline Automation (Active, $35000) -> On Track
    - Total Budget: $300,000 across 6 projects
    - Engineering: 3 projects, $225,000
    - Marketing: 1 project, $25,000
    - HR: 1 project, $15,000
    - Sales: 1 project, $35,000
    - At Risk Projects: Mobile App Development, Employee Training Program
    """
    checks_passed: list[str] = []

    errors = []

    # =========================================================================
    # CHECK 1: Health Score Property in Database Schema
    # =========================================================================
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        db_projects = None
        for page in all_pages:
            if page.get("_id") == "db-projects" or (
                page.get("type") == "database" and "project portfolio" in page.get("title", "").lower()
            ):
                db_projects = page
                break

        if not db_projects:
            errors.append("[X] Database 'Project Portfolio' (db-projects) not found")
        else:
            checks_passed.append("[C] Database 'Project Portfolio' (db-projects)")

        db_metadata = db_projects.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Find Health Score property
        health_score_prop = None
        health_score_prop_id = None
        for prop in properties:
            if prop.get("name", "").lower() == "health score":
                health_score_prop = prop
                health_score_prop_id = prop.get("_id")
                break

        if not health_score_prop:
            errors.append("[X] Health Score property not found in Project Portfolio database")
        else:
            checks_passed.append("[C] Health Score property found in Project Portfolio database")

        if health_score_prop.get("type") != "select":
            errors.append(f"Health Score property should be 'select' type, got '{health_score_prop.get('type')}'")

        # Check options
        options = health_score_prop.get("options", [])
        option_map = {}
        for opt in options:
            opt_name = opt.get("name", "").lower()
            opt_color = opt.get("color", "").lower()
            option_map[opt_name] = opt_color

        expected_options = {
            "at risk": "red",
            "needs attention": "yellow",
            "on track": "green",
        }

        for exp_name, exp_color in expected_options.items():
            if exp_name not in option_map:
                errors.append(f"Health Score missing option '{exp_name}'")
            elif option_map[exp_name] != exp_color:
                errors.append(f"Health Score option '{exp_name}' should have color '{exp_color}', got '{option_map[exp_name]}'")

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        # =========================================================================
        # CHECK 2-7: Row Health Score Values
        # =========================================================================
        # Expected health scores based on business logic:
        # At Risk: Status='On Hold' OR Budget>75000
        # Needs Attention: Status='Planning' (and not At Risk)
        # On Track: everything else
        expected_health_scores = {
            "db-projects-row-1": ("Website Redesign", "on track"),
            "db-projects-row-2": ("Q1 Marketing Campaign", "on track"),
            "db-projects-row-3": ("Mobile App Development", "at risk"),  # Budget > 75000
            "db-projects-row-4": ("Customer Portal", "on track"),  # Budget = 75000, not > 75000
            "db-projects-row-5": ("Employee Training Program", "at risk"),  # Status = On Hold
            "db-projects-row-6": ("Sales Pipeline Automation", "on track"),
        }

        for row_id, (project_name, expected_score) in expected_health_scores.items():
            row = None
            for page in all_pages:
                if page.get("_id") == row_id:
                    row = page
                    break

            if not row:
                errors.append(f"Row '{project_name}' ({row_id}) not found")
                continue

            cell_values = row.get("cellValues", {})

            # Get Health Score value using prop ID
            health_score_value = None
            if health_score_prop_id:
                health_score_value = cell_values.get(health_score_prop_id)

            # Also try to find by key containing "health"
            if health_score_value is None:
                for key, value in cell_values.items():
                    if "health" in key.lower():
                        health_score_value = value
                        break

            if health_score_value is None:
                errors.append(f"Row '{project_name}' missing Health Score value")
            elif str(health_score_value).lower() != expected_score:
                errors.append(f"Row '{project_name}' Health Score should be '{expected_score}', got '{health_score_value}'")

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        # =========================================================================
        # CHECK 8: Dashboard Page Created Under Getting Started
        # =========================================================================
        dashboard_page = None
        for page in all_pages:
            page_type = page.get("type", "")
            # Pages can be type "page", "normal", or no type at all (not database/row)
            if page_type not in ["database", "row"]:
                title = page.get("title", "")
                parent_id = page.get("parentId", "")
                if "executive project dashboard" in title.lower() and "📈" in title:
                    # Check if parent is Getting Started (page-1)
                    if parent_id == "page-1":
                        dashboard_page = page
                        break
                    # Also check parent title
                    for p in all_pages:
                        if p.get("_id") == parent_id and "getting started" in p.get("title", "").lower():
                            dashboard_page = page
                            break

        if not dashboard_page:
            errors.append("[X] Dashboard page '📈 Executive Project Dashboard' not found under 'Getting Started'")
        else:
            checks_passed.append("[C] Dashboard page '📈 Executive Project Dashboard' found under 'Getting Started'")

        content = dashboard_page.get("content", [])

        # =========================================================================
        # CHECK 9: Heading 1 with dashboard title
        # =========================================================================
        heading1_found = False
        for block in content:
            if block.get("type") == "heading1":
                block_content = block.get("content", "")
                if "executive project dashboard" in block_content.lower() and "📈" in block_content:
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("Heading 1 '📈 Executive Project Dashboard' not found")

        # =========================================================================
        # CHECK 10: Callout with blue background and date
        # =========================================================================
        callout_valid = False
        for block in content:
            if block.get("type") == "callout":
                props = block.get("properties", {})
                bg_color = props.get("backgroundColor", "").lower()
                block_content = block.get("content", "")

                if "blue" in bg_color and "last updated" in block_content.lower():
                    # Check for a date pattern (various formats)
                    date_pattern = r'\d{4}[-/]\d{1,2}[-/]\d{1,2}|\d{1,2}[-/]\d{1,2}[-/]\d{4}|\w+\s+\d{1,2},?\s+\d{4}|\d{1,2}\s+\w+\s+\d{4}'
                    if re.search(date_pattern, block_content):
                        callout_valid = True
                        break

        if not callout_valid:
            errors.append("Callout with blue background and 'Last updated: [date]' not found")

        # =========================================================================
        # CHECK 11: Budget Analysis Heading
        # =========================================================================
        budget_heading_found = False
        for block in content:
            if block.get("type") == "heading2":
                block_content = block.get("content", "")
                if "budget analysis" in block_content.lower() and "💰" in block_content:
                    budget_heading_found = True
                    break

        if not budget_heading_found:
            errors.append("Heading 2 '💰 Budget Analysis' not found")

        # =========================================================================
        # CHECK 12: Budget Summary Paragraph
        # =========================================================================
        budget_paragraph_valid = False
        for block in content:
            if block.get("type") == "paragraph":
                block_content = block.get("content", "").lower().replace(",", "").replace(" ", "")
                # Check for total budget mention (around $300,000) and count (6 projects)
                # Accept formats: $300000, $300,000, 300000, 300,000
                has_budget = "300000" in block_content or "300k" in block_content
                has_count = "6" in block.get("content", "") and "project" in block.get("content", "").lower()
                if has_budget and has_count:
                    budget_paragraph_valid = True
                    break

        if not budget_paragraph_valid:
            errors.append("Budget summary paragraph with '$300,000' and '6 projects' not found")

        # =========================================================================
        # CHECK 13: Department Breakdown - heading3 blocks for each department
        # =========================================================================
        departments_found = {
            "engineering": False,
            "marketing": False,
            "hr": False,
            "sales": False
        }

        for block in content:
            if block.get("type") == "heading3":
                block_content = block.get("content", "").lower()
                for dept in departments_found.keys():
                    if dept in block_content:
                        departments_found[dept] = True

        missing_depts = [dept for dept, found in departments_found.items() if not found]
        if missing_depts:
            errors.append(f"Missing department heading3 blocks for: {', '.join(missing_depts)}")

        # =========================================================================
        # CHECK 14: Projects Requiring Attention Heading
        # =========================================================================
        attention_heading_found = False
        for block in content:
            if block.get("type") == "heading2":
                block_content = block.get("content", "")
                if "projects requiring attention" in block_content.lower() and "⚡" in block_content:
                    attention_heading_found = True
                    break

        if not attention_heading_found:
            errors.append("Heading 2 '⚡ Projects Requiring Attention' not found")

        # =========================================================================
        # CHECK 15: Numbered List with At Risk projects
        # =========================================================================
        at_risk_projects_found = {
            "mobile app development": False,
            "employee training program": False
        }

        for block in content:
            if block.get("type") == "numberedList":
                block_content = block.get("content", "").lower()
                for project in at_risk_projects_found.keys():
                    if project in block_content:
                        at_risk_projects_found[project] = True

        missing_projects = [proj for proj, found in at_risk_projects_found.items() if not found]
        if missing_projects:
            errors.append(f"Numbered list missing At Risk projects: {', '.join(missing_projects)}")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Meeting Scheduler Database Task
# =============================================================================
def _validate_agent_meeting_scheduler_database(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a 'Meeting Scheduler' database inside the 'Onboarding Guide' page, positioned immediately after the 'First Week Goals' numbered list. Configure properties: 'Meeting Title' (title), 'Attendees' (multi_select: 'New Hire' blue, 'Manager' green, 'HR' purple, 'IT' orange, 'Team' yellow), 'Date' (date), 'Duration (min)' (number), 'Meeting Type' (select: '1:1' gray, 'Group' blue, 'Training' green, 'Review' purple), 'Completed' (checkbox). Query 'Contacts Directory' to find all contacts with Type='Partner', then create meeting entries for each: Meeting Title='Partner Sync: [Contact Name]', Attendees=['Manager', 'Team'], Date={start: '2026-01-20'}, Duration=60, Meeting Type='Group', Completed=false. After creating the database, add a divider block followed by a paragraph with italic text: '*Automated partner meetings scheduled for [COUNT] partners*'.

    Initial Data:
    - Onboarding Guide is page-13
    - Last 'First Week Goals' block is block-13-10 at order 9
    - Partners in Contacts Directory: Michael Chen, Robert Garcia (2 total)
    """
    checks_passed: list[str] = []

    errors = []

    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # =========================================================================
        # CHECK 1: Meeting Scheduler Database Exists inside Onboarding Guide
        # =========================================================================
        meeting_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                parent_id = page.get("parentId", "")
                if "meeting scheduler" in title:
                    # Check if parent is Onboarding Guide (page-13)
                    if parent_id == "page-13":
                        meeting_db = page
                        break
                    # Also check parent title
                    for p in all_pages:
                        if p.get("_id") == parent_id and "onboarding guide" in p.get("title", "").lower():
                            meeting_db = page
                            break

        if not meeting_db:
            errors.append("[X] Meeting Scheduler database not found inside Onboarding Guide page")
        else:
            checks_passed.append("[C] Meeting Scheduler database found inside Onboarding Guide page")

        db_id = meeting_db.get("_id")
        db_metadata = meeting_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # =========================================================================
        # CHECK 2-7: Validate Database Properties
        # =========================================================================
        # Create property lookup
        prop_map = {}
        for prop in properties:
            prop_map[prop.get("name", "").lower()] = prop

        # CHECK 2: Meeting Title (title)
        if "meeting title" not in prop_map:
            errors.append("Property 'Meeting Title' not found")
        elif prop_map["meeting title"].get("type") != "title":
            errors.append("Property 'Meeting Title' should be type 'title'")

        # CHECK 3: Attendees (multi_select with specific options)
        if "attendees" not in prop_map:
            errors.append("Property 'Attendees' not found")
        else:
            attendees_prop = prop_map["attendees"]
            if attendees_prop.get("type") != "multi_select":
                errors.append("Property 'Attendees' should be type 'multi_select'")
            else:
                options = attendees_prop.get("options", [])
                option_map = {opt.get("name", "").lower(): opt.get("color", "").lower() for opt in options}
                expected_attendees = {
                    "new hire": "blue",
                    "manager": "green",
                    "hr": "purple",
                    "it": "orange",
                    "team": "yellow"
                }
                for name, color in expected_attendees.items():
                    if name not in option_map:
                        errors.append(f"Attendees missing option '{name}'")
                    elif option_map[name] != color:
                        errors.append(f"Attendees option '{name}' should have color '{color}'")

        # CHECK 4: Date (date)
        if "date" not in prop_map:
            errors.append("Property 'Date' not found")
        elif prop_map["date"].get("type") != "date":
            errors.append("Property 'Date' should be type 'date'")

        # CHECK 5: Duration (min) (number)
        duration_found = False
        for prop_name in prop_map:
            if "duration" in prop_name:
                if prop_map[prop_name].get("type") == "number":
                    duration_found = True
                else:
                    errors.append("Property 'Duration (min)' should be type 'number'")
                break
        if not duration_found:
            errors.append("Property 'Duration (min)' not found")

        # CHECK 6: Meeting Type (select with specific options)
        if "meeting type" not in prop_map:
            errors.append("Property 'Meeting Type' not found")
        else:
            meeting_type_prop = prop_map["meeting type"]
            if meeting_type_prop.get("type") != "select":
                errors.append("Property 'Meeting Type' should be type 'select'")
            else:
                options = meeting_type_prop.get("options", [])
                option_map = {opt.get("name", "").lower(): opt.get("color", "").lower() for opt in options}
                expected_types = {
                    "1:1": "gray",
                    "group": "blue",
                    "training": "green",
                    "review": "purple"
                }
                for name, color in expected_types.items():
                    if name not in option_map:
                        errors.append(f"Meeting Type missing option '{name}'")
                    elif option_map[name] != color:
                        errors.append(f"Meeting Type option '{name}' should have color '{color}'")

        # CHECK 7: Completed (checkbox)
        if "completed" not in prop_map:
            errors.append("Property 'Completed' not found")
        elif prop_map["completed"].get("type") != "checkbox":
            errors.append("Property 'Completed' should be type 'checkbox'")

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        # =========================================================================
        # CHECK 8-10: Validate Meeting Rows for Partners
        # =========================================================================
        # Expected partners: Michael Chen, Robert Garcia
        expected_partners = ["michael chen", "robert garcia"]

        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        # CHECK 10: Correct number of rows (2)
        if len(rows) != 2:
            errors.append(f"Expected 2 meeting rows (one per partner), found {len(rows)}")

        # Find property IDs for validation
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        # CHECK 8-9: Validate each partner meeting row
        found_partners = set()
        for row in rows:
            title = row.get("title", "").lower()
            cell_values = row.get("cellValues", {})

            # Check title format: "Partner Sync: [Name]"
            if "partner sync" not in title:
                errors.append(f"Row title '{row.get('title')}' should be 'Partner Sync: [Contact Name]'")
                continue

            # Extract partner name from title
            for partner in expected_partners:
                if partner in title:
                    found_partners.add(partner)

                    # Validate cell values
                    # Check Attendees contains Manager and Team
                    attendees_id = prop_id_map.get("attendees")
                    if attendees_id:
                        attendees = cell_values.get(attendees_id, [])
                        attendees_lower = [a.lower() for a in attendees] if isinstance(attendees, list) else []
                        if "manager" not in attendees_lower or "team" not in attendees_lower:
                            errors.append(f"Row for '{partner}' should have Attendees=['Manager', 'Team']")

                    # Check Date = 2026-01-20
                    date_id = prop_id_map.get("date")
                    if date_id:
                        date_val = cell_values.get(date_id, {})
                        start_date = date_val.get("start", "") if isinstance(date_val, dict) else str(date_val)
                        if "2026-01-20" not in str(start_date):
                            errors.append(f"Row for '{partner}' should have Date start='2026-01-20'")

                    # Check Duration = 60
                    duration_id = None
                    for pname, pid in prop_id_map.items():
                        if "duration" in pname:
                            duration_id = pid
                            break
                    if duration_id:
                        duration = cell_values.get(duration_id)
                        if duration != 60:
                            errors.append(f"Row for '{partner}' should have Duration=60, got {duration}")

                    # Check Meeting Type = Group
                    meeting_type_id = prop_id_map.get("meeting type")
                    if meeting_type_id:
                        meeting_type = cell_values.get(meeting_type_id, "")
                        if str(meeting_type).lower() != "group":
                            errors.append(f"Row for '{partner}' should have Meeting Type='Group'")

                    # Check Completed = false
                    completed_id = prop_id_map.get("completed")
                    if completed_id:
                        completed = cell_values.get(completed_id, True)
                        if completed is not False:
                            errors.append(f"Row for '{partner}' should have Completed=false")
                    break

        # Check all expected partners have rows
        missing_partners = set(expected_partners) - found_partners
        if missing_partners:
            errors.append(f"Missing meeting rows for partners: {', '.join(missing_partners)}")

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        # =========================================================================
        # CHECK 11-12: Divider and Italic Summary Paragraph
        # =========================================================================
        # Get blocks from Onboarding Guide page
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": "page-13"}})
        if not all_blocks:
            all_blocks = []

        # Sort blocks by order
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0))

        # CHECK 11: Find divider block
        divider_found = False
        divider_order = -1
        for block in sorted_blocks:
            if block.get("type") == "divider":
                divider_found = True
                divider_order = block.get("order", 0)
                break

        if not divider_found:
            errors.append("Divider block not found after database")

        # CHECK 12: Find italic summary paragraph after divider
        summary_found = False
        for block in sorted_blocks:
            if block.get("type") == "paragraph":
                content = block.get("content", "")
                props = block.get("properties", {})
                block_order = block.get("order", 0)

                # Check it's after divider (if divider exists)
                if divider_order >= 0 and block_order <= divider_order:
                    continue

                # Check for italic markdown (*text*) or italic property
                has_italic_markdown = bool(re.search(r'(?<!\*)\*[^*]+partner\s+meetings[^*]+\*(?!\*)', content, re.IGNORECASE))
                is_italic_property = props.get("italic", False)

                # Check content contains required text with count
                content_lower = content.lower()
                if "partner meetings" in content_lower and "2" in content:
                    if has_italic_markdown or is_italic_property:
                        summary_found = True
                        break

        if not summary_found:
            errors.append("Italic summary paragraph '*Automated partner meetings scheduled for 2 partners*' not found after divider")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Client Engagement Tracker Task
# =============================================================================
def _validate_agent_client_engagement_tracker(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a 'Client Engagement Tracker' database as a child of 'Team Directory' with these
    properties: 'Client Name' (title), 'Last Contact' (date), 'Engagement Score' (number),
    'Account Status' (select: 'Active' green, 'At Risk' yellow, 'Churned' red, 'Prospect' blue),
    'Next Action' (text), 'Tags' (multi_select: copy exact options and colors from Tags property
    in Contacts Directory database). After creation, populate it by querying 'Contacts Directory'
    database for all rows where Type='Client'. For each client contact found, create a row in the
    new database with: Client Name=[contact name], Last Contact={start: '2026-01-10'},
    Engagement Score=85 if Tags contains 'VIP' else 70, Account Status='Active' if Tags contains
    'Active' else 'At Risk', Tags=copy the exact tags from the contact. Then add a heading_2
    '📊 Engagement Metrics' at the top of the Team Directory page with a bulleted list showing
    'Total Clients: [X]', 'Average Engagement Score: [Y]', 'At Risk Accounts: [Z]'.

    Expected from initial data:
    - Client contacts: Sarah Johnson (VIP, Active), Lisa Thompson (VIP, Active)
    - Both have VIP tag -> Engagement Score = 85
    - Both have Active tag -> Account Status = 'Active'
    - Total Clients: 2, Average Engagement Score: 85, At Risk Accounts: 0
    """
    checks_passed: list[str] = []

    errors = []

    # Step 1: Find the Client Engagement Tracker database
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        client_tracker_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "client engagement tracker" in title:
                    client_tracker_db = page
                    break

        if not client_tracker_db:
            errors.append("[X] Database 'Client Engagement Tracker' not found")
        else:
            checks_passed.append("[C] Database 'Client Engagement Tracker'")

        # Step 2: Check parent is Team Directory (page-8)
        parent_id = client_tracker_db.get("parentId")
        if parent_id != "page-8":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "team directory" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Team Directory' (page-8), got parentId: {parent_id}")

        # Step 3: Validate database properties exist with correct types
        db_metadata = client_tracker_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        expected_properties = {
            "client name": "title",
            "last contact": "date",
            "engagement score": "number",
            "account status": "select",
            "next action": "text",
            "tags": "multi_select",
        }

        prop_name_to_obj = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_name_to_obj[prop_name_lower] = prop

        for expected_name, expected_type in expected_properties.items():
            if expected_name not in prop_name_to_obj:
                errors.append(f"Property '{expected_name}' not found in database")
            else:
                actual_type = prop_name_to_obj[expected_name].get("type", "")
                if actual_type != expected_type:
                    errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{actual_type}'")

        # Step 4: Validate Account Status select options
        account_status_prop = prop_name_to_obj.get("account status")
        if account_status_prop:
            options = account_status_prop.get("options", [])
            expected_options = {
                "active": "green",
                "at risk": "yellow",
                "churned": "red",
                "prospect": "blue",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Account Status missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Account Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")
        else:
            errors.append("Account Status property not found")

        # Step 5: Validate Tags multi_select options (must match Contacts Directory Tags)
        tags_prop = prop_name_to_obj.get("tags")
        if tags_prop:
            options = tags_prop.get("options", [])
            # Expected from Contacts Directory Tags property
            expected_tag_options = {
                "vip": "red",
                "active": "green",
                "follow up": "yellow",
                "new": "blue",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_tag_options.items():
                if exp_name not in found_options:
                    errors.append(f"Tags missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Tags option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")
        else:
            errors.append("Tags property not found")

        # Step 6: Find and validate rows in the Client Engagement Tracker database
        db_id = client_tracker_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        # Build property ID map
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        # Helper to get cell value by property name
        def get_cell_value(cell_values, prop_name_lower):
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Also try matching by property name directly in cell_values keys
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                    return val
            return None

        # Expected clients from initial data:
        # Sarah Johnson: Tags = ["VIP", "Active"] -> Score=85, Status='Active'
        # Lisa Thompson: Tags = ["VIP", "Active"] -> Score=85, Status='Active'
        expected_clients = {
            "sarah johnson": {"score": 85, "status": "active", "tags": ["VIP", "Active"]},
            "lisa thompson": {"score": 85, "status": "active", "tags": ["VIP", "Active"]},
        }

        found_clients = {}
        for row in rows:
            title = row.get("title", "").lower()
            for client_name in expected_clients.keys():
                if client_name in title:
                    found_clients[client_name] = row
                    break

        # Validate each expected client row
        for client_name, expected_values in expected_clients.items():
            if client_name not in found_clients:
                errors.append(f"Row for '{client_name}' not found in Client Engagement Tracker")
                continue

            row = found_clients[client_name]
            cell_values = row.get("cellValues", {})

            # Check Last Contact = {start: '2026-01-10'}
            last_contact = get_cell_value(cell_values, "last contact")
            if last_contact:
                start_val = last_contact.get("start", "") if isinstance(last_contact, dict) else str(last_contact)
                if "2026-01-10" not in str(start_val):
                    errors.append(f"{client_name}: Last Contact should be '2026-01-10', got '{start_val}'")
            else:
                errors.append(f"{client_name}: Missing Last Contact")

            # Check Engagement Score
            engagement_score = get_cell_value(cell_values, "engagement score")
            if engagement_score is not None:
                if int(engagement_score) != expected_values["score"]:
                    errors.append(f"{client_name}: Engagement Score should be {expected_values['score']}, got {engagement_score}")
            else:
                errors.append(f"{client_name}: Missing Engagement Score")

            # Check Account Status
            account_status = get_cell_value(cell_values, "account status")
            if account_status:
                if str(account_status).lower() != expected_values["status"]:
                    errors.append(f"{client_name}: Account Status should be '{expected_values['status']}', got '{account_status}'")
            else:
                errors.append(f"{client_name}: Missing Account Status")

            # Check Tags
            tags = get_cell_value(cell_values, "tags")
            if tags:
                if isinstance(tags, list):
                    tags_lower = [t.lower() for t in tags]
                    for expected_tag in expected_values["tags"]:
                        if expected_tag.lower() not in tags_lower:
                            errors.append(f"{client_name}: Tags should contain '{expected_tag}'")
                else:
                    errors.append(f"{client_name}: Tags should be a list, got {type(tags)}")
            else:
                errors.append(f"{client_name}: Missing Tags")

        # Step 7: Validate blocks on Team Directory page (page-8)
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})
        team_directory_blocks = [b for b in all_blocks if b.get("pageId") == "page-8"]

        # Sort by order to check position
        team_directory_blocks.sort(key=lambda b: b.get("order", 999))

        # Check for heading2 with "📊 Engagement Metrics"
        heading_found = False
        heading_order = None
        for block in team_directory_blocks:
            if block.get("type") == "heading2":
                content = block.get("content", "")
                if "engagement metrics" in content.lower() and "📊" in content:
                    heading_found = True
                    heading_order = block.get("order", 999)
                    break

        if not heading_found:
            errors.append("Heading2 '📊 Engagement Metrics' not found on Team Directory page")

        # Check for bulleted list items with metrics
        # Expected: Total Clients: 2, Average Engagement Score: 85, At Risk Accounts: 0
        metrics_found = {"total clients": False, "average engagement score": False, "at risk accounts": False}

        for block in team_directory_blocks:
            if block.get("type") == "bulletedList":
                content = block.get("content", "").lower()
                if "total clients" in content and "2" in content:
                    metrics_found["total clients"] = True
                if "average engagement score" in content and "85" in content:
                    metrics_found["average engagement score"] = True
                if "at risk accounts" in content and "0" in content:
                    metrics_found["at risk accounts"] = True

        for metric_name, found in metrics_found.items():
            if not found:
                if metric_name == "total clients":
                    errors.append("Bulleted list item 'Total Clients: 2' not found")
                elif metric_name == "average engagement score":
                    errors.append("Bulleted list item 'Average Engagement Score: 85' not found")
                elif metric_name == "at risk accounts":
                    errors.append("Bulleted list item 'At Risk Accounts: 0' not found")

        # Return result
        if errors:
            errors.append("[X] Validation failed: {'; '.join(errors)}")
        else:
            checks_passed.append("[C] Validation succeeded")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Automated Inventory Reordering Task
# =============================================================================
def _validate_agent_automated_inventory_reordering(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create an automated inventory reordering system by updating the 'Inventory Tracker'
    database schema and generating purchase orders. First, add a new property to Inventory Tracker:
    'Reorder Threshold' (number) and set it to 5 for Electronics/Hardware categories, 10 for
    Office Supplies, and 3 for all others. Then create a 'Purchase Orders' database as child of
    'Inventory Tracker' page with properties: 'PO Number' (title), 'Item' (text), 'Quantity to Order'
    (number), 'Unit Cost' (number), 'Total Cost' (number), 'Vendor' (text), 'Status' (select:
    'Draft' gray, 'Submitted' blue, 'Approved' green, 'Received' purple), 'Order Date' (date).
    For each inventory item where Quantity <= Reorder Threshold, create a purchase order:
    PO Number='PO-2026-[sequential number]', Item=[Item Name], Quantity to Order=(Reorder Threshold × 2)
    - current Quantity, Unit Cost=[item's Unit Price], Total Cost=Quantity to Order × Unit Cost,
    Status='Draft', Order Date=today. Finally, add a callout at the top of Inventory Tracker page
    (orange_background) with 'Pending Orders: [X] items below threshold - Total PO value: $[Y]'.

    Expected from initial data:
    - Items needing PO (Quantity <= Threshold):
      - Standing Desk: Furniture, Qty=3, Threshold=3, Price=699 -> Qty to Order=3, Total=2097
      - Wireless Keyboard: Hardware, Qty=0, Threshold=5, Price=79 -> Qty to Order=10, Total=790
    - Pending Orders: 2, Total PO value: $2887
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # Step 1: Find the Inventory Tracker database
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        inventory_db = None
        for page in all_pages:
            if page.get("_id") == "db-inventory" or (
                page.get("type") == "database" and "inventory tracker" in page.get("title", "").lower()
            ):
                inventory_db = page
                break

        if not inventory_db:
            errors.append("[X] Database 'Inventory Tracker' not found")
        else:
            checks_passed.append("[C] Database 'Inventory Tracker'")

        inventory_db_id = inventory_db.get("_id")

        # Step 2: Check that 'Reorder Threshold' property was added
        db_metadata = inventory_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        reorder_threshold_prop = None
        prop_name_to_obj = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_name_to_obj[prop_name_lower] = prop
            if "reorder" in prop_name_lower and "threshold" in prop_name_lower:
                reorder_threshold_prop = prop

        if not reorder_threshold_prop:
            errors.append("'Reorder Threshold' property not found in Inventory Tracker")
        elif reorder_threshold_prop.get("type") != "number":
            errors.append(f"'Reorder Threshold' should be type 'number', got '{reorder_threshold_prop.get('type')}'")

        # Step 3: Validate threshold values for inventory rows
        inventory_rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == inventory_db_id:
                inventory_rows.append(page)

        # Build property ID map for inventory
        inv_prop_id_map = {}
        for prop in properties:
            inv_prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_inv_cell_value(cell_values, prop_name_lower):
            prop_id = inv_prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                    return val
            return None

        # Expected thresholds by category
        expected_thresholds = {
            "electronics": 5,
            "hardware": 5,
            "office supplies": 10,
            "furniture": 3,
            "software": 3,
        }

        # Validate each row has the correct threshold set
        for row in inventory_rows:
            cell_values = row.get("cellValues", {})
            item_name = row.get("title", "")
            category = get_inv_cell_value(cell_values, "category")
            threshold = get_inv_cell_value(cell_values, "reorder threshold")

            if category:
                category_lower = str(category).lower()
                expected_threshold = expected_thresholds.get(category_lower, 3)

                if threshold is not None:
                    try:
                        if int(threshold) != expected_threshold:
                            errors.append(f"{item_name}: Reorder Threshold should be {expected_threshold} for {category}, got {threshold}")
                    except (ValueError, TypeError):
                        errors.append(f"{item_name}: Reorder Threshold should be a number, got {threshold}")
                else:
                    errors.append(f"{item_name}: Missing Reorder Threshold value")

        # Step 4: Find the Purchase Orders database as child of Inventory Tracker
        purchase_orders_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "purchase order" in title:
                    parent_id = page.get("parentId")
                    if parent_id == inventory_db_id:
                        purchase_orders_db = page
                        break
                    # Also check if parent is Inventory Tracker by title
                    for p in all_pages:
                        if p.get("_id") == parent_id and "inventory tracker" in p.get("title", "").lower():
                            purchase_orders_db = page
                            break
                    if purchase_orders_db:
                        break

        if not purchase_orders_db:
            errors.append("'Purchase Orders' database not found as child of Inventory Tracker")
        else:
            # Step 5: Validate Purchase Orders database properties
            po_metadata = purchase_orders_db.get("databaseMetadata", {})
            po_properties = po_metadata.get("properties", [])

            expected_po_props = {
                "item": "text",
                "quantity to order": "number",
                "unit cost": "number",
                "total cost": "number",
                "vendor": "text",
                "status": "select",
                "order date": "date",
            }

            po_prop_name_to_obj = {}
            for prop in po_properties:
                prop_name_lower = prop.get("name", "").lower()
                po_prop_name_to_obj[prop_name_lower] = prop

            # Check for title property (PO Number)
            has_title_prop = False
            for prop in po_properties:
                if prop.get("type") == "title":
                    has_title_prop = True
                    break

            if not has_title_prop:
                errors.append("Purchase Orders database missing title property (PO Number)")

            for expected_name, expected_type in expected_po_props.items():
                found = False
                for prop_name, prop in po_prop_name_to_obj.items():
                    if expected_name.replace(" ", "") in prop_name.replace(" ", ""):
                        found = True
                        if prop.get("type") != expected_type:
                            errors.append(f"PO property '{expected_name}' should be type '{expected_type}', got '{prop.get('type')}'")
                        break
                if not found:
                    errors.append(f"PO property '{expected_name}' not found")

            # Step 6: Validate Status select options
            status_prop = po_prop_name_to_obj.get("status")
            if status_prop:
                options = status_prop.get("options", [])
                expected_status_options = {
                    "draft": "gray",
                    "submitted": "blue",
                    "approved": "green",
                    "received": "purple",
                }

                found_options = {}
                for opt in options:
                    opt_name = opt.get("name", "").lower()
                    opt_color = opt.get("color", "").lower()
                    found_options[opt_name] = opt_color

                for exp_name, exp_color in expected_status_options.items():
                    if exp_name not in found_options:
                        errors.append(f"PO Status missing option '{exp_name}'")
                    elif found_options[exp_name] != exp_color:
                        errors.append(f"PO Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

            # Step 7: Validate purchase order rows
            po_db_id = purchase_orders_db.get("_id")
            po_rows = []
            for page in all_pages:
                if page.get("type") == "row" and page.get("databaseId") == po_db_id:
                    po_rows.append(page)

            # Build property ID map for PO
            po_prop_id_map = {}
            for prop in po_properties:
                po_prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

            def get_po_cell_value(cell_values, prop_name_lower):
                prop_id = po_prop_id_map.get(prop_name_lower)
                if prop_id and prop_id in cell_values:
                    return cell_values.get(prop_id)
                for key, val in cell_values.items():
                    if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                        return val
                return None

            # Expected PO items:
            # Standing Desk: Qty=3, Threshold=3, Price=699 -> Qty to Order=3, Total=2097
            # Wireless Keyboard: Qty=0, Threshold=5, Price=79 -> Qty to Order=10, Total=790
            expected_pos = {
                "standing desk": {"qty_to_order": 3, "unit_cost": 699, "total_cost": 2097},
                "wireless keyboard": {"qty_to_order": 10, "unit_cost": 79, "total_cost": 790},
            }

            found_pos = {}
            for row in po_rows:
                title = row.get("title", "").lower()
                cell_values = row.get("cellValues", {})

                # Check if this is one of the expected POs by item name
                item_value = get_po_cell_value(cell_values, "item")
                if item_value:
                    item_lower = str(item_value).lower()
                else:
                    item_lower = ""

                for expected_item in expected_pos.keys():
                    if expected_item in item_lower or expected_item in title:
                        found_pos[expected_item] = row
                        break

            # Validate each expected PO
            for item_name, expected_values in expected_pos.items():
                if item_name not in found_pos:
                    errors.append(f"Purchase order for '{item_name}' not found")
                    continue

                row = found_pos[item_name]
                cell_values = row.get("cellValues", {})
                po_title = row.get("title", "")

                # Check PO Number format
                if not (po_title.startswith("PO-2026-") or "po-2026-" in po_title.lower()):
                    errors.append(f"{item_name} PO: PO Number should start with 'PO-2026-', got '{po_title}'")

                # Check Quantity to Order
                qty_to_order = get_po_cell_value(cell_values, "quantity to order")
                if qty_to_order is not None:
                    try:
                        if int(qty_to_order) != expected_values["qty_to_order"]:
                            errors.append(f"{item_name} PO: Quantity to Order should be {expected_values['qty_to_order']}, got {qty_to_order}")
                    except (ValueError, TypeError):
                        errors.append(f"{item_name} PO: Quantity to Order should be a number")
                else:
                    errors.append(f"{item_name} PO: Missing Quantity to Order")

                # Check Unit Cost
                unit_cost = get_po_cell_value(cell_values, "unit cost")
                if unit_cost is not None:
                    try:
                        if int(unit_cost) != expected_values["unit_cost"]:
                            errors.append(f"{item_name} PO: Unit Cost should be {expected_values['unit_cost']}, got {unit_cost}")
                    except (ValueError, TypeError):
                        errors.append(f"{item_name} PO: Unit Cost should be a number")
                else:
                    errors.append(f"{item_name} PO: Missing Unit Cost")

                # Check Total Cost
                total_cost = get_po_cell_value(cell_values, "total cost")
                if total_cost is not None:
                    try:
                        if int(total_cost) != expected_values["total_cost"]:
                            errors.append(f"{item_name} PO: Total Cost should be {expected_values['total_cost']}, got {total_cost}")
                    except (ValueError, TypeError):
                        errors.append(f"{item_name} PO: Total Cost should be a number")
                else:
                    errors.append(f"{item_name} PO: Missing Total Cost")

                # Check Status = Draft
                status = get_po_cell_value(cell_values, "status")
                if status:
                    if str(status).lower() != "draft":
                        errors.append(f"{item_name} PO: Status should be 'Draft', got '{status}'")
                else:
                    errors.append(f"{item_name} PO: Missing Status")

            # Check we have exactly 2 POs (not more)
            if len(po_rows) != 2:
                errors.append(f"Expected exactly 2 purchase orders, found {len(po_rows)}")

        # Step 8: Validate callout on Inventory Tracker page
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})
        inventory_blocks = [b for b in all_blocks if b.get("pageId") == inventory_db_id]

        callout_found = False
        for block in inventory_blocks:
            if block.get("type") == "callout":
                props = block.get("properties", {})
                bg_color = props.get("backgroundColor", "").lower()
                content = block.get("content", "").lower()

                if "orange" in bg_color:
                    # Check for pending orders count and total value
                    has_pending_count = "pending orders" in content and "2" in content
                    has_total_value = "2887" in content or "$2,887" in content.replace(",", "")

                    if has_pending_count and has_total_value:
                        callout_found = True
                        break

        if not callout_found:
            errors.append("Orange callout with 'Pending Orders: 2' and 'Total PO value: $2887' not found on Inventory Tracker page")

        # Return result
        if errors:
            errors.append("[X] Validation failed: {'; '.join(errors)}")
        else:
            checks_passed.append("[C] Validation succeeded")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# VIP Client Migration Task
# =============================================================================
def _validate_agent_vip_client_migration(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Perform a complex migration between databases. First, query 'Contacts Directory' for all
    contacts where Type='Client' and Tags contains 'VIP'. For each VIP client, create a corresponding
    project in 'Project Portfolio' database with: Project Name='[Company] - Account Management 2026',
    Status='Planning', Department='Sales', Budget=50000, Timeline start=today and end=90 days from today.
    Then create a new database 'Client Engagement Tracker' under 'Team Directory' page with properties:
    'Client Name' (title), 'Contact Email' (email), 'Last Meeting' (date), 'Next Touchpoint' (date),
    'Account Status' (select with 'Active' green, 'At Risk' yellow, 'Churned' red, 'Prospecting' blue),
    'Engagement Score' (number), 'Account Manager' (text), 'Notes' (text). Populate this database with
    all VIP clients, setting Account Status='Active', Engagement Score=85, Next Touchpoint=14 days from
    today. Finally, add a section at the top of 'Sprint Planning - Jan 15' page (after the main heading
    but before 'Attendees'): heading_2 '🌟 VIP Client Projects' followed by a callout (purple background)
    'New VIP client projects added: [X]' and a numbered list of the new project names.

    Expected from initial data:
    - VIP Clients (Type='Client' AND Tags contains 'VIP'):
      1. Sarah Johnson - TechCorp, email: sarah.j@techcorp.com
      2. Lisa Thompson - Enterprise Co, email: lthompson@enterprise.com
    - Total VIP clients: 2
    - New projects: "TechCorp - Account Management 2026", "Enterprise Co - Account Management 2026"
    - Sprint Planning page (page-3-1): Insert after heading1 (order 0) but before "Attendees" (order 1)
    """
    checks_passed: list[str] = []

    errors = []
    try:
        from datetime import datetime, timedelta

        # Get today's date for validation
        today = datetime.now().date()
        today_str = today.strftime("%Y-%m-%d")
        ninety_days_str = (today + timedelta(days=90)).strftime("%Y-%m-%d")
        fourteen_days_str = (today + timedelta(days=14)).strftime("%Y-%m-%d")

        # ==========================================================================
        # Check 1: Verify new projects in Project Portfolio database for VIP clients
        # ==========================================================================
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # Find Project Portfolio database (db-projects)
        project_portfolio_db = None
        for page in all_pages:
            if page.get("_id") == "db-projects" or (page.get("type") == "database" and "project portfolio" in page.get("title", "").lower()):
                project_portfolio_db = page
                break

        if not project_portfolio_db:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")

        db_projects_id = project_portfolio_db.get("_id")
        db_projects_metadata = project_portfolio_db.get("databaseMetadata", {})
        db_projects_properties = db_projects_metadata.get("properties", [])

        # Build property ID map for db-projects
        projects_prop_id_map = {}
        for prop in db_projects_properties:
            projects_prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        # Find all rows in Project Portfolio
        project_rows = []
        for page in all_pages:
            if page.get("type") == "row" and (page.get("databaseId") == db_projects_id or page.get("parentId") == db_projects_id):
                project_rows.append(page)

        # Expected VIP client companies
        expected_vip_companies = ["techcorp", "enterprise co"]
        found_vip_projects = []

        for row in project_rows:
            title = row.get("title", "").lower()
            for company in expected_vip_companies:
                if company in title and "account management 2026" in title:
                    found_vip_projects.append(row)
                    break

        if len(found_vip_projects) < 2:
            errors.append(f"Expected 2 VIP client projects in Project Portfolio, found {len(found_vip_projects)}")

        # Validate each VIP project has correct properties
        for row in found_vip_projects:
            cell_values = row.get("cellValues", {})
            title = row.get("title", "")

            # Helper to get cell value
            def get_project_cell_value(prop_name_lower):
                prop_id = projects_prop_id_map.get(prop_name_lower)
                if prop_id and prop_id in cell_values:
                    return cell_values.get(prop_id)
                for key, val in cell_values.items():
                    if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                        return val
                return None

            # Check Status = Planning
            status = get_project_cell_value("status")
            if status:
                if "planning" not in str(status).lower():
                    errors.append(f"Project '{title}' Status should be 'Planning', got '{status}'")
            else:
                errors.append(f"Project '{title}' missing Status")

            # Check Department = Sales
            department = get_project_cell_value("department")
            if department:
                if "sales" not in str(department).lower():
                    errors.append(f"Project '{title}' Department should be 'Sales', got '{department}'")
            else:
                errors.append(f"Project '{title}' missing Department")

            # Check Budget = 50000
            budget = get_project_cell_value("budget ($)")
            if budget is None:
                budget = get_project_cell_value("budget")
            if budget is not None:
                if int(budget) != 50000:
                    errors.append(f"Project '{title}' Budget should be 50000, got {budget}")
            else:
                errors.append(f"Project '{title}' missing Budget")

            # Check Timeline (start=today, end=today+90)
            timeline = get_project_cell_value("timeline")
            if timeline:
                if isinstance(timeline, dict):
                    start_date = timeline.get("start", "")
                    end_date = timeline.get("end", "")
                    # Allow some flexibility (within a day or two)
                    if start_date and today_str not in start_date:
                        # Just check it's a reasonable date
                        pass  # Be lenient on exact date
                    if end_date:
                        # Check end date is approximately 90 days from start
                        pass  # Be lenient on exact date
                else:
                    errors.append(f"Project '{title}' Timeline should be a date range")
            else:
                errors.append(f"Project '{title}' missing Timeline")

        # ==========================================================================
        # Check 2-4: Verify Client Engagement Tracker database exists and has correct schema
        # ==========================================================================
        vip_tracker_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "client engagement tracker" in title:
                    # Make sure it's the new one under Team Directory
                    parent_id = page.get("parentId")
                    if parent_id == "page-8":
                        vip_tracker_db = page
                        break
                    # Also check parent title
                    for p in all_pages:
                        if p.get("_id") == parent_id and "team directory" in p.get("title", "").lower():
                            vip_tracker_db = page
                            break

        if not vip_tracker_db:
            errors.append("New 'Client Engagement Tracker' database not found under Team Directory")
        else:
            # Check parent is Team Directory (page-8)
            parent_id = vip_tracker_db.get("parentId")
            if parent_id != "page-8":
                parent_page = None
                for page in all_pages:
                    if page.get("_id") == parent_id:
                        parent_page = page
                        break
                if not parent_page or "team directory" not in parent_page.get("title", "").lower():
                    errors.append(f"Client Engagement Tracker parent is not 'Team Directory' (page-8)")

            # Check 3: Validate database properties
            db_metadata = vip_tracker_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            expected_properties = {
                "client name": "title",
                "contact email": "email",
                "last meeting": "date",
                "next touchpoint": "date",
                "account status": "select",
                "engagement score": "number",
                "account manager": "text",
                "notes": "text",
            }

            prop_name_to_obj = {}
            for prop in properties:
                prop_name_lower = prop.get("name", "").lower()
                prop_name_to_obj[prop_name_lower] = prop

            for expected_name, expected_type in expected_properties.items():
                if expected_name not in prop_name_to_obj:
                    errors.append(f"Client Engagement Tracker missing property '{expected_name}'")
                else:
                    actual_type = prop_name_to_obj[expected_name].get("type", "")
                    if actual_type != expected_type:
                        errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{actual_type}'")

            # Check 4: Validate Account Status select options
            account_status_prop = prop_name_to_obj.get("account status")
            if account_status_prop:
                options = account_status_prop.get("options", [])
                expected_options = {
                    "active": "green",
                    "at risk": "yellow",
                    "churned": "red",
                    "prospecting": "blue",
                }

                found_options = {}
                for opt in options:
                    opt_name = opt.get("name", "").lower()
                    opt_color = opt.get("color", "").lower()
                    found_options[opt_name] = opt_color

                for exp_name, exp_color in expected_options.items():
                    if exp_name not in found_options:
                        errors.append(f"Account Status missing option '{exp_name}'")
                    elif found_options[exp_name] != exp_color:
                        errors.append(f"Account Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")
            else:
                errors.append("Account Status property not found in Client Engagement Tracker")

            # ==========================================================================
            # Check 5: Verify rows exist for VIP clients with correct data
            # ==========================================================================
            db_id = vip_tracker_db.get("_id")
            tracker_rows = []
            for page in all_pages:
                if page.get("type") == "row" and (page.get("databaseId") == db_id or page.get("parentId") == db_id):
                    tracker_rows.append(page)

            # Build property ID map
            prop_id_map = {}
            for prop in properties:
                prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

            # Helper to get cell value
            def get_tracker_cell_value(cell_values, prop_name_lower):
                prop_id = prop_id_map.get(prop_name_lower)
                if prop_id and prop_id in cell_values:
                    return cell_values.get(prop_id)
                for key, val in cell_values.items():
                    if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                        return val
                return None

            # Expected VIP clients
            expected_vip_clients = {
                "sarah johnson": {"email": "sarah.j@techcorp.com", "company": "TechCorp"},
                "lisa thompson": {"email": "lthompson@enterprise.com", "company": "Enterprise Co"},
            }

            found_vip_clients = {}
            for row in tracker_rows:
                title = row.get("title", "").lower()
                for client_name in expected_vip_clients.keys():
                    if client_name in title:
                        found_vip_clients[client_name] = row
                        break

            if len(found_vip_clients) < 2:
                errors.append(f"Expected 2 VIP client rows in Client Engagement Tracker, found {len(found_vip_clients)}")

            for client_name, expected_values in expected_vip_clients.items():
                if client_name not in found_vip_clients:
                    errors.append(f"Row for '{client_name}' not found in Client Engagement Tracker")
                    continue

                row = found_vip_clients[client_name]
                cell_values = row.get("cellValues", {})

                # Check Contact Email matches source
                email = get_tracker_cell_value(cell_values, "contact email")
                if email:
                    if expected_values["email"].lower() not in str(email).lower():
                        errors.append(f"{client_name}: Contact Email should contain '{expected_values['email']}', got '{email}'")
                else:
                    errors.append(f"{client_name}: Missing Contact Email")

                # Check Account Status = Active
                account_status = get_tracker_cell_value(cell_values, "account status")
                if account_status:
                    if "active" not in str(account_status).lower():
                        errors.append(f"{client_name}: Account Status should be 'Active', got '{account_status}'")
                else:
                    errors.append(f"{client_name}: Missing Account Status")

                # Check Engagement Score = 85
                engagement_score = get_tracker_cell_value(cell_values, "engagement score")
                if engagement_score is not None:
                    if int(engagement_score) != 85:
                        errors.append(f"{client_name}: Engagement Score should be 85, got {engagement_score}")
                else:
                    errors.append(f"{client_name}: Missing Engagement Score")

                # Check Next Touchpoint = 14 days from today (be lenient)
                next_touchpoint = get_tracker_cell_value(cell_values, "next touchpoint")
                if next_touchpoint:
                    # Just verify it exists and is a date
                    pass  # Be lenient on exact date
                else:
                    errors.append(f"{client_name}: Missing Next Touchpoint")

        # ==========================================================================
        # Check 6: Verify Sprint Planning page has VIP section with correct structure
        # ==========================================================================
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})
        sprint_planning_blocks = [b for b in all_blocks if b.get("pageId") == "page-3-1"]
        sprint_planning_blocks.sort(key=lambda b: b.get("order", 999))

        # Find heading_2 with "🌟 VIP Client Projects"
        vip_heading_found = False
        vip_heading_order = None
        for block in sprint_planning_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "vip client projects" in content.lower() and "🌟" in content:
                    vip_heading_found = True
                    vip_heading_order = block.get("order", 999)
                    break

        if not vip_heading_found:
            errors.append("heading_2 '🌟 VIP Client Projects' not found on Sprint Planning page")

        # Find purple callout with count of projects
        callout_found = False
        for block in sprint_planning_blocks:
            block_type = block.get("type", "")
            if block_type == "callout":
                props = block.get("properties", {})
                bg_color = props.get("backgroundColor", "").lower()
                content = block.get("content", "").lower()

                has_purple = "purple" in bg_color
                has_project_count = "new vip client projects added" in content and "2" in content

                if has_purple and has_project_count:
                    callout_found = True
                    break

        if not callout_found:
            errors.append("Purple callout with 'New VIP client projects added: 2' not found on Sprint Planning page")

        # Find numbered list with project names
        numbered_list_items = []
        for block in sprint_planning_blocks:
            block_type = block.get("type", "")
            if block_type in ["numberedList", "numbered_list", "numberedListItem", "numbered"]:
                content = block.get("content", "").lower()
                numbered_list_items.append(content)

        # Check if project names are in the numbered list
        techcorp_project_found = any("techcorp" in item and "account management" in item for item in numbered_list_items)
        enterprise_project_found = any("enterprise" in item and "account management" in item for item in numbered_list_items)

        if not techcorp_project_found:
            errors.append("Numbered list item with TechCorp project not found on Sprint Planning page")
        if not enterprise_project_found:
            errors.append("Numbered list item with Enterprise Co project not found on Sprint Planning page")

        # Return result
        if errors:
            errors.append("[X] Validation failed: {'; '.join(errors)}")
        else:
            checks_passed.append("[C] Validation succeeded")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Data Quality Audit System Task
# =============================================================================
def _validate_agent_data_quality_audit_system(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Build a comprehensive 'Data Quality Audit System'. Create a new database
    'Data Quality Metrics' as a child of 'Engineering Wiki' with properties: 'Database Name'
    (title), 'Total Records' (number), 'Invalid Records' (number), 'Missing Data Fields'
    (number), 'Quality Score' (select with 'Excellent (95-100%)' green, 'Good (85-94%)' blue,
    'Fair (70-84%)' yellow, 'Poor (<70%)' red), 'Last Audit Date' (date), 'Issues Found'
    (multi_select with 'Missing Values' orange, 'Invalid Dates' red, 'Duplicate Entries' purple,
    'Incorrect Types' yellow), 'Remediation Status' (select with 'Not Started' gray, 'In Progress'
    blue, 'Completed' green). Audit each database: 1) For 'Task Tracker' - check for tasks without
    assignees or due dates, 2) For 'Project Portfolio' - find projects without budgets or timelines,
    3) For 'Inventory Tracker' - identify items with quantity but In Stock=false. Calculate
    Quality Score as: (Total Records - Invalid Records - Missing Data Fields) / Total Records * 100.
    Then create a page 'Data Quality Dashboard' under the new database with heading_1
    '🔍 Data Quality Dashboard', a large callout (red background if any database has Poor quality,
    yellow if Fair, green if all Good/Excellent) showing 'Overall Data Health: [status]'. Add
    heading_2 '📊 Quality Metrics by Database' with a bulleted list for each database:
    '• [Database]: [Quality Score]% - Issues: [list issues]'. End with heading_2
    '⚡ Remediation Actions' and numbered list of specific fixes needed for each database
    with Poor/Fair quality.

    Expected data from initial state:
    - Task Tracker: 8 records, all have assignees and due dates → 0 issues, 100% quality (Excellent)
    - Project Portfolio: 6 records, all have budgets and timelines → 0 issues, 100% quality (Excellent)
    - Inventory Tracker: 10 records, no items with quantity>0 and In Stock=false → 0 issues, 100% quality (Excellent)
    - All databases = Excellent quality → Green callout, no remediation needed
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # Get all pages
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # ==========================================================================
        # PART 1: Find and validate Data Quality Metrics database
        # ==========================================================================

        data_quality_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "data quality metrics" in title:
                    data_quality_db = page
                    break

        if not data_quality_db:
            errors.append("[X] Database 'Data Quality Metrics' not found")
        else:
            checks_passed.append("[C] Database 'Data Quality Metrics'")

        # Check parent is Engineering Wiki (page-4)
        parent_id = data_quality_db.get("parentId")
        if parent_id != "page-4":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "engineering wiki" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent should be 'Engineering Wiki' (page-4), got parentId: {parent_id}")

        # ==========================================================================
        # PART 2: Validate database properties
        # ==========================================================================

        db_metadata = data_quality_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Required properties with their types
        required_properties = {
            "database name": "title",
            "total records": "number",
            "invalid records": "number",
            "missing data fields": "number",
            "quality score": "select",
            "last audit date": "date",
            "issues found": "multi_select",
            "remediation status": "select",
        }

        found_properties = {}
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            found_properties[prop_name] = prop

        for expected_name, expected_type in required_properties.items():
            found = False
            for prop_name, prop in found_properties.items():
                if expected_name in prop_name or prop_name in expected_name:
                    found = True
                    if prop.get("type") != expected_type:
                        errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{prop.get('type')}'")
                    break
            if not found:
                errors.append(f"Property '{expected_name}' not found in database")

        # Validate Quality Score select options
        quality_score_prop = None
        for prop in properties:
            if "quality score" in prop.get("name", "").lower():
                quality_score_prop = prop
                break

        if quality_score_prop:
            options = quality_score_prop.get("options", [])
            expected_quality_options = {
                "excellent": "green",
                "good": "blue",
                "fair": "yellow",
                "poor": "red",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_key, exp_color in expected_quality_options.items():
                option_found = False
                for opt_name, opt_color in found_options.items():
                    if exp_key in opt_name:
                        option_found = True
                        if exp_color not in opt_color:
                            errors.append(f"Quality Score option containing '{exp_key}' should have color '{exp_color}'")
                        break
                if not option_found:
                    errors.append(f"Quality Score missing option containing '{exp_key}'")

        # Validate Issues Found multi_select options
        issues_found_prop = None
        for prop in properties:
            if "issues found" in prop.get("name", "").lower():
                issues_found_prop = prop
                break

        if issues_found_prop:
            options = issues_found_prop.get("options", [])
            expected_issues_options = {
                "missing values": "orange",
                "invalid dates": "red",
                "duplicate entries": "purple",
                "incorrect types": "yellow",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_issues_options.items():
                option_found = False
                for opt_name, opt_color in found_options.items():
                    if exp_name in opt_name or opt_name in exp_name:
                        option_found = True
                        if exp_color not in opt_color:
                            errors.append(f"Issues Found option '{exp_name}' should have color '{exp_color}'")
                        break
                if not option_found:
                    errors.append(f"Issues Found missing option '{exp_name}'")

        # Validate Remediation Status select options
        remediation_prop = None
        for prop in properties:
            if "remediation status" in prop.get("name", "").lower():
                remediation_prop = prop
                break

        if remediation_prop:
            options = remediation_prop.get("options", [])
            expected_remediation_options = {
                "not started": "gray",
                "in progress": "blue",
                "completed": "green",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_remediation_options.items():
                option_found = False
                for opt_name, opt_color in found_options.items():
                    if exp_name in opt_name or opt_name in exp_name:
                        option_found = True
                        if exp_color not in opt_color:
                            errors.append(f"Remediation Status option '{exp_name}' should have color '{exp_color}'")
                        break
                if not option_found:
                    errors.append(f"Remediation Status missing option '{exp_name}'")

        # ==========================================================================
        # PART 3: Validate audit rows in the database
        # ==========================================================================

        db_id = data_quality_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        # Expected audit data:
        # Task Tracker: 8 records, 0 invalid, 0 missing, 100% quality (Excellent)
        # Project Portfolio: 6 records, 0 invalid, 0 missing, 100% quality (Excellent)
        # Inventory Tracker: 10 records, 0 invalid, 0 missing, 100% quality (Excellent)

        expected_audits = {
            "task tracker": {"total": 8, "invalid": 0, "missing": 0},
            "project portfolio": {"total": 6, "invalid": 0, "missing": 0},
            "inventory tracker": {"total": 10, "invalid": 0, "missing": 0},
        }

        # Helper to get cell value
        def get_cell_value(row, prop_name_lower, props):
            cell_values = row.get("cellValues", {})
            for prop in props:
                if prop_name_lower in prop.get("name", "").lower():
                    prop_id = prop.get("_id")
                    if prop_id and prop_id in cell_values:
                        return cell_values[prop_id]
            # Also try direct key match
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        found_audits = {}
        for row in rows:
            title = row.get("title", "").lower()
            for db_name in expected_audits.keys():
                if db_name in title:
                    found_audits[db_name] = row
                    break

        for db_name, expected in expected_audits.items():
            if db_name not in found_audits:
                errors.append(f"Audit row for '{db_name}' not found in Data Quality Metrics database")
                continue

            row = found_audits[db_name]

            # Check Total Records
            total = get_cell_value(row, "total records", properties)
            if total is not None:
                try:
                    if int(total) != expected["total"]:
                        errors.append(f"{db_name} Total Records should be {expected['total']}, got {total}")
                except (ValueError, TypeError):
                    errors.append(f"{db_name} Total Records has invalid value: {total}")
            else:
                errors.append(f"{db_name} missing Total Records")

            # Check Invalid Records
            invalid = get_cell_value(row, "invalid records", properties)
            if invalid is not None:
                try:
                    if int(invalid) != expected["invalid"]:
                        errors.append(f"{db_name} Invalid Records should be {expected['invalid']}, got {invalid}")
                except (ValueError, TypeError):
                    errors.append(f"{db_name} Invalid Records has invalid value: {invalid}")
            else:
                errors.append(f"{db_name} missing Invalid Records")

            # Check Missing Data Fields
            missing = get_cell_value(row, "missing data", properties)
            if missing is not None:
                try:
                    if int(missing) != expected["missing"]:
                        errors.append(f"{db_name} Missing Data Fields should be {expected['missing']}, got {missing}")
                except (ValueError, TypeError):
                    errors.append(f"{db_name} Missing Data Fields has invalid value: {missing}")
            else:
                errors.append(f"{db_name} missing Missing Data Fields")

            # Check Quality Score is Excellent (since all are 100%)
            quality = get_cell_value(row, "quality score", properties)
            if quality:
                if "excellent" not in str(quality).lower():
                    errors.append(f"{db_name} Quality Score should be 'Excellent' (100%), got '{quality}'")
            else:
                errors.append(f"{db_name} missing Quality Score")

        # ==========================================================================
        # PART 4: Find and validate Data Quality Dashboard page
        # ==========================================================================

        dashboard_page = None
        for page in all_pages:
            if page.get("type") in ["normal", "page", None]:
                title = page.get("title", "").lower()
                if "data quality dashboard" in title:
                    dashboard_page = page
                    break

        if not dashboard_page:
            errors.append("[X] Page 'Data Quality Dashboard' not found")
        else:
            checks_passed.append("[C] Page 'Data Quality Dashboard' found")

        # Check parent is the Data Quality Metrics database
        dashboard_parent = dashboard_page.get("parentId")
        if dashboard_parent != db_id:
            # Check if parent title matches
            parent_page = None
            for page in all_pages:
                if page.get("_id") == dashboard_parent:
                    parent_page = page
                    break
            if not parent_page or "data quality metrics" not in parent_page.get("title", "").lower():
                errors.append(f"Dashboard parent should be Data Quality Metrics database, got parentId: {dashboard_parent}")

        # ==========================================================================
        # PART 5: Validate dashboard content
        # ==========================================================================

        dashboard_page_id = dashboard_page.get("_id")
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": dashboard_page_id}})
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0))

        # Check heading_1 '🔍 Data Quality Dashboard'
        heading1_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading1", "heading_1"]:
                content = block.get("content", "")
                if "data quality dashboard" in content.lower() and "🔍" in content:
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("heading_1 '🔍 Data Quality Dashboard' not found")

        # Check callout with green background (all databases are Excellent)
        callout_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type == "callout":
                props = block.get("properties", {})
                bg_color = props.get("backgroundColor", block.get("backgroundColor", ""))
                content = block.get("content", "")

                # Should be green since all databases are Excellent/Good
                has_green_bg = "green" in str(bg_color).lower() if bg_color else False
                has_health_text = "overall data health" in content.lower() or "data health" in content.lower()

                if has_green_bg and has_health_text:
                    callout_found = True
                    break

        if not callout_found:
            errors.append("Green callout with 'Overall Data Health' status not found (should be green since all databases are Excellent)")

        # Check heading_2 '📊 Quality Metrics by Database'
        metrics_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "quality metrics" in content.lower() and "database" in content.lower() and "📊" in content:
                    metrics_heading_found = True
                    break

        if not metrics_heading_found:
            errors.append("heading_2 '📊 Quality Metrics by Database' not found")

        # Check bulleted list with database quality metrics
        databases_mentioned = {"task tracker": False, "project portfolio": False, "inventory tracker": False}
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["bulletedList", "bulleted_list", "bullet", "bulleted_list_item"]:
                content = block.get("content", "").lower()
                for db_name in databases_mentioned.keys():
                    if db_name in content:
                        # Should mention quality score (should be ~100% or Excellent)
                        if "100" in content or "excellent" in content or "%" in content:
                            databases_mentioned[db_name] = True

        for db_name, found in databases_mentioned.items():
            if not found:
                errors.append(f"Bulleted list item for '{db_name}' with quality score not found")

        # Check heading_2 '⚡ Remediation Actions'
        remediation_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "remediation" in content.lower() and "actions" in content.lower() and "⚡" in content:
                    remediation_heading_found = True
                    break

        if not remediation_heading_found:
            errors.append("heading_2 '⚡ Remediation Actions' not found")

        # ==========================================================================
        # RETURN RESULT
        # ==========================================================================

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Q4 Task Migration Task
# =============================================================================
def _validate_agent_q4_task_migration(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a database migration task by first creating a new 'Task Archive Q4 2025' database
    under 'Engineering Wiki' with the same schema as 'Task Tracker' (copy all property definitions
    including select options and colors). Query Task Tracker for all rows where Status='Done' and
    Created date is before 2026-01-01. For each matching row, create a corresponding row in Task
    Archive Q4 2025 with identical cell values, then update the original Task Tracker row by setting
    its Status to 'Archived' (first add 'Archived' as a new gray option to the Status select property).
    After migration, create a summary page 'Q4 2025 Task Migration Report' under Engineering Wiki with
    heading_1 '📦 Q4 2025 Task Migration Report', a callout (blue background) showing 'Successfully
    migrated [X] completed tasks to archive', and a numbered list of migrated task titles.

    Expected migrated rows from initial data (Status='Done' AND Created < 2026-01-01):
    1. "Implement user notifications" (db-tasks-row-4) - Created: 2025-12-28, Status: Done
    2. "Add unit tests for auth module" (db-tasks-row-8) - Created: 2025-12-23, Status: Done
    Total: 2 rows to migrate
    """
    checks_passed: list[str] = []

    errors = []

    # Get all pages
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # ==========================================================================
        # STEP 1: Validate 'Task Archive Q4 2025' database exists under Engineering Wiki (15%)
        # ==========================================================================

        archive_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "task archive q4 2025" in title:
                    archive_db = page
                    break

        if not archive_db:
            errors.append("[X] Database 'Task Archive Q4 2025' not found")
        else:
            checks_passed.append("[C] Database 'Task Archive Q4 2025'")

        # Check parent is Engineering Wiki (page-4)
        archive_parent_id = archive_db.get("parentId")
        if archive_parent_id != "page-4":
            # Also check if parent has title "Engineering Wiki"
            parent_page = None
            for page in all_pages:
                if page.get("_id") == archive_parent_id:
                    parent_page = page
                    break
            if not parent_page or "engineering wiki" not in parent_page.get("title", "").lower():
                errors.append(f"Archive database parent is not 'Engineering Wiki', got parentId: {archive_parent_id}")

        archive_db_id = archive_db.get("_id")

        # ==========================================================================
        # STEP 2: Validate archive database has identical schema to Task Tracker (20%)
        # ==========================================================================

        # Get Task Tracker database schema
        task_tracker_db = None
        for page in all_pages:
            if page.get("_id") == "db-tasks" or (page.get("type") == "database" and "task tracker" in page.get("title", "").lower()):
                task_tracker_db = page
                break

        if not task_tracker_db:
            errors.append("[X] Original 'Task Tracker' database not found")
        else:
            checks_passed.append("[C] Original 'Task Tracker' database")

        task_tracker_metadata = task_tracker_db.get("databaseMetadata", {})
        task_tracker_props = task_tracker_metadata.get("properties", [])

        archive_metadata = archive_db.get("databaseMetadata", {})
        archive_props = archive_metadata.get("properties", [])

        # Check required property types exist (excluding 'Archived' which is added later to source only)
        # We need: title, 2x select (Status, Priority), people, date, multi_select, checkbox, created_time
        required_prop_types = {
            "title": False,
            "select": 0,  # Need 2 select properties (Status and Priority)
            "people": False,
            "date": False,
            "multi_select": False,
            "checkbox": False,
            "created_time": False,
        }

        for prop in archive_props:
            prop_type = prop.get("type", "")
            if prop_type == "select":
                required_prop_types["select"] += 1
            elif prop_type in required_prop_types:
                required_prop_types[prop_type] = True

        for prop_type, found in required_prop_types.items():
            if prop_type == "select":
                if found < 2:
                    errors.append(f"Archive database should have at least 2 select properties, found {found}")
            elif not found:
                errors.append(f"Archive database missing property type '{prop_type}'")

        # Validate Status select has matching options from Task Tracker (except Archived)
        archive_status_prop = None
        for prop in archive_props:
            if prop.get("type") == "select" and "status" in prop.get("name", "").lower():
                archive_status_prop = prop
                break

        if archive_status_prop:
            archive_status_options = {opt.get("name", "").lower(): opt.get("color", "").lower()
                                       for opt in archive_status_prop.get("options", [])}
            # Original Status options: Not Started (gray), In Progress (blue), In Review (yellow), Done (green), Blocked (red)
            expected_status_options = {
                "not started": "gray",
                "in progress": "blue",
                "in review": "yellow",
                "done": "green",
                "blocked": "red",
            }
            for opt_name, opt_color in expected_status_options.items():
                if opt_name not in archive_status_options:
                    errors.append(f"Archive Status missing option '{opt_name}'")
                elif archive_status_options[opt_name] != opt_color:
                    errors.append(f"Archive Status option '{opt_name}' should have color '{opt_color}', got '{archive_status_options[opt_name]}'")
        else:
            errors.append("Archive database missing Status select property")

        # Validate Priority select has matching options
        archive_priority_prop = None
        for prop in archive_props:
            if prop.get("type") == "select" and "priority" in prop.get("name", "").lower():
                archive_priority_prop = prop
                break

        if archive_priority_prop:
            archive_priority_options = {opt.get("name", "").lower(): opt.get("color", "").lower()
                                         for opt in archive_priority_prop.get("options", [])}
            # Original Priority options: Low (gray), Medium (yellow), High (orange), Urgent (red)
            expected_priority_options = {
                "low": "gray",
                "medium": "yellow",
                "high": "orange",
                "urgent": "red",
            }
            for opt_name, opt_color in expected_priority_options.items():
                if opt_name not in archive_priority_options:
                    errors.append(f"Archive Priority missing option '{opt_name}'")
                elif archive_priority_options[opt_name] != opt_color:
                    errors.append(f"Archive Priority option '{opt_name}' should have color '{opt_color}', got '{archive_priority_options[opt_name]}'")
        else:
            errors.append("Archive database missing Priority select property")

        # Validate Tags multi_select has matching options
        archive_tags_prop = None
        for prop in archive_props:
            if prop.get("type") == "multi_select" and "tag" in prop.get("name", "").lower():
                archive_tags_prop = prop
                break

        if archive_tags_prop:
            archive_tags_options = {opt.get("name", "").lower(): opt.get("color", "").lower()
                                     for opt in archive_tags_prop.get("options", [])}
            # Original Tags options: Bug (red), Feature (blue), Documentation (purple), Design (pink), Backend (green), Frontend (orange)
            expected_tags_options = {
                "bug": "red",
                "feature": "blue",
                "documentation": "purple",
                "design": "pink",
                "backend": "green",
                "frontend": "orange",
            }
            for opt_name, opt_color in expected_tags_options.items():
                if opt_name not in archive_tags_options:
                    errors.append(f"Archive Tags missing option '{opt_name}'")
                elif archive_tags_options[opt_name] != opt_color:
                    errors.append(f"Archive Tags option '{opt_name}' should have color '{opt_color}', got '{archive_tags_options[opt_name]}'")
        else:
            errors.append("Archive database missing Tags multi_select property")

        # ==========================================================================
        # STEP 3: Validate 'Archived' option added to Task Tracker Status property (10%)
        # ==========================================================================

        task_tracker_status_prop = None
        task_tracker_props_current = task_tracker_db.get("databaseMetadata", {}).get("properties", [])
        for prop in task_tracker_props_current:
            if prop.get("type") == "select" and "status" in prop.get("name", "").lower():
                task_tracker_status_prop = prop
                break

        if task_tracker_status_prop:
            status_options = {opt.get("name", "").lower(): opt.get("color", "").lower()
                              for opt in task_tracker_status_prop.get("options", [])}
            if "archived" not in status_options:
                errors.append("Task Tracker Status property missing 'Archived' option")
            elif status_options["archived"] != "gray":
                errors.append(f"Task Tracker 'Archived' option should have color 'gray', got '{status_options['archived']}'")
        else:
            errors.append("Task Tracker Status property not found")

        # ==========================================================================
        # STEP 4: Validate migrated rows exist in Task Archive Q4 2025 (20%)
        # ==========================================================================

        # Find rows in archive database
        archive_rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == archive_db_id:
                archive_rows.append(page)

        # Expected migrated tasks (Status='Done' AND Created < 2026-01-01)
        expected_migrated_titles = [
            "implement user notifications",
            "add unit tests for auth module",
        ]

        if len(archive_rows) < 2:
            errors.append(f"Expected at least 2 migrated rows in archive database, found {len(archive_rows)}")
        else:
            archive_row_titles = [row.get("title", "").lower() for row in archive_rows]
            for expected_title in expected_migrated_titles:
                found = False
                for row_title in archive_row_titles:
                    if expected_title in row_title:
                        found = True
                        break
                if not found:
                    errors.append(f"Migrated row '{expected_title}' not found in archive database")

        # Validate cell values are copied correctly for at least one migrated row
        for archive_row in archive_rows:
            row_title = archive_row.get("title", "").lower()
            if "implement user notifications" in row_title:
                cell_values = archive_row.get("cellValues", {})
                # Check that some key values are present (Priority: High, Tags include Feature)
                cell_str = str(cell_values).lower()
                if "high" not in cell_str:
                    errors.append("Migrated row 'Implement user notifications' missing Priority 'High'")
                break

        # ==========================================================================
        # STEP 5: Validate original rows in Task Tracker updated to 'Archived' status (15%)
        # ==========================================================================

        # Check original rows (db-tasks-row-4 and db-tasks-row-8) have Status='Archived'
        original_row_ids = ["db-tasks-row-4", "db-tasks-row-8"]
        for row_id in original_row_ids:
            original_row = None
            for page in all_pages:
                if page.get("_id") == row_id:
                    original_row = page
                    break

            if original_row:
                cell_values = original_row.get("cellValues", {})
                # Find status value - can be by property ID or name
                status_value = None
                for key, val in cell_values.items():
                    if "status" in key.lower():
                        status_value = val
                        break
                if status_value:
                    if str(status_value).lower() != "archived":
                        errors.append(f"Original row {row_id} Status should be 'Archived', got '{status_value}'")
                else:
                    errors.append(f"Original row {row_id} missing Status cell value")
            else:
                errors.append(f"Original row {row_id} not found in Task Tracker")

        # ==========================================================================
        # STEP 6: Validate summary report page exists under Engineering Wiki (10%)
        # ==========================================================================

        report_page = None
        for page in all_pages:
            if page.get("type") in ["normal", "page", None] or page.get("type") == "normal":
                title = page.get("title", "").lower()
                if "q4 2025 task migration report" in title:
                    report_page = page
                    break

        if not report_page:
            errors.append("[X] Summary page 'Q4 2025 Task Migration Report' not found")
        else:
            checks_passed.append("[C] Summary page 'Q4 2025 Task Migration Report' found")

        # Check parent is Engineering Wiki (page-4)
        report_parent_id = report_page.get("parentId") if report_page else None
        if report_parent_id != "page-4":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == report_parent_id:
                    parent_page = page
                    break
            if not parent_page or "engineering wiki" not in parent_page.get("title", "").lower():
                errors.append(f"Report page parent is not 'Engineering Wiki', got parentId: {report_parent_id}")

        report_page_id = report_page.get("_id")

        # ==========================================================================
        # STEP 7: Validate summary report page content (10%)
        # ==========================================================================

        # Get blocks for the report page
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})
        report_blocks = [b for b in all_blocks if b.get("pageId") == report_page_id]

        # Sort blocks by order
        sorted_blocks = sorted(report_blocks, key=lambda x: x.get("order", 0))

        # Check heading_1 '📦 Q4 2025 Task Migration Report'
        heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading1", "heading_1"]:
                content = block.get("content", "")
                if "q4 2025 task migration report" in content.lower() and "📦" in content:
                    heading_found = True
                    break

        if not heading_found:
            errors.append("heading_1 '📦 Q4 2025 Task Migration Report' not found")

        # Check callout with blue background showing migration count
        callout_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type == "callout":
                props = block.get("properties", {})
                bg_color = props.get("backgroundColor", block.get("backgroundColor", ""))
                content = block.get("content", "")

                has_blue_bg = "blue" in str(bg_color).lower() if bg_color else False
                has_migration_text = "successfully migrated" in content.lower() and ("task" in content.lower() or "2" in content)

                if has_blue_bg and has_migration_text:
                    callout_found = True
                    break

        if not callout_found:
            errors.append("Blue callout with 'Successfully migrated [X] completed tasks to archive' not found")

        # Check numbered list with migrated task titles
        numbered_list_items = []
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["numberedList", "numbered_list", "numberedListItem", "numbered_list_item"]:
                content = block.get("content", "").lower()
                numbered_list_items.append(content)

        # Check that both migrated task titles are in numbered list
        migrated_tasks_in_list = {"implement user notifications": False, "add unit tests for auth module": False}
        for task_title in migrated_tasks_in_list.keys():
            for item in numbered_list_items:
                if task_title in item:
                    migrated_tasks_in_list[task_title] = True
                    break

        for task_title, found in migrated_tasks_in_list.items():
            if not found:
                errors.append(f"Numbered list missing migrated task '{task_title}'")

        # ==========================================================================
        # RETURN RESULT
        # ==========================================================================

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Automated Expense Tracking System Task
# =============================================================================
def _validate_agent_automated_expense_tracking(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create an automated expense tracking system by connecting multiple databases.
    First, create a new 'Expense Tracker' database under 'Company Handbook' with properties:
    'Expense Name' (title), 'Amount' (number), 'Category' (select: 'Personnel' purple,
    'Software' blue, 'Marketing' green, 'Operations' orange, 'R&D' pink), 'Related Project' (text),
    'Date' (date), 'Approved' (checkbox). Query 'Project Portfolio' and for each Active project,
    create expense entries: one for 'Personnel - [Project Name]' with Amount = Budget × 0.6,
    Category='Personnel', another for 'Operations - [Project Name]' with Amount = Budget × 0.4,
    Category='Operations', Date=today, Related Project=[Project Name]. Then query 'Task Tracker'
    and for each task with Priority='Urgent', add an expense 'Overtime - [Task Name]' with
    Amount=500, Category='Personnel', Related Project='Emergency Fund'. Create a summary page
    'Expense Allocation Report' under the database with heading_1 '💸 Expense Allocation Report',
    heading_2 '📊 Category Breakdown' listing total amounts per category, and a callout
    (red background if total > $200,000, green otherwise) showing 'Total Allocated Expenses: $[TOTAL]'.

    Expected from initial data:
    - Active projects: Website Redesign ($50,000), Customer Portal ($75,000), Sales Pipeline Automation ($35,000)
    - Personnel expenses: 30000 + 45000 + 21000 + 500 = $96,500
    - Operations expenses: 20000 + 30000 + 14000 = $64,000
    - Total: $160,500 (green callout since < $200,000)
    """
    checks_passed: list[str] = []

    errors = []

    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})

        # ==========================================================================
        # CHECK 1: Expense Tracker Database Exists Under Company Handbook
        # ==========================================================================

        expense_tracker_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "expense tracker" in title:
                    expense_tracker_db = page
                    break

        if not expense_tracker_db:
            errors.append("[X] Database 'Expense Tracker' not found")
        else:
            checks_passed.append("[C] Database 'Expense Tracker'")

        # Verify parent is Company Handbook (page-16)
        parent_id = expense_tracker_db.get("parentId")
        if parent_id != "page-16":
            # Check if parent has the right title
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "company handbook" not in parent_page.get("title", "").lower():
                errors.append(f"Expense Tracker database parent should be 'Company Handbook' (page-16), got parentId: {parent_id}")

        db_id = expense_tracker_db.get("_id")
        db_metadata = expense_tracker_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # ==========================================================================
        # CHECK 2: Database Properties Configuration
        # ==========================================================================

        expected_props = [
            ("Expense Name", "title"),
            ("Amount", "number"),
            ("Category", "select"),
            ("Related Project", "text"),
            ("Date", "date"),
            ("Approved", "checkbox"),
        ]

        # Build property lookup
        prop_by_name = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_by_name[prop_name_lower] = prop

        for exp_name, exp_type in expected_props:
            prop = prop_by_name.get(exp_name.lower())
            if not prop:
                errors.append(f"Property '{exp_name}' not found in Expense Tracker database")
            elif prop.get("type") != exp_type:
                errors.append(f"Property '{exp_name}' should have type '{exp_type}', got '{prop.get('type')}'")

        # Validate Category select options
        category_prop = prop_by_name.get("category")
        if category_prop:
            options = category_prop.get("options", [])
            expected_options = {
                "personnel": "purple",
                "software": "blue",
                "marketing": "green",
                "operations": "orange",
                "r&d": "pink",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Category missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Category option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # ==========================================================================
        # CHECK 3: Expense Entries from Active Projects
        # ==========================================================================

        # Expected Active projects and their budgets
        active_projects = {
            "website redesign": 50000,
            "customer portal": 75000,
            "sales pipeline automation": 35000,
        }

        # Get all rows in the Expense Tracker database
        expense_rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                expense_rows.append(page)

        # Build property ID map for cell value lookups
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values[prop_id]
            # Fallback: search by key substring
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                    return val
            return None

        # Verify Personnel and Operations expense for each Active project
        for project_name, budget in active_projects.items():
            personnel_amount = budget * 0.6
            operations_amount = budget * 0.4

            # Find Personnel expense row
            personnel_found = False
            for row in expense_rows:
                title = row.get("title", "").lower()
                if f"personnel - {project_name}" in title or (
                    "personnel" in title and project_name in title
                ):
                    # Verify Amount
                    amount = get_cell_value(row, "amount")
                    if amount is not None:
                        if abs(float(amount) - personnel_amount) > 0.01:
                            errors.append(f"Personnel expense for '{project_name}' should have Amount {personnel_amount}, got {amount}")
                    else:
                        errors.append(f"Personnel expense for '{project_name}' missing Amount")

                    # Verify Category
                    category = get_cell_value(row, "category")
                    if category:
                        if "personnel" not in str(category).lower():
                            errors.append(f"Personnel expense for '{project_name}' should have Category 'Personnel', got '{category}'")
                    else:
                        errors.append(f"Personnel expense for '{project_name}' missing Category")

                    # Verify Related Project
                    related_project = get_cell_value(row, "related project")
                    if related_project:
                        if project_name not in str(related_project).lower():
                            errors.append(f"Personnel expense for '{project_name}' should have Related Project containing '{project_name}'")

                    personnel_found = True
                    break

            if not personnel_found:
                errors.append(f"Personnel expense row for '{project_name}' not found")

            # Find Operations expense row
            operations_found = False
            for row in expense_rows:
                title = row.get("title", "").lower()
                if f"operations - {project_name}" in title or (
                    "operations" in title and project_name in title
                ):
                    # Verify Amount
                    amount = get_cell_value(row, "amount")
                    if amount is not None:
                        if abs(float(amount) - operations_amount) > 0.01:
                            errors.append(f"Operations expense for '{project_name}' should have Amount {operations_amount}, got {amount}")
                    else:
                        errors.append(f"Operations expense for '{project_name}' missing Amount")

                    # Verify Category
                    category = get_cell_value(row, "category")
                    if category:
                        if "operations" not in str(category).lower():
                            errors.append(f"Operations expense for '{project_name}' should have Category 'Operations', got '{category}'")
                    else:
                        errors.append(f"Operations expense for '{project_name}' missing Category")

                    # Verify Related Project
                    related_project = get_cell_value(row, "related project")
                    if related_project:
                        if project_name not in str(related_project).lower():
                            errors.append(f"Operations expense for '{project_name}' should have Related Project containing '{project_name}'")

                    operations_found = True
                    break

            if not operations_found:
                errors.append(f"Operations expense row for '{project_name}' not found")

        # ==========================================================================
        # CHECK 4: Expense Entry from Urgent Tasks
        # ==========================================================================

        # Expected Urgent task: "Update database schema"
        urgent_task_name = "update database schema"

        overtime_found = False
        for row in expense_rows:
            title = row.get("title", "").lower()
            if "overtime" in title and urgent_task_name in title:
                # Verify Amount = 500
                amount = get_cell_value(row, "amount")
                if amount is not None:
                    if abs(float(amount) - 500) > 0.01:
                        errors.append(f"Overtime expense for '{urgent_task_name}' should have Amount 500, got {amount}")
                else:
                    errors.append(f"Overtime expense for '{urgent_task_name}' missing Amount")

                # Verify Category = Personnel
                category = get_cell_value(row, "category")
                if category:
                    if "personnel" not in str(category).lower():
                        errors.append(f"Overtime expense should have Category 'Personnel', got '{category}'")
                else:
                    errors.append(f"Overtime expense missing Category")

                # Verify Related Project = Emergency Fund
                related_project = get_cell_value(row, "related project")
                if related_project:
                    if "emergency fund" not in str(related_project).lower():
                        errors.append(f"Overtime expense should have Related Project 'Emergency Fund', got '{related_project}'")
                else:
                    errors.append(f"Overtime expense missing Related Project")

                overtime_found = True
                break

        if not overtime_found:
            errors.append(f"Overtime expense row for '{urgent_task_name}' not found")

        # ==========================================================================
        # CHECK 5: Summary Page Exists Under Database
        # ==========================================================================

        report_page = None
        for page in all_pages:
            # Accept pages that are either type "normal" or don't have a type (child pages)
            page_type = page.get("type", "")
            if page_type in ("normal", "") or page_type not in ("database", "row"):
                title = page.get("title", "").lower()
                if "expense allocation report" in title:
                    report_page = page
                    break

        if not report_page:
            errors.append("[X] Summary page 'Expense Allocation Report' not found")
        else:
            checks_passed.append("[C] Summary page 'Expense Allocation Report' found")

        # Verify parent is the Expense Tracker database
        report_parent_id = report_page.get("parentId") if report_page else None
        if report_parent_id != db_id:
            errors.append(f"Summary page should be under Expense Tracker database, got parentId: {report_parent_id}")

        report_page_id = report_page.get("_id")

        # ==========================================================================
        # CHECK 6: Summary Page Content
        # ==========================================================================

        # Get blocks for the report page
        report_blocks = [b for b in all_blocks if b.get("pageId") == report_page_id]

        # Check for heading1 with "💸 Expense Allocation Report"
        heading1_found = False
        for block in report_blocks:
            if block.get("type") == "heading1":
                content = block.get("content", "")
                if "expense allocation report" in content.lower():
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("Missing heading1 '💸 Expense Allocation Report' in summary page")

        # Check for heading2 with "📊 Category Breakdown"
        heading2_found = False
        for block in report_blocks:
            if block.get("type") == "heading2":
                content = block.get("content", "")
                if "category breakdown" in content.lower():
                    heading2_found = True
                    break

        if not heading2_found:
            errors.append("Missing heading2 '📊 Category Breakdown' in summary page")

        # Check for callout with correct background color and total
        # Expected total: $160,500 (< $200,000 so green background)
        expected_total = 160500
        callout_found = False
        for block in report_blocks:
            if block.get("type") == "callout":
                content = block.get("content", "")
                props = block.get("properties", {})
                bg_color = props.get("backgroundColor", "").lower()

                if "total" in content.lower() and "expense" in content.lower():
                    # Check for green background (since 160500 < 200000)
                    if "green" not in bg_color:
                        errors.append(f"Callout should have green background (total < $200,000), got '{bg_color}'")

                    # Check if total amount is mentioned
                    # Allow for various formats: $160,500 or $160500 or 160500
                    content_normalized = content.replace(",", "").replace("$", "")
                    if "160500" not in content_normalized:
                        # Also check for close approximations (some rounding)
                        import re
                        numbers = re.findall(r'\d+', content_normalized)
                        total_found = False
                        for num in numbers:
                            if abs(int(num) - expected_total) < 100:
                                total_found = True
                                break
                        if not total_found:
                            errors.append(f"Callout should show total expenses around ${expected_total}")

                    callout_found = True
                    break

        if not callout_found:
            errors.append("Missing callout with 'Total Allocated Expenses' in summary page")

        # ==========================================================================
        # RETURN RESULT
        # ==========================================================================

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Team Productivity Metrics Task
# =============================================================================
def _validate_agent_team_productivity_metrics(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: On the 'Weekly Standup Template' page, insert a new section immediately AFTER the
    'What I plan to do next week' section but BEFORE the 'Blockers' heading. Add heading_2
    '📊 Team Productivity Metrics'. Below this, query the 'Task Tracker' database to aggregate
    data and create the following structure: First, add a callout with blue background and 📈
    icon containing 'Weekly Performance: [X]% tasks completed, [Y] story points delivered'.
    Then add heading_3 '🏃 Task Distribution by Status' followed by a bulleted list showing
    counts for each status ('Not Started: [A]', 'In Progress: [B]', 'In Review: [C]', 'Done: [D]',
    'Blocked: [E]'). Add a divider, then heading_3 '🎯 Priority Breakdown' with a numbered list
    showing task counts by priority. Finally, add a paragraph with italic text 'Generated on
    [TODAY] - Next update scheduled for [NEXT_MONDAY]'. The new content must maintain the exact
    same formatting style as the existing sections.

    Expected values from Task Tracker database:
    - Total tasks: 8
    - Done tasks: 2 (25% completion rate)
    - Status counts: Not Started=2, In Progress=2, In Review=1, Done=2, Blocked=1
    - Priority counts: Low=1, Medium=3, High=3, Urgent=1
    - Note: No "Story Points" property exists in Task Tracker, so model may use placeholder
    """
    checks_passed: list[str] = []

    errors = []

    # Step 1: Find the Weekly Standup Template page (page-9)
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        standup_page = None
        for page in all_pages:
            if page.get("_id") == "page-9":
                standup_page = page
                break
            title = page.get("title", "").lower()
            if "weekly standup template" in title:
                standup_page = page
                break

        if not standup_page:
            errors.append("[X] Page 'Weekly Standup Template' (page-9) not found")
        else:
            checks_passed.append("[C] Page 'Weekly Standup Template' (page-9)")

        standup_page_id = standup_page.get("_id")

        # Step 2: Get all blocks on the page sorted by order
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": standup_page_id}})
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0))

        # Step 3: Find key position markers
        what_i_plan_idx = -1
        blockers_idx = -1
        team_productivity_idx = -1

        for i, block in enumerate(sorted_blocks):
            content = block.get("content", "").lower()
            block_type = block.get("type", "")

            if block_type in ["heading2", "heading_2"]:
                if "what i plan to do next week" in content:
                    what_i_plan_idx = i
                elif "blockers" in content:
                    blockers_idx = i
                elif "team productivity metrics" in content:
                    team_productivity_idx = i

        # Step 4: Verify heading_2 "📊 Team Productivity Metrics" exists
        if team_productivity_idx == -1:
            errors.append("heading_2 '📊 Team Productivity Metrics' not found on page")
        else:
            # Check it has the emoji
            team_block = sorted_blocks[team_productivity_idx]
            content = team_block.get("content", "")
            if "📊" not in content:
                errors.append("heading_2 'Team Productivity Metrics' should contain '📊' emoji")

        # Step 5: Verify positioning - new section should be AFTER "What I plan to do next week" and BEFORE "Blockers"
        if team_productivity_idx != -1 and what_i_plan_idx != -1:
            if team_productivity_idx <= what_i_plan_idx:
                errors.append("Team Productivity Metrics section should appear AFTER 'What I plan to do next week'")

        if team_productivity_idx != -1 and blockers_idx != -1:
            if team_productivity_idx >= blockers_idx:
                errors.append("Team Productivity Metrics section should appear BEFORE 'Blockers'")

        # Step 6: Check for callout with blue background and 📈 icon
        callout_found = False
        callout_valid = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type == "callout":
                props = block.get("properties", {})
                bg_color = props.get("backgroundColor", "").lower()
                icon = props.get("icon", "")
                content = block.get("content", "").lower()

                # Check for blue background
                has_blue_bg = "blue" in bg_color

                # Check for 📈 icon
                has_icon = "📈" in icon or "📈" in content

                # Check content mentions weekly performance and completion %
                has_weekly_perf = "weekly performance" in content
                has_completion = "%" in block.get("content", "") and ("complete" in content or "task" in content)

                if has_blue_bg and has_icon:
                    callout_found = True
                    if has_weekly_perf and has_completion:
                        callout_valid = True
                        # Validate the completion percentage is around 25%
                        # Extract number before %
                        content_raw = block.get("content", "")
                        percent_match = re.search(r'(\d+)\s*%', content_raw)
                        if percent_match:
                            pct = int(percent_match.group(1))
                            if pct != 25:
                                errors.append(f"Completion percentage should be 25% (2/8 tasks), got {pct}%")
                    break

        if not callout_found:
            errors.append("Callout with blue background and 📈 icon not found")
        elif not callout_valid:
            errors.append("Callout should contain 'Weekly Performance' with completion percentage")

        # Step 7: Check heading_3 "🏃 Task Distribution by Status"
        status_heading_found = False
        status_heading_idx = -1
        for i, block in enumerate(sorted_blocks):
            block_type = block.get("type", "")
            if block_type in ["heading3", "heading_3"]:
                content = block.get("content", "")
                if "task distribution" in content.lower() and "status" in content.lower():
                    status_heading_found = True
                    status_heading_idx = i
                    if "🏃" not in content:
                        errors.append("heading_3 'Task Distribution by Status' should contain '🏃' emoji")
                    break

        if not status_heading_found:
            errors.append("heading_3 '🏃 Task Distribution by Status' not found")

        # Step 8: Check bulleted list with status counts
        expected_statuses = {
            "not started": 2,
            "in progress": 2,
            "in review": 1,
            "done": 2,
            "blocked": 1
        }
        found_statuses = set()

        for i, block in enumerate(sorted_blocks):
            block_type = block.get("type", "")
            if block_type in ["bulletedList", "bulleted_list", "bulleted_list_item", "bulleted"]:
                content = block.get("content", "").lower()
                for status_name, expected_count in expected_statuses.items():
                    if status_name in content:
                        found_statuses.add(status_name)
                        # Check count is correct
                        content_raw = block.get("content", "")
                        count_match = re.search(r'(\d+)', content_raw)
                        if count_match:
                            count = int(count_match.group(1))
                            if count != expected_count:
                                errors.append(f"Status '{status_name}' count should be {expected_count}, got {count}")

        missing_statuses = set(expected_statuses.keys()) - found_statuses
        if missing_statuses:
            errors.append(f"Missing status entries in bulleted list: {', '.join(missing_statuses)}")

        # Step 9: Check for divider
        divider_found = False
        for block in sorted_blocks:
            if block.get("type") == "divider":
                divider_found = True
                break

        if not divider_found:
            errors.append("Divider not found in the Team Productivity Metrics section")

        # Step 10: Check heading_3 "🎯 Priority Breakdown"
        priority_heading_found = False
        priority_heading_idx = -1
        for i, block in enumerate(sorted_blocks):
            block_type = block.get("type", "")
            if block_type in ["heading3", "heading_3"]:
                content = block.get("content", "")
                if "priority breakdown" in content.lower():
                    priority_heading_found = True
                    priority_heading_idx = i
                    if "🎯" not in content:
                        errors.append("heading_3 'Priority Breakdown' should contain '🎯' emoji")
                    break

        if not priority_heading_found:
            errors.append("heading_3 '🎯 Priority Breakdown' not found")

        # Step 11: Check numbered list with priority counts
        expected_priorities = {
            "low": 1,
            "medium": 3,
            "high": 3,
            "urgent": 1
        }
        found_priorities = set()

        for i, block in enumerate(sorted_blocks):
            block_type = block.get("type", "")
            if block_type in ["numberedList", "numbered_list", "numbered_list_item", "numbered"]:
                content = block.get("content", "").lower()
                for priority_name, expected_count in expected_priorities.items():
                    if priority_name in content:
                        found_priorities.add(priority_name)
                        # Check count is correct
                        content_raw = block.get("content", "")
                        count_match = re.search(r'(\d+)', content_raw)
                        if count_match:
                            count = int(count_match.group(1))
                            if count != expected_count:
                                errors.append(f"Priority '{priority_name}' count should be {expected_count}, got {count}")

        missing_priorities = set(expected_priorities.keys()) - found_priorities
        if missing_priorities:
            errors.append(f"Missing priority entries in numbered list: {', '.join(missing_priorities)}")

        # Step 12: Check for italic paragraph with "Generated on" and "Next update scheduled"
        italic_paragraph_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["paragraph", "text"]:
                content = block.get("content", "")
                content_lower = content.lower()

                if "generated on" in content_lower and "next update" in content_lower:
                    # Check for italic formatting - either markdown or property
                    props = block.get("properties", {})
                    has_italic_markdown = re.search(r'\*[^*]+\*', content) is not None
                    is_italic_property = props.get("italic", False)

                    if has_italic_markdown or is_italic_property:
                        italic_paragraph_found = True
                        break
                    else:
                        # Even without explicit italic formatting, accept if content is correct
                        # as the text may be wrapped in markdown
                        italic_paragraph_found = True
                        break

        if not italic_paragraph_found:
            errors.append("Italic paragraph with 'Generated on [DATE] - Next update scheduled for [DATE]' not found")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# High-Value Contacts Dashboard Task
# =============================================================================
def _validate_agent_high_value_contacts_dashboard(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: In the 'Contacts Directory' database, add a new property called 'Engagement Score' (number)
    after the 'Tags' property. Then calculate and populate this field for all existing contacts:
    VIP tag = 50 points, Active tag = 30 points, Client type = 40 points, Partner type = 35 points,
    multiple tags add up. Create a new page titled '🎯 High-Value Contacts Dashboard' as a child of
    'Team Directory'. Structure it with heading_1 '🎯 High-Value Contacts Dashboard', then query
    Contacts Directory to create: heading_2 '🏆 Top 10 Contacts by Engagement' followed by a numbered
    list showing 'Name (Company) - Score: [X]' for the top 10 scoring contacts. Add heading_2
    '📊 Engagement Distribution' with a bulleted list showing score ranges: 'High (80+): [A] contacts',
    'Medium (40-79): [B] contacts', 'Low (<40): [C] contacts'. Create an inline database
    'Contact Actions' with properties: 'Contact' (title), 'Score' (number), 'Last Contact' (date),
    'Next Action' (select with 'Email' blue, 'Call' green, 'Meeting' purple, 'Follow-up' yellow),
    'Priority' (checkbox). Add rows for all contacts with score ≥ 80, setting Last Contact to 7 days ago,
    Next Action='Meeting' for scores ≥100 else 'Email', Priority=true for VIP tags.

    Expected scores based on initial data:
    - Sarah Johnson: 120 (VIP=50 + Active=30 + Client=40)
    - Lisa Thompson: 120 (VIP=50 + Active=30 + Client=40)
    - Michael Chen: 65 (Active=30 + Partner=35)
    - Robert Garcia: 65 (Active=30 + Partner=35)
    - Emily Davis: 30 (Active=30, Vendor=0)
    - James Wilson: 0 (New=0 + Follow Up=0 + Lead=0)
    - Amanda Lee: 0 (New=0 + Lead=0)

    Distribution:
    - High (80+): 2 contacts (Sarah=120, Lisa=120)
    - Medium (40-79): 2 contacts (Michael=65, Robert=65)
    - Low (<40): 3 contacts (Emily=30, James=0, Amanda=0)

    Contacts with score >= 80: Sarah Johnson (120), Lisa Thompson (120)
    Both have VIP tag, both have score >= 100, so Next Action = Meeting, Priority = true
    """
    checks_passed: list[str] = []

    errors = []

    # Expected scores based on scoring rules
    try:
        EXPECTED_SCORES = {
            "Sarah Johnson": 120,      # VIP=50 + Active=30 + Client=40
            "Michael Chen": 65,        # Active=30 + Partner=35
            "Emily Davis": 30,         # Active=30 (Vendor=0)
            "James Wilson": 0,         # New=0 + Follow Up=0 + Lead=0
            "Lisa Thompson": 120,      # VIP=50 + Active=30 + Client=40
            "Robert Garcia": 65,       # Active=30 + Partner=35
            "Amanda Lee": 0            # New=0 + Lead=0
        }

        # === CHECK 1: Engagement Score property in Contacts Directory ===
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        contacts_db = None
        for page in all_pages:
            if page.get("type") == "database" and "contacts directory" in page.get("title", "").lower():
                contacts_db = page
                break

        if not contacts_db:
            errors.append("[X] Contacts Directory database not found")
        else:
            checks_passed.append("[C] Contacts Directory database")

        db_metadata = contacts_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        engagement_score_prop = None
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            if "engagement" in prop_name and "score" in prop_name:
                if prop.get("type") == "number":
                    engagement_score_prop = prop
                    break

        if not engagement_score_prop:
            errors.append("[X] Engagement Score (number) property not found in Contacts Directory")
        else:
            checks_passed.append("[C] Engagement Score (number) property found in Contacts Directory")

        engagement_prop_id = engagement_score_prop.get("_id")

        # === CHECK 2: Correct engagement scores for all contacts ===
        contact_rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == contacts_db.get("_id"):
                if not page.get("isArchived"):
                    contact_rows.append(page)

        if len(contact_rows) < 7:
            errors.append(f"Expected at least 7 contact rows, found {len(contact_rows)}")

        actual_scores = {}
        for row in contact_rows:
            name = row.get("title", "")
            cell_values = row.get("cellValues", {})

            # Find engagement score value
            score = None
            for key, value in cell_values.items():
                # Check by property ID
                if key == engagement_prop_id:
                    score = value
                    break
                # Check by property name pattern in key
                if "engagement" in key.lower():
                    score = value
                    break

            # Also check all properties by name
            if score is None:
                for key, value in cell_values.items():
                    for prop in properties:
                        if prop.get("_id") == key and "engagement" in prop.get("name", "").lower():
                            score = value
                            break
                    if score is not None:
                        break

            if name in EXPECTED_SCORES:
                actual_scores[name] = score
                if score is None:
                    errors.append(f"Contact '{name}' has no Engagement Score set")
                elif score != EXPECTED_SCORES[name]:
                    errors.append(f"Contact '{name}' has score {score}, expected {EXPECTED_SCORES[name]}")

        if len(actual_scores) < 7:
            errors.append(f"Only found scores for {len(actual_scores)} of 7 expected contacts")

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        # === CHECK 3: Dashboard page structure ===
        dashboard = None
        for page in all_pages:
            # Accept pages without a type field (newly created) or with type "normal"
            page_type = page.get("type")
            if (page_type is None or page_type == "normal") and not page.get("isArchived"):
                title = page.get("title", "")
                if "high-value contacts dashboard" in title.lower() or ("🎯" in title and "contacts" in title.lower()):
                    dashboard = page
                    break

        if not dashboard:
            errors.append("[X] Dashboard page '🎯 High-Value Contacts Dashboard' not found")
        else:
            checks_passed.append("[C] Dashboard page '🎯 High-Value Contacts Dashboard'")

        # Check parent is Team Directory (page-8)
        parent_id = dashboard.get("parentId")
        if parent_id != "page-8":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "team directory" not in parent_page.get("title", "").lower():
                errors.append(f"Dashboard parent is not 'Team Directory' (page-8), got parentId: {parent_id}")

        # Get dashboard blocks
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})
        dashboard_blocks = [b for b in all_blocks if b.get("pageId") == dashboard.get("_id")]
        dashboard_blocks = sorted(dashboard_blocks, key=lambda x: x.get("order", 0))

        # Check for required headings
        has_title_heading = False
        has_top10_heading = False
        has_distribution_heading = False

        for block in dashboard_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            content_lower = content.lower()

            if block_type == "heading1":
                if "high-value contacts dashboard" in content_lower or "🎯" in content:
                    has_title_heading = True
            elif block_type == "heading2":
                if "top 10" in content_lower or "🏆" in content:
                    has_top10_heading = True
                if "engagement distribution" in content_lower or "📊" in content:
                    has_distribution_heading = True

        if not has_title_heading:
            errors.append("Missing heading_1 '🎯 High-Value Contacts Dashboard'")
        if not has_top10_heading:
            errors.append("Missing heading_2 '🏆 Top 10 Contacts by Engagement'")
        if not has_distribution_heading:
            errors.append("Missing heading_2 '📊 Engagement Distribution'")

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        # === CHECK 4: Top 10 contacts numbered list ===
        numbered_lists = [b for b in dashboard_blocks if b.get("type") == "numberedList"]

        top10_valid = False
        for block in numbered_lists:
            content = block.get("content", "")
            # Check for format "Name (Company) - Score: X" pattern
            if re.search(r'\w+.*\(.*\).*-.*score.*:\s*\d+', content, re.IGNORECASE):
                top10_valid = True
                break
            # Also accept if it contains high-scoring contact names
            content_lower = content.lower()
            if "sarah johnson" in content_lower or "lisa thompson" in content_lower:
                top10_valid = True
                break

        if not top10_valid:
            errors.append("Missing numbered list with top contacts in format 'Name (Company) - Score: [X]'")

        # === CHECK 5: Engagement distribution bulleted list ===
        bulleted_lists = [b for b in dashboard_blocks if b.get("type") == "bulletedList"]

        # Expected: High (80+): 2, Medium (40-79): 2, Low (<40): 3
        distribution_found = False
        for block in bulleted_lists:
            content = block.get("content", "").lower()
            # Check for distribution patterns
            if ("high" in content and ("80" in content or "2" in content)) or \
               ("medium" in content and ("40" in content or "2" in content)) or \
               ("low" in content and ("3" in content or "40" in content)):
                distribution_found = True
                break
            # Also check for count pattern
            if re.search(r'(high|medium|low).*\d+.*contact', content, re.IGNORECASE):
                distribution_found = True
                break

        if not distribution_found:
            errors.append("Missing bulleted list with engagement distribution (High/Medium/Low counts)")

        # === CHECK 6: Contact Actions inline database ===
        contact_actions_db = None
        for page in all_pages:
            if page.get("type") == "database" and not page.get("isArchived"):
                title = page.get("title", "").lower()
                if "contact actions" in title:
                    # Should be a child of dashboard
                    if page.get("parentId") == dashboard.get("_id"):
                        contact_actions_db = page
                        break

        if not contact_actions_db:
            errors.append("[X] Inline database 'Contact Actions' not found under dashboard page")
        else:
            checks_passed.append("[C] Inline database 'Contact Actions' found under dashboard page")

        # Check properties
        ca_metadata = contact_actions_db.get("databaseMetadata", {})
        ca_props = ca_metadata.get("properties", [])
        ca_prop_map = {p.get("name", "").lower(): p for p in ca_props}

        required_props = {
            "contact": "title",
            "score": "number",
            "last contact": "date",
            "next action": "select",
            "priority": "checkbox"
        }

        for prop_name, prop_type in required_props.items():
            found = False
            for ca_name, ca_prop in ca_prop_map.items():
                if prop_name in ca_name and ca_prop.get("type") == prop_type:
                    found = True
                    break
            if not found:
                errors.append(f"Contact Actions missing property '{prop_name}' ({prop_type})")

        # Check Next Action select options
        next_action_prop = None
        for name, prop in ca_prop_map.items():
            if "next action" in name and prop.get("type") == "select":
                next_action_prop = prop
                break

        if next_action_prop:
            options = next_action_prop.get("options", [])
            option_names = {o.get("name", "").lower().replace("-", "").replace(" ", "") for o in options}
            required_options = {"email", "call", "meeting", "followup"}

            missing_options = required_options - option_names
            if missing_options:
                errors.append(f"Next Action select missing options: {missing_options}")

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        # === CHECK 7: Contact Actions rows ===
        ca_rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == contact_actions_db.get("_id"):
                if not page.get("isArchived"):
                    ca_rows.append(page)

        # Should have rows for contacts with score >= 80 (Sarah=120, Lisa=120)
        if len(ca_rows) < 2:
            errors.append("[X] Contact Actions should have at least 2 rows (contacts with score >= 80), found {len(ca_rows)}")
        else:
            checks_passed.append("[C] Contact Actions should have at least 2 rows (contacts with score >= 80)")

        high_score_contacts = {"sarah johnson", "lisa thompson"}
        found_contacts = set()

        for row in ca_rows:
            title = row.get("title", "").lower()
            for contact in high_score_contacts:
                if contact in title:
                    found_contacts.add(contact)
                    break

        if len(found_contacts) < 2:
            errors.append(f"Contact Actions should include Sarah Johnson and Lisa Thompson (score >= 80), only found: {found_contacts}")

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# VIP Client Follow-up Task
# =============================================================================
def _validate_agent_vip_client_followup(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: In the 'Contacts Directory' database, first add a new property named 'Last Contact Date'
    (date type) at position 6 (after Tags, before LinkedIn). Then find all contacts where Type='Client'
    AND Tags contains 'VIP', and for each one create a corresponding task in the 'Task Tracker' database
    with these exact values: Task Name='Follow up with [contact name]', Status='Not Started',
    Priority='High', Due Date=14 days from today, Tags=['Frontend'] (yes, use Frontend tag for client
    follow-ups as per company convention), Assignee=user-2. After creating all tasks, update the original
    VIP client contacts by setting their Tags to include 'Follow Up' (keep existing tags). Finally, create
    a summary page titled 'VIP Client Action Items' under the 'Team Directory' page with heading_1
    '👥 VIP Client Follow-ups', a callout (yellow background) showing 'Generated [X] follow-up tasks on
    [today's date]', and a numbered list of all the created tasks.

    Expected VIP Clients (Type='Client' AND Tags contains 'VIP'):
    - Sarah Johnson (db-contacts-row-1): Tags=['VIP', 'Active']
    - Lisa Thompson (db-contacts-row-5): Tags=['VIP', 'Active']
    """
    checks_passed: list[str] = []

    errors = []

    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})

        # =========================================================================
        # Check 1: Validate 'Last Contact Date' property added to Contacts Directory
        # =========================================================================
        contacts_db = None
        for page in all_pages:
            if page.get("_id") == "db-contacts" or (
                page.get("type") == "database" and
                "contacts directory" in page.get("title", "").lower()
            ):
                contacts_db = page
                break

        if not contacts_db:
            errors.append("[X] Contacts Directory database not found")
        else:
            checks_passed.append("[C] Contacts Directory database")

        db_metadata = contacts_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Find Last Contact Date property
        last_contact_prop = None
        for prop in properties:
            if prop.get("name", "").lower() == "last contact date":
                last_contact_prop = prop
                break

        if not last_contact_prop:
            errors.append("Property 'Last Contact Date' not found in Contacts Directory")
        else:
            if last_contact_prop.get("type") != "date":
                errors.append(f"'Last Contact Date' should be type 'date', got '{last_contact_prop.get('type')}'")
            # Check position: should be at order 6 (after Tags[5], before LinkedIn)
            prop_order = last_contact_prop.get("order")
            if prop_order != 6:
                errors.append(f"'Last Contact Date' should be at order 6, got {prop_order}")

        # =========================================================================
        # Check 2: Validate LinkedIn and Added properties shifted
        # =========================================================================
        linkedin_prop = None
        added_prop = None
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            if prop_name == "linkedin":
                linkedin_prop = prop
            elif prop_name == "added":
                added_prop = prop

        if linkedin_prop and linkedin_prop.get("order") != 7:
            errors.append(f"'LinkedIn' should be at order 7 after insertion, got {linkedin_prop.get('order')}")
        if added_prop and added_prop.get("order") != 8:
            errors.append(f"'Added' should be at order 8 after insertion, got {added_prop.get('order')}")

        # =========================================================================
        # Check 3-9: Validate follow-up tasks created in Task Tracker
        # =========================================================================
        tasks_db_id = "db-tasks"

        # Find all new tasks with "Follow up with" pattern
        follow_up_tasks = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == tasks_db_id:
                title = page.get("title", "")
                if "follow up with" in title.lower():
                    follow_up_tasks.append(page)

        # Expected VIP clients: Sarah Johnson, Lisa Thompson
        expected_contacts = ["sarah johnson", "lisa thompson"]
        found_contacts = set()

        for task in follow_up_tasks:
            title = task.get("title", "").lower()
            cell_values = task.get("cellValues", {})

            # Check which contact this is for
            contact_name = None
            for expected in expected_contacts:
                if expected in title:
                    contact_name = expected
                    found_contacts.add(expected)
                    break

            if not contact_name:
                continue

            # Helper to get cell value by property ID pattern
            def get_cell(pattern):
                for key, val in cell_values.items():
                    if pattern in key.lower():
                        return val
                return None

            # Check Status = 'Not Started'
            status = get_cell("status")
            if status != "Not Started":
                errors.append(f"Task for {contact_name}: Status should be 'Not Started', got '{status}'")

            # Check Priority = 'High'
            priority = get_cell("priority")
            if priority != "High":
                errors.append(f"Task for {contact_name}: Priority should be 'High', got '{priority}'")

            # Check Due Date = 14 days from today (2026-01-29)
            due_date = get_cell("due")
            if due_date:
                due_start = due_date.get("start", "") if isinstance(due_date, dict) else str(due_date)
                if "2026-01-29" not in str(due_start):
                    errors.append(f"Task for {contact_name}: Due Date should be '2026-01-29' (14 days from 2026-01-15), got '{due_start}'")
            else:
                errors.append(f"Task for {contact_name}: Due Date not set")

            # Check Tags = ['Frontend']
            tags = get_cell("tags")
            if tags:
                if isinstance(tags, list):
                    if "Frontend" not in tags:
                        errors.append(f"Task for {contact_name}: Tags should include 'Frontend', got {tags}")
                else:
                    errors.append(f"Task for {contact_name}: Tags should be a list with 'Frontend', got {tags}")
            else:
                errors.append(f"Task for {contact_name}: Tags not set")

            # Check Assignee = 'user-2'
            assignee = get_cell("assignee")
            if assignee != "user-2":
                errors.append(f"Task for {contact_name}: Assignee should be 'user-2', got '{assignee}'")

        # Check all expected contacts have tasks
        for expected in expected_contacts:
            if expected not in found_contacts:
                errors.append(f"No follow-up task created for {expected}")

        # Check exact count - should be exactly 2 tasks
        if len(found_contacts) != 2:
            errors.append(f"Expected 2 follow-up tasks for VIP clients, found {len(found_contacts)}")

        # =========================================================================
        # Check 10: Validate contact tags updated to include 'Follow Up'
        # =========================================================================
        vip_client_rows = ["db-contacts-row-1", "db-contacts-row-5"]  # Sarah Johnson, Lisa Thompson

        for row_id in vip_client_rows:
            row = None
            for page in all_pages:
                if page.get("_id") == row_id:
                    row = page
                    break

            if row:
                cell_values = row.get("cellValues", {})
                # Get tags
                tags = None
                for key, val in cell_values.items():
                    if "tags" in key.lower():
                        tags = val
                        break

                row_title = row.get("title", "")
                if tags and isinstance(tags, list):
                    if "Follow Up" not in tags:
                        errors.append(f"Contact {row_title}: Tags should include 'Follow Up', got {tags}")
                    # Check original tags preserved
                    if "VIP" not in tags:
                        errors.append(f"Contact {row_title}: Original 'VIP' tag should be preserved, got {tags}")
                    if "Active" not in tags:
                        errors.append(f"Contact {row_title}: Original 'Active' tag should be preserved, got {tags}")
                else:
                    errors.append(f"Contact {row_title}: Tags not found or not a list")

        # =========================================================================
        # Check 11-14: Validate summary page under Team Directory
        # =========================================================================
        summary_page = None
        for page in all_pages:
            page_type = page.get("type", "")
            if page_type != "database" and page_type != "row":
                title = page.get("title", "")
                if "vip client action items" in title.lower():
                    summary_page = page
                    break

        if not summary_page:
            errors.append("Summary page 'VIP Client Action Items' not found")
        else:
            # Check parent is Team Directory (page-8)
            parent_id = summary_page.get("parentId")
            if parent_id != "page-8":
                parent_page = None
                for page in all_pages:
                    if page.get("_id") == parent_id:
                        parent_page = page
                        break
                if not parent_page or "team directory" not in parent_page.get("title", "").lower():
                    errors.append(f"Summary page should be under 'Team Directory', got parentId: {parent_id}")

            # Get blocks for this page
            page_id = summary_page.get("_id")
            page_blocks = [b for b in all_blocks if b.get("pageId") == page_id]

            # Check heading_1 with '👥 VIP Client Follow-ups'
            heading_found = False
            for block in page_blocks:
                if block.get("type") == "heading1":
                    content = block.get("content", "")
                    if "vip client follow-ups" in content.lower() and "👥" in content:
                        heading_found = True
                        break
            if not heading_found:
                errors.append("Summary page missing heading_1 '👥 VIP Client Follow-ups'")

            # Check callout with yellow background
            callout_found = False
            for block in page_blocks:
                if block.get("type") == "callout":
                    props = block.get("properties", {})
                    bg_color = props.get("backgroundColor", "").lower()
                    content = block.get("content", "").lower()
                    if "yellow" in bg_color:
                        if "generated" in content and "2" in content and "follow" in content:
                            callout_found = True
                            break
            if not callout_found:
                errors.append("Summary page missing yellow callout with task generation info")

            # Check numbered list with tasks
            numbered_list_items = []
            for block in page_blocks:
                if block.get("type") == "numberedList":
                    content = block.get("content", "").lower()
                    if "follow up" in content or "sarah" in content or "lisa" in content:
                        numbered_list_items.append(block)

            if len(numbered_list_items) < 2:
                errors.append(f"Summary page should have numbered list with 2 task items, found {len(numbered_list_items)}")

        # =========================================================================
        # Return result
        # =========================================================================
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Reward Functions Registry
# =============================================================================
# Onboarding Progress Tracker Task 2
# =============================================================================
def _validate_agent_onboarding_progress_tracker_2(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Navigate to the 'Onboarding Guide' page and insert a new section immediately AFTER
    the 'First Week Goals' section but BEFORE any subsequent content. Add heading_2
    '📊 New Hire Tracking System', then create an inline database with title 'Onboarding Progress
    Tracker'. Configure with these exact properties in order: 'Employee Name' (title), 'Start Date'
    (date), 'Manager' (text), 'Department' (select: copy all options from Project Portfolio's
    Department property), 'Week 1 Tasks' (multi_select: 'Workstation Setup', 'Account Creation',
    'Team Introduction', 'Handbook Review'), 'Week 1 Complete' (checkbox), 'Training Modules'
    (multi_select: 'Security Training', 'Product Overview', 'Tools Training', 'Process Training'),
    'Training Complete' (checkbox), 'Buddy Assigned' (text), 'First Project' (text). Then query
    the 'Task Tracker' database for all tasks assigned to user-3 with Status='Not Started', and
    create a row in the tracker: 'New Developer - Q1 2026', Start Date=today, Manager='Alice Smith',
    Department='Engineering', Week 1 Tasks=['Workstation Setup', 'Account Creation'],
    First Project=[title of first task found].

    Expected values from initial data:
    - Onboarding Guide is page-13
    - First Week Goals is block-13-7 at order 6
    - Project Portfolio Department options: Engineering, Marketing, Sales, Operations, HR
    - Task Tracker task for user-3 with Status='Not Started': "Write API documentation" (db-tasks-row-3)
    - Today's date: 2026-01-15
    """
    checks_passed: list[str] = []

    errors = []

    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})

        # Step 1: Find heading_2 '📊 New Hire Tracking System' on page-13
        onboarding_page_id = "page-13"
        page_blocks = [b for b in all_blocks if b.get("pageId") == onboarding_page_id]
        sorted_blocks = sorted(page_blocks, key=lambda b: b.get("order", 0))

        new_hire_heading = None
        for block in sorted_blocks:
            block_type = block.get("type", "")
            content = block.get("content", "")
            if block_type in ["heading2", "heading_2"]:
                if "new hire tracking system" in content.lower() and "📊" in content:
                    new_hire_heading = block
                    break

        if not new_hire_heading:
            errors.append("heading_2 '📊 New Hire Tracking System' not found on Onboarding Guide page")

        # Step 2: Verify heading appears after 'First Week Goals' (order > 6)
        if new_hire_heading:
            heading_order = new_hire_heading.get("order", 0)
            # First Week Goals is at order 6, so new heading should be > 6
            if heading_order <= 6:
                errors.append(f"New Hire Tracking System heading should appear after First Week Goals (order > 6), got order {heading_order}")

        # Step 3: Find inline database 'Onboarding Progress Tracker'
        tracker_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "onboarding progress tracker" in title:
                    tracker_db = page
                    break

        if not tracker_db:
            errors.append("[X] Database 'Onboarding Progress Tracker' not found")
        else:
            checks_passed.append("[C] Database 'Onboarding Progress Tracker'")

        # Step 4: Verify database is inline (parentId = page-13)
        parent_id = tracker_db.get("parentId")
        if parent_id != onboarding_page_id:
            # Check if parent is Onboarding Guide
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "onboarding guide" not in parent_page.get("title", "").lower():
                errors.append(f"Database should be inline within Onboarding Guide (page-13), got parentId: {parent_id}")

        # Step 5: Validate database properties - 10 properties with correct names and types
        db_metadata = tracker_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        expected_properties = [
            ("Employee Name", "title"),
            ("Start Date", "date"),
            ("Manager", "text"),
            ("Department", "select"),
            ("Week 1 Tasks", "multi_select"),
            ("Week 1 Complete", "checkbox"),
            ("Training Modules", "multi_select"),
            ("Training Complete", "checkbox"),
            ("Buddy Assigned", "text"),
            ("First Project", "text"),
        ]

        prop_map = {}
        for prop in properties:
            prop_map[prop.get("name", "").lower()] = prop

        for expected_name, expected_type in expected_properties:
            prop = prop_map.get(expected_name.lower())
            if not prop:
                errors.append(f"Property '{expected_name}' not found in database")
            elif prop.get("type") != expected_type:
                errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{prop.get('type')}'")

        # Step 6: Validate Department select has all 5 options from Project Portfolio
        dept_prop = prop_map.get("department")
        if dept_prop:
            dept_options = dept_prop.get("options", [])
            expected_depts = ["engineering", "marketing", "sales", "operations", "hr"]
            found_depts = [opt.get("name", "").lower() for opt in dept_options]
            for dept in expected_depts:
                if dept not in found_depts:
                    errors.append(f"Department property missing option '{dept}'")

        # Step 7: Validate Week 1 Tasks multi_select options
        week1_prop = prop_map.get("week 1 tasks")
        if week1_prop:
            week1_options = week1_prop.get("options", [])
            expected_week1 = ["workstation setup", "account creation", "team introduction", "handbook review"]
            found_week1 = [opt.get("name", "").lower() for opt in week1_options]
            for task in expected_week1:
                if task not in found_week1:
                    errors.append(f"Week 1 Tasks property missing option '{task}'")

        # Step 8: Validate Training Modules multi_select options
        training_prop = prop_map.get("training modules")
        if training_prop:
            training_options = training_prop.get("options", [])
            expected_training = ["security training", "product overview", "tools training", "process training"]
            found_training = [opt.get("name", "").lower() for opt in training_options]
            for module in expected_training:
                if module not in found_training:
                    errors.append(f"Training Modules property missing option '{module}'")

        # Step 9-14: Find and validate row 'New Developer - Q1 2026'
        db_id = tracker_db.get("_id")
        rows = [p for p in all_pages if p.get("type") == "row" and p.get("databaseId") == db_id]

        new_dev_row = None
        for row in rows:
            title = row.get("title", "")
            if "new developer" in title.lower() and "q1 2026" in title.lower():
                new_dev_row = row
                break

        if not new_dev_row:
            errors.append("Row 'New Developer - Q1 2026' not found in Onboarding Progress Tracker")
        else:
            cell_values = new_dev_row.get("cellValues", {})

            # Build property ID map
            prop_id_map = {}
            for prop in properties:
                prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

            def get_cell_value(prop_name_lower):
                prop_id = prop_id_map.get(prop_name_lower)
                if prop_id:
                    return cell_values.get(prop_id)
                for key, val in cell_values.items():
                    if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                        return val
                return None

            # Step 10: Check Start Date = today (2026-01-15)
            start_date = get_cell_value("start date")
            if start_date:
                start_val = start_date.get("start", "") if isinstance(start_date, dict) else str(start_date)
                if "2026-01-15" not in str(start_val):
                    errors.append(f"Row Start Date should be '2026-01-15' (today), got '{start_val}'")
            else:
                errors.append("Row missing Start Date")

            # Step 11: Check Manager = 'Alice Smith'
            manager = get_cell_value("manager")
            if manager:
                if "alice smith" not in str(manager).lower():
                    errors.append(f"Row Manager should be 'Alice Smith', got '{manager}'")
            else:
                errors.append("Row missing Manager")

            # Step 12: Check Department = 'Engineering'
            department = get_cell_value("department")
            if department:
                if "engineering" not in str(department).lower():
                    errors.append(f"Row Department should be 'Engineering', got '{department}'")
            else:
                errors.append("Row missing Department")

            # Step 13: Check Week 1 Tasks contains 'Workstation Setup' and 'Account Creation'
            week1_tasks = get_cell_value("week 1 tasks")
            if week1_tasks:
                tasks_list = week1_tasks if isinstance(week1_tasks, list) else [week1_tasks]
                tasks_lower = [str(t).lower() for t in tasks_list]
                if not any("workstation setup" in t for t in tasks_lower):
                    errors.append("Row Week 1 Tasks missing 'Workstation Setup'")
                if not any("account creation" in t for t in tasks_lower):
                    errors.append("Row Week 1 Tasks missing 'Account Creation'")
            else:
                errors.append("Row missing Week 1 Tasks")

            # Step 14: Check First Project = 'Write API documentation'
            first_project = get_cell_value("first project")
            if first_project:
                if "write api documentation" not in str(first_project).lower():
                    errors.append(f"Row First Project should be 'Write API documentation', got '{first_project}'")
            else:
                errors.append("Row missing First Project")

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Department Budget Analysis Task 2
# =============================================================================
def _validate_agent_department_budget_analysis_2(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Query the 'Project Portfolio' database and create a comprehensive financial analysis.
    First, add a new property to the database called 'ROI Status' (select with options:
    'Exceeding Target' green, 'On Target' blue, 'Below Target' yellow, 'At Risk' red). For each
    project, calculate ROI Status based on: if Budget < 30000 set 'Exceeding Target', if
    30000-50000 set 'On Target', if 50001-80000 set 'Below Target', else 'At Risk'. Then create
    an inline database inside the 'Company Handbook' page (place it after the first heading but
    before any other content). Title it 'Department Budget Analysis' with properties: 'Department'
    (select matching Project Portfolio options), 'Total Budget' (number), 'Active Projects'
    (number), 'Average Budget' (number). Populate with aggregated data from Project Portfolio
    grouped by Department.

    Expected values from initial data:
    - Project Portfolio: db-projects
    - Company Handbook: page-16
    - Projects and their budgets/departments:
      - row-1: Website Redesign, 50000, Engineering -> On Target
      - row-2: Q1 Marketing Campaign, 25000, Marketing -> Exceeding Target
      - row-3: Mobile App Development, 100000, Engineering -> At Risk
      - row-4: Customer Portal, 75000, Engineering -> Below Target
      - row-5: Employee Training Program, 15000, HR -> Exceeding Target
      - row-6: Sales Pipeline Automation, 35000, Sales -> On Target
    - Department aggregations:
      - Engineering: Total=225000, Projects=3, Avg=75000
      - Marketing: Total=25000, Projects=1, Avg=25000
      - HR: Total=15000, Projects=1, Avg=15000
      - Sales: Total=35000, Projects=1, Avg=35000
    """
    checks_passed: list[str] = []

    errors = []
    try:

        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # Step 1: Find Project Portfolio database and check for ROI Status property
        project_portfolio = None
        for page in all_pages:
            if page.get("_id") == "db-projects" or (
                page.get("type") == "database" and "project portfolio" in page.get("title", "").lower()
            ):
                project_portfolio = page
                break

        if not project_portfolio:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")

        db_metadata = project_portfolio.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Step 1: Check ROI Status property exists
        roi_status_prop = None
        for prop in properties:
            if prop.get("name", "").lower() == "roi status":
                roi_status_prop = prop
                break

        if not roi_status_prop:
            errors.append("'ROI Status' property not found in Project Portfolio")
        elif roi_status_prop.get("type") != "select":
            errors.append("'ROI Status' property should be type 'select'")

        # Step 2: Check ROI Status has correct options with colors
        if roi_status_prop:
            options = roi_status_prop.get("options", [])
            expected_options = {
                "exceeding target": "green",
                "on target": "blue",
                "below target": "yellow",
                "at risk": "red",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"ROI Status missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"ROI Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Steps 3-8: Check each project row has correct ROI Status based on budget
        # Budget rules: <30000 = Exceeding Target, 30000-50000 = On Target, 50001-80000 = Below Target, >80000 = At Risk
        expected_roi_values = {
            "db-projects-row-1": ("Website Redesign", 50000, "on target"),
            "db-projects-row-2": ("Q1 Marketing Campaign", 25000, "exceeding target"),
            "db-projects-row-3": ("Mobile App Development", 100000, "at risk"),
            "db-projects-row-4": ("Customer Portal", 75000, "below target"),
            "db-projects-row-5": ("Employee Training Program", 15000, "exceeding target"),
            "db-projects-row-6": ("Sales Pipeline Automation", 35000, "on target"),
        }

        # Find ROI Status property ID
        roi_prop_id = roi_status_prop.get("_id") if roi_status_prop else None

        for row_id, (project_name, budget, expected_status) in expected_roi_values.items():
            row = None
            for page in all_pages:
                if page.get("_id") == row_id or (
                    page.get("type") == "row" and
                    page.get("databaseId") == "db-projects" and
                    project_name.lower() in page.get("title", "").lower()
                ):
                    row = page
                    break

            if not row:
                errors.append(f"Project row '{project_name}' not found")
                continue

            cell_values = row.get("cellValues", {})

            # Try to find ROI Status value
            roi_value = None
            if roi_prop_id and roi_prop_id in cell_values:
                roi_value = cell_values.get(roi_prop_id)
            else:
                # Search by property name pattern
                for key, val in cell_values.items():
                    if "roi" in key.lower() or "status" in key.lower():
                        roi_value = val
                        break

            if roi_value:
                if expected_status not in str(roi_value).lower():
                    errors.append(f"Project '{project_name}' (budget {budget}) should have ROI Status '{expected_status}', got '{roi_value}'")
            else:
                errors.append(f"Project '{project_name}' missing ROI Status value")

        # Step 9: Find Department Budget Analysis database in Company Handbook (page-16)
        dept_analysis_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "department budget analysis" in title:
                    dept_analysis_db = page
                    break

        if not dept_analysis_db:
            errors.append("[X] Database 'Department Budget Analysis' not found")
        else:
            checks_passed.append("[C] Database 'Department Budget Analysis'")

        # Check it's in Company Handbook (page-16)
        parent_id = dept_analysis_db.get("parentId")
        if parent_id != "page-16":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "company handbook" not in parent_page.get("title", "").lower():
                errors.append(f"Department Budget Analysis should be inline in Company Handbook (page-16), got parentId: {parent_id}")

        # Step 10: Validate database properties
        dept_db_metadata = dept_analysis_db.get("databaseMetadata", {})
        dept_properties = dept_db_metadata.get("properties", [])

        expected_props = [
            ("Department", "select"),
            ("Total Budget", "number"),
            ("Active Projects", "number"),
            ("Average Budget", "number"),
        ]

        prop_map = {}
        for prop in dept_properties:
            prop_map[prop.get("name", "").lower()] = prop

        for expected_name, expected_type in expected_props:
            prop = prop_map.get(expected_name.lower())
            if not prop:
                errors.append(f"Property '{expected_name}' not found in Department Budget Analysis")
            elif prop.get("type") != expected_type:
                errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{prop.get('type')}'")

        # Step 11: Validate Department select has options matching Project Portfolio
        dept_prop = prop_map.get("department")
        if dept_prop:
            dept_options = dept_prop.get("options", [])
            expected_depts = ["engineering", "marketing", "sales", "operations", "hr"]
            found_depts = [opt.get("name", "").lower() for opt in dept_options]
            for dept in expected_depts:
                if dept not in found_depts:
                    errors.append(f"Department property missing option '{dept}'")

        # Steps 12-15: Validate aggregated rows
        db_id = dept_analysis_db.get("_id")
        rows = [p for p in all_pages if p.get("type") == "row" and p.get("databaseId") == db_id]

        # Build property ID map for cell value lookup
        prop_id_map = {}
        for prop in dept_properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id:
                return cell_values.get(prop_id)
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                    return val
            return None

        expected_dept_data = {
            "engineering": {"total": 225000, "projects": 3, "avg": 75000},
            "marketing": {"total": 25000, "projects": 1, "avg": 25000},
            "hr": {"total": 15000, "projects": 1, "avg": 15000},
            "sales": {"total": 35000, "projects": 1, "avg": 35000},
        }

        for dept_name, expected_values in expected_dept_data.items():
            dept_row = None
            for row in rows:
                title = row.get("title", "").lower()
                dept_val = get_cell_value(row, "department")
                if dept_name in title or (dept_val and dept_name in str(dept_val).lower()):
                    dept_row = row
                    break

            if not dept_row:
                errors.append(f"Department row '{dept_name}' not found in Department Budget Analysis")
                continue

            # Check Total Budget
            total_budget = get_cell_value(dept_row, "total budget")
            if total_budget is not None:
                try:
                    if int(float(total_budget)) != expected_values["total"]:
                        errors.append(f"{dept_name} Total Budget should be {expected_values['total']}, got {total_budget}")
                except (ValueError, TypeError):
                    errors.append(f"{dept_name} Total Budget is not a valid number: {total_budget}")
            else:
                errors.append(f"{dept_name} missing Total Budget")

            # Check Active Projects
            active_projects = get_cell_value(dept_row, "active projects")
            if active_projects is not None:
                try:
                    if int(float(active_projects)) != expected_values["projects"]:
                        errors.append(f"{dept_name} Active Projects should be {expected_values['projects']}, got {active_projects}")
                except (ValueError, TypeError):
                    errors.append(f"{dept_name} Active Projects is not a valid number: {active_projects}")
            else:
                errors.append(f"{dept_name} missing Active Projects")

            # Check Average Budget
            avg_budget = get_cell_value(dept_row, "average budget")
            if avg_budget is not None:
                try:
                    if int(float(avg_budget)) != expected_values["avg"]:
                        errors.append(f"{dept_name} Average Budget should be {expected_values['avg']}, got {avg_budget}")
                except (ValueError, TypeError):
                    errors.append(f"{dept_name} Average Budget is not a valid number: {avg_budget}")
            else:
                errors.append(f"{dept_name} missing Average Budget")

        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task Escalation System Task
# =============================================================================
def _validate_agent_task_escalation_system(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create an automated task escalation system spanning multiple databases. First, in 'Task Tracker',
    find all tasks where Status='Blocked' OR (Status='In Progress' AND Due Date is within next 3 days).
    For these tasks, create corresponding rows in 'Project Portfolio' database with a special naming
    convention: 'ESCALATION: [Task Name]', Status='On Hold', Budget=0, Department=(match from task Tags:
    Bug/Backend→Engineering, Design/Frontend→Marketing), Timeline start=today end=Due Date+7days.
    Then create 'Escalation Dashboard' page under 'Feature Requests' with: heading_1 '🚨 Escalation Dashboard',
    a red callout with icon '⚡' showing 'Active escalations: [X] | Blocked tasks: [Y] | At-risk tasks: [Z]'.
    Add heading_2 '📊 Escalation Matrix' followed by a 2x2 grid (using dividers and headings) categorizing
    escalations by Urgency (based on Priority) and Impact (based on Tags count).

    Expected from initial data (date: 2026-01-15):
    - Matching tasks:
      - "Update database schema" (Status=Blocked, Tags=["Backend"], Due=2026-01-13, Priority=Urgent)
    - Escalation row: "ESCALATION: Update database schema", Status="On Hold", Budget=0,
      Department="Engineering", Timeline={start: "2026-01-15", end: "2026-01-20"}
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # ==================== Get all pages ====================
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # ==================== CHECK 1: Find Project Portfolio Database ====================
        project_portfolio_db = None
        for page in all_pages:
            if page.get("_id") == "db-projects" or (page.get("type") == "database" and "project portfolio" in page.get("title", "").lower()):
                project_portfolio_db = page
                break

        if not project_portfolio_db:
            errors.append("[X] Project Portfolio database not found")
        else:
            checks_passed.append("[C] Project Portfolio database")

        # Get Project Portfolio property IDs
        project_db_id = project_portfolio_db.get("_id")
        project_props = project_portfolio_db.get("databaseMetadata", {}).get("properties", [])
        project_prop_id_map = {}
        for prop in project_props:
            project_prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        # ==================== CHECK 2: Find Escalation Rows in Project Portfolio ====================
        escalation_rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == project_db_id:
                title = page.get("title", "")
                if "escalation" in title.lower() and ":" in title:
                    escalation_rows.append(page)

        if len(escalation_rows) == 0:
            errors.append("[X] No ESCALATION rows found in Project Portfolio database")
        else:
            checks_passed.append("[C] ESCALATION rows found in Project Portfolio database")

        # Helper to get cell value by property name
        def get_cell_value(cell_values, prop_name_lower):
            prop_id = project_prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            for key, value in cell_values.items():
                if prop_name_lower in key.lower():
                    return value
            return None

        # Validate escalation rows have correct properties
        valid_escalation_count = 0
        for row in escalation_rows:
            cell_values = row.get("cellValues", {})
            row_errors = []

            # Check Status = "On Hold"
            status_value = get_cell_value(cell_values, "status")
            if not status_value or "on hold" not in str(status_value).lower():
                row_errors.append(f"Status should be 'On Hold', got '{status_value}'")

            # Check Budget = 0
            budget_value = get_cell_value(cell_values, "budget")
            if budget_value is None:
                row_errors.append("Budget is missing")
            else:
                try:
                    if int(budget_value) != 0:
                        row_errors.append(f"Budget should be 0, got {budget_value}")
                except (ValueError, TypeError):
                    row_errors.append(f"Budget should be 0, got '{budget_value}'")

            # Check Department is either "Engineering" or "Marketing"
            dept_value = get_cell_value(cell_values, "department")
            if not dept_value:
                row_errors.append("Department is missing")
            elif "engineering" not in str(dept_value).lower() and "marketing" not in str(dept_value).lower():
                row_errors.append(f"Department should be 'Engineering' or 'Marketing', got '{dept_value}'")

            # Check Timeline has start and end
            timeline_value = get_cell_value(cell_values, "timeline")
            if not timeline_value:
                row_errors.append("Timeline is missing")
            elif isinstance(timeline_value, dict):
                if not timeline_value.get("start"):
                    row_errors.append("Timeline missing start date")
                if not timeline_value.get("end"):
                    row_errors.append("Timeline missing end date")

            if not row_errors:
                valid_escalation_count += 1
            else:
                errors.append(f"Escalation row '{row.get('title')}': " + "; ".join(row_errors))

        if valid_escalation_count == 0:
            errors.append("No valid escalation rows with correct Status, Budget, Department, and Timeline")

        # ==================== CHECK 3: Escalation Dashboard Page Under Feature Requests ====================
        dashboard_page = None
        for page in all_pages:
            if page.get("type") not in ["database", "row"]:
                title = page.get("title", "")
                if "escalation dashboard" in title.lower():
                    dashboard_page = page
                    break

        if not dashboard_page:
            errors.append("[X] Page 'Escalation Dashboard' not found")
        else:
            checks_passed.append("[C] Page 'Escalation Dashboard'")

        # Check parent is Feature Requests (page-15)
        parent_id = dashboard_page.get("parentId")
        if parent_id != "page-15":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "feature requests" not in parent_page.get("title", "").lower():
                errors.append(f"Dashboard page should be child of 'Feature Requests' (page-15), got parentId: {parent_id}")

        # ==================== CHECK 4: Get Dashboard Blocks ====================
        dashboard_page_id = dashboard_page.get("_id")
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": dashboard_page_id}})
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0)) if all_blocks else []

        # ==================== CHECK 5: heading_1 with emoji ====================
        heading1_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading1", "heading_1"]:
                content = block.get("content", "")
                if "escalation dashboard" in content.lower() and "🚨" in content:
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("heading_1 '🚨 Escalation Dashboard' not found")

        # ==================== CHECK 6: Red Callout with Lightning Icon ====================
        callout_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type == "callout":
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", block.get("backgroundColor", ""))
                icon = properties.get("icon", block.get("icon", ""))
                content = block.get("content", "")

                has_red_bg = "red" in str(bg_color).lower() if bg_color else False
                has_lightning = "⚡" in str(icon) or "⚡" in content

                # Check for format: "Active escalations: [X] | Blocked tasks: [Y] | At-risk tasks: [Z]"
                has_active_escalations = "active escalation" in content.lower()
                has_blocked_tasks = "blocked task" in content.lower() or "blocked" in content.lower()
                has_at_risk = "at-risk" in content.lower() or "at risk" in content.lower() or "risk" in content.lower()

                if has_red_bg and has_lightning and has_active_escalations and has_blocked_tasks and has_at_risk:
                    callout_found = True
                    break

        if not callout_found:
            errors.append("Red callout with '⚡' icon showing 'Active escalations: [X] | Blocked tasks: [Y] | At-risk tasks: [Z]' not found")

        # ==================== CHECK 7: heading_2 Escalation Matrix ====================
        matrix_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "escalation matrix" in content.lower() and "📊" in content:
                    matrix_heading_found = True
                    break

        if not matrix_heading_found:
            errors.append("heading_2 '📊 Escalation Matrix' not found")

        # ==================== Return Result ====================
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# VIP Client Engagement Tracker Task
# =============================================================================
def _validate_agent_vip_client_engagement_tracker(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Analyze the 'Contacts Directory' database to identify all contacts with Type='Client'
    and Tags containing 'VIP'. For these VIP clients, create a new database titled
    'VIP Client Engagement Tracker' as a child of 'Team Directory' page with properties:
    'Client Name' (title), 'Company' (text), 'Last Contact Date' (date), 'Engagement Score'
    (number from 1-10), 'Account Status' (select: 'Active' green, 'At Risk' yellow, 'Churned' red),
    'Next Action' (text), 'Quarterly Revenue' (number). Create rows for each VIP client with:
    Client Name and Company copied from Contacts, Last Contact Date={start: '2026-01-10'},
    Engagement Score=8 for all, Account Status='Active', Next Action='Schedule quarterly business review',
    Quarterly Revenue=50000. Then update the original Contacts Directory by adding a new property
    'VIP Tier' (select: 'Platinum' in purple, 'Gold' in yellow, 'Silver' in gray), and set all
    VIP+Client contacts to 'Platinum'.

    VIP Clients from initial data:
    - Sarah Johnson (TechCorp): Type='Client', Tags=['VIP', 'Active']
    - Lisa Thompson (Enterprise Co): Type='Client', Tags=['VIP', 'Active']
    """
    checks_passed: list[str] = []

    errors = []

    # ==================== CHECK 1: Find VIP Client Engagement Tracker Database ====================
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        tracker_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "vip client engagement tracker" in title:
                    tracker_db = page
                    break

        if not tracker_db:
            errors.append("[X] Database 'VIP Client Engagement Tracker' not found")
        else:
            checks_passed.append("[C] Database 'VIP Client Engagement Tracker'")

        # ==================== CHECK 2: Validate Parent is Team Directory (page-8) ====================
        parent_id = tracker_db.get("parentId")
        parent_valid = False
        if parent_id == "page-8":
            parent_valid = True
        else:
            # Check if parent has title "Team Directory"
            for page in all_pages:
                if page.get("_id") == parent_id:
                    if "team directory" in page.get("title", "").lower():
                        parent_valid = True
                    break

        if not parent_valid:
            errors.append(f"Database parent should be 'Team Directory' (page-8), got parentId: {parent_id}")

        # ==================== CHECK 3: Validate Database Properties Schema ====================
        db_metadata = tracker_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        expected_properties = [
            ("Client Name", "title"),
            ("Company", "text"),
            ("Last Contact Date", "date"),
            ("Engagement Score", "number"),
            ("Account Status", "select"),
            ("Next Action", "text"),
            ("Quarterly Revenue", "number"),
        ]

        for expected_name, expected_type in expected_properties:
            found = False
            for prop in properties:
                prop_name = prop.get("name", "").lower()
                if prop_name == expected_name.lower():
                    found = True
                    if prop.get("type") != expected_type:
                        errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{prop.get('type')}'")
                    break
            if not found:
                errors.append(f"Property '{expected_name}' not found in database")

        # ==================== CHECK 4: Validate Account Status Select Options ====================
        account_status_prop = None
        for prop in properties:
            if prop.get("name", "").lower() == "account status":
                account_status_prop = prop
                break

        if account_status_prop:
            options = account_status_prop.get("options", [])
            expected_options = {
                "active": "green",
                "at risk": "yellow",
                "churned": "red",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Account Status missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Account Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")
        else:
            errors.append("Account Status property not found for option validation")

        # ==================== CHECK 5: Validate Row Count (should be 2 VIP clients) ====================
        db_id = tracker_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                if not page.get("isArchived", False):
                    rows.append(page)

        if len(rows) != 2:
            errors.append(f"Expected 2 rows for VIP clients, got {len(rows)}")

        # ==================== CHECK 6 & 7: Validate Row Data for Both VIP Clients ====================
        # Build property ID map
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Fallback: try matching property name in cell keys
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                    return val
            return None

        expected_clients = {
            "sarah johnson": "TechCorp",
            "lisa thompson": "Enterprise Co",
        }

        found_clients = set()
        for row in rows:
            title = row.get("title", "").lower()

            # Match client by name
            matched_client = None
            for client_name, company in expected_clients.items():
                if client_name in title:
                    matched_client = (client_name, company)
                    found_clients.add(client_name)
                    break

            if matched_client:
                client_name, expected_company = matched_client
                client_display = client_name.title()

                # Check Company
                company_val = get_cell_value(row, "company")
                if company_val:
                    if expected_company.lower() not in str(company_val).lower():
                        errors.append(f"{client_display} Company should be '{expected_company}', got '{company_val}'")
                else:
                    errors.append(f"{client_display} missing Company value")

                # Check Last Contact Date = 2026-01-10
                last_contact = get_cell_value(row, "last contact date")
                if last_contact:
                    date_str = last_contact.get("start", "") if isinstance(last_contact, dict) else str(last_contact)
                    if "2026-01-10" not in str(date_str):
                        errors.append(f"{client_display} Last Contact Date should be '2026-01-10', got '{date_str}'")
                else:
                    errors.append(f"{client_display} missing Last Contact Date")

                # Check Engagement Score = 8
                engagement_score = get_cell_value(row, "engagement score")
                if engagement_score is not None:
                    if int(engagement_score) != 8:
                        errors.append(f"{client_display} Engagement Score should be 8, got {engagement_score}")
                else:
                    errors.append(f"{client_display} missing Engagement Score")

                # Check Account Status = Active
                account_status = get_cell_value(row, "account status")
                if account_status:
                    if "active" not in str(account_status).lower():
                        errors.append(f"{client_display} Account Status should be 'Active', got '{account_status}'")
                else:
                    errors.append(f"{client_display} missing Account Status")

                # Check Next Action = 'Schedule quarterly business review'
                next_action = get_cell_value(row, "next action")
                if next_action:
                    if "schedule quarterly business review" not in str(next_action).lower():
                        errors.append(f"{client_display} Next Action should be 'Schedule quarterly business review', got '{next_action}'")
                else:
                    errors.append(f"{client_display} missing Next Action")

                # Check Quarterly Revenue = 50000
                quarterly_revenue = get_cell_value(row, "quarterly revenue")
                if quarterly_revenue is not None:
                    if int(quarterly_revenue) != 50000:
                        errors.append(f"{client_display} Quarterly Revenue should be 50000, got {quarterly_revenue}")
                else:
                    errors.append(f"{client_display} missing Quarterly Revenue")

        # Check all expected clients were found
        for client_name in expected_clients.keys():
            if client_name not in found_clients:
                errors.append(f"VIP client '{client_name.title()}' not found in tracker rows")

        # ==================== CHECK 8: Contacts Directory has VIP Tier Property ====================
        contacts_db = None
        for page in all_pages:
            if page.get("type") == "database":
                if page.get("_id") == "db-contacts" or "contacts directory" in page.get("title", "").lower():
                    contacts_db = page
                    break

        if not contacts_db:
            errors.append("Contacts Directory database not found")
        else:
            contacts_metadata = contacts_db.get("databaseMetadata", {})
            contacts_properties = contacts_metadata.get("properties", [])

            vip_tier_prop = None
            for prop in contacts_properties:
                if prop.get("name", "").lower() == "vip tier":
                    vip_tier_prop = prop
                    break

            if not vip_tier_prop:
                errors.append("'VIP Tier' property not added to Contacts Directory")
            else:
                # Check property type is select
                if vip_tier_prop.get("type") != "select":
                    errors.append(f"VIP Tier property should be 'select' type, got '{vip_tier_prop.get('type')}'")

                # Check options
                tier_options = vip_tier_prop.get("options", [])
                expected_tier_options = {
                    "platinum": "purple",
                    "gold": "yellow",
                    "silver": "gray",
                }

                found_tier_options = {}
                for opt in tier_options:
                    opt_name = opt.get("name", "").lower()
                    opt_color = opt.get("color", "").lower()
                    found_tier_options[opt_name] = opt_color

                for exp_name, exp_color in expected_tier_options.items():
                    if exp_name not in found_tier_options:
                        errors.append(f"VIP Tier missing option '{exp_name}'")
                    elif found_tier_options[exp_name] != exp_color:
                        errors.append(f"VIP Tier option '{exp_name}' should have color '{exp_color}', got '{found_tier_options[exp_name]}'")

        # ==================== CHECK 9: VIP+Client Contacts Set to Platinum ====================
        if contacts_db and vip_tier_prop:
            vip_tier_prop_id = vip_tier_prop.get("_id")
            contacts_db_id = contacts_db.get("_id")

            # Find Sarah Johnson and Lisa Thompson in contacts
            vip_client_rows = []
            for page in all_pages:
                if page.get("type") == "row" and page.get("databaseId") == contacts_db_id:
                    title = page.get("title", "").lower()
                    if "sarah johnson" in title or "lisa thompson" in title:
                        vip_client_rows.append(page)

            for row in vip_client_rows:
                title = row.get("title", "")
                cell_values = row.get("cellValues", {})

                # Get VIP Tier value
                vip_tier_value = cell_values.get(vip_tier_prop_id)
                if not vip_tier_value:
                    # Try fallback matching
                    for key, val in cell_values.items():
                        if "vip" in key.lower() and "tier" in key.lower():
                            vip_tier_value = val
                            break

                if vip_tier_value:
                    if "platinum" not in str(vip_tier_value).lower():
                        errors.append(f"Contact '{title}' VIP Tier should be 'Platinum', got '{vip_tier_value}'")
                else:
                    errors.append(f"Contact '{title}' missing VIP Tier value")

        # ==================== Return Result ====================
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Retrospective Report Task
# =============================================================================
def _validate_agent_sprint_retrospective_report(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Analyze the 'Task Tracker' database and create a comprehensive 'Sprint Retrospective Report'
    under 'Meeting Notes'. First, calculate completion metrics: count of Done tasks, total tasks,
    completion percentage. Structure: heading_1 '🔄 Sprint Retrospective Report', callout
    (green_background if completion > 70%, yellow_background if 50-70%, red_background if < 50%,
    icon '📊') with 'Sprint Completion: [X]% ([Y] of [Z] tasks completed)'. Add heading_2
    '🏆 Team Performance Metrics' and create inline database 'Individual Contributions' with:
    'Team Member' (title), 'Tasks Assigned' (number), 'Tasks Completed' (number), 'Completion Rate'
    (text showing percentage), 'Top Priority Tasks' (number counting High+Urgent), 'Recognition'
    (select: 'Star Performer' gold, 'Strong Contributor' silver, 'Needs Support' bronze). Query
    Task Tracker to populate rows for each assignee, calculate their metrics, set
    Recognition='Star Performer' if completion rate > 80%, 'Strong Contributor' if > 50%, else
    'Needs Support'. End with heading_2 '🎯 Next Sprint Planning' and paragraph 'Based on velocity
    of [completed tasks count], recommended sprint capacity: [count * 1.1] tasks.'

    Expected values from initial data:
    - Total tasks: 8
    - Done tasks: 2 (row-4: Implement user notifications, row-8: Add unit tests for auth module)
    - Completion percentage: 25% (< 50%, so red_background)

    Per-user metrics:
    - Alice Chen (user-1): 3 assigned (rows 1,4,7), 1 completed (row 4), 33.3%, High+Urgent=2, Needs Support
    - Bob Martinez (user-2): 3 assigned (rows 2,5,8), 1 completed (row 8), 33.3%, High+Urgent=2, Needs Support
    - Carol Williams (user-3): 2 assigned (rows 3,6), 0 completed, 0%, High+Urgent=0, Needs Support

    Next Sprint: velocity=2, capacity=2.2
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # Step 1: Find the Sprint Retrospective Report page under Meeting Notes
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        report_page = None
        for page in all_pages:
            title = page.get("title", "")
            if "sprint retrospective report" in title.lower():
                report_page = page
                break

        if not report_page:
            errors.append("[X] Page 'Sprint Retrospective Report' not found")
        else:
            checks_passed.append("[C] Page 'Sprint Retrospective Report'")

        # Check parent is Meeting Notes (page-3 in workspace-1)
        parent_id = report_page.get("parentId")
        meeting_notes_valid = False
        if parent_id == "page-3":
            meeting_notes_valid = True
        else:
            # Check if parent has title "Meeting Notes"
            for page in all_pages:
                if page.get("_id") == parent_id:
                    if "meeting notes" in page.get("title", "").lower():
                        meeting_notes_valid = True
                    break

        if not meeting_notes_valid:
            errors.append(f"Report page should be child of 'Meeting Notes', got parentId: {parent_id}")

        # Step 2: Get all blocks for the report page
        report_page_id = report_page.get("_id")
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": report_page_id}})
        sorted_blocks = sorted(all_blocks, key=lambda b: b.get("order", 0))

        # Step 3: Check heading_1 - Sprint Retrospective Report
        heading1_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading1", "heading_1"]:
                content = block.get("content", "")
                if "sprint retrospective report" in content.lower() and "🔄" in content:
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("heading_1 '🔄 Sprint Retrospective Report' not found")

        # Step 4: Check callout with red background (since 25% < 50%) and correct metrics
        callout_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type == "callout":
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", block.get("backgroundColor", "")).lower()
                icon = properties.get("icon", block.get("icon", ""))
                content = block.get("content", "")

                # Check for red background (since completion is 25% < 50%)
                has_red_bg = "red" in bg_color

                # Check for 📊 icon
                has_icon = "📊" in icon

                # Check content has Sprint Completion info
                content_lower = content.lower()
                has_completion_text = "sprint completion" in content_lower

                # Check for percentage and task counts (2 of 8 or 25%)
                has_metrics = ("25" in content or "2" in content) and ("8" in content or "task" in content_lower)

                if has_red_bg and has_icon and has_completion_text and has_metrics:
                    callout_found = True
                    break

        if not callout_found:
            errors.append("Callout with red_background, 📊 icon, and Sprint Completion metrics not found (expected red since 25% < 50%)")

        # Step 5: Check heading_2 - Team Performance Metrics
        team_metrics_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "team performance metrics" in content.lower() and "🏆" in content:
                    team_metrics_heading_found = True
                    break

        if not team_metrics_heading_found:
            errors.append("heading_2 '🏆 Team Performance Metrics' not found")

        # Step 6: Find Individual Contributions database
        individual_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "individual contributions" in title:
                    individual_db = page
                    break

        if not individual_db:
            errors.append("[X] Database 'Individual Contributions' not found")
        else:
            checks_passed.append("[C] Database 'Individual Contributions'")

        # Check database is inline (parent is the report page)
        db_parent_id = individual_db.get("parentId")
        if db_parent_id != report_page_id:
            errors.append(f"Individual Contributions database should be inline under report page, got parentId: {db_parent_id}")

        # Step 7: Validate database properties
        db_metadata = individual_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        expected_properties = [
            ("Team Member", "title"),
            ("Tasks Assigned", "number"),
            ("Tasks Completed", "number"),
            ("Completion Rate", "text"),
            ("Top Priority Tasks", "number"),
            ("Recognition", "select"),
        ]

        for expected_name, expected_type in expected_properties:
            found = False
            for prop in properties:
                prop_name = prop.get("name", "").lower()
                if expected_name.lower() in prop_name or prop_name in expected_name.lower():
                    found = True
                    if prop.get("type") != expected_type:
                        errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{prop.get('type')}'")
                    break
            if not found:
                errors.append(f"Property '{expected_name}' not found in Individual Contributions database")

        # Step 8: Validate Recognition select options
        recognition_prop = None
        for prop in properties:
            if "recognition" in prop.get("name", "").lower():
                recognition_prop = prop
                break

        if recognition_prop:
            options = recognition_prop.get("options", [])
            expected_options = {
                "star performer": "gold",
                "strong contributor": "silver",
                "needs support": "bronze",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Recognition missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Recognition option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 9: Find and validate database rows
        db_id = individual_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        # Build property ID map
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Try matching by property name in cell_values keys
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        # Expected per-user metrics
        expected_users = {
            "alice chen": {"assigned": 3, "completed": 1, "high_urgent": 2, "recognition": "needs support"},
            "bob martinez": {"assigned": 3, "completed": 1, "high_urgent": 2, "recognition": "needs support"},
            "carol williams": {"assigned": 2, "completed": 0, "high_urgent": 0, "recognition": "needs support"},
        }

        # Check we have at least 3 rows
        if len(rows) < 3:
            errors.append(f"Expected at least 3 rows in Individual Contributions database, got {len(rows)}")
        else:
            found_users = set()
            for row in rows:
                title = row.get("title", "").lower()

                # Match user
                matched_user = None
                for user_name in expected_users.keys():
                    if user_name in title or title in user_name:
                        matched_user = user_name
                        break

                if matched_user:
                    found_users.add(matched_user)
                    expected = expected_users[matched_user]

                    # Check Tasks Assigned
                    assigned = get_cell_value(row, "tasks assigned")
                    if assigned is not None:
                        try:
                            if int(assigned) != expected["assigned"]:
                                errors.append(f"{matched_user.title()}: Tasks Assigned should be {expected['assigned']}, got {assigned}")
                        except (ValueError, TypeError):
                            errors.append(f"{matched_user.title()}: Tasks Assigned has invalid value: {assigned}")

                    # Check Tasks Completed
                    completed = get_cell_value(row, "tasks completed")
                    if completed is not None:
                        try:
                            if int(completed) != expected["completed"]:
                                errors.append(f"{matched_user.title()}: Tasks Completed should be {expected['completed']}, got {completed}")
                        except (ValueError, TypeError):
                            errors.append(f"{matched_user.title()}: Tasks Completed has invalid value: {completed}")

                    # Check Top Priority Tasks (High+Urgent count)
                    high_urgent = get_cell_value(row, "top priority tasks")
                    if high_urgent is not None:
                        try:
                            if int(high_urgent) != expected["high_urgent"]:
                                errors.append(f"{matched_user.title()}: Top Priority Tasks should be {expected['high_urgent']}, got {high_urgent}")
                        except (ValueError, TypeError):
                            errors.append(f"{matched_user.title()}: Top Priority Tasks has invalid value: {high_urgent}")

                    # Check Recognition
                    recognition = get_cell_value(row, "recognition")
                    if recognition:
                        if expected["recognition"] not in str(recognition).lower():
                            errors.append(f"{matched_user.title()}: Recognition should be '{expected['recognition'].title()}', got '{recognition}'")

            # Check all expected users were found
            for user_name in expected_users.keys():
                if user_name not in found_users:
                    errors.append(f"Row for '{user_name.title()}' not found in Individual Contributions database")

        # Step 10: Check heading_2 - Next Sprint Planning
        next_sprint_heading_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type in ["heading2", "heading_2"]:
                content = block.get("content", "")
                if "next sprint planning" in content.lower() and "🎯" in content:
                    next_sprint_heading_found = True
                    break

        if not next_sprint_heading_found:
            errors.append("heading_2 '🎯 Next Sprint Planning' not found")

        # Step 11: Check paragraph with velocity and capacity recommendation
        velocity_paragraph_found = False
        for block in sorted_blocks:
            block_type = block.get("type", "")
            if block_type == "paragraph":
                content = block.get("content", "").lower()
                # Check for velocity mention (should be 2)
                has_velocity = "velocity" in content and "2" in content
                # Check for capacity recommendation (should be ~2.2)
                has_capacity = "capacity" in content or "recommended" in content

                if has_velocity and has_capacity:
                    velocity_paragraph_found = True
                    break

        if not velocity_paragraph_found:
            errors.append("Paragraph with velocity (2) and recommended sprint capacity not found")

        # Step 12: Verify Task Tracker database still exists
        task_tracker = None
        for page in all_pages:
            if page.get("_id") == "db-tasks" or "task tracker" in page.get("title", "").lower():
                if page.get("type") == "database":
                    task_tracker = page
                    break

        if not task_tracker:
            errors.append("Task Tracker database not found - source data may have been corrupted")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Purchase Order System Task
# =============================================================================
def _validate_agent_purchase_order_system(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Analyze the 'Inventory Tracker' database to identify all items where In Stock=false
    or Quantity < 5. Create a 'Purchase Order System' database as a child of 'Inventory Tracker'
    with properties: 'PO Number' (title), 'Item' (text), 'SKU' (text), 'Quantity to Order' (number),
    'Unit Cost' (number), 'Total Cost' (number), 'Vendor' (select with 'TechSupply Co' blue,
    'OfficeMax' green, 'FurnitureHub' brown, 'DirectHardware' gray), 'Order Status' (select with
    'Draft' gray, 'Submitted' blue, 'Approved' green, 'Received' purple), 'Order Date' (date),
    'Expected Delivery' (date). For each low-stock item, create a PO: PO Number='PO-2026-[3-digit
    sequence]', Item=[item name], SKU=[item SKU], Quantity to Order=(10 - current quantity),
    Unit Cost=[item price], Total Cost=[quantity * price], Vendor based on category
    (Electronics='TechSupply Co', Furniture='FurnitureHub', Hardware='DirectHardware',
    others='OfficeMax'), Order Status='Draft', Order Date=today, Expected Delivery=7 days from today.

    Expected from initial data:
    Low-stock items (In Stock=false OR Quantity < 5):
    - Standing Desk (FURN-002): Qty=3, Category=Furniture, Price=699
      -> Qty to Order=7, Total Cost=4893, Vendor=FurnitureHub
    - Wireless Keyboard (HARD-001): Qty=0, In Stock=false, Category=Hardware, Price=79
      -> Qty to Order=10, Total Cost=790, Vendor=DirectHardware
    """
    checks_passed: list[str] = []

    errors = []

    # Step 1: Find the Inventory Tracker database
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        inventory_db = None
        for page in all_pages:
            if page.get("_id") == "db-inventory" or (
                page.get("type") == "database" and "inventory tracker" in page.get("title", "").lower()
            ):
                inventory_db = page
                break

        if not inventory_db:
            errors.append("[X] Database 'Inventory Tracker' not found")
        else:
            checks_passed.append("[C] Database 'Inventory Tracker'")

        inventory_db_id = inventory_db.get("_id")

        # Step 2: Find the Purchase Order System database as child of Inventory Tracker
        po_system_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "purchase order system" in title:
                    parent_id = page.get("parentId")
                    if parent_id == inventory_db_id:
                        po_system_db = page
                        break
                    # Also check if parent is Inventory Tracker by title
                    for p in all_pages:
                        if p.get("_id") == parent_id and "inventory tracker" in p.get("title", "").lower():
                            po_system_db = page
                            break
                    if po_system_db:
                        break

        if not po_system_db:
            errors.append("[X] 'Purchase Order System' database not found as child of Inventory Tracker")
        else:
            checks_passed.append("[C] 'Purchase Order System' database found as child of Inventory Tracker")

        # Step 3: Validate database properties
        db_metadata = po_system_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Build property lookup
        prop_by_name = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_by_name[prop_name_lower] = prop

        # Required properties with their types
        required_props = {
            "po number": "title",
            "item": "text",
            "sku": "text",
            "quantity to order": "number",
            "unit cost": "number",
            "total cost": "number",
            "vendor": "select",
            "order status": "select",
            "order date": "date",
            "expected delivery": "date",
        }

        # Check each required property exists with correct type
        for prop_name, expected_type in required_props.items():
            found = False
            for db_prop_name, prop in prop_by_name.items():
                # Flexible matching
                if prop_name.replace(" ", "") in db_prop_name.replace(" ", ""):
                    found = True
                    if prop.get("type") != expected_type:
                        errors.append(f"Property '{prop_name}' should be type '{expected_type}', got '{prop.get('type')}'")
                    break
            if not found:
                errors.append(f"Required property '{prop_name}' not found")

        # Step 4: Validate Vendor select options
        vendor_prop = None
        for prop_name, prop in prop_by_name.items():
            if "vendor" in prop_name:
                vendor_prop = prop
                break

        if vendor_prop:
            options = vendor_prop.get("options", [])
            expected_vendor_options = {
                "techsupply co": "blue",
                "officemax": "green",
                "furniturehub": "brown",
                "directhardware": "gray",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_vendor_options.items():
                if exp_name not in found_options:
                    errors.append(f"Vendor option '{exp_name}' not found")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Vendor option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 5: Validate Order Status select options
        status_prop = None
        for prop_name, prop in prop_by_name.items():
            if "order status" in prop_name or "status" in prop_name:
                status_prop = prop
                break

        if status_prop:
            options = status_prop.get("options", [])
            expected_status_options = {
                "draft": "gray",
                "submitted": "blue",
                "approved": "green",
                "received": "purple",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_status_options.items():
                if exp_name not in found_options:
                    errors.append(f"Order Status option '{exp_name}' not found")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Order Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 6: Validate PO rows
        po_db_id = po_system_db.get("_id")
        po_rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == po_db_id:
                po_rows.append(page)

        # Should have exactly 2 PO rows
        if len(po_rows) != 2:
            errors.append(f"Expected 2 PO rows (Standing Desk and Wireless Keyboard), got {len(po_rows)}")

        # Build property ID map for PO database
        po_prop_id_map = {}
        for prop in properties:
            po_prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_po_cell_value(cell_values, prop_name_lower):
            prop_id = po_prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                    return val
            return None

        # Expected PO data
        expected_pos = {
            "standing desk": {
                "sku": "FURN-002",
                "quantity_to_order": 7,  # 10 - 3
                "unit_cost": 699,
                "total_cost": 4893,  # 7 * 699
                "vendor": "furniturehub",
            },
            "wireless keyboard": {
                "sku": "HARD-001",
                "quantity_to_order": 10,  # 10 - 0
                "unit_cost": 79,
                "total_cost": 790,  # 10 * 79
                "vendor": "directhardware",
            },
        }

        found_pos = {}
        for row in po_rows:
            title = row.get("title", "").lower()
            cell_values = row.get("cellValues", {})

            # Try to match by item name
            item_value = get_po_cell_value(cell_values, "item")
            item_lower = str(item_value).lower() if item_value else ""

            for expected_item in expected_pos.keys():
                if expected_item in item_lower or expected_item in title:
                    found_pos[expected_item] = row
                    break

        # Step 7: Validate each expected PO row
        for item_name, expected_values in expected_pos.items():
            if item_name not in found_pos:
                errors.append(f"Purchase order for '{item_name}' not found")
                continue

            row = found_pos[item_name]
            cell_values = row.get("cellValues", {})
            po_title = row.get("title", "")

            # Check PO Number format (PO-2026-XXX)
            has_valid_po_number = False
            if re.match(r"PO-2026-\d{3}", po_title, re.IGNORECASE):
                has_valid_po_number = True
            else:
                # Check in cell values if title property is separate
                po_num = get_po_cell_value(cell_values, "po number")
                if po_num and re.match(r"PO-2026-\d{3}", str(po_num), re.IGNORECASE):
                    has_valid_po_number = True

            if not has_valid_po_number:
                errors.append(f"{item_name}: PO Number should match 'PO-2026-XXX' format")

            # Check SKU
            sku = get_po_cell_value(cell_values, "sku")
            if not sku or expected_values["sku"].lower() not in str(sku).lower():
                errors.append(f"{item_name}: SKU should be '{expected_values['sku']}', got '{sku}'")

            # Check Quantity to Order
            qty = get_po_cell_value(cell_values, "quantity to order")
            if qty is None or int(qty) != expected_values["quantity_to_order"]:
                errors.append(f"{item_name}: Quantity to Order should be {expected_values['quantity_to_order']}, got {qty}")

            # Check Unit Cost
            unit_cost = get_po_cell_value(cell_values, "unit cost")
            if unit_cost is None or int(unit_cost) != expected_values["unit_cost"]:
                errors.append(f"{item_name}: Unit Cost should be {expected_values['unit_cost']}, got {unit_cost}")

            # Check Total Cost
            total_cost = get_po_cell_value(cell_values, "total cost")
            if total_cost is None or int(total_cost) != expected_values["total_cost"]:
                errors.append(f"{item_name}: Total Cost should be {expected_values['total_cost']}, got {total_cost}")

            # Check Vendor
            vendor = get_po_cell_value(cell_values, "vendor")
            if not vendor or expected_values["vendor"] not in str(vendor).lower():
                errors.append(f"{item_name}: Vendor should be '{expected_values['vendor']}', got '{vendor}'")

            # Check Order Status = Draft
            status = get_po_cell_value(cell_values, "order status")
            if not status or "draft" not in str(status).lower():
                errors.append(f"{item_name}: Order Status should be 'Draft', got '{status}'")

            # Check Order Date (should be today: 2026-01-15)
            order_date = get_po_cell_value(cell_values, "order date")
            if order_date:
                date_str = order_date.get("start", "") if isinstance(order_date, dict) else str(order_date)
                if "2026-01-15" not in date_str:
                    errors.append(f"{item_name}: Order Date should be '2026-01-15', got '{date_str}'")
            else:
                errors.append(f"{item_name}: Order Date is missing")

            # Check Expected Delivery (should be 7 days from today: 2026-01-22)
            delivery_date = get_po_cell_value(cell_values, "expected delivery")
            if delivery_date:
                date_str = delivery_date.get("start", "") if isinstance(delivery_date, dict) else str(delivery_date)
                if "2026-01-22" not in date_str:
                    errors.append(f"{item_name}: Expected Delivery should be '2026-01-22', got '{date_str}'")
            else:
                errors.append(f"{item_name}: Expected Delivery is missing")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Performance Dashboard Task
# =============================================================================
def _validate_agent_sprint_performance_dashboard(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Query the 'Task Tracker' database and create a comprehensive 'Sprint Performance Dashboard'
    page as a child of 'Engineering Wiki'. First, count all tasks by their 'Status' values. Then create
    the page with heading_1 '🚀 Sprint Performance Dashboard', followed by a callout with blue_background
    and text 'Last updated: [TODAY'S DATE]'. Add a heading_2 '📊 Task Status Overview' with a paragraph
    showing 'Total tasks: [TOTAL_COUNT]'. Create a bulleted list showing each status with its count:
    '• Not Started: [X] ([Y]%)', '• In Progress: [X] ([Y]%)', '• In Review: [X] ([Y]%)',
    '• Done: [X] ([Y]%)', '• Blocked: [X] ([Y]%)'. Then add a heading_2 '⚠️ Blocked Tasks' and list all
    task names where Status='Blocked' with their assignee in format '• [TASK_NAME] (Assigned to: [ASSIGNEE])'.
    Finally, add a heading_2 '✅ Completion Rate' with a paragraph showing 'Sprint completion: [X]%
    ([Y] of [Z] tasks marked as Done)'.

    Expected values from initial data (Task Tracker db-tasks):
    - Total tasks: 8
    - Not Started: 2 (25%)
    - In Progress: 2 (25%)
    - In Review: 1 (12.5%)
    - Done: 2 (25%)
    - Blocked: 1 (12.5%)
    - Blocked task: "Update database schema" assigned to Bob Martinez (user-2)
    - Completion rate: 25% (2 of 8 tasks)
    """
    checks_passed: list[str] = []

    errors = []

    # Step 1: Find the Sprint Performance Dashboard page under Engineering Wiki (page-4)
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        dashboard_page = None
        for page in all_pages:
            parent_id = page.get("parentId", "")
            title = page.get("title", "").lower()
            if parent_id == "page-4" and "sprint performance dashboard" in title:
                dashboard_page = page
                break

        if not dashboard_page:
            # Also check if parent is Engineering Wiki by title
            for page in all_pages:
                title = page.get("title", "").lower()
                if "sprint performance dashboard" in title:
                    parent_id = page.get("parentId", "")
                    for p in all_pages:
                        if p.get("_id") == parent_id and "engineering wiki" in p.get("title", "").lower():
                            dashboard_page = page
                            break
                    if dashboard_page:
                        break

        if not dashboard_page:
            errors.append("[X] Sprint Performance Dashboard page not found as a child of Engineering Wiki")
        else:
            checks_passed.append("[C] Sprint Performance Dashboard page found as a child of Engineering Wiki")

        dashboard_page_id = dashboard_page.get("_id")

        # Step 2: Get all blocks in the dashboard page
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})
        blocks = [b for b in all_blocks if b.get("pageId") == dashboard_page_id]

        if not blocks:
            errors.append("[X] No blocks found in the Sprint Performance Dashboard page")
        else:
            checks_passed.append("[C] blocks found in the Sprint Performance Dashboard page")

        # Step 3: Verify Heading 1 with dashboard title
        heading1_found = False
        for block in blocks:
            if block.get("type") == "heading1":
                content = block.get("content", "").lower()
                if "sprint performance dashboard" in content:
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("Missing heading1 with 'Sprint Performance Dashboard' title")

        # Step 4: Verify blue callout with date reference
        callout_found = False
        for block in blocks:
            if block.get("type") == "callout":
                props = block.get("properties", {})
                bg_color = props.get("backgroundColor", "").lower()
                content = block.get("content", "").lower()
                # Check for blue background and date-related text
                if "blue" in bg_color:
                    if "last updated" in content or "updated" in content or re.search(r'\d{4}[-/]\d{2}[-/]\d{2}|\d{1,2}[-/]\d{1,2}[-/]\d{2,4}', content):
                        callout_found = True
                        break

        if not callout_found:
            errors.append("Missing callout with blue_background and date reference")

        # Step 5: Verify heading2 for Task Status Overview
        status_overview_heading_found = False
        for block in blocks:
            if block.get("type") == "heading2":
                content = block.get("content", "").lower()
                if "status" in content and "overview" in content:
                    status_overview_heading_found = True
                    break
                if "task status" in content:
                    status_overview_heading_found = True
                    break

        if not status_overview_heading_found:
            errors.append("Missing heading2 for 'Task Status Overview'")

        # Step 6: Verify total tasks paragraph
        total_tasks_found = False
        for block in blocks:
            if block.get("type") == "paragraph":
                content = block.get("content", "").lower()
                if "total" in content and "task" in content and "8" in content:
                    total_tasks_found = True
                    break

        if not total_tasks_found:
            errors.append("Missing paragraph with 'Total tasks: 8'")

        # Step 7: Verify bulleted list with status counts
        # Expected: Not Started=2, In Progress=2, In Review=1, Done=2, Blocked=1
        status_checks = {
            "not started": False,
            "in progress": False,
            "in review": False,
            "done": False,
            "blocked": False
        }

        for block in blocks:
            if block.get("type") == "bulletedList":
                content = block.get("content", "").lower()

                # Check Not Started: 2 (25%)
                if "not started" in content and ("2" in content or "two" in content):
                    status_checks["not started"] = True

                # Check In Progress: 2 (25%)
                if "in progress" in content and ("2" in content or "two" in content):
                    status_checks["in progress"] = True

                # Check In Review: 1 (12.5% or 13%)
                if "in review" in content and ("1" in content or "one" in content):
                    status_checks["in review"] = True

                # Check Done: 2 (25%)
                if "done" in content and ("2" in content or "two" in content):
                    status_checks["done"] = True

                # Check Blocked: 1 (12.5% or 13%)
                if "blocked" in content and ("1" in content or "one" in content):
                    status_checks["blocked"] = True

        missing_statuses = [s for s, found in status_checks.items() if not found]
        if missing_statuses:
            errors.append(f"Missing status counts in bulleted list: {', '.join(missing_statuses)}")

        # Step 8: Verify heading2 for Blocked Tasks
        blocked_heading_found = False
        for block in blocks:
            if block.get("type") == "heading2":
                content = block.get("content", "").lower()
                if "blocked" in content and "task" in content:
                    blocked_heading_found = True
                    break

        if not blocked_heading_found:
            errors.append("Missing heading2 for 'Blocked Tasks'")

        # Step 9: Verify blocked task listing with assignee
        # Expected: "Update database schema" with "Bob Martinez"
        blocked_task_found = False
        for block in blocks:
            if block.get("type") == "bulletedList":
                content = block.get("content", "").lower()
                if "update database schema" in content:
                    if "bob" in content or "martinez" in content:
                        blocked_task_found = True
                        break

        if not blocked_task_found:
            errors.append("Missing blocked task 'Update database schema' with assignee 'Bob Martinez'")

        # Step 10: Verify heading2 for Completion Rate
        completion_heading_found = False
        for block in blocks:
            if block.get("type") == "heading2":
                content = block.get("content", "").lower()
                if "completion" in content and "rate" in content:
                    completion_heading_found = True
                    break

        if not completion_heading_found:
            errors.append("Missing heading2 for 'Completion Rate'")

        # Step 11: Verify completion rate paragraph
        # Expected: 25% (2 of 8 tasks)
        completion_para_found = False
        for block in blocks:
            if block.get("type") == "paragraph":
                content = block.get("content", "").lower()
                # Check for percentage (25%) and ratio (2 of 8)
                has_percentage = "25" in content
                has_ratio = ("2" in content and "8" in content) or "2 of 8" in content
                if has_percentage and has_ratio:
                    completion_para_found = True
                    break

        if not completion_para_found:
            errors.append("Missing completion rate paragraph with '25%' and '2 of 8 tasks'")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Q1 Task Analysis Task
# =============================================================================
def _validate_agent_q1_task_analysis(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a comprehensive 'Q1 Task Analysis' by merging data from 'Task Tracker' and
    'Project Portfolio' databases. First, create a new page 'Q1 Task Analysis Report' under
    'Project Roadmap'. Add heading_1 '📈 Q1 Task Analysis Report'. Query Task Tracker to group
    tasks by the Tags multi_select values. For each tag, create a heading_2 '[TAG_EMOJI] [TAG_NAME] Tasks'
    where TAG_EMOJI is: 🐛 for Bug, ✨ for Feature, 📝 for Documentation, 🎨 for Design, 🔧 for Backend,
    🖥️ for Frontend. Under each tag heading, add a numbered list of all tasks with that tag showing
    '1. [TASK_NAME] - Status: [STATUS] - Priority: [PRIORITY]'. After all tag sections, add heading_2
    '💰 Budget Impact Analysis' and create a table showing: for Backend and Frontend tagged tasks,
    calculate 15% of the total budget from all Active projects in Project Portfolio as estimated
    cost impact. Show as 'Development Tasks Budget Impact: $[AMOUNT] (15% of active projects budget)'.

    Expected data from initial state:
    - Task Tracker (db-tasks) has 8 tasks with various tags
    - Tags: Bug, Feature, Documentation, Design, Backend, Frontend
    - Bug tasks (2): Fix login authentication bug, Fix mobile responsive issues
    - Feature tasks (2): Implement user notifications, Create onboarding flow
    - Documentation tasks (1): Write API documentation
    - Design tasks (2): Design new dashboard layout, Create onboarding flow
    - Backend tasks (5): Fix login authentication bug, Write API documentation, Implement user notifications,
      Update database schema, Add unit tests for auth module
    - Frontend tasks (4): Design new dashboard layout, Implement user notifications, Create onboarding flow,
      Fix mobile responsive issues
    - Active projects budget: Website Redesign ($50,000) + Customer Portal ($75,000) +
      Sales Pipeline Automation ($35,000) = $160,000
    - 15% of $160,000 = $24,000
    """
    checks_passed: list[str] = []

    errors = []

    # Step 1: Find the Q1 Task Analysis Report page under Project Roadmap (page-2)
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        report_page = None
        for page in all_pages:
            # Accept pages that are either type "normal" or don't have a type, but exclude databases and rows
            page_type = page.get("type", "")
            if page_type not in ("database", "row"):
                title = page.get("title", "").lower()
                if "q1 task analysis" in title and "report" in title:
                    report_page = page
                    break

        if not report_page:
            errors.append("[X] Page 'Q1 Task Analysis Report' not found")
        else:
            checks_passed.append("[C] Page 'Q1 Task Analysis Report'")

        # Check parent is Project Roadmap (page-2)
        parent_id = report_page.get("parentId")
        if parent_id != "page-2":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "project roadmap" not in parent_page.get("title", "").lower():
                errors.append(f"Report page parent is not 'Project Roadmap' (page-2), got parentId: {parent_id}")

        # Get all blocks for the report page
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})
        page_id = report_page.get("_id")
        blocks = [b for b in all_blocks if b.get("pageId") == page_id]

        # Also check the page's content array if blocks collection is empty
        if not blocks:
            page_content = report_page.get("content", [])
            if page_content:
                blocks = page_content

        # Step 2: Check for heading_1 with "📈 Q1 Task Analysis Report"
        heading1_found = False
        for block in blocks:
            if block.get("type") == "heading1":
                content = block.get("content", "")
                if "📈" in content and "q1" in content.lower() and "task analysis" in content.lower():
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("Missing heading_1 '📈 Q1 Task Analysis Report'")

        # Step 3-14: Check for tag sections with correct emojis and task listings
        tag_emoji_map = {
            "bug": "🐛",
            "feature": "✨",
            "documentation": "📝",
            "design": "🎨",
            "backend": "🔧",
            "frontend": "🖥️",
        }

        # Expected tasks per tag (from initial data)
        expected_tasks_per_tag = {
            "bug": ["fix login authentication bug", "fix mobile responsive issues"],
            "feature": ["implement user notifications", "create onboarding flow"],
            "documentation": ["write api documentation"],
            "design": ["design new dashboard layout", "create onboarding flow"],
            "backend": [
                "fix login authentication bug",
                "write api documentation",
                "implement user notifications",
                "update database schema",
                "add unit tests for auth module",
            ],
            "frontend": [
                "design new dashboard layout",
                "implement user notifications",
                "create onboarding flow",
                "fix mobile responsive issues",
            ],
        }

        # Check each tag section
        for tag_name, emoji in tag_emoji_map.items():
            # Find heading_2 for this tag
            tag_heading_found = False
            for block in blocks:
                if block.get("type") == "heading2":
                    content = block.get("content", "")
                    if emoji in content and tag_name in content.lower():
                        tag_heading_found = True
                        break

            if not tag_heading_found:
                errors.append(f"Missing heading_2 '{emoji} {tag_name.title()} Tasks'")
                continue

            # Check for numbered list items with expected tasks
            expected_tasks = expected_tasks_per_tag[tag_name]
            found_tasks = []

            for block in blocks:
                if block.get("type") == "numberedList":
                    content = block.get("content", "").lower()
                    for task_name in expected_tasks:
                        if task_name in content:
                            # Check for Status and Priority format
                            if "status" in content and "priority" in content:
                                found_tasks.append(task_name)

            # Check if we found at least some of the expected tasks
            missing_tasks = [t for t in expected_tasks if t not in found_tasks]
            if len(missing_tasks) > len(expected_tasks) / 2:
                errors.append(
                    f"Tag '{tag_name}': Missing most expected tasks. Expected {len(expected_tasks)} tasks, found {len(found_tasks)}"
                )

        # Step 15: Check for Budget Impact Analysis heading
        budget_heading_found = False
        for block in blocks:
            if block.get("type") == "heading2":
                content = block.get("content", "")
                if "💰" in content and "budget" in content.lower() and "impact" in content.lower():
                    budget_heading_found = True
                    break

        if not budget_heading_found:
            errors.append("Missing heading_2 '💰 Budget Impact Analysis'")

        # Step 16: Check for budget calculation - $24,000 (15% of $160,000)
        budget_calculation_found = False
        for block in blocks:
            content = block.get("content", "").lower()
            # Check for $24,000 or $24000 or 24000 or 24,000
            has_amount = (
                "$24,000" in block.get("content", "")
                or "$24000" in block.get("content", "")
                or "24,000" in content
                or "24000" in content
            )
            has_percentage = "15%" in content or "15 percent" in content
            has_active_budget = "active" in content and "budget" in content

            if has_amount and (has_percentage or has_active_budget):
                budget_calculation_found = True
                break

        if not budget_calculation_found:
            errors.append(
                "Missing budget calculation showing '$24,000' or '$24000' (15% of active projects budget $160,000)"
            )

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Planning Q1 2026 Task
# =============================================================================
def _validate_agent_sprint_planning_q1_2026(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a new database titled 'Sprint Planning Q1 2026' as a child of the 'Project Roadmap'
    page with table layout. Configure it with these exact properties: 'Task' (title), 'Sprint'
    (select with options: 'Sprint 1' blue, 'Sprint 2' green, 'Sprint 3' yellow, 'Sprint 4' orange,
    'Backlog' gray), 'Story Points' (number), 'Assignee' (text), 'Status' (select with options:
    'Todo' gray, 'In Progress' blue, 'Testing' yellow, 'Done' green), 'Dependencies' (text),
    'Due Date' (date). Then query the existing 'Task Tracker' database and for each task with
    'Status' of 'In Progress' or 'In Review', create a corresponding row in the new sprint database
    with: Task=[task name], Sprint='Sprint 1', Story Points=5, Assignee=[copy from original],
    Status='In Progress' if original is 'In Progress' or 'Testing' if original is 'In Review',
    Due Date=[copy from original].

    Expected from initial data (Task Tracker db-tasks):
    Tasks with Status='In Progress' or 'In Review':
    1. "Fix login authentication bug" - Status: In Progress, Assignee: user-1, Due Date: 2026-01-14
    2. "Design new dashboard layout" - Status: In Review, Assignee: user-2, Due Date: 2026-01-17
    3. "Create onboarding flow" - Status: In Progress, Assignee: user-3, Due Date: 2026-01-19

    Total: 3 tasks should be migrated
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # ==========================================================================
        # Check 1: Find the Sprint Planning Q1 2026 database
        # ==========================================================================
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        sprint_planning_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "sprint planning q1 2026" in title:
                    sprint_planning_db = page
                    break

        if not sprint_planning_db:
            errors.append("[X] Database 'Sprint Planning Q1 2026' not found")
        else:
            checks_passed.append("[C] Database 'Sprint Planning Q1 2026'")

        # ==========================================================================
        # Check 2: Verify parent is Project Roadmap (page-2)
        # ==========================================================================
        parent_id = sprint_planning_db.get("parentId")
        if parent_id != "page-2":
            # Also check if parent has title "Project Roadmap"
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "project roadmap" not in parent_page.get("title", "").lower():
                errors.append(
                    f"Database parent is not 'Project Roadmap' (page-2), got parentId: {parent_id}"
                )

        # ==========================================================================
        # Check 3: Validate database layout is table
        # ==========================================================================
        db_metadata = sprint_planning_db.get("databaseMetadata", {})
        layout = db_metadata.get("layout", "")
        if layout != "table":
            errors.append(f"Database layout should be 'table', got '{layout}'")

        # ==========================================================================
        # Check 4-10: Validate database properties
        # ==========================================================================
        properties = db_metadata.get("properties", [])

        # Build property name to object map
        prop_name_to_obj = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_name_to_obj[prop_name_lower] = prop

        # Check 4: Task (title)
        if "task" not in prop_name_to_obj:
            errors.append("Property 'Task' not found in database")
        else:
            if prop_name_to_obj["task"].get("type") != "title":
                errors.append(
                    f"Property 'Task' should have type 'title', got '{prop_name_to_obj['task'].get('type')}'"
                )

        # Check 5: Sprint (select with specific options)
        if "sprint" not in prop_name_to_obj:
            errors.append("Property 'Sprint' not found in database")
        else:
            sprint_prop = prop_name_to_obj["sprint"]
            if sprint_prop.get("type") != "select":
                errors.append(
                    f"Property 'Sprint' should have type 'select', got '{sprint_prop.get('type')}'"
                )
            else:
                options = sprint_prop.get("options", [])
                expected_sprint_options = {
                    "sprint 1": "blue",
                    "sprint 2": "green",
                    "sprint 3": "yellow",
                    "sprint 4": "orange",
                    "backlog": "gray",
                }
                found_options = {}
                for opt in options:
                    opt_name = opt.get("name", "").lower()
                    opt_color = opt.get("color", "").lower()
                    found_options[opt_name] = opt_color

                for exp_name, exp_color in expected_sprint_options.items():
                    if exp_name not in found_options:
                        errors.append(f"Sprint property missing option '{exp_name}'")
                    elif found_options[exp_name] != exp_color:
                        errors.append(
                            f"Sprint option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'"
                        )

        # Check 6: Story Points (number)
        if "story points" not in prop_name_to_obj:
            errors.append("Property 'Story Points' not found in database")
        else:
            if prop_name_to_obj["story points"].get("type") != "number":
                errors.append(
                    f"Property 'Story Points' should have type 'number', got '{prop_name_to_obj['story points'].get('type')}'"
                )

        # Check 7: Assignee (text)
        if "assignee" not in prop_name_to_obj:
            errors.append("Property 'Assignee' not found in database")
        else:
            if prop_name_to_obj["assignee"].get("type") != "text":
                errors.append(
                    f"Property 'Assignee' should have type 'text', got '{prop_name_to_obj['assignee'].get('type')}'"
                )

        # Check 8: Status (select with specific options)
        if "status" not in prop_name_to_obj:
            errors.append("Property 'Status' not found in database")
        else:
            status_prop = prop_name_to_obj["status"]
            if status_prop.get("type") != "select":
                errors.append(
                    f"Property 'Status' should have type 'select', got '{status_prop.get('type')}'"
                )
            else:
                options = status_prop.get("options", [])
                expected_status_options = {
                    "todo": "gray",
                    "in progress": "blue",
                    "testing": "yellow",
                    "done": "green",
                }
                found_options = {}
                for opt in options:
                    opt_name = opt.get("name", "").lower()
                    opt_color = opt.get("color", "").lower()
                    found_options[opt_name] = opt_color

                for exp_name, exp_color in expected_status_options.items():
                    if exp_name not in found_options:
                        errors.append(f"Status property missing option '{exp_name}'")
                    elif found_options[exp_name] != exp_color:
                        errors.append(
                            f"Status option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'"
                        )

        # Check 9: Dependencies (text)
        if "dependencies" not in prop_name_to_obj:
            errors.append("Property 'Dependencies' not found in database")
        else:
            if prop_name_to_obj["dependencies"].get("type") != "text":
                errors.append(
                    f"Property 'Dependencies' should have type 'text', got '{prop_name_to_obj['dependencies'].get('type')}'"
                )

        # Check 10: Due Date (date)
        if "due date" not in prop_name_to_obj:
            errors.append("Property 'Due Date' not found in database")
        else:
            if prop_name_to_obj["due date"].get("type") != "date":
                errors.append(
                    f"Property 'Due Date' should have type 'date', got '{prop_name_to_obj['due date'].get('type')}'"
                )

        # ==========================================================================
        # Check 11-13: Validate rows migrated from Task Tracker
        # ==========================================================================
        db_id = sprint_planning_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and (
                page.get("databaseId") == db_id or page.get("parentId") == db_id
            ):
                rows.append(page)

        # Build property ID map for cell value lookup
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        # Helper to get cell value by property name
        def get_cell_value(cell_values, prop_name_lower):
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Also try matching by property name directly in cell_values
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                    return val
            return None

        # Expected migrated tasks
        expected_tasks = {
            "fix login authentication bug": {
                "sprint": "sprint 1",
                "story_points": 5,
                "assignee": "user-1",
                "status": "in progress",
                "due_date": "2026-01-14",
            },
            "design new dashboard layout": {
                "sprint": "sprint 1",
                "story_points": 5,
                "assignee": "user-2",
                "status": "testing",  # In Review maps to Testing
                "due_date": "2026-01-17",
            },
            "create onboarding flow": {
                "sprint": "sprint 1",
                "story_points": 5,
                "assignee": "user-3",
                "status": "in progress",
                "due_date": "2026-01-19",
            },
        }

        found_tasks = {}
        for row in rows:
            title = row.get("title", "").lower()
            for task_name in expected_tasks.keys():
                if task_name in title:
                    found_tasks[task_name] = row
                    break

        if len(found_tasks) < 3:
            errors.append(
                f"Expected 3 migrated task rows in Sprint Planning Q1 2026, found {len(found_tasks)}"
            )

        for task_name, expected_values in expected_tasks.items():
            if task_name not in found_tasks:
                errors.append(f"Row for '{task_name}' not found in Sprint Planning Q1 2026")
                continue

            row = found_tasks[task_name]
            cell_values = row.get("cellValues", {})

            # Check Sprint = Sprint 1
            sprint_val = get_cell_value(cell_values, "sprint")
            if sprint_val:
                if "sprint 1" not in str(sprint_val).lower():
                    errors.append(
                        f"Task '{task_name}': Sprint should be 'Sprint 1', got '{sprint_val}'"
                    )
            else:
                errors.append(f"Task '{task_name}': Missing Sprint value")

            # Check Story Points = 5
            story_points = get_cell_value(cell_values, "story points")
            if story_points is not None:
                try:
                    if int(story_points) != 5:
                        errors.append(
                            f"Task '{task_name}': Story Points should be 5, got {story_points}"
                        )
                except (ValueError, TypeError):
                    errors.append(
                        f"Task '{task_name}': Story Points should be 5, got {story_points}"
                    )
            else:
                errors.append(f"Task '{task_name}': Missing Story Points value")

            # Check Assignee matches original
            assignee = get_cell_value(cell_values, "assignee")
            if assignee:
                if expected_values["assignee"].lower() not in str(assignee).lower():
                    errors.append(
                        f"Task '{task_name}': Assignee should contain '{expected_values['assignee']}', got '{assignee}'"
                    )
            else:
                errors.append(f"Task '{task_name}': Missing Assignee value")

            # Check Status (In Progress -> In Progress, In Review -> Testing)
            status = get_cell_value(cell_values, "status")
            if status:
                if expected_values["status"] not in str(status).lower():
                    errors.append(
                        f"Task '{task_name}': Status should be '{expected_values['status']}', got '{status}'"
                    )
            else:
                errors.append(f"Task '{task_name}': Missing Status value")

            # Check Due Date matches original
            due_date = get_cell_value(cell_values, "due date")
            if due_date:
                due_date_str = (
                    due_date.get("start", "") if isinstance(due_date, dict) else str(due_date)
                )
                if expected_values["due_date"] not in str(due_date_str):
                    errors.append(
                        f"Task '{task_name}': Due Date should contain '{expected_values['due_date']}', got '{due_date_str}'"
                    )
            else:
                errors.append(f"Task '{task_name}': Missing Due Date value")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Cross-Database Contacts Sync Task
# =============================================================================
def _validate_agent_cross_database_contacts_sync(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Perform a cross-database synchronization between 'Project Portfolio' and 'Contacts Directory'.
    For each project in Project Portfolio where Status='Active' or 'Planning', check if the Project Lead
    exists in Contacts Directory. If not, create new contact rows with Name='[User ID] - Project Lead',
    Email='[userid]@company.com', Type='Partner', Tags=['Active', 'New']. Then create a new page
    '🔄 Database Sync Report' under 'Project Documentation' with: heading_1 '🔄 Database Sync Report',
    heading_2 '✅ Sync Summary' with paragraph 'Synchronized [X] projects with contacts database.
    Created [Y] new contact entries.' For each new contact created, add a heading_3 '➕ New Contact Added'
    with bulleted list showing project name and lead details. End with a code block (language: json)
    containing a summary object: {"syncDate": "[current date]", "projectsChecked": X, "contactsCreated": Y,
    "errors": 0}.

    Expected values from initial data:
    - Projects with Active/Planning status: 4
      - Website Redesign (Active) -> user-1
      - Mobile App Development (Planning) -> user-5
      - Customer Portal (Active) -> user-3
      - Sales Pipeline Automation (Active) -> user-2
    - No existing contacts match "[user-id] - Project Lead" pattern
    - Expected new contacts: 4 (user-1, user-2, user-3, user-5)
    - Parent page for report: page-22 (Project Documentation)
    """
    checks_passed: list[str] = []
    try:

        import json

        errors = []

        # Expected project leads that need contacts created
        expected_leads = {"user-1", "user-2", "user-3", "user-5"}

        # ==================== CHECK 1: New contacts created ====================
        # Get all rows in Contacts Directory (db-contacts)
        contact_rows = final_state_backend.query(
            {"collection": "pages", "filter": {"databaseId": "db-contacts", "type": "row"}}
        )

        leads_with_contacts = set()
        contact_validation_details = []

        if contact_rows:
            for contact in contact_rows:
                cell_values = contact.get("cellValues", {})
                title = contact.get("title", "")

                # Check if this is a newly created project lead contact
                # Name should contain "[user-id] - Project Lead" pattern
                name_match = re.search(
                    r"(user-\d+)\s*-\s*Project Lead", title, re.IGNORECASE
                )
                if not name_match:
                    # Also check in the name property
                    name_prop = cell_values.get("db-contacts-prop-name", "")
                    name_match = re.search(
                        r"(user-\d+)\s*-\s*Project Lead", str(name_prop), re.IGNORECASE
                    )

                if name_match:
                    user_id = name_match.group(1).lower()
                    validation_errors = []

                    # Verify email format: [userid]@company.com
                    email = cell_values.get("db-contacts-prop-email", "")
                    # Accept variations like user1@company.com or user-1@company.com
                    user_num = user_id.replace("user-", "")
                    expected_email_patterns = [
                        f"user{user_num}@company.com",
                        f"user-{user_num}@company.com",
                        f"{user_id}@company.com",
                    ]
                    email_valid = any(
                        pattern.lower() == str(email).lower()
                        for pattern in expected_email_patterns
                    )
                    if not email_valid:
                        validation_errors.append(
                            f"Email should be '[userid]@company.com', got '{email}'"
                        )

                    # Verify Type = "Partner"
                    contact_type = cell_values.get("db-contacts-prop-type", "")
                    type_valid = str(contact_type).lower() == "partner"
                    if not type_valid:
                        validation_errors.append(
                            f"Type should be 'Partner', got '{contact_type}'"
                        )

                    # Verify Tags contain both "Active" and "New"
                    tags = cell_values.get("db-contacts-prop-tags", [])
                    if isinstance(tags, list):
                        tags_lower = [str(t).lower() for t in tags]
                        has_active = "active" in tags_lower
                        has_new = "new" in tags_lower
                        if not has_active:
                            validation_errors.append("Tags missing 'Active'")
                        if not has_new:
                            validation_errors.append("Tags missing 'New'")
                        tags_valid = has_active and has_new
                    else:
                        validation_errors.append(f"Tags should be a list, got '{tags}'")
                        tags_valid = False

                    # If all validations pass, count this lead
                    if email_valid and type_valid and tags_valid:
                        leads_with_contacts.add(user_id)
                    else:
                        contact_validation_details.append(
                            f"{user_id}: {'; '.join(validation_errors)}"
                        )

        # Check if all expected leads have contacts
        missing_leads = expected_leads - leads_with_contacts
        if missing_leads:
            errors.append(f"Missing contact entries for leads: {sorted(missing_leads)}")
        if contact_validation_details:
            errors.append(
                f"Contact validation issues: {'; '.join(contact_validation_details)}"
            )

        # ==================== CHECK 2-8: Sync Report Page ====================
        # Find the sync report page
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        report_page = None
        for page in all_pages:
            title = page.get("title", "")
            if "Database Sync Report" in title:
                # Verify parent is page-22 (Project Documentation)
                parent_id = page.get("parentId", "")
                if parent_id == "page-22":
                    report_page = page
                    break
                else:
                    # Check if parent has "Project Documentation" title
                    for p in all_pages:
                        if p.get("_id") == parent_id:
                            if "project documentation" in p.get("title", "").lower():
                                report_page = page
                                break

        if not report_page:
            errors.append(
                "Sync report page '🔄 Database Sync Report' not found under 'Project Documentation'"
            )
        else:
            # Check content blocks
            blocks = report_page.get("content", [])

            has_heading1 = False
            has_heading2 = False
            has_summary_paragraph = False
            has_heading3 = False
            has_bulleted_list = False
            has_json_code_block = False

            for block in blocks:
                block_type = block.get("type", "")
                content = block.get("content", "")
                props = block.get("properties", {})

                # Check heading_1 with report title
                if block_type == "heading1":
                    if "Database Sync Report" in content:
                        has_heading1 = True

                # Check heading_2 for sync summary
                if block_type == "heading2":
                    if "Sync Summary" in content:
                        has_heading2 = True

                # Check summary paragraph
                if block_type == "paragraph":
                    content_lower = content.lower()
                    if "synchronized" in content_lower and "project" in content_lower:
                        if "created" in content_lower and "contact" in content_lower:
                            has_summary_paragraph = True

                # Check heading_3 for new contacts
                if block_type == "heading3":
                    if "New Contact Added" in content:
                        has_heading3 = True

                # Check bulleted lists
                if block_type == "bulletedList":
                    has_bulleted_list = True

                # Check JSON code block
                if block_type == "code":
                    language = props.get("language", "").lower()
                    if language == "json":
                        # Try to parse the content as JSON
                        try:
                            json_content = json.loads(content)
                            # Check for required keys (case-insensitive)
                            content_keys_lower = {k.lower(): k for k in json_content.keys()}
                            required_keys = [
                                "syncdate",
                                "projectschecked",
                                "contactscreated",
                                "errors",
                            ]
                            has_all_keys = all(
                                key in content_keys_lower for key in required_keys
                            )
                            if has_all_keys:
                                has_json_code_block = True
                        except (json.JSONDecodeError, TypeError):
                            pass

            if not has_heading1:
                errors.append("Report missing heading_1 with 'Database Sync Report'")
            if not has_heading2:
                errors.append("Report missing heading_2 with 'Sync Summary'")
            if not has_summary_paragraph:
                errors.append(
                    "Report missing summary paragraph with synchronized/created info"
                )
            if not has_heading3:
                errors.append("Report missing heading_3 with 'New Contact Added'")
            if not has_bulleted_list:
                errors.append("Report missing bulleted lists for contact details")
            if not has_json_code_block:
                errors.append(
                    "Report missing JSON code block with required summary object keys"
                )

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Quarterly Expenses Inventory Sync Task
# =============================================================================
def _validate_agent_quarterly_expenses_inventory_sync(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a 'Quarterly Expenses' database under 'Company Handbook' with properties: 'Expense Item' (title), 'Quarter' (select: 'Q1 2026' blue, 'Q2 2026' green, 'Q3 2026' orange, 'Q4 2026' red), 'Department' (select with same options as Project Portfolio), 'Amount' (number), 'Category' (select: 'Infrastructure' gray, 'Personnel' blue, 'Marketing' purple, 'Operations' green), 'Approved By' (text), 'Invoice Number' (text), 'Paid' (checkbox). Query 'Inventory Tracker' for items with Quantity < 5 and create expense entries: Expense Item='Reorder: [Item Name]', Quarter='Q1 2026', Department='Operations', Amount=[Unit Price * 10], Category='Infrastructure', Invoice Number='INV-2026-[order based on SKU]', Paid=false. Then find the 'Team Directory' page and add immediately after the 'Design Team' heading a new heading_2 '💰 Q1 Budget Alerts' with a callout (red background) 'Low inventory items requiring reorder: [count] items totaling $[sum of amounts]'.

    Expected values from initial data:
    - Low inventory items (Quantity < 5):
      - Standing Desk: SKU=FURN-002, Quantity=3, Unit Price=699, Amount=6990
      - Wireless Keyboard: SKU=HARD-001, Quantity=0, Unit Price=79, Amount=790
    - Invoice Number ordering by SKU: FURN-002 -> INV-2026-001, HARD-001 -> INV-2026-002
    - Total: 2 items, sum = $7780
    - Project Portfolio Department options: Engineering (blue), Marketing (purple), Sales (green), Operations (orange), HR (pink)
    """
    checks_passed: list[str] = []

    errors = []

    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})

        # ==========================================================================
        # CHECK 1: Quarterly Expenses Database Exists Under Company Handbook
        # ==========================================================================

        quarterly_expenses_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "quarterly expenses" in title:
                    quarterly_expenses_db = page
                    break

        if not quarterly_expenses_db:
            errors.append("[X] Database 'Quarterly Expenses' not found")
        else:
            checks_passed.append("[C] Database 'Quarterly Expenses'")

        # Verify parent is Company Handbook (page-16)
        parent_id = quarterly_expenses_db.get("parentId")
        if parent_id != "page-16":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "company handbook" not in parent_page.get("title", "").lower():
                errors.append(f"Quarterly Expenses database parent should be 'Company Handbook' (page-16), got parentId: {parent_id}")

        db_id = quarterly_expenses_db.get("_id")
        db_metadata = quarterly_expenses_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # ==========================================================================
        # CHECK 2: Database Properties Configuration
        # ==========================================================================

        expected_props = [
            ("Expense Item", "title"),
            ("Quarter", "select"),
            ("Department", "select"),
            ("Amount", "number"),
            ("Category", "select"),
            ("Approved By", "text"),
            ("Invoice Number", "text"),
            ("Paid", "checkbox"),
        ]

        # Build property lookup
        prop_by_name = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_by_name[prop_name_lower] = prop

        for exp_name, exp_type in expected_props:
            prop = prop_by_name.get(exp_name.lower())
            if not prop:
                errors.append(f"Property '{exp_name}' not found in Quarterly Expenses database")
            elif prop.get("type") != exp_type:
                errors.append(f"Property '{exp_name}' should have type '{exp_type}', got '{prop.get('type')}'")

        # ==========================================================================
        # CHECK 3: Quarter Select Options
        # ==========================================================================

        quarter_prop = prop_by_name.get("quarter")
        if quarter_prop:
            options = quarter_prop.get("options", [])
            expected_quarter_options = {
                "q1 2026": "blue",
                "q2 2026": "green",
                "q3 2026": "orange",
                "q4 2026": "red",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_quarter_options.items():
                if exp_name not in found_options:
                    errors.append(f"Quarter missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Quarter option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # ==========================================================================
        # CHECK 4: Department Select Options (same as Project Portfolio)
        # ==========================================================================

        department_prop = prop_by_name.get("department")
        if department_prop:
            options = department_prop.get("options", [])
            expected_dept_options = {
                "engineering": "blue",
                "marketing": "purple",
                "sales": "green",
                "operations": "orange",
                "hr": "pink",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_dept_options.items():
                if exp_name not in found_options:
                    errors.append(f"Department missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Department option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # ==========================================================================
        # CHECK 5: Category Select Options
        # ==========================================================================

        category_prop = prop_by_name.get("category")
        if category_prop:
            options = category_prop.get("options", [])
            expected_category_options = {
                "infrastructure": "gray",
                "personnel": "blue",
                "marketing": "purple",
                "operations": "green",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_category_options.items():
                if exp_name not in found_options:
                    errors.append(f"Category missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Category option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # ==========================================================================
        # CHECK 6: Expense Entries from Low Inventory Items
        # ==========================================================================

        # Get all rows in the Quarterly Expenses database
        expense_rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                expense_rows.append(page)

        # Build property ID map for cell value lookups
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values[prop_id]
            # Fallback: search by key substring
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                    return val
            return None

        # Expected low inventory items (Quantity < 5)
        # SKU ordering: FURN-002 comes before HARD-001 alphabetically
        expected_items = {
            "standing desk": {
                "amount": 6990,  # 699 * 10
                "invoice_number": "INV-2026-001",  # FURN comes before HARD
                "quarter": "q1 2026",
                "department": "operations",
                "category": "infrastructure",
                "paid": False,
            },
            "wireless keyboard": {
                "amount": 790,  # 79 * 10
                "invoice_number": "INV-2026-002",
                "quarter": "q1 2026",
                "department": "operations",
                "category": "infrastructure",
                "paid": False,
            },
        }

        found_items = {}
        for row in expense_rows:
            title = row.get("title", "").lower()
            for item_name in expected_items.keys():
                # Check if title matches "Reorder: [Item Name]" pattern
                if f"reorder: {item_name}" in title or (
                    "reorder" in title and item_name in title
                ):
                    found_items[item_name] = row
                    break

        # Validate each expected expense entry
        for item_name, expected_values in expected_items.items():
            if item_name not in found_items:
                errors.append(f"Expense entry for 'Reorder: {item_name}' not found")
                continue

            row = found_items[item_name]

            # Check Amount
            amount = get_cell_value(row, "amount")
            if amount is not None:
                if int(float(amount)) != expected_values["amount"]:
                    errors.append(f"{item_name}: Amount should be {expected_values['amount']}, got {amount}")
            else:
                errors.append(f"{item_name}: Amount is missing")

            # Check Quarter
            quarter = get_cell_value(row, "quarter")
            if quarter:
                if expected_values["quarter"] not in str(quarter).lower():
                    errors.append(f"{item_name}: Quarter should be 'Q1 2026', got '{quarter}'")
            else:
                errors.append(f"{item_name}: Quarter is missing")

            # Check Department
            department = get_cell_value(row, "department")
            if department:
                if expected_values["department"] not in str(department).lower():
                    errors.append(f"{item_name}: Department should be 'Operations', got '{department}'")
            else:
                errors.append(f"{item_name}: Department is missing")

            # Check Category
            category = get_cell_value(row, "category")
            if category:
                if expected_values["category"] not in str(category).lower():
                    errors.append(f"{item_name}: Category should be 'Infrastructure', got '{category}'")
            else:
                errors.append(f"{item_name}: Category is missing")

            # Check Invoice Number
            invoice = get_cell_value(row, "invoice number")
            if invoice:
                if expected_values["invoice_number"].lower() not in str(invoice).lower():
                    errors.append(f"{item_name}: Invoice Number should be '{expected_values['invoice_number']}', got '{invoice}'")
            else:
                errors.append(f"{item_name}: Invoice Number is missing")

            # Check Paid = false
            paid = get_cell_value(row, "paid")
            if paid is not None:
                if paid not in (False, "false", 0, "0", ""):
                    errors.append(f"{item_name}: Paid should be false, got '{paid}'")
            # Missing paid is acceptable as default is false

        # ==========================================================================
        # CHECK 7: Team Directory Budget Alert Blocks
        # ==========================================================================

        # Find Team Directory page (page-8)
        team_directory_page_id = "page-8"

        # Get blocks for Team Directory page
        team_blocks = [b for b in all_blocks if b.get("pageId") == team_directory_page_id]

        # Sort blocks by order to check positioning
        team_blocks_sorted = sorted(team_blocks, key=lambda b: b.get("order", 0))

        # Find the Design Team heading (block-8-7, order=6)
        design_team_block = None
        design_team_order = None
        for block in team_blocks_sorted:
            if block.get("type") == "heading2":
                content = block.get("content", "").lower()
                if "design team" in content:
                    design_team_block = block
                    design_team_order = block.get("order", 0)
                    break

        if not design_team_block:
            errors.append("'Design Team' heading not found in Team Directory page")
        else:
            # Look for the Q1 Budget Alerts heading_2 after Design Team
            budget_alert_heading_found = False
            budget_alert_callout_found = False

            for block in team_blocks_sorted:
                block_order = block.get("order", 0)
                block_type = block.get("type", "")
                content = block.get("content", "")

                # Check for budget alert heading after Design Team
                if block_type == "heading2" and block_order > design_team_order:
                    if "q1 budget alert" in content.lower() or "💰" in content:
                        budget_alert_heading_found = True

                # Check for callout with red background and summary content
                if block_type == "callout" and block_order > design_team_order:
                    props = block.get("properties", {})
                    bg_color = props.get("backgroundColor", "").lower()

                    if "red" in bg_color:
                        content_lower = content.lower()
                        # Check for count "2" and sum "$7780" or "7780"
                        has_count = "2" in content
                        has_sum = "7780" in content or "$7780" in content or "7,780" in content
                        has_reorder_text = "reorder" in content_lower or "low inventory" in content_lower

                        if has_count and has_sum and has_reorder_text:
                            budget_alert_callout_found = True
                        elif "red" in bg_color:
                            # Red callout found but content doesn't match
                            if not has_count:
                                errors.append(f"Budget alert callout missing count '2' items")
                            if not has_sum:
                                errors.append(f"Budget alert callout missing sum '$7780'")
                            if not has_reorder_text:
                                errors.append(f"Budget alert callout missing 'reorder' or 'low inventory' text")

            if not budget_alert_heading_found:
                errors.append("Missing heading_2 '💰 Q1 Budget Alerts' after Design Team heading")

            if not budget_alert_callout_found:
                errors.append("Missing callout with red background and correct summary (2 items, $7780)")

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Project Portfolio Inventory Sync Task
# =============================================================================
def _validate_agent_project_portfolio_inventory_sync(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: In the 'Project Portfolio' database, find all projects with 'Status' of 'Active' or
    'Planning'. For each, calculate the number of days remaining until the end date. Then update
    the 'Inventory Tracker' database schema by adding a new property 'Project Assignment' (select)
    with options matching the active/planning project names found, each with a unique color (use
    blue, green, purple, orange in that order). After updating the schema, find the first 4 items
    in Inventory Tracker where 'In Stock' is true, and update each row to assign it to one of the
    projects (assign items sequentially to projects in alphabetical order by project name).

    Expected values from initial data:
    - Projects with Active/Planning status (alphabetically):
      1. Customer Portal (Active) - end: 2026-02-11
      2. Mobile App Development (Planning) - end: 2026-05-12
      3. Sales Pipeline Automation (Active) - end: 2026-02-26
      4. Website Redesign (Active) - end: 2026-03-13
    - Color assignments (in alphabetical order):
      1. Customer Portal - blue
      2. Mobile App Development - green
      3. Sales Pipeline Automation - purple
      4. Website Redesign - orange
    - First 4 in-stock items (by order):
      1. MacBook Pro 14" (row-1, order 0) -> Customer Portal
      2. Dell Monitor 27" (row-2, order 1) -> Mobile App Development
      3. Office Chair Ergonomic (row-3, order 2) -> Sales Pipeline Automation
      4. Standing Desk (row-4, order 3) -> Website Redesign
    """
    checks_passed: list[str] = []

    errors = []

    # Expected Active/Planning projects (alphabetically sorted) with colors
    try:
        expected_projects_with_colors = [
            ("Customer Portal", "blue"),
            ("Mobile App Development", "green"),
            ("Sales Pipeline Automation", "purple"),
            ("Website Redesign", "orange"),
        ]

        # Expected item-to-project assignments (first 4 in-stock items by order)
        expected_assignments = {
            "db-inventory-row-1": "Customer Portal",  # MacBook Pro 14"
            "db-inventory-row-2": "Mobile App Development",  # Dell Monitor 27"
            "db-inventory-row-3": "Sales Pipeline Automation",  # Office Chair Ergonomic
            "db-inventory-row-4": "Website Redesign",  # Standing Desk
        }

        # ==========================================================================
        # CHECK 1: Verify 'Project Assignment' property exists in Inventory Tracker
        # ==========================================================================
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        inventory_db = None
        for page in all_pages:
            if page.get("_id") == "db-inventory":
                inventory_db = page
                break

        if not inventory_db:
            errors.append("[X] Inventory Tracker database (db-inventory) not found")
        else:
            checks_passed.append("[C] Inventory Tracker database (db-inventory)")

        properties = inventory_db.get("databaseMetadata", {}).get("properties", [])

        project_assignment_prop = None
        for prop in properties:
            if prop.get("name", "").lower() == "project assignment":
                project_assignment_prop = prop
                break

        if not project_assignment_prop:
            errors.append("[X] 'Project Assignment' property not found in Inventory Tracker schema")
        else:
            checks_passed.append("[C] 'Project Assignment' property found in Inventory Tracker schema")

        # ==========================================================================
        # CHECK 2: Verify property type is 'select'
        # ==========================================================================
        if project_assignment_prop.get("type") != "select":
            errors.append(
                f"'Project Assignment' property type should be 'select', got '{project_assignment_prop.get('type')}'"
            )

        # ==========================================================================
        # CHECK 3: Verify options match project names (4 options)
        # ==========================================================================
        options = project_assignment_prop.get("options", [])
        if len(options) != 4:
            errors.append(f"'Project Assignment' should have exactly 4 options, got {len(options)}")

        # Create a map of option name to color
        option_color_map = {}
        for opt in options:
            opt_name = opt.get("name", "")
            opt_color = opt.get("color", "")
            option_color_map[opt_name] = opt_color

        # Verify all expected projects exist
        for project_name, expected_color in expected_projects_with_colors:
            if project_name not in option_color_map:
                errors.append(f"'Project Assignment' missing option '{project_name}'")

        # ==========================================================================
        # CHECK 4: Verify colors are assigned correctly
        # ==========================================================================
        for project_name, expected_color in expected_projects_with_colors:
            if project_name in option_color_map:
                actual_color = option_color_map[project_name]
                if actual_color != expected_color:
                    errors.append(
                        f"Option '{project_name}' should have color '{expected_color}', got '{actual_color}'"
                    )

        # ==========================================================================
        # CHECK 5: Verify first 4 in-stock items have correct project assignments
        # ==========================================================================
        # Get the property ID for 'Project Assignment'
        project_assignment_prop_id = project_assignment_prop.get("_id")

        for row_id, expected_project in expected_assignments.items():
            row = None
            for page in all_pages:
                if page.get("_id") == row_id:
                    row = page
                    break

            if not row:
                errors.append(f"Inventory row '{row_id}' not found")
                continue

            cell_values = row.get("cellValues", {})

            # Try to get the assigned project using property ID first, then by name
            assigned_project = None
            if project_assignment_prop_id and project_assignment_prop_id in cell_values:
                assigned_project = cell_values[project_assignment_prop_id]
            else:
                # Fallback: search for project assignment by key pattern
                for key, value in cell_values.items():
                    if "project" in key.lower() and "assignment" in key.lower():
                        assigned_project = value
                        break
                    # Also check for the property name directly
                    if key.lower() == "project assignment":
                        assigned_project = value
                        break

            if not assigned_project:
                errors.append(
                    f"Row '{row_id}' ({row.get('title', 'unknown')}) has no 'Project Assignment' value"
                )
            elif assigned_project != expected_project:
                errors.append(
                    f"Row '{row_id}' ({row.get('title', 'unknown')}) should be assigned to '{expected_project}', got '{assigned_project}'"
                )

        # Return result
        if errors:
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Department Budget Aggregation Task
# =============================================================================
def _validate_agent_department_budget_aggregation(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Query the 'Project Portfolio' database to analyze budget allocation by department.
    Create a new database titled 'Department Budget Analysis' as a child of the 'Project Portfolio'
    page with properties: 'Department' (title), 'Total Budget' (number), 'Project Count' (number),
    'Average Budget' (number), 'Status Breakdown' (text), 'Largest Project' (text). For each unique
    department in the Project Portfolio, create a row with calculated values: sum of all project
    budgets, count of projects, average budget per project, comma-separated list of statuses with
    counts (e.g., 'Active: 2, Planning: 1'), and the name of the highest budget project. After
    populating the database, add an inline database titled 'Critical Budget Items' on the same page
    with properties: 'Item' (title), 'Department' (select with same options as Project Portfolio),
    'Amount' (number), 'Priority' (select: 'High' red, 'Medium' yellow, 'Low' green). Add 3 rows
    for departments with total budget over $50,000.

    Expected values from initial data (Project Portfolio db-projects):
    - Engineering: Total=225000, Count=3, Avg=75000, Status="Active: 2, Planning: 1", Largest="Mobile App Development"
    - Marketing: Total=25000, Count=1, Avg=25000, Status="Completed: 1", Largest="Q1 Marketing Campaign"
    - Sales: Total=35000, Count=1, Avg=35000, Status="Active: 1", Largest="Sales Pipeline Automation"
    - HR: Total=15000, Count=1, Avg=15000, Status="On Hold: 1", Largest="Employee Training Program"

    Departments with budget > $50,000: Only Engineering ($225,000)
    Critical Budget Items: 3 rows for Engineering department
    """
    checks_passed: list[str] = []

    errors = []

    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # ==========================================================================
        # Check 1: Department Budget Analysis Database Exists with correct parent
        # ==========================================================================
        dept_budget_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "department budget analysis" in title:
                    dept_budget_db = page
                    break

        if not dept_budget_db:
            errors.append("[X] Database 'Department Budget Analysis' not found")
        else:
            checks_passed.append("[C] Database 'Department Budget Analysis'")

        # Check parent is Project Portfolio (db-projects)
        parent_id = dept_budget_db.get("parentId")
        if parent_id != "db-projects":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "project portfolio" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Project Portfolio' (db-projects), got parentId: {parent_id}")

        # ==========================================================================
        # Check 2: Department Budget Analysis Database Properties
        # ==========================================================================
        db_metadata = dept_budget_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        expected_properties = [
            ("Department", "title"),
            ("Total Budget", "number"),
            ("Project Count", "number"),
            ("Average Budget", "number"),
            ("Status Breakdown", "text"),
            ("Largest Project", "text"),
        ]

        prop_map = {}
        for prop in properties:
            prop_map[prop.get("name", "").lower()] = prop

        for expected_name, expected_type in expected_properties:
            prop = prop_map.get(expected_name.lower())
            if not prop:
                errors.append(f"Property '{expected_name}' not found in Department Budget Analysis")
            elif prop.get("type") != expected_type:
                errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{prop.get('type')}'")

        # ==========================================================================
        # Check 3: Department Budget Analysis Rows Existence
        # ==========================================================================
        db_id = dept_budget_db.get("_id")
        rows = [p for p in all_pages if p.get("type") == "row" and p.get("databaseId") == db_id]

        # Build property ID map for cell value lookup
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id:
                return cell_values.get(prop_id)
            # Fallback: search by property name pattern
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                    return val
            return None

        # Find rows by department name
        dept_rows = {}
        for row in rows:
            title = row.get("title", "").lower()
            if "engineering" in title:
                dept_rows["engineering"] = row
            elif "marketing" in title:
                dept_rows["marketing"] = row
            elif "sales" in title:
                dept_rows["sales"] = row
            elif "hr" in title:
                dept_rows["hr"] = row

        expected_depts = ["engineering", "marketing", "sales", "hr"]
        for dept in expected_depts:
            if dept not in dept_rows:
                errors.append(f"Row for department '{dept}' not found in Department Budget Analysis")

        # ==========================================================================
        # Check 4: Engineering Department Row Values (most important)
        # ==========================================================================
        if "engineering" in dept_rows:
            eng_row = dept_rows["engineering"]

            # Total Budget = 225000
            total_budget = get_cell_value(eng_row, "total budget")
            if total_budget is not None:
                if int(total_budget) != 225000:
                    errors.append(f"Engineering Total Budget should be 225000, got {total_budget}")
            else:
                errors.append("Engineering row missing Total Budget")

            # Project Count = 3
            project_count = get_cell_value(eng_row, "project count")
            if project_count is not None:
                if int(project_count) != 3:
                    errors.append(f"Engineering Project Count should be 3, got {project_count}")
            else:
                errors.append("Engineering row missing Project Count")

            # Average Budget = 75000
            avg_budget = get_cell_value(eng_row, "average budget")
            if avg_budget is not None:
                if int(avg_budget) != 75000:
                    errors.append(f"Engineering Average Budget should be 75000, got {avg_budget}")
            else:
                errors.append("Engineering row missing Average Budget")

            # Status Breakdown contains "Active" with count 2 and "Planning" with count 1
            status_breakdown = get_cell_value(eng_row, "status breakdown")
            if status_breakdown:
                status_str = str(status_breakdown).lower()
                if "active" not in status_str or "2" not in status_str:
                    errors.append(f"Engineering Status Breakdown should contain 'Active: 2', got '{status_breakdown}'")
                if "planning" not in status_str or "1" not in status_str:
                    errors.append(f"Engineering Status Breakdown should contain 'Planning: 1', got '{status_breakdown}'")
            else:
                errors.append("Engineering row missing Status Breakdown")

            # Largest Project = Mobile App Development
            largest_project = get_cell_value(eng_row, "largest project")
            if largest_project:
                if "mobile app development" not in str(largest_project).lower():
                    errors.append(f"Engineering Largest Project should be 'Mobile App Development', got '{largest_project}'")
            else:
                errors.append("Engineering row missing Largest Project")

        # ==========================================================================
        # Check 5: Other Department Rows (Marketing, Sales, HR)
        # ==========================================================================
        other_dept_expected = {
            "marketing": {
                "total_budget": 25000,
                "project_count": 1,
                "average_budget": 25000,
                "largest_project": "q1 marketing campaign",
            },
            "sales": {
                "total_budget": 35000,
                "project_count": 1,
                "average_budget": 35000,
                "largest_project": "sales pipeline automation",
            },
            "hr": {
                "total_budget": 15000,
                "project_count": 1,
                "average_budget": 15000,
                "largest_project": "employee training program",
            },
        }

        for dept, expected in other_dept_expected.items():
            if dept in dept_rows:
                row = dept_rows[dept]

                # Total Budget
                total_budget = get_cell_value(row, "total budget")
                if total_budget is not None:
                    if int(total_budget) != expected["total_budget"]:
                        errors.append(f"{dept.title()} Total Budget should be {expected['total_budget']}, got {total_budget}")
                else:
                    errors.append(f"{dept.title()} row missing Total Budget")

                # Project Count
                project_count = get_cell_value(row, "project count")
                if project_count is not None:
                    if int(project_count) != expected["project_count"]:
                        errors.append(f"{dept.title()} Project Count should be {expected['project_count']}, got {project_count}")
                else:
                    errors.append(f"{dept.title()} row missing Project Count")

                # Largest Project
                largest_project = get_cell_value(row, "largest project")
                if largest_project:
                    if expected["largest_project"] not in str(largest_project).lower():
                        errors.append(f"{dept.title()} Largest Project should contain '{expected['largest_project']}', got '{largest_project}'")
                else:
                    errors.append(f"{dept.title()} row missing Largest Project")

        # ==========================================================================
        # Check 6: Critical Budget Items Database Exists
        # ==========================================================================
        critical_budget_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "critical budget items" in title:
                    critical_budget_db = page
                    break

        if not critical_budget_db:
            errors.append("[X] Database 'Critical Budget Items' not found")
        else:
            checks_passed.append("[C] Database 'Critical Budget Items'")

        # Check parent is db-projects (same page as Department Budget Analysis)
        cbi_parent_id = critical_budget_db.get("parentId")
        if cbi_parent_id != "db-projects":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == cbi_parent_id:
                    parent_page = page
                    break
            if not parent_page or "project portfolio" not in parent_page.get("title", "").lower():
                errors.append(f"Critical Budget Items should be on Project Portfolio page, got parentId: {cbi_parent_id}")

        # ==========================================================================
        # Check 7: Critical Budget Items Database Properties
        # ==========================================================================
        cbi_metadata = critical_budget_db.get("databaseMetadata", {})
        cbi_properties = cbi_metadata.get("properties", [])

        cbi_prop_map = {}
        for prop in cbi_properties:
            cbi_prop_map[prop.get("name", "").lower()] = prop

        # Check Item (title)
        item_prop = cbi_prop_map.get("item")
        if not item_prop:
            errors.append("Critical Budget Items missing 'Item' property")
        elif item_prop.get("type") != "title":
            errors.append(f"Critical Budget Items 'Item' property should be type 'title', got '{item_prop.get('type')}'")

        # Check Department (select)
        dept_prop = cbi_prop_map.get("department")
        if not dept_prop:
            errors.append("Critical Budget Items missing 'Department' property")
        elif dept_prop.get("type") != "select":
            errors.append(f"Critical Budget Items 'Department' property should be type 'select', got '{dept_prop.get('type')}'")
        else:
            # Check Department options match Project Portfolio
            dept_options = dept_prop.get("options", [])
            expected_dept_names = ["engineering", "marketing", "sales", "operations", "hr"]
            found_dept_names = [opt.get("name", "").lower() for opt in dept_options]
            for dept_name in expected_dept_names:
                if dept_name not in found_dept_names:
                    errors.append(f"Critical Budget Items 'Department' missing option '{dept_name}'")

        # Check Amount (number)
        amount_prop = cbi_prop_map.get("amount")
        if not amount_prop:
            errors.append("Critical Budget Items missing 'Amount' property")
        elif amount_prop.get("type") != "number":
            errors.append(f"Critical Budget Items 'Amount' property should be type 'number', got '{amount_prop.get('type')}'")

        # Check Priority (select with High red, Medium yellow, Low green)
        priority_prop = cbi_prop_map.get("priority")
        if not priority_prop:
            errors.append("Critical Budget Items missing 'Priority' property")
        elif priority_prop.get("type") != "select":
            errors.append(f"Critical Budget Items 'Priority' property should be type 'select', got '{priority_prop.get('type')}'")
        else:
            priority_options = priority_prop.get("options", [])
            expected_priority_opts = {
                "high": "red",
                "medium": "yellow",
                "low": "green",
            }

            found_priority_opts = {}
            for opt in priority_options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_priority_opts[opt_name] = opt_color

            for exp_name, exp_color in expected_priority_opts.items():
                if exp_name not in found_priority_opts:
                    errors.append(f"Critical Budget Items 'Priority' missing option '{exp_name}'")
                elif found_priority_opts[exp_name] != exp_color:
                    errors.append(f"Critical Budget Items 'Priority' option '{exp_name}' should have color '{exp_color}', got '{found_priority_opts[exp_name]}'")

        # ==========================================================================
        # Check 8: Critical Budget Items Has 3 Rows for Engineering
        # ==========================================================================
        cbi_db_id = critical_budget_db.get("_id")
        cbi_rows = [p for p in all_pages if p.get("type") == "row" and p.get("databaseId") == cbi_db_id]

        if len(cbi_rows) != 3:
            errors.append(f"Critical Budget Items should have exactly 3 rows, got {len(cbi_rows)}")

        # Build property ID map for Critical Budget Items
        cbi_prop_id_map = {}
        for prop in cbi_properties:
            cbi_prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cbi_cell_value(row, prop_name_lower):
            cell_values = row.get("cellValues", {})
            prop_id = cbi_prop_id_map.get(prop_name_lower)
            if prop_id:
                return cell_values.get(prop_id)
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                    return val
            return None

        # Check all rows are for Engineering (only department > $50k)
        engineering_rows = 0
        for row in cbi_rows:
            dept_value = get_cbi_cell_value(row, "department")
            if dept_value and "engineering" in str(dept_value).lower():
                engineering_rows += 1

        if engineering_rows < 3 and len(cbi_rows) >= 3:
            errors.append(f"Critical Budget Items rows should be for Engineering (only dept > $50k), found {engineering_rows} Engineering rows out of {len(cbi_rows)}")

        # Return result
        if errors:
            errors.append("[X] Validation failed: {'; '.join(errors)}")
        else:
            checks_passed.append("[C] Validation succeeded")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Project Health Monitor Task 2
# =============================================================================
def _validate_agent_project_health_monitor_2(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Build a project health monitoring system across databases. Create a new database
    'Project Health Monitor' as child of 'Project Portfolio' with properties: 'Project' (title),
    'Health Score' (number), 'Task Completion' (number), 'Budget Used' (number),
    'Days Remaining' (number), 'Risk Level' (select: 'Low' green, 'Medium' yellow, 'High' orange,
    'Critical' red), 'Action Required' (checkbox), 'Notes' (text). For each active project in
    Project Portfolio, create a monitoring row. Calculate health score as:
    100 - (days elapsed / total days * 50) - (budget used percentage * 50). Query Task Tracker
    for tasks with matching project in Related Project field (you'll need to add this field first)
    and calculate completion percentage. Set Risk Level based on health score (>70=Low, 50-70=Medium,
    30-50=High, <30=Critical). Check Action Required if Risk Level is High or Critical. In Notes,
    add 'Automated assessment: [risk factors]'.

    Active projects in initial data:
    - Website Redesign (Status=Active, Timeline: 2025-12-13 to 2026-03-13, Budget: $50,000)
    - Customer Portal (Status=Active, Timeline: 2025-11-28 to 2026-02-11, Budget: $75,000)
    - Sales Pipeline Automation (Status=Active, Timeline: 2025-12-28 to 2026-02-26, Budget: $35,000)

    Note: Budget used is assumed to be 0% for all projects (no actual spend data exists).
    Related Project field should be added to Task Tracker but doesn't need to be populated.
    """
    checks_passed: list[str] = []

    errors = []
    try:

        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        # ==========================================================================
        # Check 1: Find Project Health Monitor database
        # ==========================================================================
        health_monitor_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "project health monitor" in title:
                    health_monitor_db = page
                    break

        if not health_monitor_db:
            errors.append("[X] Database 'Project Health Monitor' not found")
        else:
            checks_passed.append("[C] Database 'Project Health Monitor'")

        # ==========================================================================
        # Check 2: Verify parent is Project Portfolio (db-projects)
        # ==========================================================================
        parent_id = health_monitor_db.get("parentId")
        if parent_id != "db-projects":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "project portfolio" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Project Portfolio', got parentId: {parent_id}")

        # ==========================================================================
        # Check 3: Validate database properties (8 properties with correct types)
        # ==========================================================================
        db_metadata = health_monitor_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        expected_properties = [
            ("project", "title"),
            ("health score", "number"),
            ("task completion", "number"),
            ("budget used", "number"),
            ("days remaining", "number"),
            ("risk level", "select"),
            ("action required", "checkbox"),
            ("notes", "text"),
        ]

        found_props = {}
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            found_props[prop_name] = prop

        for expected_name, expected_type in expected_properties:
            found = False
            for prop_name, prop in found_props.items():
                # Flexible matching for property names
                if expected_name in prop_name or prop_name in expected_name:
                    found = True
                    if prop.get("type") != expected_type:
                        errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{prop.get('type')}'")
                    break
            if not found:
                errors.append(f"Property '{expected_name}' not found in database")

        # ==========================================================================
        # Check 4: Validate Risk Level select options (4 options with correct colors)
        # ==========================================================================
        risk_level_prop = None
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            if "risk" in prop_name and "level" in prop_name:
                risk_level_prop = prop
                break

        if risk_level_prop:
            options = risk_level_prop.get("options", [])
            expected_options = {
                "low": "green",
                "medium": "yellow",
                "high": "orange",
                "critical": "red",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Risk Level missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Risk Level option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")
        else:
            errors.append("Risk Level property not found")

        # ==========================================================================
        # Check 5: Verify Related Project field added to Task Tracker
        # ==========================================================================
        task_tracker = None
        for page in all_pages:
            if page.get("_id") == "db-tasks":
                task_tracker = page
                break
            elif page.get("type") == "database" and "task tracker" in page.get("title", "").lower():
                task_tracker = page
                break

        if task_tracker:
            task_props = task_tracker.get("databaseMetadata", {}).get("properties", [])
            related_project_found = False
            for prop in task_props:
                prop_name = prop.get("name", "").lower()
                if "related" in prop_name and "project" in prop_name:
                    related_project_found = True
                    break
            if not related_project_found:
                errors.append("'Related Project' field not added to Task Tracker")
        else:
            errors.append("Task Tracker database not found")

        # ==========================================================================
        # Check 6: Verify monitoring rows for all 3 active projects
        # ==========================================================================
        db_id = health_monitor_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        active_projects = ["website redesign", "customer portal", "sales pipeline automation"]
        found_projects = []
        project_rows = {}

        for row in rows:
            row_title = row.get("title", "").lower()
            for proj in active_projects:
                if proj in row_title:
                    found_projects.append(proj)
                    project_rows[proj] = row
                    break

        for proj in active_projects:
            if proj not in found_projects:
                errors.append(f"Missing monitoring row for active project: '{proj}'")

        # Build property ID map for cell value lookups
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(cell_values, prop_name_lower):
            """Helper to get cell value by property name."""
            # Try direct property ID lookup
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values.get(prop_id)
            # Try fuzzy match in cell_values keys
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        # ==========================================================================
        # Check 7-10: Validate row data for each found project
        # ==========================================================================
        for row in rows:
            row_title = row.get("title", "").lower()
            cell_values = row.get("cellValues", {})

            # Check 7: Health Score exists and is within 0-100 range
            health_score = get_cell_value(cell_values, "health score")
            if health_score is None:
                # Try alternative lookup
                for key, val in cell_values.items():
                    if "health" in key.lower() and "score" in key.lower():
                        health_score = val
                        break

            if health_score is not None:
                try:
                    score = float(health_score)
                    if score < 0 or score > 100:
                        errors.append(f"Row '{row_title}' has invalid health score: {score} (should be 0-100)")
                except (ValueError, TypeError):
                    errors.append(f"Row '{row_title}' has non-numeric health score: {health_score}")
            else:
                errors.append(f"Row '{row_title}' missing Health Score")

            # Check 8: Risk Level matches health score threshold
            risk_level = get_cell_value(cell_values, "risk level")
            if risk_level is None:
                for key, val in cell_values.items():
                    if "risk" in key.lower() and "level" in key.lower():
                        risk_level = val
                        break

            if risk_level and health_score is not None:
                risk_lower = str(risk_level).lower()
                try:
                    score = float(health_score)
                    expected_risk = None
                    if score > 70:
                        expected_risk = "low"
                    elif score >= 50:
                        expected_risk = "medium"
                    elif score >= 30:
                        expected_risk = "high"
                    else:
                        expected_risk = "critical"

                    if expected_risk not in risk_lower:
                        errors.append(f"Row '{row_title}' has risk level '{risk_level}' but health score {score} suggests '{expected_risk}'")
                except (ValueError, TypeError):
                    pass  # Already caught in check 7
            elif not risk_level:
                errors.append(f"Row '{row_title}' missing Risk Level")

            # Check 9: Action Required logic (true for High/Critical, false for Low/Medium)
            action_required = get_cell_value(cell_values, "action required")
            if action_required is None:
                for key, val in cell_values.items():
                    if "action" in key.lower() and "required" in key.lower():
                        action_required = val
                        break

            if action_required is not None and risk_level:
                risk_lower = str(risk_level).lower()
                should_be_true = "high" in risk_lower or "critical" in risk_lower
                if should_be_true and not action_required:
                    errors.append(f"Row '{row_title}' should have Action Required=true for risk level '{risk_level}'")
                elif not should_be_true and action_required:
                    errors.append(f"Row '{row_title}' should have Action Required=false for risk level '{risk_level}'")

            # Check 10: Notes contain "Automated assessment"
            notes = get_cell_value(cell_values, "notes")
            if notes is None:
                for key, val in cell_values.items():
                    if "notes" in key.lower():
                        notes = val
                        break

            if notes:
                if "automated assessment" not in str(notes).lower():
                    errors.append(f"Row '{row_title}' Notes should contain 'Automated assessment'")
            else:
                errors.append(f"Row '{row_title}' missing Notes")

        # Return result
        if errors:
            errors.append("[X] Validation failed: {'; '.join(errors)}")
        else:
            checks_passed.append("[C] Validation succeeded")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Velocity Tracking System Task
# =============================================================================
def _validate_agent_sprint_velocity_tracking_system(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a sprint velocity tracking system. Query the 'Task Tracker' database to analyze
    completed tasks (Status='Done' and Completed=true). Group them by the user in the 'Assignee'
    field and count tasks per person. Create a new database 'Sprint Performance Metrics' as child
    of 'Sprint Planning - Jan 15' with table layout and properties: 'Team Member' (title),
    'Tasks Completed' (number), 'Bug Fixes' (number), 'Features Delivered' (number),
    'Documentation Tasks' (number), 'Performance Score' (select with 'Excellent' green, 'Good' blue,
    'Needs Improvement' yellow), 'Sprint' (text). For each unique assignee found, create a row with
    their task count. Count their tasks by Tags to populate Bug Fixes (tasks with 'Bug' tag),
    Features Delivered (tasks with 'Feature' tag), and Documentation Tasks (tasks with 'Documentation'
    tag). Set Performance Score based on total: ≥5 tasks='Excellent', 3-4='Good', <3='Needs Improvement'.
    Set Sprint='Current Sprint'.

    Expected values from initial data (Task Tracker completed tasks):
    - db-tasks-row-4 (user-1/Alice Chen): Status=Done, Completed=true, Tags=[Feature, Backend, Frontend]
    - db-tasks-row-8 (user-2/Bob Martinez): Status=Done, Completed=true, Tags=[Backend]

    Expected rows in Sprint Performance Metrics:
    - Alice Chen: Tasks Completed=1, Bug Fixes=0, Features Delivered=1, Documentation Tasks=0, Performance Score=Needs Improvement
    - Bob Martinez: Tasks Completed=1, Bug Fixes=0, Features Delivered=0, Documentation Tasks=0, Performance Score=Needs Improvement
    """
    checks_passed: list[str] = []

    errors = []

    # Step 1: Find the Sprint Performance Metrics database
    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        sprint_metrics_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "sprint performance metrics" in title:
                    sprint_metrics_db = page
                    break

        if not sprint_metrics_db:
            errors.append("[X] Database 'Sprint Performance Metrics' not found")
        else:
            checks_passed.append("[C] Database 'Sprint Performance Metrics'")

        # Step 2: Validate parent is 'Sprint Planning - Jan 15' (page-3-1)
        parent_id = sprint_metrics_db.get("parentId")
        if parent_id != "page-3-1":
            # Check if parent has title "Sprint Planning - Jan 15"
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "sprint planning - jan 15" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent is not 'Sprint Planning - Jan 15' (page-3-1), got parentId: {parent_id}")

        # Step 3: Validate database layout is table
        db_metadata = sprint_metrics_db.get("databaseMetadata", {})
        layout = db_metadata.get("layout", "")
        if layout != "table":
            errors.append(f"Database layout should be 'table', got '{layout}'")

        # Step 4: Validate database properties - types
        properties = db_metadata.get("properties", [])
        expected_properties = [
            ("Team Member", "title"),
            ("Tasks Completed", "number"),
            ("Bug Fixes", "number"),
            ("Features Delivered", "number"),
            ("Documentation Tasks", "number"),
            ("Performance Score", "select"),
            ("Sprint", "text"),
        ]

        prop_name_to_id = {}
        for prop in properties:
            prop_name_to_id[prop.get("name", "").lower()] = prop.get("_id")

        for expected_name, expected_type in expected_properties:
            found = False
            for prop in properties:
                prop_name = prop.get("name", "")
                if prop_name.lower() == expected_name.lower():
                    found = True
                    if prop.get("type") != expected_type:
                        errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{prop.get('type')}'")
                    break
            if not found:
                errors.append(f"Property '{expected_name}' not found in database")

        # Step 5: Validate Performance Score select options with colors
        performance_score_prop = None
        for prop in properties:
            if prop.get("name", "").lower() == "performance score":
                performance_score_prop = prop
                break

        if performance_score_prop:
            options = performance_score_prop.get("options", [])
            expected_options = {
                "excellent": "green",
                "good": "blue",
                "needs improvement": "yellow",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Performance Score missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Performance Score option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")
        else:
            errors.append("Performance Score property not found")

        # Step 6: Find rows in the Sprint Performance Metrics database
        db_id = sprint_metrics_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        # Get users for name lookup
        all_users = final_state_backend.query({"collection": "users", "filter": {}})
        user_id_to_name = {}
        for user in all_users:
            user_id_to_name[user.get("_id")] = user.get("name", "")

        # Helper to get cell value by property name
        def get_cell_value(cell_values, prop_name_lower):
            # Try by property ID first
            for prop in properties:
                if prop.get("name", "").lower() == prop_name_lower:
                    prop_id = prop.get("_id")
                    if prop_id and prop_id in cell_values:
                        return cell_values[prop_id]
            # Try matching by property name in cell key
            for key, val in cell_values.items():
                if prop_name_lower in key.lower():
                    return val
            return None

        # Step 7: Validate row for user-1 (Alice Chen)
        alice_row = None
        for row in rows:
            title = row.get("title", "").lower()
            if "alice" in title or "chen" in title:
                alice_row = row
                break

        if not alice_row:
            errors.append("Row for Alice Chen (user-1) not found")
        else:
            cell_values = alice_row.get("cellValues", {})

            # Check Tasks Completed = 1
            tasks_completed = get_cell_value(cell_values, "tasks completed")
            if tasks_completed is None:
                errors.append("Alice Chen row missing Tasks Completed")
            elif int(tasks_completed) != 1:
                errors.append(f"Alice Chen Tasks Completed should be 1, got {tasks_completed}")

            # Check Bug Fixes = 0
            bug_fixes = get_cell_value(cell_values, "bug fixes")
            if bug_fixes is not None and int(bug_fixes) != 0:
                errors.append(f"Alice Chen Bug Fixes should be 0, got {bug_fixes}")

            # Check Features Delivered = 1
            features_delivered = get_cell_value(cell_values, "features delivered")
            if features_delivered is None:
                errors.append("Alice Chen row missing Features Delivered")
            elif int(features_delivered) != 1:
                errors.append(f"Alice Chen Features Delivered should be 1, got {features_delivered}")

            # Check Documentation Tasks = 0
            doc_tasks = get_cell_value(cell_values, "documentation tasks")
            if doc_tasks is not None and int(doc_tasks) != 0:
                errors.append(f"Alice Chen Documentation Tasks should be 0, got {doc_tasks}")

            # Check Sprint = 'Current Sprint'
            sprint = get_cell_value(cell_values, "sprint")
            if sprint is None:
                errors.append("Alice Chen row missing Sprint")
            elif "current sprint" not in str(sprint).lower():
                errors.append(f"Alice Chen Sprint should be 'Current Sprint', got '{sprint}'")

            # Check Performance Score = 'Needs Improvement' (< 3 tasks)
            perf_score = get_cell_value(cell_values, "performance score")
            if perf_score is None:
                errors.append("Alice Chen row missing Performance Score")
            elif "needs improvement" not in str(perf_score).lower():
                errors.append(f"Alice Chen Performance Score should be 'Needs Improvement', got '{perf_score}'")

        # Step 8: Validate row for user-2 (Bob Martinez)
        bob_row = None
        for row in rows:
            title = row.get("title", "").lower()
            if "bob" in title or "martinez" in title:
                bob_row = row
                break

        if not bob_row:
            errors.append("Row for Bob Martinez (user-2) not found")
        else:
            cell_values = bob_row.get("cellValues", {})

            # Check Tasks Completed = 1
            tasks_completed = get_cell_value(cell_values, "tasks completed")
            if tasks_completed is None:
                errors.append("Bob Martinez row missing Tasks Completed")
            elif int(tasks_completed) != 1:
                errors.append(f"Bob Martinez Tasks Completed should be 1, got {tasks_completed}")

            # Check Bug Fixes = 0
            bug_fixes = get_cell_value(cell_values, "bug fixes")
            if bug_fixes is not None and int(bug_fixes) != 0:
                errors.append(f"Bob Martinez Bug Fixes should be 0, got {bug_fixes}")

            # Check Features Delivered = 0
            features_delivered = get_cell_value(cell_values, "features delivered")
            if features_delivered is not None and int(features_delivered) != 0:
                errors.append(f"Bob Martinez Features Delivered should be 0, got {features_delivered}")

            # Check Documentation Tasks = 0
            doc_tasks = get_cell_value(cell_values, "documentation tasks")
            if doc_tasks is not None and int(doc_tasks) != 0:
                errors.append(f"Bob Martinez Documentation Tasks should be 0, got {doc_tasks}")

            # Check Sprint = 'Current Sprint'
            sprint = get_cell_value(cell_values, "sprint")
            if sprint is None:
                errors.append("Bob Martinez row missing Sprint")
            elif "current sprint" not in str(sprint).lower():
                errors.append(f"Bob Martinez Sprint should be 'Current Sprint', got '{sprint}'")

            # Check Performance Score = 'Needs Improvement' (< 3 tasks)
            perf_score = get_cell_value(cell_values, "performance score")
            if perf_score is None:
                errors.append("Bob Martinez row missing Performance Score")
            elif "needs improvement" not in str(perf_score).lower():
                errors.append(f"Bob Martinez Performance Score should be 'Needs Improvement', got '{perf_score}'")

        if errors:
            errors.append("[X] Validation failed: {'; '.join(errors)}")
        else:
            checks_passed.append("[C] Validation succeeded")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Performance Metrics Inline Database Task
# =============================================================================
def _validate_agent_sprint_performance_metrics_inline(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Create a new inline database titled 'Sprint Performance Metrics' inside the 'Q1 Goals' page, positioned immediately after any existing content. Configure it with the following properties: 'Sprint Name' (title), 'Start Date' (date), 'End Date' (date), 'Team' (select with options 'Frontend' in blue, 'Backend' in green, 'QA' in yellow, 'DevOps' in purple), 'Story Points Planned' (number), 'Story Points Completed' (number), 'Velocity' (number), 'Sprint Health' (select with options 'Excellent' in green, 'Good' in blue, 'Needs Improvement' in orange, 'Critical' in red). Then query the 'Task Tracker' database to calculate metrics: count all tasks with Tags containing 'Frontend' and Status='Done' as frontend completed points. Add 3 rows to the new database: 'Sprint 1' (Start: 2026-01-01, End: 2026-01-14, Team: Frontend, Planned: 40, Completed: [frontend count], Velocity: [completed/14 rounded to 1 decimal]), 'Sprint 2' (dates: 2026-01-15 to 2026-01-28, Team: Backend, Planned: 50, Completed: 0), 'Sprint 3' (dates: 2026-01-29 to 2026-02-11, Team: QA, Planned: 35, Completed: 0). Set Sprint Health based on completion percentage: >90%='Excellent', 70-90%='Good', 50-70%='Needs Improvement', <50%='Critical'.

    Expected values from initial data:
    - Q1 Goals page: page-2-1
    - Task Tracker database: db-tasks
    - Frontend tasks with Status='Done': 1 (only "Implement user notifications" - db-tasks-row-4)
    - Sprint 1: Completed=1, Velocity=0.1 (1/14 rounded), Health=Critical (2.5% < 50%)
    - Sprint 2 & 3: Completed=0, Health=Critical (0% < 50%)
    """
    checks_passed: list[str] = []

    errors = []
    try:

        # Step 1: Find the Sprint Performance Metrics database
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})

        sprint_metrics_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "sprint performance metrics" in title:
                    # Check parent to ensure it's the Q1 Goals one
                    parent_id = page.get("parentId")
                    if parent_id == "page-2-1":
                        sprint_metrics_db = page
                        break
                    # Also check parent title
                    parent_page = next((p for p in all_pages if p.get("_id") == parent_id), None)
                    if parent_page and "q1 goals" in parent_page.get("title", "").lower():
                        sprint_metrics_db = page
                        break

        if not sprint_metrics_db:
            errors.append("[X] Database 'Sprint Performance Metrics' not found as child of Q1 Goals page")
        else:
            checks_passed.append("[C] Database 'Sprint Performance Metrics' found as child of Q1 Goals page")

        db_id = sprint_metrics_db.get("_id")

        # Step 2: Verify inline database block exists on Q1 Goals page
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {"pageId": "page-2-1"}})

        inline_db_block = None
        for block in all_blocks:
            if block.get("type") == "database":
                props = block.get("properties", {})
                if props.get("databaseId") == db_id:
                    inline_db_block = block
                    break

        if not inline_db_block:
            errors.append("Inline database block not found on Q1 Goals page (page-2-1)")

        # Step 3: Validate database properties
        db_metadata = sprint_metrics_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        expected_props = [
            ("Sprint Name", "title"),
            ("Start Date", "date"),
            ("End Date", "date"),
            ("Team", "select"),
            ("Story Points Planned", "number"),
            ("Story Points Completed", "number"),
            ("Velocity", "number"),
            ("Sprint Health", "select"),
        ]

        prop_name_to_obj = {}
        for prop in properties:
            prop_name_lower = prop.get("name", "").lower()
            prop_name_to_obj[prop_name_lower] = prop

        for expected_name, expected_type in expected_props:
            prop_key = expected_name.lower()
            if prop_key not in prop_name_to_obj:
                errors.append(f"Property '{expected_name}' not found")
            else:
                actual_type = prop_name_to_obj[prop_key].get("type", "")
                if actual_type != expected_type:
                    errors.append(f"Property '{expected_name}' type should be '{expected_type}', got '{actual_type}'")

        # Step 4: Validate Team select options
        team_prop = prop_name_to_obj.get("team")
        if team_prop:
            options = team_prop.get("options", [])
            expected_team_options = {
                "frontend": "blue",
                "backend": "green",
                "qa": "yellow",
                "devops": "purple",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_team_options.items():
                if exp_name not in found_options:
                    errors.append(f"Team missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Team option '{exp_name}' should be {exp_color}, got {found_options[exp_name]}")
        else:
            errors.append("Team property not found")

        # Step 5: Validate Sprint Health select options
        health_prop = prop_name_to_obj.get("sprint health")
        if health_prop:
            options = health_prop.get("options", [])
            expected_health_options = {
                "excellent": "green",
                "good": "blue",
                "needs improvement": "orange",
                "critical": "red",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_health_options.items():
                if exp_name not in found_options:
                    errors.append(f"Sprint Health missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Sprint Health option '{exp_name}' should be {exp_color}, got {found_options[exp_name]}")
        else:
            errors.append("Sprint Health property not found")

        # Step 6: Find rows in database
        db_id = sprint_metrics_db.get("_id")
        rows = [p for p in all_pages if p.get("type") == "row" and p.get("databaseId") == db_id]

        # Helper to get property ID
        def get_prop_id(prop_name):
            prop = prop_name_to_obj.get(prop_name.lower())
            return prop.get("_id") if prop else None

        # Helper to get cell value
        def get_cell(row, prop_name):
            prop_id = get_prop_id(prop_name)
            cell_values = row.get("cellValues", {})
            if prop_id and prop_id in cell_values:
                return cell_values[prop_id]
            # Fallback: search by property name in key
            prop_name_normalized = prop_name.lower().replace(" ", "")
            for key, val in cell_values.items():
                if prop_name_normalized in key.lower().replace(" ", "").replace("-", "").replace("_", ""):
                    return val
            return None

        # Step 7: Validate Sprint 1 row
        sprint1 = None
        for row in rows:
            title = row.get("title", "").lower()
            if "sprint 1" in title or title == "sprint 1":
                sprint1 = row
                break

        if not sprint1:
            errors.append("Row 'Sprint 1' not found")
        else:
            # Start Date: 2026-01-01
            start = get_cell(sprint1, "Start Date")
            if start:
                start_val = start.get("start", "") if isinstance(start, dict) else str(start)
                if "2026-01-01" not in str(start_val):
                    errors.append(f"Sprint 1 Start Date should be 2026-01-01, got {start_val}")
            else:
                errors.append("Sprint 1 missing Start Date")

            # End Date: 2026-01-14
            end = get_cell(sprint1, "End Date")
            if end:
                end_val = end.get("start", "") if isinstance(end, dict) else str(end)
                if "2026-01-14" not in str(end_val):
                    errors.append(f"Sprint 1 End Date should be 2026-01-14, got {end_val}")
            else:
                errors.append("Sprint 1 missing End Date")

            # Team: Frontend
            team = get_cell(sprint1, "Team")
            if not team or "frontend" not in str(team).lower():
                errors.append(f"Sprint 1 Team should be 'Frontend', got {team}")

            # Story Points Planned: 40
            planned = get_cell(sprint1, "Story Points Planned")
            if planned is None or int(planned) != 40:
                errors.append(f"Sprint 1 Story Points Planned should be 40, got {planned}")

            # Story Points Completed: 1 (frontend count)
            completed = get_cell(sprint1, "Story Points Completed")
            if completed is None or int(completed) != 1:
                errors.append(f"Sprint 1 Story Points Completed should be 1, got {completed}")

            # Velocity: 0.1 (1/14 rounded to 1 decimal)
            velocity = get_cell(sprint1, "Velocity")
            if velocity is not None:
                try:
                    vel_float = float(velocity)
                    if abs(vel_float - 0.1) > 0.05:
                        errors.append(f"Sprint 1 Velocity should be ~0.1, got {velocity}")
                except (ValueError, TypeError):
                    errors.append(f"Sprint 1 Velocity should be numeric, got {velocity}")
            else:
                errors.append("Sprint 1 missing Velocity")

            # Sprint Health: Critical (1/40 = 2.5% < 50%)
            health = get_cell(sprint1, "Sprint Health")
            if not health or "critical" not in str(health).lower():
                errors.append(f"Sprint 1 Sprint Health should be 'Critical', got {health}")

        # Step 8: Validate Sprint 2 row
        sprint2 = None
        for row in rows:
            title = row.get("title", "").lower()
            if "sprint 2" in title or title == "sprint 2":
                sprint2 = row
                break

        if not sprint2:
            errors.append("Row 'Sprint 2' not found")
        else:
            # Start Date: 2026-01-15
            start = get_cell(sprint2, "Start Date")
            if start:
                start_val = start.get("start", "") if isinstance(start, dict) else str(start)
                if "2026-01-15" not in str(start_val):
                    errors.append(f"Sprint 2 Start Date should be 2026-01-15, got {start_val}")
            else:
                errors.append("Sprint 2 missing Start Date")

            # End Date: 2026-01-28
            end = get_cell(sprint2, "End Date")
            if end:
                end_val = end.get("start", "") if isinstance(end, dict) else str(end)
                if "2026-01-28" not in str(end_val):
                    errors.append(f"Sprint 2 End Date should be 2026-01-28, got {end_val}")
            else:
                errors.append("Sprint 2 missing End Date")

            # Team: Backend
            team = get_cell(sprint2, "Team")
            if not team or "backend" not in str(team).lower():
                errors.append(f"Sprint 2 Team should be 'Backend', got {team}")

            # Story Points Planned: 50
            planned = get_cell(sprint2, "Story Points Planned")
            if planned is None or int(planned) != 50:
                errors.append(f"Sprint 2 Story Points Planned should be 50, got {planned}")

            # Story Points Completed: 0
            completed = get_cell(sprint2, "Story Points Completed")
            if completed is not None and int(completed) != 0:
                errors.append(f"Sprint 2 Story Points Completed should be 0, got {completed}")

            # Sprint Health: Critical (0/50 = 0% < 50%)
            health = get_cell(sprint2, "Sprint Health")
            if not health or "critical" not in str(health).lower():
                errors.append(f"Sprint 2 Sprint Health should be 'Critical', got {health}")

        # Step 9: Validate Sprint 3 row
        sprint3 = None
        for row in rows:
            title = row.get("title", "").lower()
            if "sprint 3" in title or title == "sprint 3":
                sprint3 = row
                break

        if not sprint3:
            errors.append("Row 'Sprint 3' not found")
        else:
            # Start Date: 2026-01-29
            start = get_cell(sprint3, "Start Date")
            if start:
                start_val = start.get("start", "") if isinstance(start, dict) else str(start)
                if "2026-01-29" not in str(start_val):
                    errors.append(f"Sprint 3 Start Date should be 2026-01-29, got {start_val}")
            else:
                errors.append("Sprint 3 missing Start Date")

            # End Date: 2026-02-11
            end = get_cell(sprint3, "End Date")
            if end:
                end_val = end.get("start", "") if isinstance(end, dict) else str(end)
                if "2026-02-11" not in str(end_val):
                    errors.append(f"Sprint 3 End Date should be 2026-02-11, got {end_val}")
            else:
                errors.append("Sprint 3 missing End Date")

            # Team: QA
            team = get_cell(sprint3, "Team")
            if not team or "qa" not in str(team).lower():
                errors.append(f"Sprint 3 Team should be 'QA', got {team}")

            # Story Points Planned: 35
            planned = get_cell(sprint3, "Story Points Planned")
            if planned is None or int(planned) != 35:
                errors.append(f"Sprint 3 Story Points Planned should be 35, got {planned}")

            # Story Points Completed: 0
            completed = get_cell(sprint3, "Story Points Completed")
            if completed is not None and int(completed) != 0:
                errors.append(f"Sprint 3 Story Points Completed should be 0, got {completed}")

            # Sprint Health: Critical (0/35 = 0% < 50%)
            health = get_cell(sprint3, "Sprint Health")
            if not health or "critical" not in str(health).lower():
                errors.append(f"Sprint 3 Sprint Health should be 'Critical', got {health}")

        if errors:
            errors.append("[X] Validation failed: {'; '.join(errors)}")
        else:
            checks_passed.append("[C] Validation succeeded")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Inventory Forecasting System Task
# =============================================================================
def _validate_agent_inventory_forecasting_system(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Prompt: Build a comprehensive inventory forecasting system. Query 'Inventory Tracker' to
    analyze all items. Create inline database 'Reorder Forecasting' in 'Product Specs' page with:
    'Item' (title), 'Current Stock' (number), 'Daily Usage Rate' (number), 'Days Until Reorder'
    (number), 'Suggested Reorder Qty' (number), 'Urgency' (select: 'Immediate' red, 'This Week'
    orange, 'Next Week' yellow, 'No Rush' green). Calculate Daily Usage Rate as 2 for Electronics,
    1 for Hardware, 3 for Office Supplies, 0.5 for others. Days Until Reorder = Current Stock /
    Daily Usage Rate. Add row for each item with Quantity < 50. Set Urgency: <3 days='Immediate',
    3-7='This Week', 8-14='Next Week', >14='No Rush'. Then create '📊 Inventory Forecast Dashboard'
    page under 'Feature Requests' with heading_1 '🔮 30-Day Inventory Forecast', callout
    (urgency-based color) showing '[X] items need reordering within 7 days'. Add heading_2
    '🚨 Critical Reorders' listing all 'Immediate' urgency items as numbered list: '1. [Item] -
    Stock exhausted in [Y] days'. Add heading_2 '📈 Category Analysis' with bullet points showing
    total items per category needing reorder within 30 days.

    Expected data from initial state:
    Items with Quantity < 50 (7 items):
    - MacBook Pro 14" (Electronics): Stock=12, Rate=2, Days=6 -> "This Week"
    - Dell Monitor 27" (Electronics): Stock=25, Rate=2, Days=12.5 -> "Next Week"
    - Office Chair Ergonomic (Furniture): Stock=8, Rate=0.5, Days=16 -> "No Rush"
    - Standing Desk (Furniture): Stock=3, Rate=0.5, Days=6 -> "This Week"
    - Wireless Keyboard (Hardware): Stock=0, Rate=1, Days=0 -> "Immediate"
    - USB-C Hub (Hardware): Stock=45, Rate=1, Days=45 -> "No Rush"
    - Adobe Creative Cloud License (Software): Stock=10, Rate=0.5, Days=20 -> "No Rush"

    Items needing reorder within 7 days: 3 (MacBook Pro, Standing Desk, Wireless Keyboard)
    Immediate urgency: 1 (Wireless Keyboard - 0 days)
    Categories in 30 days: Electronics(2), Furniture(2), Hardware(2), Software(1)
    """
    checks_passed: list[str] = []

    errors = []

    try:
        all_pages = final_state_backend.query({"collection": "pages", "filter": {}})
        all_blocks = final_state_backend.query({"collection": "blocks", "filter": {}})

        # =========================================================================
        # Part 1: Validate "Reorder Forecasting" Inline Database (40%)
        # =========================================================================

        # Step 1.1: Find the database
        reorder_db = None
        for page in all_pages:
            if page.get("type") == "database":
                title = page.get("title", "").lower()
                if "reorder forecasting" in title:
                    reorder_db = page
                    break

        if not reorder_db:
            errors.append("[X] Database 'Reorder Forecasting' not found")
        else:
            checks_passed.append("[C] Database 'Reorder Forecasting'")

        # Step 1.2: Check parent is Product Specs (page-10)
        parent_id = reorder_db.get("parentId")
        if parent_id != "page-10":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "product specs" not in parent_page.get("title", "").lower():
                errors.append(f"Database parent should be 'Product Specs' (page-10), got parentId: {parent_id}")

        # Step 1.3: Check database is inline (has a page parent, not workspace-level)
        # If parentId is set to a page ID (like page-10), it's inline
        # Accept isInline=true OR having a page parent
        is_inline = reorder_db.get("isInline", False)
        has_page_parent = parent_id and parent_id.startswith("page-")
        if not is_inline and not has_page_parent:
            errors.append("Database should be inline (isInline=true or have a page parent)")

        # Step 1.4: Validate database properties schema
        db_metadata = reorder_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        expected_properties = [
            ("Item", "title"),
            ("Current Stock", "number"),
            ("Daily Usage Rate", "number"),
            ("Days Until Reorder", "number"),
            ("Suggested Reorder Qty", "number"),
            ("Urgency", "select"),
        ]

        prop_name_map = {}
        for prop in properties:
            prop_name_map[prop.get("name", "").lower()] = prop

        for expected_name, expected_type in expected_properties:
            prop = prop_name_map.get(expected_name.lower())
            if not prop:
                errors.append(f"Property '{expected_name}' not found in database")
            elif prop.get("type") != expected_type:
                errors.append(f"Property '{expected_name}' should have type '{expected_type}', got '{prop.get('type')}'")

        # Step 1.5: Validate Urgency select options
        urgency_prop = prop_name_map.get("urgency")
        if urgency_prop:
            options = urgency_prop.get("options", [])
            expected_options = {
                "immediate": "red",
                "this week": "orange",
                "next week": "yellow",
                "no rush": "green",
            }

            found_options = {}
            for opt in options:
                opt_name = opt.get("name", "").lower()
                opt_color = opt.get("color", "").lower()
                found_options[opt_name] = opt_color

            for exp_name, exp_color in expected_options.items():
                if exp_name not in found_options:
                    errors.append(f"Urgency missing option '{exp_name}'")
                elif found_options[exp_name] != exp_color:
                    errors.append(f"Urgency option '{exp_name}' should have color '{exp_color}', got '{found_options[exp_name]}'")

        # Step 1.6: Validate database rows
        db_id = reorder_db.get("_id")
        rows = []
        for page in all_pages:
            if page.get("type") == "row" and page.get("databaseId") == db_id:
                rows.append(page)

        # Should have exactly 7 rows (items with Quantity < 50)
        if len(rows) != 7:
            errors.append(f"Expected 7 rows in Reorder Forecasting database, got {len(rows)}")

        # Check expected items are present
        expected_items = [
            "macbook pro",
            "dell monitor",
            "office chair",
            "standing desk",
            "wireless keyboard",
            "usb-c hub",
            "adobe creative cloud",
        ]

        row_titles = [row.get("title", "").lower() for row in rows]
        for expected_item in expected_items:
            found = any(expected_item in title for title in row_titles)
            if not found:
                errors.append(f"Expected item '{expected_item}' not found in database rows")

        # Step 1.7: Validate specific row calculations
        # Build property ID map
        prop_id_map = {}
        for prop in properties:
            prop_id_map[prop.get("name", "").lower()] = prop.get("_id")

        def get_cell_value(row, prop_name_lower):
            cell_values = row.get("cellValues", {})
            prop_id = prop_id_map.get(prop_name_lower)
            if prop_id and prop_id in cell_values:
                return cell_values[prop_id]
            # Try matching by property name in cell values keys
            for key, val in cell_values.items():
                if prop_name_lower.replace(" ", "") in key.lower().replace(" ", ""):
                    return val
            return None

        # Find and validate Wireless Keyboard row (Immediate: Stock=0, Rate=1, Days=0)
        wireless_keyboard_row = None
        for row in rows:
            if "wireless keyboard" in row.get("title", "").lower():
                wireless_keyboard_row = row
                break

        if wireless_keyboard_row:
            stock = get_cell_value(wireless_keyboard_row, "current stock")
            if stock is not None and int(stock) != 0:
                errors.append(f"Wireless Keyboard Current Stock should be 0, got {stock}")

            rate = get_cell_value(wireless_keyboard_row, "daily usage rate")
            if rate is not None and float(rate) != 1:
                errors.append(f"Wireless Keyboard Daily Usage Rate should be 1, got {rate}")

            days = get_cell_value(wireless_keyboard_row, "days until reorder")
            if days is not None and float(days) != 0:
                errors.append(f"Wireless Keyboard Days Until Reorder should be 0, got {days}")

            urgency = get_cell_value(wireless_keyboard_row, "urgency")
            if urgency and "immediate" not in str(urgency).lower():
                errors.append(f"Wireless Keyboard Urgency should be 'Immediate', got {urgency}")

        # Find and validate MacBook Pro row (This Week: Stock=12, Rate=2, Days=6)
        macbook_row = None
        for row in rows:
            if "macbook" in row.get("title", "").lower():
                macbook_row = row
                break

        if macbook_row:
            stock = get_cell_value(macbook_row, "current stock")
            if stock is not None and int(stock) != 12:
                errors.append(f"MacBook Pro Current Stock should be 12, got {stock}")

            rate = get_cell_value(macbook_row, "daily usage rate")
            if rate is not None and float(rate) != 2:
                errors.append(f"MacBook Pro Daily Usage Rate should be 2, got {rate}")

            days = get_cell_value(macbook_row, "days until reorder")
            if days is not None and float(days) != 6:
                errors.append(f"MacBook Pro Days Until Reorder should be 6, got {days}")

            urgency = get_cell_value(macbook_row, "urgency")
            if urgency and "this week" not in str(urgency).lower():
                errors.append(f"MacBook Pro Urgency should be 'This Week', got {urgency}")

        # =========================================================================
        # Part 2: Validate "📊 Inventory Forecast Dashboard" Page (60%)
        # =========================================================================

        # Step 2.1: Find the dashboard page
        dashboard_page = None
        for page in all_pages:
            page_type = page.get("type", "")
            # Accept pages that are type "page", "normal", or don't have a type (not database or row)
            if page_type not in ("database", "row"):
                title = page.get("title", "")
                if "inventory forecast dashboard" in title.lower():
                    dashboard_page = page
                    break

        if not dashboard_page:
            errors.append("[X] Page '📊 Inventory Forecast Dashboard' not found")
        else:
            checks_passed.append("[C] Page '📊 Inventory Forecast Dashboard'")

        # Step 2.2: Check parent is Feature Requests (page-15)
        parent_id = dashboard_page.get("parentId")
        if parent_id != "page-15":
            parent_page = None
            for page in all_pages:
                if page.get("_id") == parent_id:
                    parent_page = page
                    break
            if not parent_page or "feature requests" not in parent_page.get("title", "").lower():
                errors.append(f"Dashboard parent should be 'Feature Requests' (page-15), got parentId: {parent_id}")

        # Get all blocks for this page
        page_id = dashboard_page.get("_id")
        page_blocks = [b for b in all_blocks if b.get("pageId") == page_id]

        # Step 2.3: Validate heading_1 "🔮 30-Day Inventory Forecast"
        heading1_found = False
        for block in page_blocks:
            if block.get("type") == "heading1":
                content = block.get("content", "").lower()
                if "30-day" in content and "forecast" in content:
                    heading1_found = True
                    break

        if not heading1_found:
            errors.append("Missing heading_1 '🔮 30-Day Inventory Forecast'")

        # Step 2.4: Validate callout block with items count
        callout_found = False
        for block in page_blocks:
            if block.get("type") == "callout":
                content = block.get("content", "").lower()
                # Should mention "3 items" needing reorder within 7 days
                if "3" in content and ("item" in content or "reorder" in content) and "7 day" in content:
                    callout_found = True
                    break
                # Also accept variations
                if re.search(r"3\s*(items?)?.*reorder.*7", content) or re.search(r"3.*within\s*7", content):
                    callout_found = True
                    break

        if not callout_found:
            errors.append("Missing callout showing '3 items need reordering within 7 days'")

        # Step 2.5: Validate heading_2 "🚨 Critical Reorders"
        critical_heading_found = False
        for block in page_blocks:
            if block.get("type") == "heading2":
                content = block.get("content", "").lower()
                if "critical" in content and "reorder" in content:
                    critical_heading_found = True
                    break

        if not critical_heading_found:
            errors.append("Missing heading_2 '🚨 Critical Reorders'")

        # Step 2.6: Validate numbered list for Immediate urgency items
        # Should list Wireless Keyboard with 0 days
        numbered_list_found = False
        for block in page_blocks:
            if block.get("type") == "numberedList":
                content = block.get("content", "").lower()
                if "wireless keyboard" in content and ("0 day" in content or "exhausted" in content or "stock" in content):
                    numbered_list_found = True
                    break

        if not numbered_list_found:
            errors.append("Missing numbered list item for 'Wireless Keyboard' with stock exhausted in 0 days")

        # Step 2.7: Validate heading_2 "📈 Category Analysis"
        category_heading_found = False
        for block in page_blocks:
            if block.get("type") == "heading2":
                content = block.get("content", "").lower()
                if "category" in content and "analysis" in content:
                    category_heading_found = True
                    break

        if not category_heading_found:
            errors.append("Missing heading_2 '📈 Category Analysis'")

        # Step 2.8: Validate bullet points for category breakdown
        # Should mention Electronics, Furniture, Hardware, Software with counts
        category_bullets_found = 0
        categories_to_find = ["electronics", "furniture", "hardware", "software"]

        for block in page_blocks:
            if block.get("type") == "bulletedList":
                content = block.get("content", "").lower()
                for cat in categories_to_find:
                    if cat in content:
                        category_bullets_found += 1

        if category_bullets_found < 2:
            errors.append("Missing bullet points showing category analysis (expected at least 2 categories mentioned)")

        # =========================================================================
        # Final Result
        # =========================================================================

        if errors:
            errors.append("[X] Validation failed: {'; '.join(errors)}")
        else:
            checks_passed.append("[C] Validation succeeded")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    # Validation Functions
    except Exception as e:
        errors.append(f"[X] Runtime error during validation: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )
# ==============================================================================
def _validate_getting_started_task_tracker_feature_sync(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Getting Started Task Tracker Feature Sync task.

    Success criteria:
    1. Three new todos appear exactly between 'Create your first page' and
       'Try different block types' with exact text and emoji (unchecked)
    2. New todos from Task Tracker (Assignee='user-1' AND Tags contains 'Feature')
       appear after Key Features bullets with format '[Task Name] - Status: [Status]'
       and checked status matching Done status
    3. Task Tracker database has new 'Feature Flag' checkbox property with true
       values for tasks added to Getting Started
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # =========================================================================
        # PART 1: Validate Quick Start Checklist todos
        # =========================================================================

        # Find Getting Started page
        getting_started = _find_page_by_title(final_state_backend, "Getting Started")
        if not getting_started:
            errors.append("[X] Getting Started page not found")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )
        checks_passed.append("[C] Getting Started page found")

        page_id = getting_started.get("_id")
        all_blocks = _get_blocks_for_page(final_state_backend, page_id)

        if not all_blocks:
            errors.append("[X] No blocks found on Getting Started page")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        # Sort blocks by order
        blocks_sorted = sorted(all_blocks, key=lambda b: b.get("order", 0))

        # Find the anchor todos and their orders
        create_first_page_order = None
        try_different_blocks_order = None
        key_features_heading_order = None
        last_key_features_bullet_order = None

        for block in blocks_sorted:
            content = str(block.get("content", "") or "")
            block_type = block.get("type", "")
            order = block.get("order", 0)

            if block_type == "todo" and "Create your first page" in content:
                create_first_page_order = order
            elif block_type == "todo" and "Try different block types" in content:
                try_different_blocks_order = order
            elif block_type in ["heading2", "heading_2"] and "Key Features" in content:
                key_features_heading_order = order

        # Find the last bulleted list item after Key Features heading
        if key_features_heading_order is not None:
            for block in blocks_sorted:
                block_type = block.get("type", "")
                order = block.get("order", 0)
                if block_type in ["bulletedList", "bulleted_list"] and order > key_features_heading_order:
                    last_key_features_bullet_order = order

        # Expected new todos in Quick Start Checklist
        expected_checklist_todos = [
            "📚 Review team documentation",
            "🔐 Enable two-factor authentication",
            "📧 Subscribe to team newsletters"
        ]

        found_checklist_todos = []
        checklist_todo_blocks = []

        for block in blocks_sorted:
            if block.get("type") == "todo":
                content = str(block.get("content", "") or "")
                for expected_todo in expected_checklist_todos:
                    # Check if the expected text is in the content
                    if expected_todo in content:
                        found_checklist_todos.append(expected_todo)
                        checklist_todo_blocks.append(block)
                        break

        # Check all 3 todos are found
        for expected_todo in expected_checklist_todos:
            if expected_todo in found_checklist_todos:
                checks_passed.append(f"[C] New todo '{expected_todo}' found")
            else:
                errors.append(f"[X] New todo '{expected_todo}' not found")

        # Check todos are unchecked
        for block in checklist_todo_blocks:
            content = str(block.get("content", "") or "")
            checked = block.get("properties", {}).get("checked", False)
            if checked:
                errors.append(f"[X] Todo should be unchecked: '{content[:40]}...'")
            else:
                checks_passed.append(f"[C] Todo is unchecked as expected")

        # Check todo ordering (between Create your first page and Try different block types)
        if create_first_page_order is not None and try_different_blocks_order is not None:
            for block in checklist_todo_blocks:
                order = block.get("order", 0)
                content = str(block.get("content", "") or "")[:30]
                if create_first_page_order < order < try_different_blocks_order:
                    checks_passed.append(f"[C] Todo '{content}...' is correctly positioned between anchor todos")
                else:
                    errors.append(f"[X] Todo '{content}...' is not positioned between 'Create your first page' (order {create_first_page_order}) and 'Try different block types' (order {try_different_blocks_order}). Current order: {order}")
        else:
            errors.append("[X] Could not find anchor todos to verify positioning")

        # =========================================================================
        # PART 2: Validate Task Tracker todos in Key Features section
        # =========================================================================

        # Find Task Tracker database
        task_tracker = _find_database_by_title(final_state_backend, "Task Tracker")
        if not task_tracker:
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database found")

            task_tracker_id = task_tracker.get("_id")

            # Get all rows from Task Tracker
            rows = _get_database_rows(final_state_backend, task_tracker_id)

            # Filter rows where Assignee='user-1' AND Tags contains 'Feature'
            matching_tasks = []
            for row in rows:
                cell_values = row.get("cellValues", {})
                assignee = cell_values.get("db-tasks-prop-assignee", "")
                tags = cell_values.get("db-tasks-prop-tags", [])

                if assignee == "user-1" and "Feature" in tags:
                    task_name = row.get("title", "") or cell_values.get("db-tasks-prop-title", "")
                    status = cell_values.get("db-tasks-prop-status", "")
                    matching_tasks.append({
                        "row_id": row.get("_id"),
                        "task_name": task_name,
                        "status": status,
                        "should_be_checked": status == "Done"
                    })

            if not matching_tasks:
                errors.append("[X] No tasks found matching Assignee='user-1' AND Tags contains 'Feature'")
            else:
                checks_passed.append(f"[C] Found {len(matching_tasks)} task(s) matching filter criteria")

            # Check for corresponding todos in Key Features section
            for task in matching_tasks:
                expected_todo_text = f"{task['task_name']} - Status: {task['status']}"
                found_todo = False

                for block in blocks_sorted:
                    if block.get("type") == "todo":
                        content = str(block.get("content", "") or "")
                        # Check if this todo matches the expected format
                        if task['task_name'] in content and f"Status: {task['status']}" in content:
                            found_todo = True
                            order = block.get("order", 0)
                            checked = block.get("properties", {}).get("checked", False)

                            # Verify checked status
                            if checked == task['should_be_checked']:
                                checks_passed.append(f"[C] Todo '{task['task_name']}' has correct checked status ({checked})")
                            else:
                                errors.append(f"[X] Todo '{task['task_name']}' should be checked={task['should_be_checked']} but is checked={checked}")

                            # Verify position (after last Key Features bullet)
                            if last_key_features_bullet_order is not None:
                                if order > last_key_features_bullet_order:
                                    checks_passed.append(f"[C] Todo '{task['task_name']}' is positioned after Key Features bullets")
                                else:
                                    errors.append(f"[X] Todo '{task['task_name']}' should be after Key Features bullets (order {last_key_features_bullet_order}) but has order {order}")
                            break

                if not found_todo:
                    errors.append(f"[X] Todo for task '{task['task_name']}' with format '[Task Name] - Status: [Status]' not found in Key Features section")
                else:
                    checks_passed.append(f"[C] Todo for task '{task['task_name']}' found with correct format")

            # =========================================================================
            # PART 3: Validate Feature Flag property in Task Tracker
            # =========================================================================

            # Check for Feature Flag property in database metadata
            db_metadata = task_tracker.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])

            feature_flag_property = None
            for prop in properties:
                if prop.get("name") == "Feature Flag":
                    feature_flag_property = prop
                    break

            if not feature_flag_property:
                errors.append("[X] 'Feature Flag' property not found in Task Tracker database")
            else:
                checks_passed.append("[C] 'Feature Flag' property found in Task Tracker database")

                # Verify it's a checkbox type
                if feature_flag_property.get("type") == "checkbox":
                    checks_passed.append("[C] 'Feature Flag' property is checkbox type")
                else:
                    errors.append(f"[X] 'Feature Flag' property should be checkbox type but is {feature_flag_property.get('type')}")

                # Verify Feature Flag is true for matching tasks
                feature_flag_prop_id = feature_flag_property.get("_id", "")

                for task in matching_tasks:
                    # Find the row again to check Feature Flag value
                    for row in rows:
                        if row.get("_id") == task["row_id"]:
                            cell_values = row.get("cellValues", {})
                            # Try both property ID and property name as keys
                            feature_flag_value = cell_values.get(feature_flag_prop_id) or cell_values.get("Feature Flag")

                            if feature_flag_value is True:
                                checks_passed.append(f"[C] Feature Flag is true for task '{task['task_name']}'")
                            else:
                                errors.append(f"[X] Feature Flag should be true for task '{task['task_name']}' but is {feature_flag_value}")
                            break

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_vip_client_projects_report(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the VIP Client Projects Report task.

    Success criteria:
    1. Report page "VIP Client Projects Report" exists under "Team Directory"
    2. Page has heading_1 "👑 VIP Client Projects Overview"
    3. All VIP clients (Type='Client' AND Tags contains 'VIP') are included
    4. Each client has heading_2 "[Name] - [Company]"
    5. Each client section has either project bullets or orange callout
    6. Page ends with divider, heading_3 "📊 Summary Statistics", and summary paragraph
    7. Summary statistics are accurate
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # =========================================================================
        # PART 1: Find and validate report page
        # =========================================================================

        # Find Team Directory page
        team_directory = _find_page_by_title(final_state_backend, "Team Directory")
        if not team_directory:
            errors.append("[X] Team Directory page not found")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )
        team_directory_id = team_directory.get("_id")
        checks_passed.append("[C] Team Directory page found")

        # Find VIP Client Projects Report page
        report_page = _find_page_by_title(final_state_backend, "VIP Client Projects Report")
        if not report_page:
            errors.append("[X] 'VIP Client Projects Report' page not found")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )
        checks_passed.append("[C] 'VIP Client Projects Report' page found")

        # Verify report page is under Team Directory
        if report_page.get("parentId") == team_directory_id:
            checks_passed.append("[C] Report page is under Team Directory")
        else:
            errors.append(f"[X] Report page should be under Team Directory but has parentId={report_page.get('parentId')}")

        # Get blocks for report page
        report_page_id = report_page.get("_id")
        blocks = _get_blocks_for_page(final_state_backend, report_page_id)
        if not blocks:
            errors.append("[X] No blocks found on report page")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        blocks_sorted = sorted(blocks, key=lambda b: b.get("order", 0))

        # =========================================================================
        # PART 2: Validate page structure - heading_1
        # =========================================================================

        # Check for heading_1 "👑 VIP Client Projects Overview"
        found_main_heading = False
        for block in blocks_sorted:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")
            if block_type in ["heading1", "heading_1"] and "VIP Client Projects Overview" in content:
                found_main_heading = True
                if "👑" in content:
                    checks_passed.append("[C] Found heading_1 '👑 VIP Client Projects Overview'")
                else:
                    checks_passed.append("[C] Found heading_1 'VIP Client Projects Overview' (emoji optional)")
                break

        if not found_main_heading:
            errors.append("[X] heading_1 '👑 VIP Client Projects Overview' not found")

        # =========================================================================
        # PART 3: Get VIP clients from Contacts Directory
        # =========================================================================

        contacts_db = _find_database_by_title(final_state_backend, "Contacts Directory")
        if not contacts_db:
            errors.append("[X] Contacts Directory database not found")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        contacts_rows = _get_database_rows(final_state_backend, contacts_db.get("_id"))

        # Filter for VIP clients (Type='Client' AND Tags contains 'VIP')
        vip_clients = []
        for row in contacts_rows:
            cell_values = row.get("cellValues", {})
            contact_type = cell_values.get("db-contacts-prop-type", "")
            tags = cell_values.get("db-contacts-prop-tags", [])

            if contact_type == "Client" and "VIP" in tags:
                name = row.get("title", "") or cell_values.get("db-contacts-prop-name", "")
                company = cell_values.get("db-contacts-prop-company", "")
                vip_clients.append({
                    "name": name,
                    "company": company
                })

        if not vip_clients:
            errors.append("[X] No VIP clients found in Contacts Directory (Type='Client' AND Tags contains 'VIP')")
        else:
            checks_passed.append(f"[C] Found {len(vip_clients)} VIP client(s) in Contacts Directory")

        # =========================================================================
        # PART 4: Get Project Portfolio data for matching
        # =========================================================================

        projects_db = _find_database_by_title(final_state_backend, "Project Portfolio")
        projects_rows = []
        if projects_db:
            projects_rows = _get_database_rows(final_state_backend, projects_db.get("_id"))

        # Get user emails for project lead matching
        users_result = final_state_backend.query({"collection": "users", "filter": {}})
        user_emails = {}
        if isinstance(users_result, list):
            for user in users_result:
                user_emails[user.get("_id")] = user.get("email", "").lower()

        # =========================================================================
        # PART 5: Validate each VIP client section
        # =========================================================================

        clients_with_projects = 0
        total_project_value = 0

        for client in vip_clients:
            client_name = client["name"]
            client_company = client["company"]
            expected_heading = f"{client_name} - {client_company}"

            # Check for heading_2 with client name and company
            found_client_heading = False
            client_heading_order = None

            for block in blocks_sorted:
                block_type = block.get("type", "")
                content = str(block.get("content", "") or "")
                if block_type in ["heading2", "heading_2"]:
                    if client_name in content and client_company in content:
                        found_client_heading = True
                        client_heading_order = block.get("order", 0)
                        checks_passed.append(f"[C] Found heading_2 for client '{client_name} - {client_company}'")
                        break

            if not found_client_heading:
                errors.append(f"[X] heading_2 '{expected_heading}' not found for VIP client")
                continue

            # Find matching projects (Project Lead email contains part of client company name)
            matching_projects = []
            company_lower = client_company.lower().replace(" ", "")

            for project in projects_rows:
                cell_values = project.get("cellValues", {})
                lead_id = cell_values.get("db-projects-prop-lead", "")
                lead_email = user_emails.get(lead_id, "")

                # Check if project lead email contains part of company name
                if company_lower in lead_email.replace(".", "").replace("@", ""):
                    project_name = project.get("title", "") or cell_values.get("db-projects-prop-name", "")
                    status = cell_values.get("db-projects-prop-status", "")
                    budget = cell_values.get("db-projects-prop-budget", 0)
                    matching_projects.append({
                        "name": project_name,
                        "status": status,
                        "budget": budget
                    })
                    total_project_value += budget

            if matching_projects:
                clients_with_projects += 1
                # Check for bulleted list items with project info
                for project in matching_projects:
                    found_project_bullet = False
                    for block in blocks_sorted:
                        if block.get("order", 0) <= client_heading_order:
                            continue
                        block_type = block.get("type", "")
                        content = str(block.get("content", "") or "")
                        if block_type in ["bulletedList", "bulleted_list"]:
                            if project["name"] in content and "Status:" in content:
                                found_project_bullet = True
                                checks_passed.append(f"[C] Found project bullet for '{project['name']}'")
                                break
                    if not found_project_bullet:
                        errors.append(f"[X] Project bullet for '{project['name']}' not found under client '{client_name}'")
            else:
                # Check for orange callout
                found_orange_callout = False
                for block in blocks_sorted:
                    if block.get("order", 0) <= client_heading_order:
                        continue
                    block_type = block.get("type", "")
                    content = str(block.get("content", "") or "")
                    properties = block.get("properties", {})
                    bg_color = properties.get("backgroundColor", "")

                    if block_type == "callout" and "orange" in bg_color.lower():
                        if "no active projects" in content.lower() or "no projects found" in content.lower():
                            found_orange_callout = True
                            checks_passed.append(f"[C] Found orange callout for client '{client_name}' with no projects")
                            break

                if not found_orange_callout:
                    errors.append(f"[X] Orange callout 'No active projects found for this VIP client' not found for '{client_name}'")

        # =========================================================================
        # PART 6: Validate summary section
        # =========================================================================

        # Check for divider
        found_divider = False
        divider_order = None
        for block in blocks_sorted:
            if block.get("type") == "divider":
                found_divider = True
                divider_order = block.get("order", 0)

        if found_divider:
            checks_passed.append("[C] Found divider before summary")
        else:
            errors.append("[X] Divider not found before summary section")

        # Check for heading_3 "📊 Summary Statistics"
        found_summary_heading = False
        for block in blocks_sorted:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")
            if block_type in ["heading3", "heading_3"] and "Summary Statistics" in content:
                found_summary_heading = True
                if "📊" in content:
                    checks_passed.append("[C] Found heading_3 '📊 Summary Statistics'")
                else:
                    checks_passed.append("[C] Found heading_3 'Summary Statistics' (emoji optional)")
                break

        if not found_summary_heading:
            errors.append("[X] heading_3 '📊 Summary Statistics' not found")

        # Check for summary paragraph with correct statistics
        expected_vip_count = len(vip_clients)
        found_summary_paragraph = False

        for block in blocks_sorted:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")
            if block_type == "paragraph" and "Total VIP Clients:" in content:
                found_summary_paragraph = True

                # Check VIP client count
                if f"Total VIP Clients: {expected_vip_count}" in content:
                    checks_passed.append(f"[C] Summary has correct VIP client count ({expected_vip_count})")
                else:
                    errors.append(f"[X] Summary should show 'Total VIP Clients: {expected_vip_count}'")

                # Check clients with active projects count
                if f"Clients with Active Projects: {clients_with_projects}" in content:
                    checks_passed.append(f"[C] Summary has correct active projects count ({clients_with_projects})")
                else:
                    errors.append(f"[X] Summary should show 'Clients with Active Projects: {clients_with_projects}'")

                # Check total project value (be flexible with formatting)
                if f"${total_project_value}" in content or f"$ {total_project_value}" in content:
                    checks_passed.append(f"[C] Summary has correct total project value (${total_project_value})")
                elif str(total_project_value) in content:
                    checks_passed.append(f"[C] Summary has total project value {total_project_value}")
                else:
                    errors.append(f"[X] Summary should include total project value ${total_project_value}")
                break

        if not found_summary_paragraph:
            errors.append("[X] Summary paragraph with 'Total VIP Clients:' not found")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append(f"[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Contact Engagement Dashboard 2
# =============================================================================
def _validate_contact_engagement_dashboard_2(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Contact Engagement Dashboard task.

    Requirements:
    1. Contacts Directory database has new properties: Last Engagement (date), Engagement Score (number)
    2. Contacts with 'Follow Up' tag have Engagement Score = 75 and Last Engagement set
    3. Active contacts without 'Follow Up' have Engagement Score = 50 and Last Engagement set
    4. 'Engagement Analytics' page exists under Team Directory with correct structure
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # =========================================================================
        # PART 1: Validate Database Schema Changes
        # =========================================================================

        # Find Contacts Directory database
        contacts_db = _find_database_by_title(final_state_backend, "Contacts Directory")

        if not contacts_db:
            errors.append("[X] Contacts Directory database not found")
            errors.append("[X] Contacts Directory database not found")
        else:
            checks_passed.append("[C] Contacts Directory database")

        checks_passed.append("[C] Contacts Directory database found")

        db_metadata = contacts_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Check for Last Engagement property (date type)
        last_engagement_prop = None
        engagement_score_prop = None

        for prop in properties:
            prop_name = prop.get("name", "").lower()
            if "last engagement" in prop_name or "lastengagement" in prop_name:
                last_engagement_prop = prop
            if "engagement score" in prop_name or "engagementscore" in prop_name:
                engagement_score_prop = prop

        if not last_engagement_prop:
            errors.append("[X] 'Last Engagement' property not found in Contacts Directory database")
        elif last_engagement_prop.get("type") != "date":
            errors.append(f"[X] 'Last Engagement' property should be date type, found {last_engagement_prop.get('type')}")
        else:
            checks_passed.append("[C] 'Last Engagement' property (date) found in database schema")

        if not engagement_score_prop:
            errors.append("[X] 'Engagement Score' property not found in Contacts Directory database")
        elif engagement_score_prop.get("type") != "number":
            errors.append(f"[X] 'Engagement Score' property should be number type, found {engagement_score_prop.get('type')}")
        else:
            checks_passed.append("[C] 'Engagement Score' property (number) found in database schema")

        # =========================================================================
        # PART 2: Validate Contact Data Updates
        # =========================================================================

        # Get all contact rows
        contact_rows = _get_database_rows(final_state_backend, contacts_db.get("_id"))

        follow_up_contacts = []
        active_no_follow_up_contacts = []

        for contact in contact_rows:
            cell_values = contact.get("cellValues", {})
            tags = cell_values.get("db-contacts-prop-tags", [])
            name = cell_values.get("db-contacts-prop-name", contact.get("title", "Unknown"))

            if "Follow Up" in tags:
                follow_up_contacts.append((name, contact, cell_values))
            elif "Active" in tags:
                active_no_follow_up_contacts.append((name, contact, cell_values))

        # Check Follow Up contacts have Engagement Score = 75
        follow_up_score_correct = 0
        follow_up_date_set = 0

        for name, contact, cell_values in follow_up_contacts:
            # Check engagement score
            score = None
            for key, value in cell_values.items():
                if "engagement" in key.lower() and "score" in key.lower():
                    score = value
                    break

            if score == 75:
                follow_up_score_correct += 1

            # Check last engagement date is set
            date_set = False
            for key, value in cell_values.items():
                if "engagement" in key.lower() and "last" in key.lower():
                    if value is not None and value != "":
                        date_set = True
                    break

            if date_set:
                follow_up_date_set += 1

        if len(follow_up_contacts) > 0:
            if follow_up_score_correct == len(follow_up_contacts):
                checks_passed.append(f"[C] All {len(follow_up_contacts)} 'Follow Up' contacts have Engagement Score = 75")
            else:
                errors.append(f"[X] Only {follow_up_score_correct}/{len(follow_up_contacts)} 'Follow Up' contacts have Engagement Score = 75")

            if follow_up_date_set == len(follow_up_contacts):
                checks_passed.append(f"[C] All {len(follow_up_contacts)} 'Follow Up' contacts have Last Engagement date set")
            else:
                errors.append(f"[X] Only {follow_up_date_set}/{len(follow_up_contacts)} 'Follow Up' contacts have Last Engagement date set")

        # Check Active (no Follow Up) contacts have Engagement Score = 50
        active_score_correct = 0
        active_date_set = 0

        for name, contact, cell_values in active_no_follow_up_contacts:
            # Check engagement score
            score = None
            for key, value in cell_values.items():
                if "engagement" in key.lower() and "score" in key.lower():
                    score = value
                    break

            if score == 50:
                active_score_correct += 1

            # Check last engagement date is set
            date_set = False
            for key, value in cell_values.items():
                if "engagement" in key.lower() and "last" in key.lower():
                    if value is not None and value != "":
                        date_set = True
                    break

            if date_set:
                active_date_set += 1

        if len(active_no_follow_up_contacts) > 0:
            if active_score_correct == len(active_no_follow_up_contacts):
                checks_passed.append(f"[C] All {len(active_no_follow_up_contacts)} 'Active' (no Follow Up) contacts have Engagement Score = 50")
            else:
                errors.append(f"[X] Only {active_score_correct}/{len(active_no_follow_up_contacts)} 'Active' (no Follow Up) contacts have Engagement Score = 50")

            if active_date_set == len(active_no_follow_up_contacts):
                checks_passed.append(f"[C] All {len(active_no_follow_up_contacts)} 'Active' (no Follow Up) contacts have Last Engagement date set")
            else:
                errors.append(f"[X] Only {active_date_set}/{len(active_no_follow_up_contacts)} 'Active' (no Follow Up) contacts have Last Engagement date set")

        # =========================================================================
        # PART 3: Validate Engagement Analytics Page
        # =========================================================================

        # Find Engagement Analytics page under Team Directory (page-8)
        analytics_page_results = final_state_backend.query({
            "collection": "pages",
            "filter": {"parentId": "page-8"}
        }) or []

        analytics_page = None
        for page in analytics_page_results:
            title = page.get("title", "").lower()
            if "engagement analytics" in title or ("engagement" in title and "analytics" in title):
                analytics_page = page
                break

        if not analytics_page:
            # Also search all pages
            all_pages = final_state_backend.query({
                "collection": "pages",
                "filter": {}
            }) or []

            for page in all_pages:
                title = page.get("title", "").lower()
                if "engagement analytics" in title:
                    analytics_page = page
                    break

        if not analytics_page:
            errors.append("[X] 'Engagement Analytics' page not found")
            errors.append("[X] 'Engagement Analytics' page not found")
        else:
            checks_passed.append("[C] 'Engagement Analytics' page")

        checks_passed.append("[C] 'Engagement Analytics' page found")

        # Check page is under Team Directory
        if analytics_page.get("parentId") == "page-8":
            checks_passed.append("[C] 'Engagement Analytics' page is under Team Directory")
        else:
            errors.append(f"[X] 'Engagement Analytics' page should be under Team Directory (page-8), but has parentId={analytics_page.get('parentId')}")

        # Get blocks for the analytics page
        analytics_blocks = _get_blocks_for_page(final_state_backend, analytics_page.get("_id"))

        if not analytics_blocks:
            errors.append("[X] No blocks found on Engagement Analytics page")
        else:
            sorted_blocks = sorted(analytics_blocks, key=lambda b: b.get("order", 0))

            # Check for heading_1 with "Contact Engagement Analytics"
            found_main_heading = False
            for block in sorted_blocks:
                if block.get("type") in ["heading1", "heading_1"]:
                    content = str(block.get("content", "")).lower()
                    if "contact engagement analytics" in content or ("engagement" in content and "analytics" in content):
                        found_main_heading = True
                        break

            if found_main_heading:
                checks_passed.append("[C] Found heading_1 '📊 Contact Engagement Analytics'")
            else:
                errors.append("[X] heading_1 'Contact Engagement Analytics' not found")

            # Check for orange callout with follow-up count
            found_orange_callout = False
            expected_follow_up_count = len(follow_up_contacts)

            for block in sorted_blocks:
                if block.get("type") == "callout":
                    properties = block.get("properties", {})
                    bg_color = properties.get("backgroundColor", "")
                    content = str(block.get("content", ""))

                    if "orange" in bg_color.lower():
                        if "follow-up" in content.lower() or "follow up" in content.lower():
                            # Check if count is mentioned
                            if str(expected_follow_up_count) in content:
                                found_orange_callout = True
                            elif any(char.isdigit() for char in content):
                                # Has some count
                                found_orange_callout = True
                            break

            if found_orange_callout:
                checks_passed.append(f"[C] Found orange callout with follow-up count ({expected_follow_up_count})")
            else:
                errors.append(f"[X] Orange callout with follow-up count not found (expected count: {expected_follow_up_count})")

            # Check for heading_2 with "Engagement Metrics"
            found_metrics_heading = False
            for block in sorted_blocks:
                if block.get("type") in ["heading2", "heading_2"]:
                    content = str(block.get("content", "")).lower()
                    if "engagement metrics" in content or "metrics" in content:
                        found_metrics_heading = True
                        break

            if found_metrics_heading:
                checks_passed.append("[C] Found heading_2 '📈 Engagement Metrics by Contact Type'")
            else:
                errors.append("[X] heading_2 'Engagement Metrics by Contact Type' not found")

            # Check for numbered lists with contact names and scores
            numbered_list_blocks = [b for b in sorted_blocks if b.get("type") in ["numberedList", "numbered_list"]]

            if len(numbered_list_blocks) >= 4:
                checks_passed.append(f"[C] Found {len(numbered_list_blocks)} numbered list items for contact types")
            else:
                errors.append(f"[X] Expected at least 4 numbered list items for contact types, found {len(numbered_list_blocks)}")

        # Final score calculation
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Bug Tracker Statistics Dashboard
# =============================================================================
def _validate_bug_tracker_statistics_dashboard(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Bug Tracker Statistics Dashboard task.

    Requirements:
    1. Bug Statistics Dashboard section added after Low Priority heading
    2. Yellow callout with summary
    3. Paragraph with bug statistics (Total: 5, Fixed: 2, Outstanding: 3)
    4. Related Bug Tasks section with numbered list from Task Tracker
    5. Inline database "Bug Resolution Timeline" with correct schema and 3 rows
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # =========================================================================
        # PART 1: Validate Bug Tracker page exists
        # =========================================================================

        bug_tracker_results = final_state_backend.query({
            "collection": "pages",
            "filter": {"_id": "page-11"}
        })

        if not bug_tracker_results:
            errors.append("[X] Bug Tracker page (page-11) not found")
            errors.append("[X] Bug Tracker page not found")
        else:
            checks_passed.append("[C] Bug Tracker page")

        checks_passed.append("[C] Bug Tracker page found")

        # Get all blocks for Bug Tracker page
        blocks_results = final_state_backend.query({
            "collection": "blocks",
            "filter": {"pageId": "page-11"}
        }) or []

        sorted_blocks = sorted(blocks_results, key=lambda b: b.get("order", 0))

        # =========================================================================
        # PART 2: Validate Bug Statistics Dashboard heading_2
        # =========================================================================

        found_dashboard_heading = False
        for block in sorted_blocks:
            if block.get("type") in ["heading2", "heading_2"]:
                content = str(block.get("content", ""))
                if "Bug Statistics Dashboard" in content:
                    found_dashboard_heading = True
                    break

        if found_dashboard_heading:
            checks_passed.append("[C] Found heading_2 'Bug Statistics Dashboard'")
        else:
            errors.append("[X] heading_2 'Bug Statistics Dashboard' not found")

        # =========================================================================
        # PART 3: Validate yellow callout with summary
        # =========================================================================

        found_yellow_callout = False
        for block in sorted_blocks:
            if block.get("type") == "callout":
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", "")
                content = str(block.get("content", ""))

                if "yellow" in bg_color.lower() and "Bug Tracking Summary" in content:
                    found_yellow_callout = True
                    break

        if found_yellow_callout:
            checks_passed.append("[C] Found yellow callout with 'Bug Tracking Summary'")
        else:
            errors.append("[X] Yellow callout with 'Bug Tracking Summary' not found")

        # =========================================================================
        # PART 4: Validate statistics paragraph
        # =========================================================================

        found_stats_paragraph = False
        for block in sorted_blocks:
            if block.get("type") == "paragraph":
                content = str(block.get("content", ""))
                # Check for key statistics (Total: 5, Fixed: 2 (40%), Outstanding: 3)
                if "Total" in content and "Fixed" in content and "Outstanding" in content:
                    if "5" in content and "2" in content and "3" in content:
                        found_stats_paragraph = True
                        break

        if found_stats_paragraph:
            checks_passed.append("[C] Found statistics paragraph with correct counts")
        else:
            errors.append("[X] Statistics paragraph with 'Total Bugs: 5 | Fixed: 2 | Outstanding: 3' not found")

        # =========================================================================
        # PART 5: Validate Related Bug Tasks heading_3
        # =========================================================================

        found_related_heading = False
        for block in sorted_blocks:
            if block.get("type") in ["heading3", "heading_3"]:
                content = str(block.get("content", ""))
                if "Related Bug Tasks" in content or "Bug Tasks" in content:
                    found_related_heading = True
                    break

        if found_related_heading:
            checks_passed.append("[C] Found heading_3 'Related Bug Tasks in Task Tracker'")
        else:
            errors.append("[X] heading_3 'Related Bug Tasks in Task Tracker' not found")

        # =========================================================================
        # PART 6: Validate numbered lists with bug tasks
        # =========================================================================

        numbered_lists = [b for b in sorted_blocks if b.get("type") in ["numberedList", "numbered_list"]]

        found_login_bug_task = False
        found_mobile_bug_task = False

        for block in numbered_lists:
            content = str(block.get("content", ""))
            if "login" in content.lower() and ("authentication" in content.lower() or "auth" in content.lower()):
                if "Status" in content and "Priority" in content:
                    found_login_bug_task = True
            if "mobile" in content.lower() and "responsive" in content.lower():
                if "Status" in content and "Priority" in content:
                    found_mobile_bug_task = True

        if found_login_bug_task:
            checks_passed.append("[C] Found numbered list for 'Fix login authentication bug' task")
        else:
            errors.append("[X] Numbered list for 'Fix login authentication bug' task not found")

        if found_mobile_bug_task:
            checks_passed.append("[C] Found numbered list for 'Fix mobile responsive issues' task")
        else:
            errors.append("[X] Numbered list for 'Fix mobile responsive issues' task not found")

        # =========================================================================
        # PART 7: Validate inline database "Bug Resolution Timeline"
        # =========================================================================

        # Search for database with title containing "Bug Resolution Timeline"
        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {"type": "database"}
        }) or []

        resolution_db = None
        for page in all_pages:
            title = page.get("title", "")
            if "Bug Resolution Timeline" in title:
                resolution_db = page
                break

        if not resolution_db:
            errors.append("[X] Inline database 'Bug Resolution Timeline' not found")
        else:
            checks_passed.append("[C] Found inline database 'Bug Resolution Timeline'")

            # Validate database properties
            db_metadata = resolution_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])

            prop_names = {prop.get("name", "").lower(): prop.get("type", "") for prop in properties}

            required_props = {
                "bug id": "title",
                "description": "text",
                "reported date": "date",
                "resolution date": "date",
                "days to resolve": "number"
            }

            all_props_correct = True
            for prop_name, expected_type in required_props.items():
                found = False
                for actual_name, actual_type in prop_names.items():
                    if prop_name in actual_name or actual_name in prop_name:
                        if actual_type == expected_type:
                            found = True
                            break
                if not found:
                    all_props_correct = False
                    errors.append(f"[X] Database missing property '{prop_name}' ({expected_type})")

            if all_props_correct:
                checks_passed.append("[C] Database has all required properties with correct types")

            # Validate database has 3 rows
            db_id = resolution_db.get("_id")
            db_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": db_id, "type": "row"}
            }) or []

            if len(db_rows) >= 3:
                checks_passed.append(f"[C] Database has {len(db_rows)} rows (expected 3)")
            else:
                errors.append(f"[X] Database should have 3 rows, found {len(db_rows)}")

        # Final score calculation
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Inventory Analysis System
# =============================================================================
def _validate_inventory_analysis_system(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Inventory Analysis System task.

    Requirements:
    1. 'Inventory Alerts' database exists as child of 'Team Resources' page
    2. Database has required properties: Alert Type (select), Item Name (text),
       Current Quantity (number), SKU (text), Action Required (text), Date Created (created_time)
    3. Alert Type has correct options: 'Low Stock' (yellow), 'Out of Stock' (red), 'Reorder Soon' (orange)
    4. Rows exist for items with quantity < 5 OR In Stock = false
       - Standing Desk: Low Stock, qty 3, SKU FURN-002, Action: "Monitor closely"
       - Wireless Keyboard: Out of Stock, qty 0, SKU HARD-001, Action: "Reorder immediately"
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # =========================================================================
        # PART 1: Find Team Resources page
        # =========================================================================

        team_resources = _find_page_by_title(final_state_backend, "Team Resources")
        if not team_resources:
            errors.append("[X] Team Resources page not found")
            errors.append("[X] Team Resources page not found")
        else:
            checks_passed.append("[C] Team Resources page")

        team_resources_id = team_resources.get("_id")
        checks_passed.append("[C] Team Resources page found")

        # =========================================================================
        # PART 2: Find Inventory Alerts database under Team Resources
        # =========================================================================

        # Search for database with title "Inventory Alerts"
        all_dbs = final_state_backend.query({
            "collection": "pages",
            "filter": {"type": "database"}
        }) or []

        inventory_alerts_db = None
        for db in all_dbs:
            title = db.get("title", "")
            if "Inventory Alerts" in title:
                inventory_alerts_db = db
                break

        if not inventory_alerts_db:
            errors.append("[X] 'Inventory Alerts' database not found")
            errors.append("[X] 'Inventory Alerts' database not found")
        else:
            checks_passed.append("[C] 'Inventory Alerts' database")

        checks_passed.append("[C] 'Inventory Alerts' database found")

        # Check parent is Team Resources
        if inventory_alerts_db.get("parentId") == team_resources_id:
            checks_passed.append("[C] 'Inventory Alerts' database is under Team Resources")
        else:
            errors.append(f"[X] 'Inventory Alerts' database should be under Team Resources ({team_resources_id}), but has parentId={inventory_alerts_db.get('parentId')}")

        # =========================================================================
        # PART 3: Validate database schema
        # =========================================================================

        db_metadata = inventory_alerts_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Build property map for easier lookup
        prop_map = {}
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            prop_map[prop_name] = prop

        # Required properties and their types
        required_props = {
            "alert type": "select",
            "item name": ("text", "title"),  # Could be either
            "current quantity": "number",
            "sku": "text",
            "action required": "text",
            "date created": "created_time"
        }

        for prop_name, expected_type in required_props.items():
            found = False
            for actual_name, actual_prop in prop_map.items():
                # Flexible matching for property names
                if prop_name in actual_name or actual_name in prop_name or \
                   prop_name.replace(" ", "") in actual_name.replace(" ", ""):
                    actual_type = actual_prop.get("type", "")
                    if isinstance(expected_type, tuple):
                        if actual_type in expected_type:
                            found = True
                            checks_passed.append(f"[C] Property '{prop_name}' found with type '{actual_type}'")
                            break
                    else:
                        if actual_type == expected_type:
                            found = True
                            checks_passed.append(f"[C] Property '{prop_name}' found with type '{expected_type}'")
                            break

            if not found:
                errors.append(f"[X] Property '{prop_name}' with correct type not found")

        # =========================================================================
        # PART 4: Validate Alert Type select options
        # =========================================================================

        alert_type_prop = None
        for actual_name, actual_prop in prop_map.items():
            if "alert" in actual_name and "type" in actual_name:
                alert_type_prop = actual_prop
                break

        if alert_type_prop:
            options = alert_type_prop.get("options", [])
            option_map = {opt.get("name", "").lower(): opt.get("color", "") for opt in options}

            expected_options = {
                "low stock": "yellow",
                "out of stock": "red",
                "reorder soon": "orange"
            }

            for opt_name, expected_color in expected_options.items():
                found_opt = False
                for actual_name, actual_color in option_map.items():
                    if opt_name in actual_name or actual_name in opt_name:
                        if expected_color in actual_color.lower():
                            found_opt = True
                            checks_passed.append(f"[C] Alert Type option '{opt_name}' has {expected_color} color")
                            break

                if not found_opt:
                    errors.append(f"[X] Alert Type option '{opt_name}' with {expected_color} color not found")
        else:
            errors.append("[X] Alert Type property not found for option validation")

        # =========================================================================
        # PART 5: Validate database rows
        # =========================================================================

        db_id = inventory_alerts_db.get("_id")
        db_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": db_id, "type": "row"}
        }) or []

        if len(db_rows) < 2:
            errors.append(f"[X] Expected at least 2 rows in Inventory Alerts, found {len(db_rows)}")
        else:
            checks_passed.append(f"[C] Found {len(db_rows)} rows in Inventory Alerts database")

        # Check for specific items
        # Expected: Standing Desk (Low Stock, qty 3, FURN-002, Monitor closely)
        # Expected: Wireless Keyboard (Out of Stock, qty 0, HARD-001, Reorder immediately)

        found_standing_desk = False
        found_wireless_keyboard = False

        for row in db_rows:
            cell_values = row.get("cellValues", {})
            title = row.get("title", "")

            # Check all cell values for item identification
            item_name = ""
            sku = ""
            quantity = None
            alert_type = ""
            action_required = ""

            for key, value in cell_values.items():
                key_lower = key.lower()
                if "item" in key_lower and "name" in key_lower:
                    item_name = str(value) if value else ""
                elif "sku" in key_lower:
                    sku = str(value) if value else ""
                elif "quantity" in key_lower:
                    quantity = value
                elif "alert" in key_lower and "type" in key_lower:
                    alert_type = str(value) if value else ""
                elif "action" in key_lower:
                    action_required = str(value) if value else ""

            # Also check title
            if not item_name:
                item_name = title

            # Check for Standing Desk (Low Stock)
            if "standing desk" in item_name.lower() or "standing desk" in title.lower() or sku == "FURN-002":
                found_standing_desk = True
                if "low" in alert_type.lower() and "stock" in alert_type.lower():
                    checks_passed.append("[C] Standing Desk has 'Low Stock' alert type")
                else:
                    errors.append(f"[X] Standing Desk should have 'Low Stock' alert type, found '{alert_type}'")

                if quantity == 3 or str(quantity) == "3":
                    checks_passed.append("[C] Standing Desk has quantity 3")
                else:
                    errors.append(f"[X] Standing Desk should have quantity 3, found {quantity}")

                if "monitor" in action_required.lower():
                    checks_passed.append("[C] Standing Desk has 'Monitor closely' action")
                else:
                    errors.append(f"[X] Standing Desk should have 'Monitor closely' action, found '{action_required}'")

            # Check for Wireless Keyboard (Out of Stock)
            if "wireless keyboard" in item_name.lower() or "wireless keyboard" in title.lower() or sku == "HARD-001":
                found_wireless_keyboard = True
                if "out" in alert_type.lower() and "stock" in alert_type.lower():
                    checks_passed.append("[C] Wireless Keyboard has 'Out of Stock' alert type")
                else:
                    errors.append(f"[X] Wireless Keyboard should have 'Out of Stock' alert type, found '{alert_type}'")

                if quantity == 0 or str(quantity) == "0":
                    checks_passed.append("[C] Wireless Keyboard has quantity 0")
                else:
                    errors.append(f"[X] Wireless Keyboard should have quantity 0, found {quantity}")

                if "reorder" in action_required.lower() and "immediately" in action_required.lower():
                    checks_passed.append("[C] Wireless Keyboard has 'Reorder immediately' action")
                else:
                    errors.append(f"[X] Wireless Keyboard should have 'Reorder immediately' action, found '{action_required}'")

        if not found_standing_desk:
            errors.append("[X] Standing Desk row not found in Inventory Alerts")

        if not found_wireless_keyboard:
            errors.append("[X] Wireless Keyboard row not found in Inventory Alerts")

        # Final score calculation
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Task Dependency Tracker
# =============================================================================
def _validate_task_dependency_tracker(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Task Dependency Tracker task.

    Requirements:
    1. '🔗 Task Dependencies Report' page exists under Meeting Notes
    2. Page has heading_1 '🔗 Blocked Tasks Analysis'
    3. Page has heading_2 '⛔ Currently Blocked Items' with numbered list
    4. Page has heading_2 '👥 Team Impact Analysis' with assignee paragraphs
    5. 'Dependency Matrix' database exists with correct schema
    6. Database has rows for blocked tasks with correct values
    7. Red callout with 🚨 icon and ALERT text exists
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # =========================================================================
        # PART 1: Find Meeting Notes and Task Dependencies Report page
        # =========================================================================

        meeting_notes = _find_page_by_title(final_state_backend, "Meeting Notes")
        if not meeting_notes:
            errors.append("[X] Meeting Notes page not found")
            errors.append("[X] Meeting Notes page not found")
        else:
            checks_passed.append("[C] Meeting Notes page")

        meeting_notes_id = meeting_notes.get("_id")
        checks_passed.append("[C] Meeting Notes page found")

        # Find Task Dependencies Report page
        # Note: Don't filter by type since MCP-created pages may not have type set
        report_page = None
        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {}
        }) or []

        for page in all_pages:
            title = page.get("title", "")
            page_type = page.get("type", "")
            # Skip database and row types
            if page_type in ["database", "row"]:
                continue
            if "Task Dependencies Report" in title or "Dependencies Report" in title:
                report_page = page
                break

        if not report_page:
            errors.append("[X] '🔗 Task Dependencies Report' page not found")
            errors.append("[X] 'Task Dependencies Report' page not found")
        else:
            checks_passed.append("[C] 'Task Dependencies Report' page")

        checks_passed.append("[C] 'Task Dependencies Report' page found")

        # Check parent is Meeting Notes
        if report_page.get("parentId") == meeting_notes_id:
            checks_passed.append("[C] Report page is under Meeting Notes")
        else:
            errors.append(f"[X] Report page should be under Meeting Notes ({meeting_notes_id}), but has parentId={report_page.get('parentId')}")

        report_page_id = report_page.get("_id")

        # =========================================================================
        # PART 2: Validate page structure - headings
        # =========================================================================

        blocks = _get_blocks_for_page(final_state_backend, report_page_id)
        if not blocks:
            errors.append("[X] No blocks found on report page")
            errors.append("[X] No blocks found on report page")
        else:
            checks_passed.append("[C] blocks found on report page")

        sorted_blocks = sorted(blocks, key=lambda b: b.get("order", 0))

        # Check for heading_1 'Blocked Tasks Analysis'
        found_main_heading = False
        for block in sorted_blocks:
            if block.get("type") in ["heading1", "heading_1"]:
                content = str(block.get("content", ""))
                if "Blocked Tasks Analysis" in content:
                    found_main_heading = True
                    break

        if found_main_heading:
            checks_passed.append("[C] Found heading_1 'Blocked Tasks Analysis'")
        else:
            errors.append("[X] heading_1 'Blocked Tasks Analysis' not found")

        # Check for heading_2 'Currently Blocked Items'
        found_blocked_heading = False
        for block in sorted_blocks:
            if block.get("type") in ["heading2", "heading_2"]:
                content = str(block.get("content", ""))
                if "Currently Blocked" in content or "Blocked Items" in content:
                    found_blocked_heading = True
                    break

        if found_blocked_heading:
            checks_passed.append("[C] Found heading_2 'Currently Blocked Items'")
        else:
            errors.append("[X] heading_2 'Currently Blocked Items' not found")

        # Check for heading_2 'Team Impact Analysis'
        found_team_heading = False
        for block in sorted_blocks:
            if block.get("type") in ["heading2", "heading_2"]:
                content = str(block.get("content", ""))
                if "Team Impact" in content:
                    found_team_heading = True
                    break

        if found_team_heading:
            checks_passed.append("[C] Found heading_2 'Team Impact Analysis'")
        else:
            errors.append("[X] heading_2 'Team Impact Analysis' not found")

        # =========================================================================
        # PART 3: Validate blocked tasks numbered list
        # =========================================================================

        # The blocked task in default_backend is "Update database schema" (db-tasks-row-5)
        found_blocked_task_list = False
        for block in sorted_blocks:
            if block.get("type") in ["numberedList", "numbered_list"]:
                content = str(block.get("content", ""))
                if "Update database schema" in content:
                    found_blocked_task_list = True
                    if "Blocked since" in content or "blocked since" in content or "2026" in content:
                        checks_passed.append("[C] Numbered list has blocked task with date")
                    else:
                        checks_passed.append("[C] Numbered list has blocked task")
                    break

        if not found_blocked_task_list:
            errors.append("[X] Numbered list with 'Update database schema' blocked task not found")

        # =========================================================================
        # PART 4: Validate team impact analysis
        # =========================================================================

        # Bob Martinez (user-2) has 1 blocked task
        found_bob_impact = False
        for block in sorted_blocks:
            if block.get("type") == "paragraph":
                content = str(block.get("content", ""))
                if "Bob Martinez" in content and ("blocked" in content.lower() or "task" in content.lower()):
                    found_bob_impact = True
                    if "**" in content or "1" in content:
                        checks_passed.append("[C] Found team impact for Bob Martinez with count")
                    else:
                        checks_passed.append("[C] Found team impact for Bob Martinez")
                    break

        if not found_bob_impact:
            errors.append("[X] Team impact paragraph for 'Bob Martinez' not found")

        # =========================================================================
        # PART 5: Validate Dependency Matrix database
        # =========================================================================

        all_dbs = final_state_backend.query({
            "collection": "pages",
            "filter": {"type": "database"}
        }) or []

        dependency_db = None
        for db in all_dbs:
            title = db.get("title", "")
            if "Dependency Matrix" in title or "dependency matrix" in title.lower():
                dependency_db = db
                break

        if not dependency_db:
            errors.append("[X] 'Dependency Matrix' database not found")
        else:
            checks_passed.append("[C] 'Dependency Matrix' database found")

            # Validate schema
            db_metadata = dependency_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])

            prop_map = {}
            for prop in properties:
                prop_name = prop.get("name", "").lower()
                prop_map[prop_name] = prop

            required_props = {
                "blocked task": ("title", "text"),
                "blocking factor": "text",
                "resolution owner": "text",
                "expected unblock date": "date",
                "impact level": "select"
            }

            for prop_name, expected_type in required_props.items():
                found = False
                for actual_name, actual_prop in prop_map.items():
                    if prop_name in actual_name or actual_name in prop_name:
                        actual_type = actual_prop.get("type", "")
                        if isinstance(expected_type, tuple):
                            if actual_type in expected_type:
                                found = True
                                break
                        else:
                            if actual_type == expected_type:
                                found = True
                                break

                if found:
                    checks_passed.append(f"[C] Property '{prop_name}' found")
                else:
                    errors.append(f"[X] Property '{prop_name}' not found")

            # Validate Impact Level select options
            impact_prop = None
            for actual_name, actual_prop in prop_map.items():
                if "impact" in actual_name and "level" in actual_name:
                    impact_prop = actual_prop
                    break

            if impact_prop:
                options = impact_prop.get("options", [])
                option_names = [opt.get("name", "").lower() for opt in options]

                expected_options = ["critical", "high", "medium", "low"]
                for opt in expected_options:
                    if any(opt in name for name in option_names):
                        checks_passed.append(f"[C] Impact Level option '{opt}' found")
                    else:
                        errors.append(f"[X] Impact Level option '{opt}' not found")

            # =========================================================================
            # PART 6: Validate database rows
            # =========================================================================

            db_id = dependency_db.get("_id")
            db_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": db_id, "type": "row"}
            }) or []

            if len(db_rows) >= 1:
                checks_passed.append(f"[C] Found {len(db_rows)} rows in Dependency Matrix")
            else:
                errors.append("[X] No rows found in Dependency Matrix")

            # Check for Update database schema row
            found_schema_row = False
            for row in db_rows:
                title = row.get("title", "")
                cell_values = row.get("cellValues", {})

                # Check if this is the blocked task row
                if "Update database schema" in title or "database schema" in title.lower():
                    found_schema_row = True

                    # Check Blocking Factor
                    for key, value in cell_values.items():
                        if "blocking" in key.lower() and "factor" in key.lower():
                            if value and "investigation" in str(value).lower():
                                checks_passed.append("[C] Blocking Factor is 'Requires investigation'")
                            break

                    # Check Resolution Owner (should be Bob Martinez)
                    for key, value in cell_values.items():
                        if "resolution" in key.lower() or "owner" in key.lower():
                            if value and "Bob" in str(value):
                                checks_passed.append("[C] Resolution Owner is Bob Martinez")
                            break

                    # Check Impact Level (should be High for Urgent priority)
                    for key, value in cell_values.items():
                        if "impact" in key.lower():
                            if value and "high" in str(value).lower():
                                checks_passed.append("[C] Impact Level is 'High' for Urgent task")
                            break
                    break

            if not found_schema_row:
                errors.append("[X] Row for 'Update database schema' not found in Dependency Matrix")

        # =========================================================================
        # PART 7: Validate red callout with ALERT
        # =========================================================================

        found_alert_callout = False
        for block in sorted_blocks:
            if block.get("type") == "callout":
                content = str(block.get("content", ""))
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", "")

                if "ALERT" in content.upper() and "red" in bg_color.lower():
                    found_alert_callout = True
                    checks_passed.append("[C] Found red ALERT callout")
                    break

        if not found_alert_callout:
            errors.append("[X] Red callout with 'ALERT' not found")

        # Final score calculation
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Cross-Database Financial Analysis
# =============================================================================
def _validate_cross_database_financial_analysis(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Cross-Database Financial Analysis task.

    Requirements:
    1. 'Department Budget Analysis' inline database exists in Engineering Wiki (page-4)
    2. Database has correct properties: Department (title), Active Projects (number),
       Completed Projects (number), Total Budget Allocated (number),
       Average Project Cost (number), Budget Status (select with options)
    3. Budget Status has options: 'Under Budget' (green), 'On Track' (blue), 'Over Budget' (red)
    4. Database has rows for each department with calculated values:
       - Engineering: 2 Active, 0 Completed, $125,000 total, $62,500 avg
       - Marketing: 0 Active, 1 Completed, $25,000 total, $25,000 avg
       - Sales: 1 Active, 0 Completed, $35,000 total, $35,000 avg
    5. '💰 Q1 Financial Report' page exists as child of Engineering Wiki
    6. Report has heading_1 '📊 Departmental Budget Analysis'
    7. Report has blue callout with 'Total Company Project Investment: $185,000'
    8. Report has heading_2 '🏆 Top Spending Departments'
    9. Report has numbered list sorted by budget: Engineering, Sales, Marketing
    10. Report has quote block with exact text
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # =========================================================================
        # PART 1: Find Engineering Wiki page
        # =========================================================================

        engineering_wiki = final_state_backend.query({
            "collection": "pages",
            "filter": {"_id": "page-4"}
        })

        if not engineering_wiki:
            errors.append("[X] Engineering Wiki page (page-4) not found")
            errors.append("[X] Engineering Wiki page not found")
        else:
            checks_passed.append("[C] Engineering Wiki page")

        checks_passed.append("[C] Engineering Wiki page found")

        # =========================================================================
        # PART 2: Find Department Budget Analysis database
        # =========================================================================

        all_dbs = final_state_backend.query({
            "collection": "pages",
            "filter": {"type": "database"}
        }) or []

        budget_db = None
        # Find the database that matches our requirements:
        # 1. Title contains "Department Budget Analysis"
        # 2. Parent is page-4 (Engineering Wiki)
        # 3. Has a property with type "title" named "Department" (not "Name")
        for db in all_dbs:
            title = db.get("title", "")
            parent_id = db.get("parentId", "")

            if "Department Budget Analysis" in title or "budget analysis" in title.lower():
                # Check if this is under Engineering Wiki and has correct schema
                if parent_id == "page-4":
                    db_metadata = db.get("databaseMetadata", {})
                    properties = db_metadata.get("properties", [])

                    # Check if it has a "Department" title property (our requirement)
                    has_dept_title = any(
                        p.get("type") == "title" and "department" in p.get("name", "").lower()
                        for p in properties
                    )

                    # Also accept if it has "Name" as title (fallback for flexibility)
                    if has_dept_title:
                        budget_db = db
                        break
                    elif budget_db is None:
                        # Keep this as fallback if no better match found
                        budget_db = db

        if not budget_db:
            errors.append("[X] 'Department Budget Analysis' database not found")
            errors.append("[X] 'Department Budget Analysis' database not found")
        else:
            checks_passed.append("[C] 'Department Budget Analysis' database")

        checks_passed.append("[C] 'Department Budget Analysis' database found")

        # Check database is under Engineering Wiki (page-4)
        if budget_db.get("parentId") == "page-4":
            checks_passed.append("[C] Database is under Engineering Wiki")
        else:
            errors.append(f"[X] Database should be under Engineering Wiki (page-4), but has parentId={budget_db.get('parentId')}")

        # =========================================================================
        # PART 3: Validate database schema
        # =========================================================================

        db_metadata = budget_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        prop_map = {}
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            prop_map[prop_name] = prop

        # Required properties and their types
        required_props = {
            "department": "title",
            "active projects": "number",
            "completed projects": "number",
            "total budget allocated": "number",
            "average project cost": "number",
            "budget status": "select"
        }

        for prop_name, expected_type in required_props.items():
            found = False
            for actual_name, actual_prop in prop_map.items():
                # Flexible matching
                if prop_name in actual_name or actual_name in prop_name or \
                   prop_name.replace(" ", "") in actual_name.replace(" ", ""):
                    actual_type = actual_prop.get("type", "")
                    if actual_type == expected_type:
                        found = True
                        checks_passed.append(f"[C] Property '{prop_name}' found with type '{expected_type}'")
                        break

            if not found:
                errors.append(f"[X] Property '{prop_name}' with type '{expected_type}' not found")

        # =========================================================================
        # PART 4: Validate Budget Status select options
        # =========================================================================

        budget_status_prop = None
        for actual_name, actual_prop in prop_map.items():
            if "budget" in actual_name and "status" in actual_name:
                budget_status_prop = actual_prop
                break

        if budget_status_prop:
            options = budget_status_prop.get("options", [])
            option_map = {opt.get("name", "").lower(): opt.get("color", "").lower() for opt in options}

            expected_options = {
                "under budget": "green",
                "on track": "blue",
                "over budget": "red"
            }

            for opt_name, expected_color in expected_options.items():
                found_opt = False
                for actual_name, actual_color in option_map.items():
                    if opt_name in actual_name or actual_name in opt_name:
                        if expected_color in actual_color:
                            found_opt = True
                            checks_passed.append(f"[C] Budget Status option '{opt_name}' has {expected_color} color")
                            break

                if not found_opt:
                    errors.append(f"[X] Budget Status option '{opt_name}' with {expected_color} color not found")
        else:
            errors.append("[X] Budget Status property not found for option validation")

        # =========================================================================
        # PART 5: Validate database rows with calculated values
        # =========================================================================

        db_id = budget_db.get("_id")
        db_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": db_id, "type": "row"}
        }) or []

        if len(db_rows) < 3:
            errors.append(f"[X] Expected at least 3 department rows, found {len(db_rows)}")
        else:
            checks_passed.append(f"[C] Found {len(db_rows)} rows in Department Budget Analysis")

        # Build property ID to name mapping from database metadata
        prop_id_to_name = {}
        for prop in properties:
            prop_id = prop.get("_id", "")
            prop_name = prop.get("name", "").lower()
            if prop_id:
                prop_id_to_name[prop_id] = prop_name

        # Expected department values based on Project Portfolio data:
        # Engineering: Website Redesign (Active, $50k) + Customer Portal (Active, $75k) = 2 Active, 0 Completed, $125k total, $62.5k avg
        # Marketing: Q1 Marketing Campaign (Completed, $25k) = 0 Active, 1 Completed, $25k total, $25k avg
        # Sales: Sales Pipeline Automation (Active, $35k) = 1 Active, 0 Completed, $35k total, $35k avg

        expected_departments = {
            "engineering": {"active": 2, "completed": 0, "total": 125000, "avg": 62500},
            "marketing": {"active": 0, "completed": 1, "total": 25000, "avg": 25000},
            "sales": {"active": 1, "completed": 0, "total": 35000, "avg": 35000}
        }

        found_departments = set()

        for row in db_rows:
            title = row.get("title", "").lower()
            cell_values = row.get("cellValues", {})

            # Identify department from row title
            dept_name = None
            for dept in expected_departments.keys():
                if dept in title:
                    dept_name = dept
                    break

            # Also check cell values for department name (values might be strings)
            if not dept_name:
                for key, value in cell_values.items():
                    if value and isinstance(value, str):
                        for dept in expected_departments.keys():
                            if dept in value.lower():
                                dept_name = dept
                                break
                    if dept_name:
                        break

            if not dept_name:
                continue

            found_departments.add(dept_name)
            expected = expected_departments[dept_name]

            # Extract values from cell_values, mapping property IDs to names
            active_count = None
            completed_count = None
            total_budget = None
            avg_cost = None

            for key, value in cell_values.items():
                # Get property name from ID mapping, or use key directly if it's already a name
                prop_name = prop_id_to_name.get(key, key).lower()

                if "active" in prop_name and "project" in prop_name:
                    active_count = value
                elif "completed" in prop_name and "project" in prop_name:
                    completed_count = value
                elif "total" in prop_name and "budget" in prop_name:
                    total_budget = value
                elif "average" in prop_name or "avg" in prop_name:
                    avg_cost = value

            # Validate values
            if active_count == expected["active"]:
                checks_passed.append(f"[C] {dept_name.title()} has correct Active Projects count ({expected['active']})")
            elif active_count is not None:
                errors.append(f"[X] {dept_name.title()} Active Projects should be {expected['active']}, found {active_count}")

            if completed_count == expected["completed"]:
                checks_passed.append(f"[C] {dept_name.title()} has correct Completed Projects count ({expected['completed']})")
            elif completed_count is not None:
                errors.append(f"[X] {dept_name.title()} Completed Projects should be {expected['completed']}, found {completed_count}")

            if total_budget == expected["total"]:
                checks_passed.append(f"[C] {dept_name.title()} has correct Total Budget (${expected['total']:,})")
            elif total_budget is not None:
                errors.append(f"[X] {dept_name.title()} Total Budget should be ${expected['total']:,}, found ${total_budget}")

            if avg_cost == expected["avg"]:
                checks_passed.append(f"[C] {dept_name.title()} has correct Average Project Cost (${expected['avg']:,})")
            elif avg_cost is not None:
                errors.append(f"[X] {dept_name.title()} Average Project Cost should be ${expected['avg']:,}, found ${avg_cost}")

        # Check all departments found
        for dept in expected_departments.keys():
            if dept not in found_departments:
                errors.append(f"[X] Row for department '{dept.title()}' not found")

        # =========================================================================
        # PART 6: Find Q1 Financial Report page
        # =========================================================================

        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {}
        }) or []

        report_page = None
        for page in all_pages:
            title = page.get("title", "")
            page_type = page.get("type", "")
            if page_type in ["database", "row"]:
                continue
            if "Q1 Financial Report" in title or "Financial Report" in title:
                report_page = page
                break

        if not report_page:
            errors.append("[X] '💰 Q1 Financial Report' page not found")
            errors.append("[X] 'Q1 Financial Report' page not found")
        else:
            checks_passed.append("[C] 'Q1 Financial Report' page")

        checks_passed.append("[C] '💰 Q1 Financial Report' page found")

        # Check parent is Engineering Wiki
        if report_page.get("parentId") == "page-4":
            checks_passed.append("[C] Report page is child of Engineering Wiki")
        else:
            errors.append(f"[X] Report page should be child of Engineering Wiki (page-4), but has parentId={report_page.get('parentId')}")

        # =========================================================================
        # PART 7: Validate report page structure
        # =========================================================================

        report_page_id = report_page.get("_id")
        blocks = final_state_backend.query({
            "collection": "blocks",
            "filter": {"pageId": report_page_id}
        }) or []

        if not blocks:
            errors.append("[X] No blocks found on Q1 Financial Report page")
        else:
            sorted_blocks = sorted(blocks, key=lambda b: b.get("order", 0))

            # Check for heading_1 '📊 Departmental Budget Analysis'
            found_main_heading = False
            for block in sorted_blocks:
                if block.get("type") in ["heading1", "heading_1"]:
                    content = str(block.get("content", ""))
                    if "Departmental Budget Analysis" in content or "Budget Analysis" in content:
                        found_main_heading = True
                        checks_passed.append("[C] Found heading_1 'Departmental Budget Analysis'")
                        break

            if not found_main_heading:
                errors.append("[X] heading_1 '📊 Departmental Budget Analysis' not found")

            # Check for blue callout with total investment ($185,000)
            found_blue_callout = False
            total_investment = 185000  # 50k + 25k + 75k + 35k

            for block in sorted_blocks:
                if block.get("type") == "callout":
                    properties = block.get("properties", {})
                    bg_color = properties.get("backgroundColor", "")
                    content = str(block.get("content", ""))

                    if "blue" in bg_color.lower():
                        # Check for total investment amount
                        if "185000" in content or "185,000" in content or "$185000" in content or "$185,000" in content:
                            found_blue_callout = True
                            checks_passed.append(f"[C] Found blue callout with total investment ${total_investment:,}")
                            break
                        elif "Total Company Project Investment" in content or "Total" in content:
                            # Accept if it mentions total investment even if format differs
                            found_blue_callout = True
                            checks_passed.append("[C] Found blue callout with total investment info")
                            break

            if not found_blue_callout:
                errors.append(f"[X] Blue callout with 'Total Company Project Investment: ${total_investment:,}' not found")

            # Check for heading_2 '🏆 Top Spending Departments'
            found_spending_heading = False
            for block in sorted_blocks:
                if block.get("type") in ["heading2", "heading_2"]:
                    content = str(block.get("content", ""))
                    if "Top Spending Departments" in content or "Spending Departments" in content:
                        found_spending_heading = True
                        checks_passed.append("[C] Found heading_2 'Top Spending Departments'")
                        break

            if not found_spending_heading:
                errors.append("[X] heading_2 '🏆 Top Spending Departments' not found")

            # Check for numbered list with departments sorted by budget
            # Expected order: Engineering ($125k), Sales ($35k), Marketing ($25k)
            numbered_lists = [b for b in sorted_blocks if b.get("type") in ["numberedList", "numbered_list"]]

            if len(numbered_lists) >= 3:
                checks_passed.append(f"[C] Found {len(numbered_lists)} numbered list items")

                # Check order - Engineering should come first
                first_list_content = str(numbered_lists[0].get("content", "")).lower()
                if "engineering" in first_list_content:
                    checks_passed.append("[C] Engineering is first in numbered list (highest budget)")
                else:
                    errors.append("[X] Engineering should be first in numbered list (highest budget)")

                # Check Sales is second
                if len(numbered_lists) >= 2:
                    second_list_content = str(numbered_lists[1].get("content", "")).lower()
                    if "sales" in second_list_content:
                        checks_passed.append("[C] Sales is second in numbered list")
                    else:
                        errors.append("[X] Sales should be second in numbered list")

                # Check Marketing is third
                if len(numbered_lists) >= 3:
                    third_list_content = str(numbered_lists[2].get("content", "")).lower()
                    if "marketing" in third_list_content:
                        checks_passed.append("[C] Marketing is third in numbered list (lowest budget)")
                    else:
                        errors.append("[X] Marketing should be third in numbered list")
            else:
                errors.append(f"[X] Expected at least 3 numbered list items for departments, found {len(numbered_lists)}")

            # Check for quote block with exact text
            found_quote = False
            expected_quote = "Financial discipline is the cornerstone of sustainable growth"

            for block in sorted_blocks:
                if block.get("type") == "quote":
                    content = str(block.get("content", ""))
                    if expected_quote.lower() in content.lower() or \
                       "financial discipline" in content.lower():
                        found_quote = True
                        checks_passed.append("[C] Found quote block with correct text")
                        break

            if not found_quote:
                errors.append(f"[X] Quote block with '{expected_quote}' not found")

        # Final score calculation
        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_contact_scoring_segmentation(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate contact scoring and segmentation system task.

    Expected calculations based on initial_data.json:
    - Sarah Johnson: Client(40) + VIP(50) + Active(30) = 120 -> Hot
    - Michael Chen: Partner(30) + Active(30) = 60 -> Warm
    - Emily Davis: Vendor(20) + Active(30) = 50 -> Warm
    - James Wilson: Lead(25) = 25 -> Cold
    - Lisa Thompson: Client(40) + VIP(50) + Active(30) = 120 -> Hot
    - Robert Garcia: Partner(30) + Active(30) = 60 -> Warm
    - Amanda Lee: Lead(25) = 25 -> Cold

    Scoring: VIP=+50, Active=+30, Client=+40, Partner=+30, Vendor=+20, Lead=+25
    Segments: >70=Hot, 50-70=Warm, 25-49=Cold, <25=Dormant
    """
    checks_passed = []
    errors = []

    try:
        # Expected contact scores and segments
        expected_contacts = {
            "sarah johnson": {"score": 120, "segment": "Hot"},
            "michael chen": {"score": 60, "segment": "Warm"},
            "emily davis": {"score": 50, "segment": "Warm"},
            "james wilson": {"score": 25, "segment": "Cold"},
            "lisa thompson": {"score": 120, "segment": "Hot"},
            "robert garcia": {"score": 60, "segment": "Warm"},
            "amanda lee": {"score": 25, "segment": "Cold"},
        }

        # =========================================================================
        # PART 1: Validate Contacts Directory updates (new properties)
        # =========================================================================

        # Find Contacts Directory database
        all_dbs = final_state_backend.query({"collection": "pages", "filter": {"type": "database"}})
        contacts_db = None

        for db in all_dbs:
            title = db.get("title", "").lower()
            if "contacts directory" in title or db.get("_id") == "db-contacts":
                contacts_db = db
                break

        if not contacts_db:
            errors.append("[X] Contacts Directory database not found")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        checks_passed.append("[C] Found Contacts Directory database")

        # Check for new properties
        db_metadata = contacts_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Build property map
        prop_map = {}
        prop_id_to_name = {}
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            prop_map[prop_name] = prop
            prop_id = prop.get("_id", "")
            if prop_id:
                prop_id_to_name[prop_id] = prop_name

        # Check Engagement Score property
        if "engagement score" in prop_map:
            prop = prop_map["engagement score"]
            if prop.get("type") == "number":
                checks_passed.append("[C] 'Engagement Score' property exists with type=number")
            else:
                errors.append(f"[X] 'Engagement Score' has wrong type: {prop.get('type')}, expected number")
        else:
            errors.append("[X] 'Engagement Score' property not found in Contacts Directory")

        # Check Last Activity Date property
        if "last activity date" in prop_map:
            prop = prop_map["last activity date"]
            if prop.get("type") == "date":
                checks_passed.append("[C] 'Last Activity Date' property exists with type=date")
            else:
                errors.append(f"[X] 'Last Activity Date' has wrong type: {prop.get('type')}, expected date")
        else:
            errors.append("[X] 'Last Activity Date' property not found in Contacts Directory")

        # Check Segment property with select options
        if "segment" in prop_map:
            prop = prop_map["segment"]
            if prop.get("type") == "select":
                checks_passed.append("[C] 'Segment' property exists with type=select")

                # Check select options
                options = prop.get("options", [])
                option_map = {opt.get("name", "").lower(): opt.get("color", "").lower() for opt in options}

                expected_options = {"hot": "red", "warm": "orange", "cold": "blue", "dormant": "gray"}
                for opt_name, expected_color in expected_options.items():
                    if opt_name in option_map:
                        actual_color = option_map[opt_name]
                        if actual_color == expected_color:
                            checks_passed.append(f"[C] Segment option '{opt_name}' has correct color '{expected_color}'")
                        else:
                            errors.append(f"[X] Segment option '{opt_name}' has color '{actual_color}', expected '{expected_color}'")
                    else:
                        errors.append(f"[X] Segment option '{opt_name}' not found")
            else:
                errors.append(f"[X] 'Segment' has wrong type: {prop.get('type')}, expected select")
        else:
            errors.append("[X] 'Segment' property not found in Contacts Directory")

        # =========================================================================
        # PART 2: Validate contact row updates (scores and segments)
        # =========================================================================

        contacts_db_id = contacts_db.get("_id", "db-contacts")
        contact_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"type": "row", "databaseId": contacts_db_id}
        })

        if not contact_rows:
            # Try parentId
            contact_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"type": "row", "parentId": contacts_db_id}
            })

        if len(contact_rows) >= 7:
            checks_passed.append(f"[C] Found {len(contact_rows)} contact rows")
        else:
            errors.append(f"[X] Expected at least 7 contact rows, found {len(contact_rows)}")

        # Validate each contact's score and segment
        contacts_validated = 0
        for row in contact_rows:
            contact_name = row.get("title", "").lower()
            cell_values = row.get("cellValues", {})

            if contact_name not in expected_contacts:
                continue

            expected = expected_contacts[contact_name]

            # Get engagement score (handle both property ID and name keys)
            score_value = None
            segment_value = None

            for key, value in cell_values.items():
                prop_name = prop_id_to_name.get(key, key).lower()
                if "engagement" in prop_name and "score" in prop_name:
                    score_value = value
                elif prop_name == "segment":
                    segment_value = value

            # Validate score (allow ±5 tolerance)
            if score_value is not None:
                try:
                    actual_score = int(score_value) if not isinstance(score_value, int) else score_value
                    if abs(actual_score - expected["score"]) <= 5:
                        checks_passed.append(f"[C] {contact_name.title()}: Engagement Score={actual_score} (expected ~{expected['score']})")
                        contacts_validated += 1
                    else:
                        errors.append(f"[X] {contact_name.title()}: Score={actual_score}, expected {expected['score']}")
                except (ValueError, TypeError):
                    errors.append(f"[X] {contact_name.title()}: Invalid score value '{score_value}'")

            # Validate segment
            if segment_value is not None:
                actual_segment = str(segment_value).lower()
                if actual_segment == expected["segment"].lower():
                    checks_passed.append(f"[C] {contact_name.title()}: Segment='{segment_value}' correct")
                else:
                    errors.append(f"[X] {contact_name.title()}: Segment='{segment_value}', expected '{expected['segment']}'")

        if contacts_validated >= 5:
            checks_passed.append(f"[C] At least 5 contacts have correct engagement scores")
        elif contacts_validated > 0:
            errors.append(f"[X] Only {contacts_validated} contacts validated, expected at least 5")

        # =========================================================================
        # PART 3: Validate Lead Nurturing Pipeline database
        # =========================================================================

        pipeline_db = None
        for db in all_dbs:
            title = db.get("title", "").lower()
            if "lead nurturing pipeline" in title:
                pipeline_db = db
                break

        if not pipeline_db:
            errors.append("[X] 'Lead Nurturing Pipeline' database not found")
        else:
            checks_passed.append("[C] Found 'Lead Nurturing Pipeline' database")

            # Check parent is Marketing Ideas (page-12)
            parent_id = pipeline_db.get("parentId", "")
            if parent_id == "page-12":
                checks_passed.append("[C] Lead Nurturing Pipeline is under Marketing Ideas (page-12)")
            else:
                # Check if parent title matches
                parent_page = final_state_backend.query({
                    "collection": "pages",
                    "filter": {"_id": parent_id}
                })
                if parent_page and "marketing" in str(parent_page[0].get("title", "")).lower():
                    checks_passed.append("[C] Lead Nurturing Pipeline is under Marketing Ideas page")
                else:
                    errors.append(f"[X] Lead Nurturing Pipeline parent is '{parent_id}', expected Marketing Ideas (page-12)")

            # Check properties
            pipeline_metadata = pipeline_db.get("databaseMetadata", {})
            pipeline_props = pipeline_metadata.get("properties", [])
            pipeline_prop_map = {p.get("name", "").lower(): p for p in pipeline_props}
            pipeline_prop_id_to_name = {p.get("_id", ""): p.get("name", "").lower() for p in pipeline_props}

            # Check Contact (title) property
            has_contact_title = any(
                p.get("type") == "title" and "contact" in p.get("name", "").lower()
                for p in pipeline_props
            )
            if has_contact_title:
                checks_passed.append("[C] Lead Nurturing Pipeline has 'Contact' title property")
            else:
                errors.append("[X] Lead Nurturing Pipeline missing 'Contact' title property")

            # Check Current Segment (text)
            if "current segment" in pipeline_prop_map:
                if pipeline_prop_map["current segment"].get("type") == "text":
                    checks_passed.append("[C] 'Current Segment' property exists with type=text")
                else:
                    errors.append(f"[X] 'Current Segment' wrong type: {pipeline_prop_map['current segment'].get('type')}")
            else:
                errors.append("[X] 'Current Segment' property not found")

            # Check Days Since Activity (number)
            if "days since activity" in pipeline_prop_map:
                if pipeline_prop_map["days since activity"].get("type") == "number":
                    checks_passed.append("[C] 'Days Since Activity' property exists with type=number")
                else:
                    errors.append(f"[X] 'Days Since Activity' wrong type")
            else:
                errors.append("[X] 'Days Since Activity' property not found")

            # Check Recommended Action (select)
            if "recommended action" in pipeline_prop_map:
                prop = pipeline_prop_map["recommended action"]
                if prop.get("type") == "select":
                    checks_passed.append("[C] 'Recommended Action' property exists with type=select")
                    options = prop.get("options", [])
                    option_names = [o.get("name", "").lower() for o in options]
                    expected_actions = ["call", "email", "meeting", "archive"]
                    for action in expected_actions:
                        if action in option_names:
                            checks_passed.append(f"[C] Recommended Action has '{action}' option")
                        else:
                            errors.append(f"[X] Recommended Action missing '{action}' option")
                else:
                    errors.append("[X] 'Recommended Action' wrong type, expected select")
            else:
                errors.append("[X] 'Recommended Action' property not found")

            # Check Priority (number)
            if "priority" in pipeline_prop_map:
                if pipeline_prop_map["priority"].get("type") == "number":
                    checks_passed.append("[C] 'Priority' property exists with type=number")
                else:
                    errors.append("[X] 'Priority' wrong type, expected number")
            else:
                errors.append("[X] 'Priority' property not found")

            # Check rows for Leads and Cold/Dormant contacts
            pipeline_db_id = pipeline_db.get("_id", "")
            pipeline_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"type": "row", "databaseId": pipeline_db_id}
            })

            if not pipeline_rows:
                pipeline_rows = final_state_backend.query({
                    "collection": "pages",
                    "filter": {"type": "row", "parentId": pipeline_db_id}
                })

            # Expected: James Wilson (Lead, Cold), Amanda Lee (Lead, Cold)
            expected_pipeline_contacts = ["james wilson", "amanda lee"]
            found_contacts = []

            for row in pipeline_rows:
                row_title = row.get("title", "").lower()
                for expected_contact in expected_pipeline_contacts:
                    if expected_contact in row_title or row_title in expected_contact:
                        found_contacts.append(expected_contact)

            if len(found_contacts) >= 2:
                checks_passed.append(f"[C] Lead Nurturing Pipeline has rows for Cold/Lead contacts: {found_contacts}")
            elif len(found_contacts) >= 1:
                checks_passed.append(f"[C] Lead Nurturing Pipeline has at least 1 Cold/Lead contact")
            else:
                errors.append("[X] Lead Nurturing Pipeline missing rows for Leads/Cold contacts (James Wilson, Amanda Lee)")

        # =========================================================================
        # PART 4: Validate Segmentation Dashboard page
        # =========================================================================

        dashboard_page = None

        # Try multiple queries to find the dashboard page
        # First, try to find pages with parentId = db-projects-row-6
        child_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {"parentId": "db-projects-row-6"}
        })

        for page in child_pages:
            title = page.get("title", "")
            if "segmentation dashboard" in title.lower() or "🎯" in title:
                dashboard_page = page
                break

        # If not found, try querying all pages (without type filter)
        if not dashboard_page:
            all_pages = final_state_backend.query({"collection": "pages", "filter": {}})
            for page in all_pages:
                title = page.get("title", "")
                if "segmentation dashboard" in title.lower() or "🎯" in title:
                    dashboard_page = page
                    break

        # Fallback: Try type: "normal"
        if not dashboard_page:
            normal_pages = final_state_backend.query({"collection": "pages", "filter": {"type": "normal"}})
            for page in normal_pages:
                title = page.get("title", "")
                if "segmentation dashboard" in title.lower() or "🎯" in title:
                    dashboard_page = page
                    break

        if not dashboard_page:
            errors.append("[X] '🎯 Segmentation Dashboard' page not found")
        else:
            checks_passed.append("[C] Found 'Segmentation Dashboard' page")

            # Check parent is Sales Pipeline Automation (db-projects-row-6)
            parent_id = dashboard_page.get("parentId", "")
            if parent_id == "db-projects-row-6":
                checks_passed.append("[C] Dashboard is under Sales Pipeline Automation project")
            else:
                # Check parent title
                parent = final_state_backend.query({"collection": "pages", "filter": {"_id": parent_id}})
                if parent and "sales pipeline" in str(parent[0].get("title", "")).lower():
                    checks_passed.append("[C] Dashboard is under Sales Pipeline Automation project")
                else:
                    errors.append(f"[X] Dashboard parent is '{parent_id}', expected Sales Pipeline Automation")

            # Get blocks for dashboard
            dashboard_id = dashboard_page.get("_id", "")
            blocks = final_state_backend.query({
                "collection": "blocks",
                "filter": {"pageId": dashboard_id}
            })

            if not blocks:
                errors.append("[X] Dashboard page has no content blocks")
            else:
                # Check for heading_1 "📊 Contact Segmentation Overview"
                found_h1 = False
                for block in blocks:
                    if block.get("type") == "heading1":
                        content = str(block.get("content", "")).lower()
                        if "segmentation" in content or "contact" in content:
                            found_h1 = True
                            checks_passed.append("[C] Found heading_1 for Contact Segmentation Overview")
                            break

                if not found_h1:
                    errors.append("[X] Missing heading_1 '📊 Contact Segmentation Overview'")

                # Check for heading_2 "🔥 Hot Leads Requiring Action"
                found_h2 = False
                for block in blocks:
                    if block.get("type") == "heading2":
                        content = str(block.get("content", "")).lower()
                        if "hot" in content or "leads" in content or "action" in content:
                            found_h2 = True
                            checks_passed.append("[C] Found heading_2 for Hot Leads Requiring Action")
                            break

                if not found_h2:
                    errors.append("[X] Missing heading_2 '🔥 Hot Leads Requiring Action'")

                # Check for numbered list with Hot contacts (Sarah Johnson, Lisa Thompson)
                numbered_lists = [b for b in blocks if b.get("type") == "numberedList"]
                hot_contacts_found = []

                for nl in numbered_lists:
                    content = str(nl.get("content", "")).lower()
                    if "sarah" in content:
                        hot_contacts_found.append("Sarah Johnson")
                    if "lisa" in content:
                        hot_contacts_found.append("Lisa Thompson")

                if len(hot_contacts_found) >= 2:
                    checks_passed.append(f"[C] Numbered list contains Hot contacts: {hot_contacts_found}")
                elif len(hot_contacts_found) >= 1:
                    checks_passed.append(f"[C] Numbered list has at least 1 Hot contact")
                else:
                    # Check bulleted lists too
                    bulleted_lists = [b for b in blocks if b.get("type") == "bulletedList"]
                    for bl in bulleted_lists:
                        content = str(bl.get("content", "")).lower()
                        if "sarah" in content:
                            hot_contacts_found.append("Sarah Johnson")
                        if "lisa" in content:
                            hot_contacts_found.append("Lisa Thompson")

                    if hot_contacts_found:
                        checks_passed.append(f"[C] Found Hot contacts in list: {hot_contacts_found}")
                    else:
                        errors.append("[X] Numbered list missing Hot contacts (Sarah Johnson, Lisa Thompson)")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in contact scoring validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_weekly_standup_content_insertion(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate content insertion in Weekly Standup Template page.

    Task requires adding these new blocks to page-9:
    1. heading_3: "⏱️ Time Estimates"
    2. callout: yellow background, "💡 Tip: Always include realistic time estimates for better planning"
    3. paragraph: "Current workload analysis: Team Member has 1 tasks in progress."
       (1 is the count of user-3's In Progress tasks from Task Tracker)
    4. divider block

    Note: MCP API appends blocks at the end. We validate that blocks exist with correct content.
    """
    checks_passed = []
    errors = []

    try:
        # =========================================================================
        # Get all blocks for Weekly Standup Template (page-9)
        # =========================================================================

        blocks = final_state_backend.query({
            "collection": "blocks",
            "filter": {"pageId": "page-9"}
        })

        if not blocks:
            errors.append("[X] No blocks found on Weekly Standup Template page (page-9)")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        # Sort blocks by order
        sorted_blocks = sorted(blocks, key=lambda b: b.get("order", 0))

        # =========================================================================
        # Verify original content still exists
        # =========================================================================

        # Find "What I plan to do next week" heading
        next_week_heading = None
        for block in sorted_blocks:
            if block.get("type") == "heading2":
                content = str(block.get("content", "")).lower()
                if "what i plan to do next week" in content or "plan to do next" in content:
                    next_week_heading = block
                    break

        if not next_week_heading:
            errors.append("[X] 'What I plan to do next week' heading not found")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        checks_passed.append("[C] Found 'What I plan to do next week' heading")

        # Verify original todo still exists
        found_original_todo = False
        for block in sorted_blocks:
            if block.get("type") == "todo":
                content = str(block.get("content", "")).lower()
                if "start working on feature" in content or "feature y" in content:
                    found_original_todo = True
                    break

        if found_original_todo:
            checks_passed.append("[C] Original todo 'Start working on feature Y' still exists")
        else:
            errors.append("[X] Original todo 'Start working on feature Y' not found")

        # =========================================================================
        # Check 1: heading_3 with "⏱️ Time Estimates" exists on page
        # =========================================================================

        heading3_block = None
        for block in sorted_blocks:
            if block.get("type") == "heading3":
                content = str(block.get("content", ""))
                if "time estimates" in content.lower() or "⏱️" in content:
                    heading3_block = block
                    checks_passed.append("[C] Found heading_3 '⏱️ Time Estimates'")
                    break

        if not heading3_block:
            errors.append("[X] heading_3 '⏱️ Time Estimates' not found")

        # =========================================================================
        # Check 2: callout with yellow background
        # =========================================================================

        callout_block = None
        for block in sorted_blocks:
            if block.get("type") == "callout":
                content = str(block.get("content", ""))
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", "").lower()

                if "realistic time estimates" in content.lower() or "better planning" in content.lower():
                    callout_block = block
                    checks_passed.append("[C] Found callout with tip text")

                    # Check yellow background
                    if bg_color == "yellow":
                        checks_passed.append("[C] Callout has yellow background")
                    else:
                        errors.append(f"[X] Callout background is '{bg_color}', expected 'yellow'")
                    break

        if not callout_block:
            errors.append("[X] Callout with tip text not found")

        # =========================================================================
        # Check 3: paragraph with workload analysis
        # Expected: "Current workload analysis: Team Member has 1 tasks in progress."
        # =========================================================================

        paragraph_block = None
        for block in sorted_blocks:
            if block.get("type") == "paragraph":
                content = str(block.get("content", ""))
                if "workload analysis" in content.lower() or "tasks in progress" in content.lower():
                    paragraph_block = block
                    checks_passed.append("[C] Found paragraph with workload analysis")

                    # Check for correct count (1)
                    if "1 tasks in progress" in content.lower() or "1 task in progress" in content.lower():
                        checks_passed.append("[C] Workload analysis shows correct count (1)")
                    elif "team member" in content.lower():
                        # Partial credit - has Team Member but maybe wrong count
                        checks_passed.append("[C] Workload analysis mentions Team Member")
                    else:
                        errors.append(f"[X] Workload analysis may have incorrect count. Content: '{content}'")
                    break

        if not paragraph_block:
            errors.append("[X] Paragraph with workload analysis not found")

        # =========================================================================
        # Check 4: divider block
        # =========================================================================

        next_week_order = next_week_heading.get("order", 0)
        divider_block = None
        for block in sorted_blocks:
            if block.get("type") == "divider":
                block_order = block.get("order", 0)
                if block_order > next_week_order:
                    divider_block = block
                    checks_passed.append("[C] Found divider block after heading")
                    break

        if not divider_block:
            errors.append("[X] Divider block not found after 'What I plan to do next week' heading")

        # =========================================================================
        # Check 5: Verify order of new blocks (heading3 < callout < paragraph < divider)
        # Note: We only check relative ordering between new blocks since MCP appends at end
        # =========================================================================

        if heading3_block and callout_block and paragraph_block and divider_block:
            h3_order = heading3_block.get("order", 0)
            callout_order = callout_block.get("order", 0)
            para_order = paragraph_block.get("order", 0)
            div_order = divider_block.get("order", 0)

            # Check relative ordering between new blocks
            if h3_order < callout_order:
                checks_passed.append("[C] heading_3 comes before callout")
            else:
                errors.append("[X] heading_3 should come before callout")

            if callout_order < para_order:
                checks_passed.append("[C] callout comes before paragraph")
            else:
                errors.append("[X] callout should come before paragraph")

            if para_order < div_order:
                checks_passed.append("[C] paragraph comes before divider")
            else:
                errors.append("[X] paragraph should come before divider")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in weekly standup validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_weekly_sprint_report(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Weekly Sprint Report page creation.

    Task requires:
    1. Create 'Weekly Sprint Report' page as child of 'Meeting Notes' (page-3)
    2. heading_1 with '🚀 Weekly Sprint Report - Week of [Monday date]'
    3. heading_2 '📊 Sprint Progress' with green callout for sprint velocity
    4. heading_3 '✅ Completed This Week' with numbered list of Done tasks
    5. heading_3 '🔄 In Review' with numbered list of In Review tasks
    6. heading_2 '⚠️ Blockers and Risks' with red callouts for blocked tasks
    7. heading_2 '📈 Team Performance' with inline database 'Sprint Metrics'

    Expected data from Task Tracker (db-tasks) for current week (2026-01-13 to 2026-01-19):
    - Done tasks in current week: 0 (Done tasks have due dates before current week)
    - In Progress: 2 (Fix login auth - Alice, Create onboarding - Carol)
    - In Review: 1 (Design dashboard - Bob)
    - Blocked: 1 (Update database schema - Bob)

    User mapping:
    - user-1: Alice Chen
    - user-2: Bob Martinez
    - user-3: Carol Williams
    """
    checks_passed = []
    errors = []

    try:
        # =========================================================================
        # Check 1: Weekly Sprint Report page exists as child of Meeting Notes
        # =========================================================================

        # Find the Weekly Sprint Report page
        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {}
        })

        sprint_report_page = None
        for page in all_pages:
            title = str(page.get("title", "")).lower()
            if "weekly sprint report" in title or "sprint report" in title:
                sprint_report_page = page
                break

        if not sprint_report_page:
            errors.append("[X] 'Weekly Sprint Report' page not found")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        checks_passed.append("[C] Found 'Weekly Sprint Report' page")

        # Check parent is Meeting Notes (page-3)
        parent_id = sprint_report_page.get("parentId")
        if parent_id == "page-3":
            checks_passed.append("[C] Page is child of 'Meeting Notes' (page-3)")
        else:
            errors.append(f"[X] Page parent is '{parent_id}', expected 'page-3' (Meeting Notes)")

        page_id = sprint_report_page.get("_id")

        # Get all blocks for the page
        blocks = final_state_backend.query({
            "collection": "blocks",
            "filter": {"pageId": page_id}
        })

        if not blocks:
            errors.append("[X] No blocks found on Weekly Sprint Report page")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        sorted_blocks = sorted(blocks, key=lambda b: b.get("order", 0))

        # =========================================================================
        # Check 2: heading_1 with Weekly Sprint Report and Week of
        # =========================================================================

        found_h1 = False
        for block in sorted_blocks:
            if block.get("type") == "heading1":
                content = str(block.get("content", ""))
                if "sprint report" in content.lower() and "week of" in content.lower():
                    found_h1 = True
                    checks_passed.append("[C] Found heading_1 with 'Sprint Report' and 'Week of'")
                    break

        if not found_h1:
            errors.append("[X] heading_1 with 'Sprint Report' and 'Week of' not found")

        # =========================================================================
        # Check 3: heading_2 Sprint Progress
        # =========================================================================

        found_sprint_progress = False
        for block in sorted_blocks:
            if block.get("type") == "heading2":
                content = str(block.get("content", "")).lower()
                if "sprint progress" in content:
                    found_sprint_progress = True
                    checks_passed.append("[C] Found heading_2 'Sprint Progress'")
                    break

        if not found_sprint_progress:
            errors.append("[X] heading_2 'Sprint Progress' not found")

        # =========================================================================
        # Check 4: Green callout with Sprint Velocity
        # =========================================================================

        found_velocity_callout = False
        for block in sorted_blocks:
            if block.get("type") == "callout":
                content = str(block.get("content", "")).lower()
                properties = block.get("properties", {})
                bg_color = str(properties.get("backgroundColor", "")).lower()

                if "sprint velocity" in content:
                    found_velocity_callout = True
                    checks_passed.append("[C] Found callout with 'Sprint Velocity'")

                    if "green" in bg_color:
                        checks_passed.append("[C] Sprint Velocity callout has green background")
                    else:
                        errors.append(f"[X] Sprint Velocity callout background is '{bg_color}', expected 'green'")
                    break

        if not found_velocity_callout:
            errors.append("[X] Callout with 'Sprint Velocity' not found")

        # =========================================================================
        # Check 5: heading_3 Completed This Week
        # =========================================================================

        found_completed_heading = False
        for block in sorted_blocks:
            if block.get("type") == "heading3":
                content = str(block.get("content", "")).lower()
                if "completed" in content and "week" in content:
                    found_completed_heading = True
                    checks_passed.append("[C] Found heading_3 'Completed This Week'")
                    break

        if not found_completed_heading:
            errors.append("[X] heading_3 'Completed This Week' not found")

        # =========================================================================
        # Check 6: heading_3 In Review
        # =========================================================================

        found_in_review_heading = False
        for block in sorted_blocks:
            if block.get("type") == "heading3":
                content = str(block.get("content", "")).lower()
                if "in review" in content:
                    found_in_review_heading = True
                    checks_passed.append("[C] Found heading_3 'In Review'")
                    break

        if not found_in_review_heading:
            errors.append("[X] heading_3 'In Review' not found")

        # Check for In Review task in numbered list
        # Expected: "Design new dashboard layout - Bob Martinez"
        found_in_review_task = False
        for block in sorted_blocks:
            if block.get("type") in ["numberedList", "numbered_list", "numberedlist"]:
                content = str(block.get("content", "")).lower()
                if "design" in content and ("bob" in content or "dashboard" in content):
                    found_in_review_task = True
                    checks_passed.append("[C] Found In Review task 'Design new dashboard layout'")
                    break

        if not found_in_review_task:
            # Also check paragraph blocks in case numbered list wasn't used
            for block in sorted_blocks:
                content = str(block.get("content", "")).lower()
                if "design" in content and "dashboard" in content and "bob" in content:
                    found_in_review_task = True
                    checks_passed.append("[C] Found In Review task reference")
                    break

        if not found_in_review_task:
            errors.append("[X] In Review task 'Design new dashboard layout - Bob Martinez' not found")

        # =========================================================================
        # Check 7: heading_2 Blockers and Risks
        # =========================================================================

        found_blockers_heading = False
        for block in sorted_blocks:
            if block.get("type") == "heading2":
                content = str(block.get("content", "")).lower()
                if "blocker" in content or "risk" in content:
                    found_blockers_heading = True
                    checks_passed.append("[C] Found heading_2 'Blockers and Risks'")
                    break

        if not found_blockers_heading:
            errors.append("[X] heading_2 'Blockers and Risks' not found")

        # =========================================================================
        # Check 8: Red callout for blocked task
        # Expected: "Update database schema" - Bob Martinez - Urgent
        # =========================================================================

        found_blocked_callout = False
        for block in sorted_blocks:
            if block.get("type") == "callout":
                content = str(block.get("content", "")).lower()
                properties = block.get("properties", {})
                bg_color = str(properties.get("backgroundColor", "")).lower()

                if "blocked" in content and ("database" in content or "schema" in content):
                    found_blocked_callout = True
                    checks_passed.append("[C] Found callout for blocked task 'Update database schema'")

                    if "red" in bg_color:
                        checks_passed.append("[C] Blocked task callout has red background")
                    else:
                        errors.append(f"[X] Blocked task callout background is '{bg_color}', expected 'red'")

                    # Check for assignee and priority info
                    if "bob" in content or "assignee" in content:
                        checks_passed.append("[C] Blocked callout includes assignee info")

                    if "urgent" in content or "priority" in content:
                        checks_passed.append("[C] Blocked callout includes priority info")

                    if "days" in content or "blocked for" in content:
                        checks_passed.append("[C] Blocked callout includes days blocked info")
                    break

        if not found_blocked_callout:
            errors.append("[X] Red callout for blocked task 'Update database schema' not found")

        # =========================================================================
        # Check 9: heading_2 Team Performance
        # =========================================================================

        found_team_performance = False
        for block in sorted_blocks:
            if block.get("type") == "heading2":
                content = str(block.get("content", "")).lower()
                if "team performance" in content:
                    found_team_performance = True
                    checks_passed.append("[C] Found heading_2 'Team Performance'")
                    break

        if not found_team_performance:
            errors.append("[X] heading_2 'Team Performance' not found")

        # =========================================================================
        # Check 10: Sprint Metrics database exists
        # =========================================================================

        all_dbs = final_state_backend.query({
            "collection": "pages",
            "filter": {"type": "database"}
        })

        sprint_metrics_db = None
        for db in all_dbs:
            title = str(db.get("title", "")).lower()
            if "sprint metrics" in title:
                sprint_metrics_db = db
                break

        if not sprint_metrics_db:
            errors.append("[X] 'Sprint Metrics' database not found")
        else:
            checks_passed.append("[C] Found 'Sprint Metrics' database")

            # Check database columns
            db_metadata = sprint_metrics_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name", "").lower() for p in properties]

            expected_cols = ["team member", "tasks completed", "tasks in progress", "efficiency score"]
            for col in expected_cols:
                if any(col in pn for pn in prop_names):
                    checks_passed.append(f"[C] Sprint Metrics has '{col}' column")
                else:
                    errors.append(f"[X] Sprint Metrics missing '{col}' column")

            # Check for rows (one per unique assignee)
            db_id = sprint_metrics_db.get("_id")
            db_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": db_id, "type": "row"}
            })

            if db_rows and len(db_rows) > 0:
                checks_passed.append(f"[C] Sprint Metrics has {len(db_rows)} row(s)")
            else:
                errors.append("[X] Sprint Metrics database has no rows")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in weekly sprint report validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_q1_planning_hub(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate Q1 2026 Planning Hub task.

    Task requires:
    1. Create hub page under Onboarding Guide (page-13) with heading_1 '🎯 Q1 2026 Planning Hub'
    2. Add inline database 'Q1 Milestones' with properties:
       - Milestone (title/text)
       - Owner (select: Alice/Bob/Carol)
       - Due Week (select: Week 1-13)
       - Status (checkbox)
       - Dependencies (multi_select)
    3. Add rows for Active projects: [Project Name] - Phase 1
       - Website Redesign → Alice (user-1)
       - Customer Portal → Carol (user-3)
       - Sales Pipeline Automation → Bob (user-2)
    4. Add heading_2 '📈 Resource Allocation' with budget sum paragraph ($160,000)
    5. Add heading_2 '🚨 Critical Path Items' with High/Urgent priority tasks
    """
    checks_passed = []
    errors = []

    try:
        # =========================================================================
        # Check 1: Hub page exists under Onboarding Guide (page-13)
        # =========================================================================

        # Find Q1 2026 Planning Hub page
        hub_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {"title": {"$regex": "Q1.*2026.*Planning.*Hub", "$options": "i"}}
        })

        if not hub_pages or len(hub_pages) == 0:
            errors.append("[X] 'Q1 2026 Planning Hub' page not found")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        hub_page = hub_pages[0]
        hub_page_id = hub_page.get("_id")
        checks_passed.append("[C] Found 'Q1 2026 Planning Hub' page")

        # Check parent is Onboarding Guide (page-13)
        parent_id = hub_page.get("parentId")
        if parent_id == "page-13":
            checks_passed.append("[C] Hub page is child of Onboarding Guide (page-13)")
        else:
            errors.append(f"[X] Hub page parent is '{parent_id}', expected 'page-13'")

        # =========================================================================
        # Check 2: Page has heading_1 with Q1 2026 Planning Hub
        # =========================================================================

        # Get blocks from page content or blocks collection
        blocks = hub_page.get("content", [])
        if not blocks:
            blocks = final_state_backend.query({
                "collection": "blocks",
                "filter": {"pageId": hub_page_id}
            }) or []

        sorted_blocks = sorted(blocks, key=lambda b: b.get("order", 0))

        found_heading1 = False
        for block in sorted_blocks:
            if block.get("type") == "heading1":
                content = str(block.get("content", ""))
                if "Q1 2026 Planning Hub" in content or "planning hub" in content.lower():
                    found_heading1 = True
                    checks_passed.append("[C] Found heading_1 with 'Q1 2026 Planning Hub'")
                    break

        if not found_heading1:
            errors.append("[X] heading_1 '🎯 Q1 2026 Planning Hub' not found")

        # =========================================================================
        # Check 3: Q1 Milestones database exists
        # =========================================================================

        all_dbs = final_state_backend.query({
            "collection": "pages",
            "filter": {"type": "database"}
        })

        q1_milestones_db = None
        for db in all_dbs:
            title = str(db.get("title", "")).lower()
            if "q1 milestones" in title or "q1milestones" in title:
                q1_milestones_db = db
                break

        if not q1_milestones_db:
            errors.append("[X] 'Q1 Milestones' database not found")
        else:
            checks_passed.append("[C] Found 'Q1 Milestones' database")
            db_id = q1_milestones_db.get("_id")

            # =========================================================================
            # Check 4: Database has required properties
            # =========================================================================

            db_metadata = q1_milestones_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])
            prop_names = [p.get("name", "").lower() for p in properties]

            # Check Owner property (select with Alice/Bob/Carol)
            owner_prop = None
            for prop in properties:
                if "owner" in prop.get("name", "").lower():
                    owner_prop = prop
                    break

            if owner_prop:
                checks_passed.append("[C] 'Owner' property exists")
                if owner_prop.get("type") == "select":
                    checks_passed.append("[C] 'Owner' is select type")
                    options = owner_prop.get("options", [])
                    option_names = [o.get("name", "").lower() for o in options]
                    for name in ["alice", "bob", "carol"]:
                        if any(name in on for on in option_names):
                            checks_passed.append(f"[C] Owner has '{name}' option")
                        else:
                            errors.append(f"[X] Owner missing '{name}' option")
            else:
                errors.append("[X] 'Owner' property not found")

            # Check Due Week property (select)
            due_week_prop = None
            for prop in properties:
                if "due week" in prop.get("name", "").lower() or "dueweek" in prop.get("name", "").lower():
                    due_week_prop = prop
                    break

            if due_week_prop:
                checks_passed.append("[C] 'Due Week' property exists")
                if due_week_prop.get("type") == "select":
                    checks_passed.append("[C] 'Due Week' is select type")
                    options = due_week_prop.get("options", [])
                    # Check for at least some week options
                    if len(options) >= 10:
                        checks_passed.append(f"[C] Due Week has {len(options)} week options")
                    else:
                        errors.append(f"[X] Due Week has only {len(options)} options, expected 13")
            else:
                errors.append("[X] 'Due Week' property not found")

            # Check Status property (checkbox)
            status_prop = None
            for prop in properties:
                if "status" in prop.get("name", "").lower():
                    status_prop = prop
                    break

            if status_prop:
                checks_passed.append("[C] 'Status' property exists")
                if status_prop.get("type") == "checkbox":
                    checks_passed.append("[C] 'Status' is checkbox type")
            else:
                errors.append("[X] 'Status' property not found")

            # Check Dependencies property (multi_select)
            deps_prop = None
            for prop in properties:
                if "dependencies" in prop.get("name", "").lower():
                    deps_prop = prop
                    break

            if deps_prop:
                checks_passed.append("[C] 'Dependencies' property exists")
                if deps_prop.get("type") == "multi_select":
                    checks_passed.append("[C] 'Dependencies' is multi_select type")
            else:
                errors.append("[X] 'Dependencies' property not found")

            # =========================================================================
            # Check 5: Database has milestone rows for Active projects
            # =========================================================================

            db_rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": db_id, "type": "row"}
            })

            if db_rows and len(db_rows) >= 3:
                checks_passed.append(f"[C] Q1 Milestones has {len(db_rows)} row(s)")

                # Check for expected milestones
                row_titles = [r.get("title", "").lower() for r in db_rows]

                expected_milestones = [
                    ("website redesign", "phase 1"),
                    ("customer portal", "phase 1"),
                    ("sales pipeline", "phase 1")
                ]

                for project, phase in expected_milestones:
                    found = False
                    for title in row_titles:
                        if project in title and phase in title:
                            found = True
                            break
                    if found:
                        checks_passed.append(f"[C] Found milestone for '{project}'")
                    else:
                        errors.append(f"[X] Missing milestone for '{project} - Phase 1'")
            else:
                errors.append(f"[X] Q1 Milestones has {len(db_rows) if db_rows else 0} rows, expected at least 3")

        # =========================================================================
        # Check 6: Resource Allocation heading and budget paragraph
        # =========================================================================

        found_resource_heading = False
        found_budget_paragraph = False

        for block in sorted_blocks:
            if block.get("type") == "heading2":
                content = str(block.get("content", "")).lower()
                if "resource" in content and "allocation" in content:
                    found_resource_heading = True
                    checks_passed.append("[C] Found heading_2 'Resource Allocation'")

            if block.get("type") == "paragraph":
                content = str(block.get("content", ""))
                # Check for budget sum: $160,000 or $160000
                if ("160,000" in content or "160000" in content) and ("budget" in content.lower() or "$" in content):
                    found_budget_paragraph = True
                    checks_passed.append("[C] Found budget paragraph with $160,000")

        if not found_resource_heading:
            errors.append("[X] heading_2 '📈 Resource Allocation' not found")

        if not found_budget_paragraph:
            errors.append("[X] Budget paragraph with $160,000 sum not found")

        # =========================================================================
        # Check 7: Critical Path Items heading and task list
        # =========================================================================

        found_critical_heading = False
        found_critical_tasks = False

        for block in sorted_blocks:
            if block.get("type") == "heading2":
                content = str(block.get("content", "")).lower()
                if "critical" in content and ("path" in content or "item" in content):
                    found_critical_heading = True
                    checks_passed.append("[C] Found heading_2 'Critical Path Items'")

            # Check for High/Urgent priority tasks in lists
            if block.get("type") in ["bulletedList", "bulleted_list", "numberedList", "numbered_list", "paragraph"]:
                content = str(block.get("content", "")).lower()
                # Expected tasks: Fix login authentication bug, Implement user notifications,
                # Update database schema, Add unit tests for auth module
                high_priority_tasks = [
                    "login authentication",
                    "user notifications",
                    "database schema",
                    "unit tests"
                ]
                for task in high_priority_tasks:
                    if task in content:
                        found_critical_tasks = True
                        break

        if not found_critical_heading:
            errors.append("[X] heading_2 '🚨 Critical Path Items' not found")

        if found_critical_tasks:
            checks_passed.append("[C] Found High/Urgent priority tasks in Critical Path section")
        else:
            errors.append("[X] No High/Urgent priority tasks found in Critical Path section")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in Q1 planning hub validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Guideline Compliance Checklist
# =============================================================================
def _validate_guideline_compliance_checklist(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Guideline Compliance Checklist task.

    Requirements:
    1. heading_2 '📊 Project Metrics Tracking' added after Security callout (order 6)
    2. Inline database 'Guideline Compliance Checklist' with correct schema
    3. 6 rows for unique guidelines with correct Category assignments
    4. Purple callout with compliance score at the end
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Expected unique guidelines (text after colon) and their categories
    EXPECTED_GUIDELINES = {
        "Always write unit tests before merging to main": "Testing",
        "Do not commit API keys or secrets to the repository": "Security",
        "Production deployments must be approved by two team leads": "Deployment",
        "Use TypeScript strict mode for all new files": "Code Quality",
        "Update the changelog for all user-facing changes": "Code Quality",
        "All user inputs must be sanitized and validated": "Security",
    }

    try:
        # =========================================================================
        # PART 1: Validate Project Guidelines page exists (page-21)
        # =========================================================================

        guidelines_page = final_state_backend.query({
            "collection": "pages",
            "filter": {"_id": "page-21"}
        })

        if not guidelines_page:
            errors.append("[X] Project Guidelines & Best Practices page (page-21) not found")
            errors.append("[X] Project Guidelines page not found")
        else:
            checks_passed.append("[C] Project Guidelines page")

        checks_passed.append("[C] Project Guidelines & Best Practices page found")

        # Get all blocks for the page
        blocks_results = final_state_backend.query({
            "collection": "blocks",
            "filter": {"pageId": "page-21"}
        }) or []

        sorted_blocks = sorted(blocks_results, key=lambda b: b.get("order", 0))

        # =========================================================================
        # PART 2: Validate heading_2 '📊 Project Metrics Tracking' exists
        # =========================================================================

        found_metrics_heading = False
        metrics_heading_order = -1
        for block in sorted_blocks:
            if block.get("type") in ["heading2", "heading_2"]:
                content = str(block.get("content", ""))
                if "Project Metrics Tracking" in content:
                    found_metrics_heading = True
                    metrics_heading_order = block.get("order", -1)
                    break

        if found_metrics_heading:
            checks_passed.append("[C] Found heading_2 '📊 Project Metrics Tracking'")
        else:
            errors.append("[X] heading_2 '📊 Project Metrics Tracking' not found")

        # =========================================================================
        # PART 3: Validate heading position (after Security callout order 6)
        # =========================================================================

        # The Security callout with 🔒 icon should be at order 6 (block-21-7)
        # The new heading should be inserted after it
        if metrics_heading_order > 6:
            checks_passed.append(f"[C] Heading is positioned after Security callout (order {metrics_heading_order})")
        elif metrics_heading_order >= 0:
            errors.append(f"[X] Heading should be after Security callout (order 6), found at order {metrics_heading_order}")

        # =========================================================================
        # PART 4: Validate inline database 'Guideline Compliance Checklist' exists
        # =========================================================================

        all_databases = final_state_backend.query({
            "collection": "pages",
            "filter": {"type": "database"}
        }) or []

        compliance_db = None
        for db in all_databases:
            title = db.get("title", "")
            if "Guideline Compliance Checklist" in title:
                compliance_db = db
                break

        if not compliance_db:
            errors.append("[X] Inline database 'Guideline Compliance Checklist' not found")
            return TaskScore(
                score=0.0,
                metadata=ScoreMetadata(
                    success_accumulator=list(dict.fromkeys(checks_passed)),
                    error_accumulator=list(dict.fromkeys(errors)),
                )
            )

        checks_passed.append("[C] Found inline database 'Guideline Compliance Checklist'")
        db_id = compliance_db.get("_id")

        # Check it's inline (parentId should be page-21)
        if compliance_db.get("parentId") == "page-21":
            checks_passed.append("[C] Database is inline on page-21")
        else:
            errors.append(f"[X] Database should be inline on page-21, found parentId: {compliance_db.get('parentId')}")

        # =========================================================================
        # PART 5: Validate database schema (properties)
        # =========================================================================

        db_metadata = compliance_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])
        prop_dict = {p.get("name"): p for p in properties}

        # Check 'Category' select property with correct options
        if "Category" in prop_dict:
            cat_prop = prop_dict["Category"]
            if cat_prop.get("type") == "select":
                options = cat_prop.get("options", [])
                option_names = {opt.get("name") for opt in options}
                expected_categories = {"Security", "Testing", "Deployment", "Code Quality"}
                if expected_categories.issubset(option_names):
                    checks_passed.append("[C] Category select property has all required options")
                else:
                    missing = expected_categories - option_names
                    errors.append(f"[X] Category select property missing options: {missing}")
            else:
                errors.append(f"[X] Category property should be 'select', found '{cat_prop.get('type')}'")
        else:
            errors.append("[X] 'Category' property not found in database schema")

        # Check 'Compliance Status' checkbox property
        if "Compliance Status" in prop_dict:
            cs_prop = prop_dict["Compliance Status"]
            if cs_prop.get("type") == "checkbox":
                checks_passed.append("[C] 'Compliance Status' checkbox property exists")
            else:
                errors.append(f"[X] 'Compliance Status' should be 'checkbox', found '{cs_prop.get('type')}'")
        else:
            errors.append("[X] 'Compliance Status' property not found in database schema")

        # Check 'Last Reviewed' date property
        if "Last Reviewed" in prop_dict:
            lr_prop = prop_dict["Last Reviewed"]
            if lr_prop.get("type") == "date":
                checks_passed.append("[C] 'Last Reviewed' date property exists")
            else:
                errors.append(f"[X] 'Last Reviewed' should be 'date', found '{lr_prop.get('type')}'")
        else:
            errors.append("[X] 'Last Reviewed' property not found in database schema")

        # =========================================================================
        # PART 6: Validate 6 rows with correct data
        # =========================================================================

        db_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": db_id, "type": "row"}
        }) or []

        if len(db_rows) == 6:
            checks_passed.append(f"[C] Found exactly 6 database rows")
        else:
            errors.append(f"[X] Expected 6 database rows, found {len(db_rows)}")

        # Check each expected guideline exists with correct category
        found_guidelines = {}
        for row in db_rows:
            title = row.get("title", "")
            cell_values = row.get("cellValues", {})

            # Try to match the guideline text
            for expected_guideline, expected_category in EXPECTED_GUIDELINES.items():
                if expected_guideline.lower() in title.lower() or title.lower() in expected_guideline.lower():
                    found_guidelines[expected_guideline] = {
                        "row": row,
                        "category": None,
                        "compliance_status": None,
                    }
                    # Find the Category value
                    for key, value in cell_values.items():
                        if "category" in key.lower():
                            found_guidelines[expected_guideline]["category"] = value
                        if "compliance" in key.lower() or "status" in key.lower():
                            found_guidelines[expected_guideline]["compliance_status"] = value
                    break

        # Validate each guideline row
        for guideline, expected_category in EXPECTED_GUIDELINES.items():
            if guideline in found_guidelines:
                checks_passed.append(f"[C] Found row for guideline: '{guideline[:40]}...'")
                row_data = found_guidelines[guideline]
                actual_category = row_data.get("category")
                if actual_category == expected_category:
                    checks_passed.append(f"[C] Category correct for '{guideline[:30]}...': {expected_category}")
                elif actual_category:
                    errors.append(f"[X] Wrong category for '{guideline[:30]}...': expected '{expected_category}', got '{actual_category}'")
            else:
                errors.append(f"[X] Missing row for guideline: '{guideline[:40]}...'")

        # =========================================================================
        # PART 7: Validate compliance score callout
        # =========================================================================

        found_compliance_callout = False
        for block in sorted_blocks:
            if block.get("type") == "callout":
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", "")
                icon = properties.get("icon", "")
                content = str(block.get("content", ""))

                if "purple" in bg_color.lower():
                    if "Compliance Score" in content and "0%" in content and "0 of 6" in content:
                        found_compliance_callout = True
                        break

        if found_compliance_callout:
            checks_passed.append("[C] Found purple compliance score callout with '0% (0 of 6 guidelines verified)'")
        else:
            errors.append("[X] Purple callout with 'Compliance Score: 0% (0 of 6 guidelines verified)' not found")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in guideline compliance checklist validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Executive Dashboard Q1 2026
# =============================================================================
def _validate_executive_dashboard_q1_2026(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Executive Dashboard Q1 2026 task.

    Requirements:
    1. Page 'Executive Dashboard Q1 2026' exists as child of Getting Started (page-1)
    2. heading_1 '📊 Executive Dashboard Q1 2026'
    3. heading_2 '🎯 Task Overview'
    4. Blue callout with 'Task Completion Rate: 25%' (2/8 tasks done)
    5. heading_3 'By Priority' with priority breakdown
    6. heading_2 '💼 Department Budgets'
    7. Numbered list with departments sorted by budget descending
    8. Divider and quote block with task/project counts
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Expected values from data analysis
    TOTAL_TASKS = 8
    TASKS_DONE = 2
    COMPLETION_RATE = 25  # 2/8 = 25%
    TOTAL_PROJECTS = 6

    # Department budgets (sorted descending)
    EXPECTED_DEPARTMENTS = [
        ("Engineering", 225000, 3),
        ("Sales", 35000, 1),
        ("Marketing", 25000, 1),
        ("HR", 15000, 1),
    ]

    try:
        # =========================================================================
        # PART 1: Validate dashboard page exists as child of Getting Started
        # =========================================================================

        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {}
        }) or []

        dashboard_page = None
        for page in all_pages:
            title = page.get("title", "")
            if "Executive Dashboard Q1 2026" in title:
                dashboard_page = page
                break

        if not dashboard_page:
            errors.append("[X] Page 'Executive Dashboard Q1 2026' not found")
            errors.append("[X] Dashboard page not found")
        else:
            checks_passed.append("[C] Dashboard page")

        checks_passed.append("[C] Found page 'Executive Dashboard Q1 2026'")

        # Check parent is page-1 (Getting Started)
        if dashboard_page.get("parentId") == "page-1":
            checks_passed.append("[C] Dashboard is child of Getting Started (page-1)")
        else:
            errors.append(f"[X] Dashboard should be child of page-1, found parentId: {dashboard_page.get('parentId')}")

        page_id = dashboard_page.get("_id")

        # Get all blocks for the dashboard page
        blocks_results = final_state_backend.query({
            "collection": "blocks",
            "filter": {"pageId": page_id}
        }) or []

        sorted_blocks = sorted(blocks_results, key=lambda b: b.get("order", 0))

        # =========================================================================
        # PART 2: Validate heading_1 '📊 Executive Dashboard Q1 2026'
        # =========================================================================

        found_heading1 = False
        for block in sorted_blocks:
            if block.get("type") in ["heading1", "heading_1"]:
                content = str(block.get("content", ""))
                if "Executive Dashboard Q1 2026" in content:
                    found_heading1 = True
                    break

        if found_heading1:
            checks_passed.append("[C] Found heading_1 '📊 Executive Dashboard Q1 2026'")
        else:
            errors.append("[X] heading_1 '📊 Executive Dashboard Q1 2026' not found")

        # =========================================================================
        # PART 3: Validate heading_2 '🎯 Task Overview'
        # =========================================================================

        found_task_overview = False
        for block in sorted_blocks:
            if block.get("type") in ["heading2", "heading_2"]:
                content = str(block.get("content", ""))
                if "Task Overview" in content:
                    found_task_overview = True
                    break

        if found_task_overview:
            checks_passed.append("[C] Found heading_2 '🎯 Task Overview'")
        else:
            errors.append("[X] heading_2 '🎯 Task Overview' not found")

        # =========================================================================
        # PART 4: Validate blue callout with completion rate
        # =========================================================================

        found_completion_callout = False
        for block in sorted_blocks:
            if block.get("type") == "callout":
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", "")
                content = str(block.get("content", ""))

                if "blue" in bg_color.lower():
                    if "Task Completion Rate" in content and "25" in content:
                        found_completion_callout = True
                        break

        if found_completion_callout:
            checks_passed.append("[C] Found blue callout with 'Task Completion Rate: 25%'")
        else:
            errors.append("[X] Blue callout with 'Task Completion Rate: 25%' not found")

        # =========================================================================
        # PART 5: Validate heading_3 'By Priority'
        # =========================================================================

        found_by_priority = False
        for block in sorted_blocks:
            if block.get("type") in ["heading3", "heading_3"]:
                content = str(block.get("content", ""))
                if "By Priority" in content or "Priority" in content:
                    found_by_priority = True
                    break

        if found_by_priority:
            checks_passed.append("[C] Found heading_3 'By Priority'")
        else:
            errors.append("[X] heading_3 'By Priority' not found")

        # =========================================================================
        # PART 6: Validate heading_2 '💼 Department Budgets'
        # =========================================================================

        found_dept_budgets = False
        for block in sorted_blocks:
            if block.get("type") in ["heading2", "heading_2"]:
                content = str(block.get("content", ""))
                if "Department Budgets" in content:
                    found_dept_budgets = True
                    break

        if found_dept_budgets:
            checks_passed.append("[C] Found heading_2 '💼 Department Budgets'")
        else:
            errors.append("[X] heading_2 '💼 Department Budgets' not found")

        # =========================================================================
        # PART 7: Validate numbered list with department budgets
        # =========================================================================

        numbered_lists = [b for b in sorted_blocks if b.get("type") in ["numberedList", "numbered_list"]]

        # Check for each department in the numbered lists
        found_engineering = False
        found_sales = False
        found_marketing = False
        found_hr = False

        for block in numbered_lists:
            content = str(block.get("content", ""))
            if "Engineering" in content and ("225" in content or "225000" in content or "225,000" in content):
                found_engineering = True
            if "Sales" in content and ("35" in content or "35000" in content or "35,000" in content):
                found_sales = True
            if "Marketing" in content and ("25" in content or "25000" in content or "25,000" in content):
                found_marketing = True
            if "HR" in content and ("15" in content or "15000" in content or "15,000" in content):
                found_hr = True

        if found_engineering:
            checks_passed.append("[C] Found Engineering budget ($225,000) in numbered list")
        else:
            errors.append("[X] Engineering budget ($225,000) not found in numbered list")

        if found_sales:
            checks_passed.append("[C] Found Sales budget ($35,000) in numbered list")
        else:
            errors.append("[X] Sales budget ($35,000) not found in numbered list")

        if found_marketing:
            checks_passed.append("[C] Found Marketing budget ($25,000) in numbered list")
        else:
            errors.append("[X] Marketing budget ($25,000) not found in numbered list")

        if found_hr:
            checks_passed.append("[C] Found HR budget ($15,000) in numbered list")
        else:
            errors.append("[X] HR budget ($15,000) not found in numbered list")

        # =========================================================================
        # PART 8: Validate divider exists
        # =========================================================================

        found_divider = any(b.get("type") == "divider" for b in sorted_blocks)
        if found_divider:
            checks_passed.append("[C] Found divider block")
        else:
            errors.append("[X] Divider block not found")

        # =========================================================================
        # PART 9: Validate quote block with counts
        # =========================================================================

        found_quote = False
        for block in sorted_blocks:
            if block.get("type") == "quote":
                content = str(block.get("content", ""))
                # Check for task and project counts
                if "8" in content and "6" in content and "tasks" in content.lower() and "projects" in content.lower():
                    found_quote = True
                    break

        if found_quote:
            checks_passed.append("[C] Found quote block with '8 tasks and 6 projects'")
        else:
            errors.append("[X] Quote block with task/project counts not found")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in executive dashboard validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# ==============================================================================
# NOTE: Temporarily commented out to use the version in notion_v2.py which returns Tuple[float, str]
# The evaluator expects (score, msg) tuple, not TaskScore object
# =============================================================================
# Financial Audit Summary Report
# =============================================================================
def _validate_financial_audit_summary_report(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Financial Audit Summary Report task.

    Requirements:
    1. 'Financial Summary Report' database exists as child of Company Handbook (page-16)
    2. Database has 6 properties: Category (title), Subcategory (text), Amount (number),
       Type (select with 4 options), Date Recorded (date), Notes (text)
    3. Type select has options: Asset (green), Liability (red), Revenue (blue), Expense (orange)
    4. 3 rows with correct data:
       - Active Projects: Amount=160000, Type=Expense
       - Planned Projects: Amount=100000, Type=Liability
       - Inventory Assets: Amount=56855, Type=Asset
    5. Company Handbook has heading_2 '💰 Financial Snapshot' after title
    6. Paragraph with Total Assets and Active Commitments
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Expected values from data analysis
    ACTIVE_PROJECTS_BUDGET = 160000  # 50000 + 75000 + 35000
    PLANNING_PROJECTS_BUDGET = 100000
    INVENTORY_VALUE = 56855  # sum of quantity * price for in-stock items
    COMPANY_HANDBOOK_ID = "page-16"

    try:
        # =========================================================================
        # PART 1: Find Financial Summary Report database
        # =========================================================================

        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {}
        }) or []

        report_db = None
        for page in all_pages:
            title = page.get("title", "")
            if "Financial Summary Report" in title and page.get("type") == "database":
                report_db = page
                break

        if not report_db:
            errors.append("[X] 'Financial Summary Report' database not found")
            errors.append("[X] Financial Summary Report database not found")
        else:
            checks_passed.append("[C] Financial Summary Report database")

        checks_passed.append("[C] Found 'Financial Summary Report' database")

        # Check parent is Company Handbook (page-16)
        if report_db.get("parentId") == COMPANY_HANDBOOK_ID:
            checks_passed.append("[C] Database is child of Company Handbook")
        else:
            errors.append(f"[X] Database should be child of page-16, found parentId: {report_db.get('parentId')}")

        db_id = report_db.get("_id")

        # =========================================================================
        # PART 2: Validate database properties
        # =========================================================================

        db_metadata = report_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Check for required properties
        # Note: Title property may be named "Category" or "Name" depending on how database was created
        required_props = {
            "Subcategory": "text",
            "Amount": "number",
            "Type": "select",
            "Date Recorded": "date",
            "Notes": "text"
        }

        # Check title property exists (can be named "Category" or "Name")
        title_prop_found = False
        for prop in properties:
            if prop.get("type") == "title":
                title_prop_found = True
                checks_passed.append(f"[C] Title property '{prop.get('name')}' exists")
                break
        if not title_prop_found:
            errors.append("[X] Title property not found")

        found_props = {}
        type_property = None

        for prop in properties:
            prop_name = prop.get("name", "")
            prop_type = prop.get("type", "")
            found_props[prop_name] = prop_type
            if prop_name == "Type":
                type_property = prop

        for prop_name, expected_type in required_props.items():
            if prop_name in found_props:
                if found_props[prop_name] == expected_type:
                    checks_passed.append(f"[C] Property '{prop_name}' exists with type '{expected_type}'")
                else:
                    errors.append(f"[X] Property '{prop_name}' has type '{found_props[prop_name]}', expected '{expected_type}'")
            else:
                errors.append(f"[X] Property '{prop_name}' not found")

        # =========================================================================
        # PART 3: Validate Type select options
        # =========================================================================

        if type_property:
            options = type_property.get("options", [])
            expected_options = {
                "Asset": "green",
                "Liability": "red",
                "Revenue": "blue",
                "Expense": "orange"
            }

            found_options = {opt.get("name"): opt.get("color") for opt in options}

            for opt_name, expected_color in expected_options.items():
                if opt_name in found_options:
                    if found_options[opt_name] == expected_color:
                        checks_passed.append(f"[C] Type option '{opt_name}' has color '{expected_color}'")
                    else:
                        errors.append(f"[X] Type option '{opt_name}' has color '{found_options[opt_name]}', expected '{expected_color}'")
                else:
                    errors.append(f"[X] Type option '{opt_name}' not found")

        # =========================================================================
        # PART 4: Validate database rows
        # =========================================================================

        rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": db_id, "type": "row"}
        }) or []

        # Check for 3 required rows
        found_active_projects = False
        found_planned_projects = False
        found_inventory_assets = False

        for row in rows:
            title = row.get("title", "")
            cell_values = row.get("cellValues", {})

            # Find Amount and Type values - try different property ID formats
            amount = None
            row_type = None

            for key, value in cell_values.items():
                if "amount" in key.lower() or key == "Amount":
                    amount = value
                if "type" in key.lower() and key != "type":
                    row_type = value

            # Also check by common patterns
            if amount is None:
                for key, value in cell_values.items():
                    if isinstance(value, (int, float)) and value > 1000:
                        amount = value
                        break

            if "Active Projects" in title:
                found_active_projects = True
                if amount is not None:
                    tolerance = ACTIVE_PROJECTS_BUDGET * 0.05
                    if abs(amount - ACTIVE_PROJECTS_BUDGET) <= tolerance:
                        checks_passed.append(f"[C] 'Active Projects' row has correct amount (~{ACTIVE_PROJECTS_BUDGET})")
                    else:
                        errors.append(f"[X] 'Active Projects' amount is {amount}, expected ~{ACTIVE_PROJECTS_BUDGET}")
                if row_type == "Expense":
                    checks_passed.append("[C] 'Active Projects' row has Type='Expense'")
                elif row_type:
                    errors.append(f"[X] 'Active Projects' Type is '{row_type}', expected 'Expense'")

            elif "Planned Projects" in title:
                found_planned_projects = True
                if amount is not None:
                    tolerance = PLANNING_PROJECTS_BUDGET * 0.05
                    if abs(amount - PLANNING_PROJECTS_BUDGET) <= tolerance:
                        checks_passed.append(f"[C] 'Planned Projects' row has correct amount (~{PLANNING_PROJECTS_BUDGET})")
                    else:
                        errors.append(f"[X] 'Planned Projects' amount is {amount}, expected ~{PLANNING_PROJECTS_BUDGET}")
                if row_type == "Liability":
                    checks_passed.append("[C] 'Planned Projects' row has Type='Liability'")
                elif row_type:
                    errors.append(f"[X] 'Planned Projects' Type is '{row_type}', expected 'Liability'")

            elif "Inventory Assets" in title:
                found_inventory_assets = True
                if amount is not None:
                    tolerance = INVENTORY_VALUE * 0.05
                    if abs(amount - INVENTORY_VALUE) <= tolerance:
                        checks_passed.append(f"[C] 'Inventory Assets' row has correct amount (~{INVENTORY_VALUE})")
                    else:
                        errors.append(f"[X] 'Inventory Assets' amount is {amount}, expected ~{INVENTORY_VALUE}")
                if row_type == "Asset":
                    checks_passed.append("[C] 'Inventory Assets' row has Type='Asset'")
                elif row_type:
                    errors.append(f"[X] 'Inventory Assets' Type is '{row_type}', expected 'Asset'")

        if not found_active_projects:
            errors.append("[X] Row 'Active Projects' not found")
        if not found_planned_projects:
            errors.append("[X] Row 'Planned Projects' not found")
        if not found_inventory_assets:
            errors.append("[X] Row 'Inventory Assets' not found")

        # =========================================================================
        # PART 5: Validate Company Handbook page updates
        # =========================================================================

        handbook_blocks = final_state_backend.query({
            "collection": "blocks",
            "filter": {"pageId": COMPANY_HANDBOOK_ID}
        }) or []

        sorted_blocks = sorted(handbook_blocks, key=lambda b: b.get("order", 0))

        # Check for heading_2 '💰 Financial Snapshot'
        found_financial_heading = False
        financial_heading_order = None

        for block in sorted_blocks:
            block_type = block.get("type", "")
            content = str(block.get("content", ""))

            if block_type in ["heading2", "heading_2"] and "Financial Snapshot" in content:
                found_financial_heading = True
                financial_heading_order = block.get("order", 0)
                if "💰" in content:
                    checks_passed.append("[C] Found heading_2 '💰 Financial Snapshot'")
                else:
                    checks_passed.append("[C] Found heading_2 'Financial Snapshot'")
                break

        if not found_financial_heading:
            errors.append("[X] heading_2 '💰 Financial Snapshot' not found on Company Handbook")

        # Check for paragraph with totals after the heading
        if financial_heading_order is not None:
            found_summary_para = False
            for block in sorted_blocks:
                block_type = block.get("type", "")
                content = str(block.get("content", ""))
                order = block.get("order", 0)

                if block_type == "paragraph" and order > financial_heading_order:
                    # Check for Total Assets and Active Commitments
                    if "Total Assets" in content or "Assets" in content:
                        if "56" in content or "56855" in content or "56,855" in content:
                            checks_passed.append("[C] Summary paragraph contains Total Assets value")
                            found_summary_para = True
                        if "160" in content or "160000" in content or "160,000" in content:
                            checks_passed.append("[C] Summary paragraph contains Active Commitments value")
                        break

            if not found_summary_para:
                errors.append("[X] Summary paragraph with financial totals not found")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in financial audit validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Inventory Low Stock Alert System
# =============================================================================
def _validate_inventory_low_stock_alert(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Inventory Low Stock Alert task.

    Requirements:
    1. 'Low Stock Alert' database exists as child of Inventory Tracker (db-inventory)
    2. Database has properties: Item (title), Current Stock (number), Value at Risk (number),
       Reorder Priority (select with Critical/red, High/orange, Medium/yellow)
    3. 3 rows for items with quantity < 10:
       - Office Chair Ergonomic: qty=8, value=3600, priority=Medium
       - Standing Desk: qty=3, value=2097, priority=Critical
       - Wireless Keyboard: qty=0, value=0, priority=Critical
    4. heading_2 '📉 Low Stock Alert System' on db-inventory page
    5. Orange callout with total value ($56,855), item count (3), critical count (2)
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Expected values
    INVENTORY_TRACKER_ID = "db-inventory"
    TOTAL_INVENTORY_VALUE = 56855
    ITEMS_BELOW_THRESHOLD = 3
    CRITICAL_REORDERS = 2

    # Expected low stock items
    EXPECTED_ITEMS = {
        "Office Chair Ergonomic": {"stock": 8, "value": 3600, "priority": "Medium"},
        "Standing Desk": {"stock": 3, "value": 2097, "priority": "Critical"},
        "Wireless Keyboard": {"stock": 0, "value": 0, "priority": "Critical"},
    }

    try:
        # =========================================================================
        # PART 1: Find Low Stock Alert database
        # =========================================================================

        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {}
        }) or []

        alert_db = None
        for page in all_pages:
            title = page.get("title", "")
            if "Low Stock Alert" in title and page.get("type") == "database":
                alert_db = page
                break

        if not alert_db:
            errors.append("[X] 'Low Stock Alert' database not found")
            errors.append("[X] Low Stock Alert database not found")
        else:
            checks_passed.append("[C] Low Stock Alert database")

        checks_passed.append("[C] Found 'Low Stock Alert' database")

        # Check parent is Inventory Tracker (db-inventory)
        if alert_db.get("parentId") == INVENTORY_TRACKER_ID:
            checks_passed.append("[C] Database is child of Inventory Tracker")
        else:
            errors.append(f"[X] Database should be child of db-inventory, found parentId: {alert_db.get('parentId')}")

        db_id = alert_db.get("_id")

        # =========================================================================
        # PART 2: Validate database properties
        # =========================================================================

        db_metadata = alert_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        required_props = {
            "Current Stock": "number",
            "Value at Risk": "number",
            "Reorder Priority": "select"
        }

        # Check title property exists
        title_prop_found = False
        for prop in properties:
            if prop.get("type") == "title":
                title_prop_found = True
                checks_passed.append(f"[C] Title property '{prop.get('name')}' exists")
                break
        if not title_prop_found:
            errors.append("[X] Title property not found")

        found_props = {}
        priority_property = None

        for prop in properties:
            prop_name = prop.get("name", "")
            prop_type = prop.get("type", "")
            found_props[prop_name] = prop_type
            if prop_name == "Reorder Priority":
                priority_property = prop

        for prop_name, expected_type in required_props.items():
            if prop_name in found_props:
                if found_props[prop_name] == expected_type:
                    checks_passed.append(f"[C] Property '{prop_name}' exists with type '{expected_type}'")
                else:
                    errors.append(f"[X] Property '{prop_name}' has type '{found_props[prop_name]}', expected '{expected_type}'")
            else:
                errors.append(f"[X] Property '{prop_name}' not found")

        # =========================================================================
        # PART 3: Validate Reorder Priority select options
        # =========================================================================

        if priority_property:
            options = priority_property.get("options", [])
            expected_options = {
                "Critical": "red",
                "High": "orange",
                "Medium": "yellow"
            }

            found_options = {opt.get("name"): opt.get("color") for opt in options}

            for opt_name, expected_color in expected_options.items():
                if opt_name in found_options:
                    if found_options[opt_name] == expected_color:
                        checks_passed.append(f"[C] Priority option '{opt_name}' has color '{expected_color}'")
                    else:
                        errors.append(f"[X] Priority option '{opt_name}' has color '{found_options[opt_name]}', expected '{expected_color}'")
                else:
                    errors.append(f"[X] Priority option '{opt_name}' not found")

        # =========================================================================
        # PART 4: Validate database rows
        # =========================================================================

        rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": db_id, "type": "row"}
        }) or []

        if len(rows) == ITEMS_BELOW_THRESHOLD:
            checks_passed.append(f"[C] Database has {ITEMS_BELOW_THRESHOLD} rows")
        else:
            errors.append(f"[X] Database has {len(rows)} rows, expected {ITEMS_BELOW_THRESHOLD}")

        # Check each expected item
        for item_name, expected_data in EXPECTED_ITEMS.items():
            found_item = False
            for row in rows:
                title = row.get("title", "")
                if item_name in title:
                    found_item = True
                    cell_values = row.get("cellValues", {})

                    # Find stock, value, and priority
                    stock = None
                    value = None
                    priority = None

                    for key, val in cell_values.items():
                        key_lower = key.lower()
                        if "stock" in key_lower or "current" in key_lower:
                            stock = val
                        elif "value" in key_lower or "risk" in key_lower:
                            value = val
                        elif "priority" in key_lower or "reorder" in key_lower:
                            priority = val

                    # Validate stock
                    if stock == expected_data["stock"]:
                        checks_passed.append(f"[C] '{item_name}' has Current Stock={expected_data['stock']}")
                    elif stock is not None:
                        errors.append(f"[X] '{item_name}' Current Stock is {stock}, expected {expected_data['stock']}")

                    # Validate priority
                    if priority == expected_data["priority"]:
                        checks_passed.append(f"[C] '{item_name}' has Priority='{expected_data['priority']}'")
                    elif priority is not None:
                        errors.append(f"[X] '{item_name}' Priority is '{priority}', expected '{expected_data['priority']}'")

                    break

            if not found_item:
                errors.append(f"[X] Row for '{item_name}' not found")

        # =========================================================================
        # PART 5: Validate heading and callout on Inventory Tracker page
        # =========================================================================

        inventory_blocks = final_state_backend.query({
            "collection": "blocks",
            "filter": {"pageId": INVENTORY_TRACKER_ID}
        }) or []

        # Check for heading_2 '📉 Low Stock Alert System'
        found_heading = False
        for block in inventory_blocks:
            block_type = block.get("type", "")
            content = str(block.get("content", ""))

            if block_type in ["heading2", "heading_2"] and "Low Stock Alert" in content:
                found_heading = True
                checks_passed.append("[C] Found heading_2 '📉 Low Stock Alert System'")
                break

        if not found_heading:
            errors.append("[X] heading_2 '📉 Low Stock Alert System' not found")

        # Check for orange callout with statistics
        found_callout = False
        for block in inventory_blocks:
            block_type = block.get("type", "")
            properties = block.get("properties", {})
            content = str(block.get("content", ""))
            bg_color = properties.get("backgroundColor", "")

            if block_type == "callout" and "orange" in bg_color.lower():
                found_callout = True
                checks_passed.append("[C] Found callout with orange background")

                # Check for total value
                if "56855" in content or "56,855" in content:
                    checks_passed.append("[C] Callout contains total inventory value ($56,855)")
                else:
                    errors.append("[X] Callout missing total inventory value ($56,855)")

                # Check for item count (3)
                if "3" in content:
                    checks_passed.append("[C] Callout contains items below threshold count")
                else:
                    errors.append("[X] Callout missing items below threshold count (3)")

                # Check for critical count (2)
                if "2" in content:
                    checks_passed.append("[C] Callout contains critical reorders count")
                else:
                    errors.append("[X] Callout missing critical reorders count (2)")

                break

        if not found_callout:
            errors.append("[X] Orange callout not found on Inventory Tracker page")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in inventory low stock alert validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Project Milestones Tracker
# =============================================================================
def _validate_project_milestones_tracker(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Project Milestones Tracker task.

    Requirements:
    1. 'Project Milestones' database exists as child of Project Portfolio (db-projects)
    2. Database has properties: Milestone (title), Project (text), Date (date),
       Status (select with Not Started/gray, In Progress/blue, Completed/green, Delayed/red),
       Owner (text), Dependencies (multi_select: Design, Engineering, Marketing, Legal, Finance),
       Impact (select: Low/green, Medium/yellow, High/orange, Critical/red)
    3. 9 milestone rows (3 per active project: Kickoff, Mid Review, Completion)
    4. Milestone dates match project timelines
    5. Project Roadmap page has heading_2 '📅 Upcoming Milestones'
    6. Bulleted list with 5 milestones following the heading
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Expected values
    PROJECT_PORTFOLIO_ID = "db-projects"
    PROJECT_ROADMAP_ID = "page-2"

    # Active projects with their timelines (from initial_data.json)
    ACTIVE_PROJECTS = {
        "Website Redesign": {"start": "2025-12-13", "end": "2026-03-13"},
        "Customer Portal": {"start": "2025-11-28", "end": "2026-02-11"},
        "Sales Pipeline Automation": {"start": "2025-12-28", "end": "2026-02-26"},
    }

    # Expected milestone row count
    EXPECTED_ROW_COUNT = 9

    try:
        # =========================================================================
        # PART 1: Find Project Milestones database
        # =========================================================================

        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {}
        }) or []

        milestones_db = None
        for page in all_pages:
            title = page.get("title", "")
            if "Project Milestones" in title and page.get("type") == "database":
                milestones_db = page
                break

        if not milestones_db:
            errors.append("[X] 'Project Milestones' database not found")
            errors.append("[X] Project Milestones database not found")
        else:
            checks_passed.append("[C] Project Milestones database")

        checks_passed.append("[C] Found 'Project Milestones' database")

        # Check parent is Project Portfolio (db-projects)
        if milestones_db.get("parentId") == PROJECT_PORTFOLIO_ID:
            checks_passed.append("[C] Database is child of Project Portfolio")
        else:
            errors.append(f"[X] Database should be child of db-projects, found parentId: {milestones_db.get('parentId')}")

        db_id = milestones_db.get("_id")

        # =========================================================================
        # PART 2: Validate database properties
        # =========================================================================

        db_metadata = milestones_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        required_props = {
            "Project": "text",
            "Date": "date",
            "Status": "select",
            "Owner": "text",
            "Dependencies": "multi_select",
            "Impact": "select"
        }

        # Check title property exists
        title_prop_found = False
        for prop in properties:
            if prop.get("type") == "title":
                title_prop_found = True
                checks_passed.append(f"[C] Title property '{prop.get('name')}' exists")
                break
        if not title_prop_found:
            errors.append("[X] Title property not found")

        found_props = {}
        status_property = None
        impact_property = None
        dependencies_property = None

        for prop in properties:
            prop_name = prop.get("name", "")
            prop_type = prop.get("type", "")
            found_props[prop_name] = prop_type
            if prop_name == "Status":
                status_property = prop
            if prop_name == "Impact":
                impact_property = prop
            if prop_name == "Dependencies":
                dependencies_property = prop

        for prop_name, expected_type in required_props.items():
            if prop_name in found_props:
                if found_props[prop_name] == expected_type:
                    checks_passed.append(f"[C] Property '{prop_name}' exists with type '{expected_type}'")
                else:
                    errors.append(f"[X] Property '{prop_name}' has type '{found_props[prop_name]}', expected '{expected_type}'")
            else:
                errors.append(f"[X] Property '{prop_name}' not found")

        # =========================================================================
        # PART 3: Validate Status select options
        # =========================================================================

        if status_property:
            options = status_property.get("options", [])
            expected_options = {
                "Not Started": "gray",
                "In Progress": "blue",
                "Completed": "green",
                "Delayed": "red"
            }

            found_options = {opt.get("name"): opt.get("color") for opt in options}

            for opt_name, expected_color in expected_options.items():
                if opt_name in found_options:
                    if found_options[opt_name] == expected_color:
                        checks_passed.append(f"[C] Status option '{opt_name}' has color '{expected_color}'")
                    else:
                        errors.append(f"[X] Status option '{opt_name}' has color '{found_options[opt_name]}', expected '{expected_color}'")
                else:
                    errors.append(f"[X] Status option '{opt_name}' not found")

        # =========================================================================
        # PART 4: Validate Impact select options
        # =========================================================================

        if impact_property:
            options = impact_property.get("options", [])
            expected_options = {
                "Low": "green",
                "Medium": "yellow",
                "High": "orange",
                "Critical": "red"
            }

            found_options = {opt.get("name"): opt.get("color") for opt in options}

            for opt_name, expected_color in expected_options.items():
                if opt_name in found_options:
                    if found_options[opt_name] == expected_color:
                        checks_passed.append(f"[C] Impact option '{opt_name}' has color '{expected_color}'")
                    else:
                        errors.append(f"[X] Impact option '{opt_name}' has color '{found_options[opt_name]}', expected '{expected_color}'")
                else:
                    errors.append(f"[X] Impact option '{opt_name}' not found")

        # =========================================================================
        # PART 5: Validate Dependencies multi_select options
        # =========================================================================

        if dependencies_property:
            options = dependencies_property.get("options", [])
            expected_deps = ["Design", "Engineering", "Marketing", "Legal", "Finance"]

            found_deps = [opt.get("name") for opt in options]

            for dep in expected_deps:
                if dep in found_deps:
                    checks_passed.append(f"[C] Dependencies option '{dep}' exists")
                else:
                    errors.append(f"[X] Dependencies option '{dep}' not found")

        # =========================================================================
        # PART 6: Validate database rows (9 milestones)
        # =========================================================================

        rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": db_id, "type": "row"}
        }) or []

        if len(rows) == EXPECTED_ROW_COUNT:
            checks_passed.append(f"[C] Database has {EXPECTED_ROW_COUNT} rows")
        else:
            errors.append(f"[X] Database has {len(rows)} rows, expected {EXPECTED_ROW_COUNT}")

        # Check milestone titles and dates
        expected_milestones = []
        for project_name, timeline in ACTIVE_PROJECTS.items():
            expected_milestones.append(f"{project_name} - Kickoff")
            expected_milestones.append(f"{project_name} - Mid Review")
            expected_milestones.append(f"{project_name} - Completion")

        found_titles = [row.get("title", "") for row in rows]

        for expected_title in expected_milestones:
            # Check if any row title contains the expected milestone
            found = False
            for title in found_titles:
                if expected_title in title:
                    found = True
                    break
            if found:
                checks_passed.append(f"[C] Milestone '{expected_title}' found")
            else:
                errors.append(f"[X] Milestone '{expected_title}' not found")

        # =========================================================================
        # PART 7: Validate Project Roadmap page has heading
        # =========================================================================

        roadmap_page = final_state_backend.query({
            "collection": "pages",
            "filter": {"_id": PROJECT_ROADMAP_ID}
        })

        if isinstance(roadmap_page, list) and roadmap_page:
            roadmap_page = roadmap_page[0]

        if not roadmap_page:
            errors.append("[X] Project Roadmap page not found")
        else:
            # Check content for heading_2 with "Upcoming Milestones"
            content = roadmap_page.get("content", [])

            # Also check blocks collection
            blocks = final_state_backend.query({
                "collection": "blocks",
                "filter": {"pageId": PROJECT_ROADMAP_ID}
            }) or []

            found_heading = False
            found_bullets = 0

            # Check both content array and blocks
            all_content = content + blocks

            for block in all_content:
                block_type = block.get("type", "")
                block_content = str(block.get("content", "") or "")

                if block_type in ["heading2", "heading_2"] and "Upcoming Milestones" in block_content:
                    found_heading = True
                    checks_passed.append("[C] Found heading_2 '📅 Upcoming Milestones' on Project Roadmap")

                if block_type in ["bulletedList", "bulleted_list"]:
                    found_bullets += 1

            if not found_heading:
                errors.append("[X] heading_2 '📅 Upcoming Milestones' not found on Project Roadmap")

            if found_bullets >= 5:
                checks_passed.append(f"[C] Found {found_bullets} bulleted list items (expected at least 5)")
            else:
                errors.append(f"[X] Found {found_bullets} bulleted list items, expected at least 5")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in project milestones tracker validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# Sprint Workload Analysis
# =============================================================================
def _validate_sprint_workload_analysis(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Sprint Workload Analysis task.

    Requirements:
    1. '📊 Sprint Workload Analysis' page exists as child of Project Roadmap (page-2)
    2. Page has heading_1 '📊 Sprint Workload Analysis'
    3. Page has callout with blue background and text containing 'Generated on' and 'Sprint Overview'
    4. Page has heading_2 '📈 Task Status Breakdown'
    5. Page has 5 bulleted list items with correct status counts:
       - Not Started: 2, In Progress: 2, In Review: 1, Done: 2, Blocked: 1
    6. Page has paragraph with 'Sprint Completion Rate: 25% (2 of 8 tasks completed)'
    7. Page has heading_2 '🚨 Overdue Tasks'
    8. Page has numbered list items for 2 overdue tasks
    9. Inline database 'Weekly Task Summary' exists as child of the new page
    10. Database has properties: Week (text), Tasks Created (number), Tasks Completed (number), Completion Rate (%) (number)
    11. Database has 1 row with: Week='Current Week', Tasks Created=8, Tasks Completed=2, Completion Rate=25
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Expected values
    PROJECT_ROADMAP_ID = "page-2"

    # Expected status counts from Task Tracker database
    EXPECTED_STATUS_COUNTS = {
        "Not Started": 2,
        "In Progress": 2,
        "In Review": 1,
        "Done": 2,
        "Blocked": 1
    }

    TOTAL_TASKS = 8
    DONE_TASKS = 2
    COMPLETION_RATE = 25  # (2/8) * 100 = 25%

    # Overdue tasks (due date < 2026-01-15 AND status != Done)
    # Fix login authentication bug - 2026-01-14 - In Progress - Alice Chen
    # Update database schema - 2026-01-13 - Blocked - Bob Martinez
    EXPECTED_OVERDUE_TASKS = [
        {"name": "Fix login authentication bug", "assignee": "Alice Chen", "due": "2026-01-14"},
        {"name": "Update database schema", "assignee": "Bob Martinez", "due": "2026-01-13"},
    ]

    try:
        # =========================================================================
        # PART 1: Find '📊 Sprint Workload Analysis' page
        # =========================================================================

        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {}
        }) or []

        analysis_page = None
        for page in all_pages:
            title = page.get("title", "")
            if "Sprint Workload Analysis" in title and page.get("type") in ["normal", None]:
                analysis_page = page
                break

        if not analysis_page:
            errors.append("[X] '📊 Sprint Workload Analysis' page not found")
            errors.append("[X] Sprint Workload Analysis page not found")
        else:
            checks_passed.append("[C] Sprint Workload Analysis page")

        checks_passed.append("[C] Found '📊 Sprint Workload Analysis' page")

        # Check parent is Project Roadmap (page-2)
        if analysis_page.get("parentId") == PROJECT_ROADMAP_ID:
            checks_passed.append("[C] Page is child of Project Roadmap")
        else:
            errors.append(f"[X] Page should be child of page-2, found parentId: {analysis_page.get('parentId')}")

        page_id = analysis_page.get("_id")

        # =========================================================================
        # PART 2: Get page content and blocks
        # =========================================================================

        page_content = analysis_page.get("content", [])

        blocks = final_state_backend.query({
            "collection": "blocks",
            "filter": {"pageId": page_id}
        }) or []

        all_content = page_content + blocks

        # =========================================================================
        # PART 3: Validate heading_1 '📊 Sprint Workload Analysis'
        # =========================================================================

        found_heading1 = False
        for block in all_content:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")

            if block_type in ["heading1", "heading_1"] and "Sprint Workload Analysis" in content:
                found_heading1 = True
                checks_passed.append("[C] Found heading_1 '📊 Sprint Workload Analysis'")
                break

        if not found_heading1:
            errors.append("[X] heading_1 '📊 Sprint Workload Analysis' not found")

        # =========================================================================
        # PART 4: Validate callout with blue background
        # =========================================================================

        found_callout = False
        for block in all_content:
            block_type = block.get("type", "")
            properties = block.get("properties", {})
            content = str(block.get("content", "") or "")
            bg_color = properties.get("backgroundColor", "")

            if block_type == "callout" and "blue" in bg_color.lower():
                found_callout = True
                checks_passed.append("[C] Found callout with blue background")

                # Check for 'Generated on' and 'Sprint Overview'
                if "Generated on" in content or "generated on" in content.lower():
                    checks_passed.append("[C] Callout contains 'Generated on'")
                else:
                    errors.append("[X] Callout missing 'Generated on' text")

                if "Sprint Overview" in content:
                    checks_passed.append("[C] Callout contains 'Sprint Overview'")
                else:
                    errors.append("[X] Callout missing 'Sprint Overview' text")
                break

        if not found_callout:
            errors.append("[X] Callout with blue background not found")

        # =========================================================================
        # PART 5: Validate heading_2 '📈 Task Status Breakdown'
        # =========================================================================

        found_status_heading = False
        for block in all_content:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")

            if block_type in ["heading2", "heading_2"] and "Task Status Breakdown" in content:
                found_status_heading = True
                checks_passed.append("[C] Found heading_2 '📈 Task Status Breakdown'")
                break

        if not found_status_heading:
            errors.append("[X] heading_2 '📈 Task Status Breakdown' not found")

        # =========================================================================
        # PART 6: Validate bulleted list with status counts
        # =========================================================================

        bullet_blocks = [b for b in all_content if b.get("type") in ["bulletedList", "bulleted_list"]]

        status_counts_found = 0
        for status, expected_count in EXPECTED_STATUS_COUNTS.items():
            for block in bullet_blocks:
                content = str(block.get("content", "") or "")
                if status in content and str(expected_count) in content:
                    status_counts_found += 1
                    checks_passed.append(f"[C] Found status count '{status}: {expected_count}'")
                    break

        if status_counts_found < 5:
            errors.append(f"[X] Found {status_counts_found}/5 status counts in bulleted list")

        # =========================================================================
        # PART 7: Validate paragraph with Sprint Completion Rate
        # =========================================================================

        found_completion_rate = False
        for block in all_content:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")

            if block_type == "paragraph" and "Sprint Completion Rate" in content:
                found_completion_rate = True
                checks_passed.append("[C] Found paragraph with 'Sprint Completion Rate'")

                # Check for percentage and counts
                if "25%" in content or "25 %" in content:
                    checks_passed.append("[C] Completion rate percentage is correct (25%)")
                else:
                    errors.append("[X] Completion rate percentage incorrect, expected 25%")

                if "2 of 8" in content or "(2/8)" in content:
                    checks_passed.append("[C] Task counts are correct (2 of 8)")
                else:
                    errors.append("[X] Task counts incorrect, expected '2 of 8'")
                break

        if not found_completion_rate:
            errors.append("[X] Paragraph with 'Sprint Completion Rate' not found")

        # =========================================================================
        # PART 8: Validate heading_2 '🚨 Overdue Tasks'
        # =========================================================================

        found_overdue_heading = False
        for block in all_content:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")

            if block_type in ["heading2", "heading_2"] and "Overdue Tasks" in content:
                found_overdue_heading = True
                checks_passed.append("[C] Found heading_2 '🚨 Overdue Tasks'")
                break

        if not found_overdue_heading:
            errors.append("[X] heading_2 '🚨 Overdue Tasks' not found")

        # =========================================================================
        # PART 9: Validate numbered list with overdue tasks
        # =========================================================================

        numbered_blocks = [b for b in all_content if b.get("type") in ["numberedList", "numbered_list"]]

        if len(numbered_blocks) >= 2:
            checks_passed.append(f"[C] Found {len(numbered_blocks)} numbered list items for overdue tasks")
        else:
            errors.append(f"[X] Found {len(numbered_blocks)} numbered list items, expected at least 2")

        # Check for specific overdue task info
        overdue_found = 0
        for task in EXPECTED_OVERDUE_TASKS:
            for block in numbered_blocks:
                content = str(block.get("content", "") or "")
                if task["name"] in content:
                    overdue_found += 1
                    checks_passed.append(f"[C] Overdue task '{task['name']}' found in list")
                    break

        if overdue_found < 2:
            errors.append(f"[X] Found {overdue_found}/2 expected overdue tasks")

        # =========================================================================
        # PART 10: Find 'Weekly Task Summary' inline database
        # =========================================================================

        summary_db = None
        for page in all_pages:
            title = page.get("title", "")
            if "Weekly Task Summary" in title and page.get("type") == "database":
                summary_db = page
                break

        if not summary_db:
            errors.append("[X] 'Weekly Task Summary' database not found")
        else:
            checks_passed.append("[C] Found 'Weekly Task Summary' database")

            # Check parent is the analysis page
            if summary_db.get("parentId") == page_id:
                checks_passed.append("[C] Database is child of Sprint Workload Analysis page")
            else:
                errors.append(f"[X] Database should be child of analysis page, found parentId: {summary_db.get('parentId')}")

            db_id = summary_db.get("_id")

            # =========================================================================
            # PART 11: Validate database properties
            # =========================================================================

            db_metadata = summary_db.get("databaseMetadata", {})
            properties = db_metadata.get("properties", [])

            required_props = {
                "Tasks Created": "number",
                "Tasks Completed": "number",
                "Completion Rate (%)": "number"
            }

            # Check title property (Week)
            title_prop_found = False
            for prop in properties:
                if prop.get("type") == "title":
                    title_prop_found = True
                    prop_name = prop.get("name", "")
                    if "Week" in prop_name:
                        checks_passed.append(f"[C] Title property 'Week' exists")
                    else:
                        checks_passed.append(f"[C] Title property '{prop_name}' exists")
                    break
            if not title_prop_found:
                errors.append("[X] Title property (Week) not found")

            found_props = {}
            for prop in properties:
                prop_name = prop.get("name", "")
                prop_type = prop.get("type", "")
                found_props[prop_name] = prop_type

            for prop_name, expected_type in required_props.items():
                if prop_name in found_props:
                    if found_props[prop_name] == expected_type:
                        checks_passed.append(f"[C] Property '{prop_name}' exists with type '{expected_type}'")
                    else:
                        errors.append(f"[X] Property '{prop_name}' has type '{found_props[prop_name]}', expected '{expected_type}'")
                else:
                    errors.append(f"[X] Property '{prop_name}' not found")

            # =========================================================================
            # PART 12: Validate database row
            # =========================================================================

            rows = final_state_backend.query({
                "collection": "pages",
                "filter": {"databaseId": db_id, "type": "row"}
            }) or []

            if len(rows) >= 1:
                checks_passed.append(f"[C] Database has {len(rows)} row(s)")
            else:
                errors.append("[X] Database has no rows, expected 1")

            # Check row values
            for row in rows:
                title = row.get("title", "")
                cell_values = row.get("cellValues", {})

                if "Current Week" in title:
                    checks_passed.append("[C] Row 'Current Week' found")

                    # Check Tasks Created = 8
                    tasks_created = None
                    tasks_completed = None
                    completion_rate = None

                    for key, val in cell_values.items():
                        key_lower = key.lower()
                        if "created" in key_lower:
                            tasks_created = val
                        elif "completed" in key_lower:
                            tasks_completed = val
                        elif "rate" in key_lower or "completion" in key_lower:
                            completion_rate = val

                    if tasks_created == 8:
                        checks_passed.append("[C] Tasks Created = 8")
                    elif tasks_created is not None:
                        errors.append(f"[X] Tasks Created is {tasks_created}, expected 8")

                    if tasks_completed == 2:
                        checks_passed.append("[C] Tasks Completed = 2")
                    elif tasks_completed is not None:
                        errors.append(f"[X] Tasks Completed is {tasks_completed}, expected 2")

                    if completion_rate == 25:
                        checks_passed.append("[C] Completion Rate = 25")
                    elif completion_rate is not None:
                        errors.append(f"[X] Completion Rate is {completion_rate}, expected 25")
                    break

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in sprint workload analysis validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# =============================================================================
# PRD Technical Requirements Insertion
# =============================================================================
def _validate_prd_technical_requirements(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the PRD Technical Requirements Insertion task.

    Requirements:
    1. New blocks added to page-47 (Comprehensive Product Requirements Document):
       - heading_3 "Overview - Technical Requirements"
       - paragraph "Technical specifications must align with current infrastructure capabilities."
       - paragraph "All implementations should follow established coding standards and security protocols."
       - bulletedList "Minimum 99.9% uptime SLA requirement"
       - bulletedList "Response time under 200ms for API calls"
       - bulletedList "Support for 10,000 concurrent users"
    2. "Requirement Tracking" database exists as child of page-47
    3. Database has properties: Requirement ID (title), Category (select), Priority (select), Status (select), Owner (text)
    4. Select options have correct colors
    5. 5 rows exist with IDs REQ-001 through REQ-005
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Expected values
    PRD_PAGE_ID = "page-47"

    # Expected paragraph text
    PARAGRAPH_1 = "Technical specifications must align with current infrastructure capabilities."
    PARAGRAPH_2 = "All implementations should follow established coding standards and security protocols."

    # Expected bullet items
    BULLET_1 = "Minimum 99.9% uptime SLA requirement"
    BULLET_2 = "Response time under 200ms for API calls"
    BULLET_3 = "Support for 10,000 concurrent users"

    # Expected database properties
    CATEGORY_OPTIONS = {
        "Functional": "blue",
        "Technical": "green",
        "Security": "red",
        "Performance": "yellow"
    }

    PRIORITY_OPTIONS = {
        "P0-Critical": "red",
        "P1-High": "orange",
        "P2-Medium": "yellow",
        "P3-Low": "gray"
    }

    STATUS_OPTIONS = {
        "Draft": "gray",
        "Review": "blue",
        "Approved": "green",
        "Implemented": "purple"
    }

    EXPECTED_ROWS = ["REQ-001", "REQ-002", "REQ-003", "REQ-004", "REQ-005"]

    try:
        # =========================================================================
        # PART 1: Get PRD page blocks
        # =========================================================================

        blocks = final_state_backend.query({
            "collection": "blocks",
            "filter": {"pageId": PRD_PAGE_ID}
        }) or []

        # Also check page content array
        prd_page = final_state_backend.query({
            "collection": "pages",
            "filter": {"_id": PRD_PAGE_ID}
        })
        if isinstance(prd_page, list) and prd_page:
            prd_page = prd_page[0]
            page_content = prd_page.get("content", [])
            blocks = blocks + page_content

        # =========================================================================
        # PART 2: Validate heading_3 "Overview - Technical Requirements"
        # =========================================================================

        found_heading = False
        for block in blocks:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")

            if block_type in ["heading3", "heading_3"] and "Overview - Technical Requirements" in content:
                found_heading = True
                checks_passed.append("[C] Found heading_3 'Overview - Technical Requirements'")
                break

        if not found_heading:
            errors.append("[X] heading_3 'Overview - Technical Requirements' not found")

        # =========================================================================
        # PART 3: Validate paragraphs
        # =========================================================================

        found_para1 = False
        found_para2 = False

        for block in blocks:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")

            if block_type == "paragraph":
                if PARAGRAPH_1 in content:
                    found_para1 = True
                    checks_passed.append("[C] Found paragraph with infrastructure capabilities text")
                if PARAGRAPH_2 in content:
                    found_para2 = True
                    checks_passed.append("[C] Found paragraph with coding standards text")

        if not found_para1:
            errors.append(f"[X] Paragraph '{PARAGRAPH_1}' not found")
        if not found_para2:
            errors.append(f"[X] Paragraph '{PARAGRAPH_2}' not found")

        # =========================================================================
        # PART 4: Validate bulleted list items
        # =========================================================================

        found_bullet1 = False
        found_bullet2 = False
        found_bullet3 = False

        for block in blocks:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")

            if block_type in ["bulletedList", "bulleted_list"]:
                if BULLET_1 in content or "99.9%" in content:
                    found_bullet1 = True
                    checks_passed.append("[C] Found bullet 'Minimum 99.9% uptime SLA requirement'")
                if BULLET_2 in content or "200ms" in content:
                    found_bullet2 = True
                    checks_passed.append("[C] Found bullet 'Response time under 200ms for API calls'")
                if BULLET_3 in content or "10,000 concurrent" in content:
                    found_bullet3 = True
                    checks_passed.append("[C] Found bullet 'Support for 10,000 concurrent users'")

        if not found_bullet1:
            errors.append(f"[X] Bullet '{BULLET_1}' not found")
        if not found_bullet2:
            errors.append(f"[X] Bullet '{BULLET_2}' not found")
        if not found_bullet3:
            errors.append(f"[X] Bullet '{BULLET_3}' not found")

        # =========================================================================
        # PART 5: Find "Requirement Tracking" database
        # =========================================================================

        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {}
        }) or []

        req_db = None
        for page in all_pages:
            title = page.get("title", "")
            if "Requirement Tracking" in title and page.get("type") == "database":
                req_db = page
                break

        if not req_db:
            errors.append("[X] 'Requirement Tracking' database not found")
            errors.append("[X] Requirement Tracking database not found")
        else:
            checks_passed.append("[C] Requirement Tracking database")

        checks_passed.append("[C] Found 'Requirement Tracking' database")

        # Check parent is PRD page
        if req_db.get("parentId") == PRD_PAGE_ID:
            checks_passed.append("[C] Database is child of PRD page")
        else:
            errors.append(f"[X] Database should be child of page-47, found parentId: {req_db.get('parentId')}")

        db_id = req_db.get("_id")

        # =========================================================================
        # PART 6: Validate database properties
        # =========================================================================

        db_metadata = req_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        # Check title property (Requirement ID)
        title_prop_found = False
        for prop in properties:
            if prop.get("type") == "title":
                title_prop_found = True
                prop_name = prop.get("name", "")
                if "Requirement" in prop_name or "ID" in prop_name:
                    checks_passed.append("[C] Title property 'Requirement ID' exists")
                else:
                    checks_passed.append(f"[C] Title property '{prop_name}' exists")
                break
        if not title_prop_found:
            errors.append("[X] Title property (Requirement ID) not found")

        # Check Owner (text) property
        found_owner = False
        for prop in properties:
            if prop.get("name") == "Owner" and prop.get("type") == "text":
                found_owner = True
                checks_passed.append("[C] Property 'Owner' exists with type 'text'")
                break
        if not found_owner:
            errors.append("[X] Property 'Owner' (text) not found")

        # =========================================================================
        # PART 7: Validate Category select options
        # =========================================================================

        category_prop = None
        for prop in properties:
            if prop.get("name") == "Category" and prop.get("type") == "select":
                category_prop = prop
                break

        if category_prop:
            checks_passed.append("[C] Property 'Category' exists with type 'select'")
            options = category_prop.get("options", [])
            found_opts = {opt.get("name"): opt.get("color") for opt in options}

            for opt_name, expected_color in CATEGORY_OPTIONS.items():
                if opt_name in found_opts:
                    if found_opts[opt_name] == expected_color:
                        checks_passed.append(f"[C] Category option '{opt_name}' has color '{expected_color}'")
                    else:
                        errors.append(f"[X] Category option '{opt_name}' has color '{found_opts[opt_name]}', expected '{expected_color}'")
                else:
                    errors.append(f"[X] Category option '{opt_name}' not found")
        else:
            errors.append("[X] Property 'Category' (select) not found")

        # =========================================================================
        # PART 8: Validate Priority select options
        # =========================================================================

        priority_prop = None
        for prop in properties:
            if prop.get("name") == "Priority" and prop.get("type") == "select":
                priority_prop = prop
                break

        if priority_prop:
            checks_passed.append("[C] Property 'Priority' exists with type 'select'")
            options = priority_prop.get("options", [])
            found_opts = {opt.get("name"): opt.get("color") for opt in options}

            for opt_name, expected_color in PRIORITY_OPTIONS.items():
                if opt_name in found_opts:
                    if found_opts[opt_name] == expected_color:
                        checks_passed.append(f"[C] Priority option '{opt_name}' has color '{expected_color}'")
                    else:
                        errors.append(f"[X] Priority option '{opt_name}' has color '{found_opts[opt_name]}', expected '{expected_color}'")
                else:
                    errors.append(f"[X] Priority option '{opt_name}' not found")
        else:
            errors.append("[X] Property 'Priority' (select) not found")

        # =========================================================================
        # PART 9: Validate Status select options
        # =========================================================================

        status_prop = None
        for prop in properties:
            if prop.get("name") == "Status" and prop.get("type") == "select":
                status_prop = prop
                break

        if status_prop:
            checks_passed.append("[C] Property 'Status' exists with type 'select'")
            options = status_prop.get("options", [])
            found_opts = {opt.get("name"): opt.get("color") for opt in options}

            for opt_name, expected_color in STATUS_OPTIONS.items():
                if opt_name in found_opts:
                    if found_opts[opt_name] == expected_color:
                        checks_passed.append(f"[C] Status option '{opt_name}' has color '{expected_color}'")
                    else:
                        errors.append(f"[X] Status option '{opt_name}' has color '{found_opts[opt_name]}', expected '{expected_color}'")
                else:
                    errors.append(f"[X] Status option '{opt_name}' not found")
        else:
            errors.append("[X] Property 'Status' (select) not found")

        # =========================================================================
        # PART 10: Validate database rows (REQ-001 to REQ-005)
        # =========================================================================

        rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": db_id, "type": "row"}
        }) or []

        if len(rows) >= 5:
            checks_passed.append(f"[C] Database has {len(rows)} rows (expected 5)")
        else:
            errors.append(f"[X] Database has {len(rows)} rows, expected 5")

        # Check for each expected row ID
        found_titles = [row.get("title", "") for row in rows]

        for req_id in EXPECTED_ROWS:
            found = False
            for title in found_titles:
                if req_id in title:
                    found = True
                    break
            if found:
                checks_passed.append(f"[C] Row '{req_id}' found")
            else:
                errors.append(f"[X] Row '{req_id}' not found")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in PRD technical requirements validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_vip_client_engagement_plan(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the VIP Client Engagement Plan task.
    Requirements:
    1. Page "VIP Client Engagement Plan" exists as child of Team Directory (page-8)
    2. heading_1 "👑 VIP Client Engagement Plan"
    3. callout with purple background and focus text
    4. Two VIP client sections (Sarah Johnson, Lisa Thompson) with contact details
    5. divider, heading_2 statistics, paragraph with totals
    6. Three new tasks in Task Tracker database
    """
    try:
        errors: list[str] = []
        checks_passed: list[str] = []

        TEAM_DIRECTORY_PAGE_ID = "page-8"
        DB_TASKS_ID = "db-tasks"

        # VIP Client data
        VIP_CLIENTS = [
            {
                "name": "Sarah Johnson",
                "company": "TechCorp",
                "email": "sarah.j@techcorp.com",
                "phone": "+1-555-0101",
                "linkedin": "https://linkedin.com/in/sarahjohnson"
            },
            {
                "name": "Lisa Thompson",
                "company": "Enterprise Co",
                "email": "lthompson@enterprise.com",
                "phone": "+1-555-0105",
                "linkedin": "https://linkedin.com/in/lisathompson"
            }
        ]

        # Expected tasks
        EXPECTED_TASKS = [
            {
                "title": "Schedule quarterly check-in with Sarah Johnson",
                "status": "Not Started",
                "priority": "High",
                "due_date": "2026-02-01",
                "tags": ["Feature", "Frontend"]
            },
            {
                "title": "Send holiday gift to Lisa Thompson",
                "status": "Not Started",
                "priority": "Medium",
                "due_date": "2026-12-15",
                "tags": ["Feature", "Frontend"]
            },
            {
                "title": "Organize VIP client appreciation event",
                "status": "Not Started",
                "priority": "High",
                "due_date": "2026-03-01",
                "tags": ["Feature", "Frontend"]
            }
        ]

        # =========================================================================
        # PART 1: Find "VIP Client Engagement Plan" page
        # =========================================================================

        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {}
        }) or []

        engagement_page = None
        for page in all_pages:
            title = page.get("title", "")
            if "VIP Client Engagement Plan" in title:
                engagement_page = page
                break

        if not engagement_page:
            errors.append("[X] Page 'VIP Client Engagement Plan' not found")
            errors.append("[X] VIP Client Engagement Plan page not found")
        else:
            checks_passed.append("[C] VIP Client Engagement Plan page")

        checks_passed.append("[C] Page 'VIP Client Engagement Plan' found")
        page_id = engagement_page.get("_id")

        # Check parent is Team Directory
        parent_id = engagement_page.get("parentId")
        if parent_id == TEAM_DIRECTORY_PAGE_ID:
            checks_passed.append("[C] Page is child of Team Directory (page-8)")
        else:
            errors.append(f"[X] Page should be child of page-8, found parentId: {parent_id}")

        # Check icon
        icon = engagement_page.get("icon", "")
        title = engagement_page.get("title", "")
        if "👑" in icon or "👑" in title:
            checks_passed.append("[C] Page has crown icon 👑")
        else:
            errors.append("[X] Page should have crown icon 👑")

        # =========================================================================
        # PART 2: Check page blocks
        # =========================================================================

        blocks = final_state_backend.query({
            "collection": "blocks",
            "filter": {"pageId": page_id}
        }) or []

        # Check heading_1
        found_heading1 = False
        for block in blocks:
            if block.get("type") in ["heading1", "heading_1"]:
                content = str(block.get("content", "") or "")
                if "VIP Client Engagement Plan" in content:
                    found_heading1 = True
                    checks_passed.append("[C] heading_1 '👑 VIP Client Engagement Plan' found")
                    break
        if not found_heading1:
            errors.append("[X] heading_1 '👑 VIP Client Engagement Plan' not found")

        # Check callout with purple background
        found_callout = False
        for block in blocks:
            if block.get("type") == "callout":
                content = str(block.get("content", "") or "")
                properties = block.get("properties", {})
                bg_color = properties.get("backgroundColor", "")
                if "purple" in bg_color.lower() or "Maintaining relationships" in content or "most valued clients" in content:
                    found_callout = True
                    checks_passed.append("[C] Callout with purple background and focus text found")
                    break
        if not found_callout:
            errors.append("[X] Callout with purple background and focus text not found")

        # =========================================================================
        # PART 3: Check VIP client sections
        # =========================================================================

        for client in VIP_CLIENTS:
            name = client["name"]
            company = client["company"]
            email = client["email"]
            phone = client["phone"]
            linkedin = client["linkedin"]

            # Check heading_3 with name and company
            found_client_heading = False
            for block in blocks:
                if block.get("type") in ["heading3", "heading_3"]:
                    content = str(block.get("content", "") or "")
                    if name in content and company in content:
                        found_client_heading = True
                        checks_passed.append(f"[C] heading_3 for '{name} - {company}' found")
                        break
            if not found_client_heading:
                errors.append(f"[X] heading_3 for '{name} - {company}' not found")

            # Check email bullet
            found_email = False
            for block in blocks:
                if block.get("type") in ["bulletedList", "bulleted_list"]:
                    content = str(block.get("content", "") or "")
                    if email in content:
                        found_email = True
                        checks_passed.append(f"[C] Email bullet for {name} found")
                        break
            if not found_email:
                errors.append(f"[X] Email bullet '{email}' not found")

            # Check phone bullet
            found_phone = False
            for block in blocks:
                if block.get("type") in ["bulletedList", "bulleted_list"]:
                    content = str(block.get("content", "") or "")
                    if phone in content or phone.replace("-", "") in content.replace("-", ""):
                        found_phone = True
                        checks_passed.append(f"[C] Phone bullet for {name} found")
                        break
            if not found_phone:
                errors.append(f"[X] Phone bullet '{phone}' not found")

            # Check linkedin bullet
            found_linkedin = False
            for block in blocks:
                if block.get("type") in ["bulletedList", "bulleted_list"]:
                    content = str(block.get("content", "") or "")
                    if linkedin in content or name.lower().replace(" ", "") in content.lower():
                        found_linkedin = True
                        checks_passed.append(f"[C] LinkedIn bullet for {name} found")
                        break
            if not found_linkedin:
                errors.append(f"[X] LinkedIn bullet '{linkedin}' not found")

        # =========================================================================
        # PART 4: Check divider
        # =========================================================================

        found_divider = False
        for block in blocks:
            if block.get("type") == "divider":
                found_divider = True
                checks_passed.append("[C] Divider found")
                break
        if not found_divider:
            errors.append("[X] Divider not found")

        # =========================================================================
        # PART 5: Check statistics section
        # =========================================================================

        # Check heading_2 for statistics
        found_stats_heading = False
        for block in blocks:
            if block.get("type") in ["heading2", "heading_2"]:
                content = str(block.get("content", "") or "")
                if "VIP Client Statistics" in content or "Statistics" in content:
                    found_stats_heading = True
                    checks_passed.append("[C] heading_2 'VIP Client Statistics' found")
                    break
        if not found_stats_heading:
            errors.append("[X] heading_2 'VIP Client Statistics' not found")

        # Check paragraph with totals
        found_total = False
        found_industries = False
        for block in blocks:
            if block.get("type") == "paragraph":
                content = str(block.get("content", "") or "")
                if "Total VIP Clients" in content and "2" in content:
                    found_total = True
                    checks_passed.append("[C] Paragraph with 'Total VIP Clients: 2' found")
                if ("TechCorp" in content and "Enterprise" in content) or "Industries" in content:
                    found_industries = True
                    checks_passed.append("[C] Paragraph with industries found")

        if not found_total:
            errors.append("[X] Paragraph with 'Total VIP Clients: 2' not found")
        if not found_industries:
            errors.append("[X] Paragraph with industries (TechCorp, Enterprise Co) not found")

        # =========================================================================
        # PART 6: Check Task Tracker tasks
        # =========================================================================

        task_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": DB_TASKS_ID, "type": "row"}
        }) or []

        for expected_task in EXPECTED_TASKS:
            task_title = expected_task["title"]
            expected_status = expected_task["status"]
            expected_priority = expected_task["priority"]
            expected_due = expected_task["due_date"]
            expected_tags = expected_task["tags"]

            # Find the task
            found_task = None
            for row in task_rows:
                title = row.get("title", "")
                if task_title in title or title in task_title:
                    found_task = row
                    break

            if not found_task:
                errors.append(f"[X] Task '{task_title}' not found in Task Tracker")
                continue

            checks_passed.append(f"[C] Task '{task_title}' found")
            cell_values = found_task.get("cellValues", {})

            # Check status
            status = cell_values.get("db-tasks-prop-status", "")
            if status == expected_status:
                checks_passed.append(f"[C] Task '{task_title}' has Status='{expected_status}'")
            else:
                errors.append(f"[X] Task '{task_title}' has Status='{status}', expected '{expected_status}'")

            # Check priority
            priority = cell_values.get("db-tasks-prop-priority", "")
            if priority == expected_priority:
                checks_passed.append(f"[C] Task '{task_title}' has Priority='{expected_priority}'")
            else:
                errors.append(f"[X] Task '{task_title}' has Priority='{priority}', expected '{expected_priority}'")

            # Check due date
            due_date = cell_values.get("db-tasks-prop-due-date", {})
            if isinstance(due_date, dict):
                due_start = due_date.get("start", "")
            else:
                due_start = str(due_date)
            if expected_due in due_start:
                checks_passed.append(f"[C] Task '{task_title}' has Due Date='{expected_due}'")
            else:
                errors.append(f"[X] Task '{task_title}' has Due Date='{due_start}', expected '{expected_due}'")

            # Check tags
            tags = cell_values.get("db-tasks-prop-tags", [])
            if isinstance(tags, list):
                has_feature = "Feature" in tags
                has_frontend = "Frontend" in tags
                if has_feature and has_frontend:
                    checks_passed.append(f"[C] Task '{task_title}' has Tags=['Feature', 'Frontend']")
                else:
                    errors.append(f"[X] Task '{task_title}' has Tags={tags}, expected ['Feature', 'Frontend']")
            else:
                errors.append(f"[X] Task '{task_title}' tags not a list: {tags}")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in VIP client engagement plan validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

def _validate_financial_dashboard_system(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Financial Dashboard System task.

    Requirements:
    1. 'Financial Dashboard' database exists under Bug Tracker (page-11)
    2. Database has correct properties: Item (title), Category (select), Amount (number),
       Status (select), Department (text), Date (date), Notes (text)
    3. Category options: 'Project Budget' (blue), 'Inventory Asset' (green), 'Operational Cost' (orange)
    4. Status options: 'Allocated' (gray), 'Spent' (red), 'Available' (green)
    5. 10 inventory rows imported with calculated amounts (Qty × Price)
    6. 6 project rows imported with budget amounts
    7. Summary page 'Financial Overview Q1 2026' exists under Q1 Goals (page-2-1)
    8. Summary page has heading_1, yellow callout with grand total, three heading_2 sections
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    try:
        # =========================================================================
        # PART 1: Find Bug Tracker page
        # =========================================================================

        bug_tracker = final_state_backend.query({
            "collection": "pages",
            "filter": {"_id": "page-11"}
        })

        if not bug_tracker:
            errors.append("[X] Bug Tracker page (page-11) not found")
            errors.append("[X] Bug Tracker page not found")
        else:
            checks_passed.append("[C] Bug Tracker page")

        checks_passed.append("[C] Bug Tracker page found")

        # =========================================================================
        # PART 2: Find Financial Dashboard database
        # =========================================================================

        all_dbs = final_state_backend.query({
            "collection": "pages",
            "filter": {"type": "database"}
        }) or []

        financial_db = None
        for db in all_dbs:
            title = db.get("title", "")
            parent_id = db.get("parentId", "")

            if "Financial Dashboard" in title or "financial dashboard" in title.lower():
                if parent_id == "page-11":
                    financial_db = db
                    break

        if not financial_db:
            errors.append("[X] 'Financial Dashboard' database not found under Bug Tracker")
            errors.append("[X] 'Financial Dashboard' database not found")
        else:
            checks_passed.append("[C] 'Financial Dashboard' database")

        checks_passed.append("[C] 'Financial Dashboard' database found")

        # Check database is under Bug Tracker (page-11)
        if financial_db.get("parentId") == "page-11":
            checks_passed.append("[C] Database is under Bug Tracker")
        else:
            errors.append(f"[X] Database should be under Bug Tracker (page-11), but has parentId={financial_db.get('parentId')}")

        # =========================================================================
        # PART 3: Validate database schema
        # =========================================================================

        db_metadata = financial_db.get("databaseMetadata", {})
        properties = db_metadata.get("properties", [])

        prop_map = {}
        for prop in properties:
            prop_name = prop.get("name", "").lower()
            prop_map[prop_name] = prop

        # Required properties and their types
        # Note: Title property can be "Item" or "Name" depending on how database was created
        required_props = {
            "category": "select",
            "amount": "number",
            "status": "select",
            "department": "text",
            "date": "date",
            "notes": "text"
        }

        # Check for title property (can be "Item" or "Name")
        title_found = False
        for actual_name, actual_prop in prop_map.items():
            if actual_prop.get("type") == "title":
                title_found = True
                checks_passed.append(f"[C] Title property '{actual_name}' found")
                break
        if not title_found:
            errors.append("[X] Title property not found")

        for prop_name, expected_type in required_props.items():
            found = False
            for actual_name, actual_prop in prop_map.items():
                if prop_name in actual_name or actual_name in prop_name:
                    actual_type = actual_prop.get("type", "")
                    if actual_type == expected_type:
                        found = True
                        checks_passed.append(f"[C] Property '{prop_name}' found with type '{expected_type}'")
                        break

            if not found:
                errors.append(f"[X] Property '{prop_name}' with type '{expected_type}' not found")

        # =========================================================================
        # PART 4: Validate Category select options
        # =========================================================================

        category_prop = None
        for actual_name, actual_prop in prop_map.items():
            if "category" in actual_name:
                category_prop = actual_prop
                break

        if category_prop:
            options = category_prop.get("options", [])
            option_map = {opt.get("name", "").lower(): opt.get("color", "").lower() for opt in options}

            expected_category_options = {
                "project budget": "blue",
                "inventory asset": "green",
                "operational cost": "orange"
            }

            for opt_name, expected_color in expected_category_options.items():
                found_opt = False
                for actual_name, actual_color in option_map.items():
                    if opt_name in actual_name or actual_name in opt_name:
                        if expected_color in actual_color:
                            found_opt = True
                            checks_passed.append(f"[C] Category option '{opt_name}' has {expected_color} color")
                            break

                if not found_opt:
                    errors.append(f"[X] Category option '{opt_name}' with {expected_color} color not found")
        else:
            errors.append("[X] Category property not found for option validation")

        # =========================================================================
        # PART 5: Validate Status select options
        # =========================================================================

        status_prop = None
        for actual_name, actual_prop in prop_map.items():
            if "status" in actual_name:
                status_prop = actual_prop
                break

        if status_prop:
            options = status_prop.get("options", [])
            option_map = {opt.get("name", "").lower(): opt.get("color", "").lower() for opt in options}

            expected_status_options = {
                "allocated": "gray",
                "spent": "red",
                "available": "green"
            }

            for opt_name, expected_color in expected_status_options.items():
                found_opt = False
                for actual_name, actual_color in option_map.items():
                    if opt_name in actual_name or actual_name in opt_name:
                        if expected_color in actual_color:
                            found_opt = True
                            checks_passed.append(f"[C] Status option '{opt_name}' has {expected_color} color")
                            break

                if not found_opt:
                    errors.append(f"[X] Status option '{opt_name}' with {expected_color} color not found")
        else:
            errors.append("[X] Status property not found for option validation")

        # =========================================================================
        # PART 6: Validate database rows - inventory items
        # =========================================================================

        db_id = financial_db.get("_id")
        db_rows = final_state_backend.query({
            "collection": "pages",
            "filter": {"databaseId": db_id, "type": "row"}
        }) or []

        # Expected inventory items with calculated amounts (Qty × Price)
        # Department mappings: Electronics/Hardware/Software→Engineering, Furniture/Office Supplies→Operations
        expected_inventory = {
            "macbook pro": {"amount": 23988, "category": "Inventory Asset", "status": "Allocated", "department": "Engineering"},
            "dell monitor": {"amount": 8725, "category": "Inventory Asset", "status": "Allocated", "department": "Engineering"},
            "office chair": {"amount": 3600, "category": "Inventory Asset", "status": "Allocated", "department": "Operations"},
            "standing desk": {"amount": 2097, "category": "Inventory Asset", "status": "Allocated", "department": "Operations"},
            "wireless keyboard": {"amount": 0, "category": "Inventory Asset", "status": "Spent", "department": "Engineering"},
            "usb-c hub": {"amount": 2655, "category": "Inventory Asset", "status": "Allocated", "department": "Engineering"},
            "printer paper": {"amount": 1600, "category": "Inventory Asset", "status": "Allocated", "department": "Operations"},
            "sticky notes": {"amount": 750, "category": "Inventory Asset", "status": "Allocated", "department": "Operations"},
            "adobe": {"amount": 5990, "category": "Inventory Asset", "status": "Allocated", "department": "Engineering"},
            "microsoft 365": {"amount": 7450, "category": "Inventory Asset", "status": "Allocated", "department": "Engineering"}
        }

        # Expected projects with budget amounts
        # Status: Active/Planning→Allocated, Completed→Spent, On Hold→Allocated
        expected_projects = {
            "website redesign": {"amount": 50000, "category": "Project Budget", "status": "Allocated", "department": "Engineering"},
            "q1 marketing": {"amount": 25000, "category": "Project Budget", "status": "Spent", "department": "Marketing"},
            "mobile app": {"amount": 100000, "category": "Project Budget", "status": "Allocated", "department": "Engineering"},
            "customer portal": {"amount": 75000, "category": "Project Budget", "status": "Allocated", "department": "Engineering"},
            "training program": {"amount": 15000, "category": "Project Budget", "status": "Allocated", "department": "HR"},
            "sales pipeline": {"amount": 35000, "category": "Project Budget", "status": "Allocated", "department": "Sales"}
        }

        # Check we have at least 16 rows (10 inventory + 6 projects)
        if len(db_rows) >= 16:
            checks_passed.append(f"[C] Found {len(db_rows)} rows (expected at least 16)")
        else:
            errors.append(f"[X] Expected at least 16 rows, found {len(db_rows)}")

        # Build property ID to name mapping
        prop_id_to_name = {}
        for prop in properties:
            prop_id = prop.get("_id", "")
            prop_name = prop.get("name", "").lower()
            if prop_id:
                prop_id_to_name[prop_id] = prop_name

        found_inventory = set()
        found_projects = set()
        total_amount = 0

        for row in db_rows:
            title = row.get("title", "").lower()
            cell_values = row.get("cellValues", {})

            # Extract values
            amount = None
            category = None
            status = None
            department = None

            for key, value in cell_values.items():
                prop_name = prop_id_to_name.get(key, key).lower()

                if "amount" in prop_name:
                    amount = value
                elif "category" in prop_name:
                    category = value
                elif "status" in prop_name:
                    status = value
                elif "department" in prop_name:
                    department = value

            if amount is not None:
                total_amount += amount

            # Check inventory items
            for inv_key, inv_expected in expected_inventory.items():
                if inv_key in title:
                    found_inventory.add(inv_key)
                    if amount == inv_expected["amount"]:
                        checks_passed.append(f"[C] Inventory '{inv_key}' has correct amount (${inv_expected['amount']:,})")
                    elif amount is not None:
                        errors.append(f"[X] Inventory '{inv_key}' amount should be ${inv_expected['amount']:,}, found ${amount}")
                    break

            # Check projects
            for proj_key, proj_expected in expected_projects.items():
                if proj_key in title:
                    found_projects.add(proj_key)
                    if amount == proj_expected["amount"]:
                        checks_passed.append(f"[C] Project '{proj_key}' has correct amount (${proj_expected['amount']:,})")
                    elif amount is not None:
                        errors.append(f"[X] Project '{proj_key}' amount should be ${proj_expected['amount']:,}, found ${amount}")
                    break

        # Check all inventory items were found
        if len(found_inventory) >= 8:
            checks_passed.append(f"[C] Found {len(found_inventory)}/10 inventory items")
        else:
            errors.append(f"[X] Only found {len(found_inventory)}/10 inventory items")

        # Check all projects were found
        if len(found_projects) >= 5:
            checks_passed.append(f"[C] Found {len(found_projects)}/6 projects")
        else:
            errors.append(f"[X] Only found {len(found_projects)}/6 projects")

        # =========================================================================
        # PART 7: Find Financial Overview Q1 2026 summary page
        # =========================================================================

        all_pages = final_state_backend.query({
            "collection": "pages",
            "filter": {}
        }) or []

        summary_page = None
        for page in all_pages:
            title = page.get("title", "")
            page_type = page.get("type", "")
            if page_type in ["database", "row"]:
                continue
            if "Financial Overview" in title and ("Q1" in title or "2026" in title):
                summary_page = page
                break

        if not summary_page:
            errors.append("[X] 'Financial Overview Q1 2026' page not found")
        else:
            checks_passed.append("[C] 'Financial Overview Q1 2026' page found")

            # Check parent is Q1 Goals (page-2-1)
            if summary_page.get("parentId") == "page-2-1":
                checks_passed.append("[C] Summary page is under Q1 Goals")
            else:
                errors.append(f"[X] Summary page should be under Q1 Goals (page-2-1), but has parentId={summary_page.get('parentId')}")

            # =========================================================================
            # PART 8: Validate summary page structure
            # =========================================================================

            summary_page_id = summary_page.get("_id")
            blocks = final_state_backend.query({
                "collection": "blocks",
                "filter": {"pageId": summary_page_id}
            }) or []

            if not blocks:
                errors.append("[X] No blocks found on summary page")
            else:
                sorted_blocks = sorted(blocks, key=lambda b: b.get("order", 0))

                # Check for heading_1 '💰 Q1 2026 Financial Overview'
                found_main_heading = False
                for block in sorted_blocks:
                    if block.get("type") in ["heading1", "heading_1"]:
                        content = str(block.get("content", ""))
                        if "Financial Overview" in content or "Q1 2026" in content:
                            found_main_heading = True
                            checks_passed.append("[C] Found heading_1 'Q1 2026 Financial Overview'")
                            break

                if not found_main_heading:
                    errors.append("[X] heading_1 '💰 Q1 2026 Financial Overview' not found")

                # Check for yellow callout with grand total ($356,855)
                found_yellow_callout = False
                grand_total = 356855  # 56855 inventory + 300000 projects

                for block in sorted_blocks:
                    if block.get("type") == "callout":
                        properties = block.get("properties", {})
                        bg_color = properties.get("backgroundColor", "")
                        content = str(block.get("content", ""))

                        if "yellow" in bg_color.lower():
                            # Check for grand total amount
                            if "356855" in content or "356,855" in content or "$356855" in content or "$356,855" in content:
                                found_yellow_callout = True
                                checks_passed.append(f"[C] Found yellow callout with grand total ${grand_total:,}")
                                break
                            elif "Total Financial Exposure" in content or "Grand Total" in content:
                                found_yellow_callout = True
                                checks_passed.append("[C] Found yellow callout with total financial exposure")
                                break

                if not found_yellow_callout:
                    errors.append(f"[X] Yellow callout with 'Total Financial Exposure: ${grand_total:,}' not found")

                # Check for three heading_2 sections
                heading2_blocks = [b for b in sorted_blocks if b.get("type") in ["heading2", "heading_2"]]
                expected_h2_sections = [
                    "Total Assets by Category",
                    "Department Allocations",
                    "Cash Flow Status"
                ]

                for section_name in expected_h2_sections:
                    found_section = False
                    for h2 in heading2_blocks:
                        content = str(h2.get("content", ""))
                        if section_name in content or section_name.lower() in content.lower():
                            found_section = True
                            checks_passed.append(f"[C] Found heading_2 section '{section_name}'")
                            break

                    if not found_section:
                        errors.append(f"[X] heading_2 section '{section_name}' not found")

        # =========================================================================
        # Final score calculation
        # =========================================================================

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in Financial Dashboard System validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# ==============================================================================
# Blocked Tasks Alert validation
# ==============================================================================
def _validate_blocked_tasks_alert(
    final_state_backend: Backend, final_state_frontend: Dict[str, Any]
) -> TaskScore:
    """
    Validate the Blocked Tasks Alert task.

    Success criteria:
    1. Exactly 2 rows in Task Tracker have Status='Blocked' and Priority='Urgent'
       (specifically 'Write API documentation' and 'Fix mobile responsive issues')
    2. 'Blocked Tasks Alert' page exists as child of 'Meeting Notes' page
    3. Page has heading_1 '🚨 Blocked Tasks Alert'
    4. Page has red callout with '⚠️ The following tasks are blocked due to missed start dates'
    5. Page has heading_2 '📋 Blocked Task Details'
    6. Numbered list contains exactly 2 items with format '[Task Name] - Assigned to [Assignee] - Originally due: [Due Date]'
    """
    errors: list[str] = []
    checks_passed: list[str] = []

    # Expected blocked tasks
    expected_blocked_tasks = ["Write API documentation", "Fix mobile responsive issues"]

    try:
        # =========================================================================
        # PART 1: Validate Task Tracker database updates
        # =========================================================================

        task_tracker = _find_database_by_title(final_state_backend, "Task Tracker")
        if not task_tracker:
            errors.append("[X] Task Tracker database not found")
            errors.append("[X] Task Tracker database not found")
        else:
            checks_passed.append("[C] Task Tracker database found")

        task_rows = _get_database_rows(final_state_backend, task_tracker.get("_id"))

        # Get user info for assignee names
        users_result = final_state_backend.query({"collection": "users", "filter": {}})
        user_names = {}
        if isinstance(users_result, list):
            for user in users_result:
                user_names[user.get("_id")] = user.get("name", user.get("email", "Unknown"))

        # Check for blocked tasks with Urgent priority
        blocked_tasks = []
        for row in task_rows:
            title = row.get("title", "")
            cell_values = row.get("cellValues", {})
            status = cell_values.get("db-tasks-prop-status", "")
            priority = cell_values.get("db-tasks-prop-priority", "")

            if status == "Blocked" and priority == "Urgent":
                assignee_id = cell_values.get("db-tasks-prop-assignee", "")
                assignee_name = user_names.get(assignee_id, "Unknown")
                due_date = cell_values.get("db-tasks-prop-due-date", {})
                due_date_str = due_date.get("start", "") if isinstance(due_date, dict) else str(due_date)

                blocked_tasks.append({
                    "title": title,
                    "assignee": assignee_name,
                    "due_date": due_date_str
                })

        # Validate exactly 2 tasks are blocked
        if len(blocked_tasks) == 2:
            checks_passed.append("[C] Exactly 2 tasks have Status='Blocked' and Priority='Urgent'")
        else:
            errors.append(f"[X] Expected 2 blocked tasks with Urgent priority, found {len(blocked_tasks)}")

        # Validate the specific tasks are blocked
        blocked_titles = [t["title"] for t in blocked_tasks]
        for expected_task in expected_blocked_tasks:
            if expected_task in blocked_titles:
                checks_passed.append(f"[C] Task '{expected_task}' is correctly blocked with Urgent priority")
            else:
                errors.append(f"[X] Task '{expected_task}' should be Blocked with Urgent priority")

        # =========================================================================
        # PART 2: Validate Blocked Tasks Alert page
        # =========================================================================

        # Find Meeting Notes page (use workspace-1 version)
        meeting_notes = _find_page_by_title(final_state_backend, "Meeting Notes")
        if not meeting_notes:
            errors.append("[X] Meeting Notes page not found")
            errors.append("[X] Meeting Notes page not found")
        else:
            checks_passed.append("[C] Meeting Notes page")
        meeting_notes_id = meeting_notes.get("_id")
        checks_passed.append("[C] Meeting Notes page found")

        # Find Blocked Tasks Alert page
        alert_page = _find_page_by_title(final_state_backend, "Blocked Tasks Alert")
        if not alert_page:
            errors.append("[X] 'Blocked Tasks Alert' page not found")
            errors.append("[X] 'Blocked Tasks Alert' page not found")
        else:
            checks_passed.append("[C] 'Blocked Tasks Alert' page found")

        # Verify alert page is under Meeting Notes
        if alert_page.get("parentId") == meeting_notes_id:
            checks_passed.append("[C] Alert page is a child of Meeting Notes")
        else:
            errors.append(f"[X] Alert page should be under Meeting Notes but has parentId={alert_page.get('parentId')}")

        # Get blocks for alert page
        alert_page_id = alert_page.get("_id")
        blocks = _get_blocks_for_page(final_state_backend, alert_page_id)
        if not blocks:
            errors.append("[X] No blocks found on alert page")
            errors.append("[X] No blocks found on alert page")
        else:
            checks_passed.append("[C] blocks found on alert page")

        blocks_sorted = sorted(blocks, key=lambda b: b.get("order", 0))

        # =========================================================================
        # PART 3: Validate page structure
        # =========================================================================

        # Check for heading_1 '🚨 Blocked Tasks Alert'
        found_main_heading = False
        for block in blocks_sorted:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")
            if block_type in ["heading1", "heading_1"] and "Blocked Tasks Alert" in content:
                found_main_heading = True
                if "🚨" in content:
                    checks_passed.append("[C] Found heading_1 '🚨 Blocked Tasks Alert'")
                else:
                    checks_passed.append("[C] Found heading_1 'Blocked Tasks Alert' (emoji optional)")
                break

        if not found_main_heading:
            errors.append("[X] heading_1 '🚨 Blocked Tasks Alert' not found")

        # Check for red callout with warning text
        found_red_callout = False
        for block in blocks_sorted:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")
            properties = block.get("properties", {})
            bg_color = properties.get("backgroundColor", "")

            if block_type == "callout" and "red" in bg_color.lower():
                if "blocked due to missed start dates" in content.lower() or "following tasks are blocked" in content.lower():
                    found_red_callout = True
                    checks_passed.append("[C] Found red callout with warning message")
                    break

        if not found_red_callout:
            errors.append("[X] Red callout with warning about blocked tasks not found")

        # Check for heading_2 '📋 Blocked Task Details'
        found_details_heading = False
        for block in blocks_sorted:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")
            if block_type in ["heading2", "heading_2"] and "Blocked Task Details" in content:
                found_details_heading = True
                if "📋" in content:
                    checks_passed.append("[C] Found heading_2 '📋 Blocked Task Details'")
                else:
                    checks_passed.append("[C] Found heading_2 'Blocked Task Details' (emoji optional)")
                break

        if not found_details_heading:
            errors.append("[X] heading_2 '📋 Blocked Task Details' not found")

        # Check for numbered list with exactly 2 items
        numbered_list_items = []
        for block in blocks_sorted:
            block_type = block.get("type", "")
            content = str(block.get("content", "") or "")
            if block_type in ["numberedList", "numbered_list", "numberedListItem", "numbered_list_item"]:
                numbered_list_items.append(content)

        if len(numbered_list_items) == 2:
            checks_passed.append("[C] Numbered list contains exactly 2 items")
        elif len(numbered_list_items) > 0:
            errors.append(f"[X] Expected 2 numbered list items, found {len(numbered_list_items)}")
        else:
            errors.append("[X] No numbered list items found")

        # Validate numbered list content format: '[Task Name] - Assigned to [Assignee] - Originally due: [Due Date]'
        for task in blocked_tasks:
            task_name = task["title"]
            found_task_in_list = False
            for item in numbered_list_items:
                if task_name in item and "Assigned to" in item and ("Originally due" in item or "due:" in item.lower()):
                    found_task_in_list = True
                    checks_passed.append(f"[C] Task '{task_name}' found in numbered list with correct format")
                    break
            if not found_task_in_list and task_name in expected_blocked_tasks:
                errors.append(f"[X] Task '{task_name}' not found in numbered list with expected format")

        return TaskScore(
            score=1.0 if len(errors) == 0 else 0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

    except Exception as e:
        logging.error(f"Runtime error in validation: {e}")
        errors.append("[X] Runtime error: {str(e)}")
        return TaskScore(
            score=0.0,
            metadata=ScoreMetadata(
                success_accumulator=list(dict.fromkeys(checks_passed)),
                error_accumulator=list(dict.fromkeys(errors)),
            )
        )

# ==============================================================================
# Maps function names to themselves for easy lookup

REWARD_FUNCTIONS_NOTION_DATABASE_V2: Dict[str, Callable[[Backend, Dict[str, Any]], Tuple[float, str]]] = {
    "_validate_agent_overdue_tasks_dashboard": _validate_agent_overdue_tasks_dashboard,
    "_validate_agent_sprint_performance_metrics_database": _validate_agent_sprint_performance_metrics_database,
    "_validate_agent_inventory_reorder_report": _validate_agent_inventory_reorder_report,
    "_validate_agent_weekly_standup_sprint_metrics": _validate_agent_weekly_standup_sprint_metrics,
    "_validate_agent_financial_overview_dashboard": _validate_agent_financial_overview_dashboard,
    "_validate_agent_client_engagement_report": _validate_agent_client_engagement_report,
    "_validate_agent_meeting_analytics_section": _validate_agent_meeting_analytics_section,
    "_validate_agent_resource_allocation_tracker": _validate_agent_resource_allocation_tracker,
    "_validate_agent_sprint_capacity_planning": _validate_agent_sprint_capacity_planning,
    "_validate_agent_quarterly_business_review": _validate_agent_quarterly_business_review,
    "_validate_agent_department_budget_allocations": _validate_agent_department_budget_allocations,
    "_validate_agent_project_risk_dashboard": _validate_agent_project_risk_dashboard,
    "_validate_agent_engineering_resource_allocation": _validate_agent_engineering_resource_allocation,
    "_validate_agent_cross_database_analytics_report": _validate_agent_cross_database_analytics_report,
    "_validate_agent_q1_budget_tracker": _validate_agent_q1_budget_tracker,
    "_validate_agent_contact_engagement_dashboard": _validate_agent_contact_engagement_dashboard,
    "_validate_agent_inventory_reorder_system": _validate_agent_inventory_reorder_system,
    "_validate_agent_contacts_outreach_priority": _validate_agent_contacts_outreach_priority,
    "_validate_agent_performance_metrics": _validate_agent_performance_metrics,
    "_validate_agent_executive_project_dashboard": _validate_agent_executive_project_dashboard,
    "_validate_agent_procurement_recommendations": _validate_agent_procurement_recommendations,
    "_validate_agent_project_portfolio_task_creation": _validate_agent_project_portfolio_task_creation,
    "_validate_agent_resource_allocation_database": _validate_agent_resource_allocation_database,
    "_validate_agent_project_health_monitor": _validate_agent_project_health_monitor,
    "_validate_agent_inventory_alerts_audit_system": _validate_agent_inventory_alerts_audit_system,
    "_validate_agent_sprint_planning_database": _validate_agent_sprint_planning_database,
    "_validate_agent_weekly_status_report": _validate_agent_weekly_status_report,
    "_validate_agent_resource_allocation_analysis": _validate_agent_resource_allocation_analysis,
    "_validate_agent_engineering_project_health": _validate_agent_engineering_project_health,
    "_validate_agent_resource_allocation_matrix_team_retrospective": _validate_agent_resource_allocation_matrix_team_retrospective,
    "_validate_agent_critical_tasks_dashboard": _validate_agent_critical_tasks_dashboard,
    "_validate_agent_inventory_health_dashboard": _validate_agent_inventory_health_dashboard,
    "_validate_agent_urgent_tasks_dashboard_auto_updates": _validate_agent_urgent_tasks_dashboard_auto_updates,
    "_validate_agent_resource_allocation_matrix_team_directory": _validate_agent_resource_allocation_matrix_team_directory,
    "_validate_agent_quarterly_business_review_inline_database": _validate_agent_quarterly_business_review_inline_database,
    "_validate_agent_resource_allocation_tracker_company_handbook": _validate_agent_resource_allocation_tracker_company_handbook,
    "_validate_agent_financial_overview_dashboard_root": _validate_agent_financial_overview_dashboard_root,
    "_validate_agent_inventory_alert_system_schema_changes": _validate_agent_inventory_alert_system_schema_changes,
    "_validate_agent_workspace_analytics_system": _validate_agent_workspace_analytics_system,
    "_validate_agent_resource_allocation_dashboard_dependencies": _validate_agent_resource_allocation_dashboard_dependencies,
    "_validate_agent_high_value_active_projects_report": _validate_agent_high_value_active_projects_report,
    "_validate_agent_resource_planning_dashboard_workload_analysis": _validate_agent_resource_planning_dashboard_workload_analysis,
    "_validate_agent_expense_tracking_database": _validate_agent_expense_tracking_database,
    "_validate_agent_project_portfolio_risk_level_property": _validate_agent_project_portfolio_risk_level_property,
    "_validate_agent_project_portfolio_analytics_dashboard": _validate_agent_project_portfolio_analytics_dashboard,
    "_validate_agent_onboarding_progress_tracker": _validate_agent_onboarding_progress_tracker,
    "_validate_agent_workspace_databases_quick_links": _validate_agent_workspace_databases_quick_links,
    "_validate_agent_weekly_task_burndown_report": _validate_agent_weekly_task_burndown_report,
    "_validate_agent_crm_analytics_dashboard_lead_pipeline": _validate_agent_crm_analytics_dashboard_lead_pipeline,
    "_validate_agent_team_workload_analytics_dashboard": _validate_agent_team_workload_analytics_dashboard,
    "_validate_agent_budget_analysis_report_categorization": _validate_agent_budget_analysis_report_categorization,
    "_validate_agent_inventory_reorder_report_critical_alerts": _validate_agent_inventory_reorder_report_critical_alerts,
    "_validate_agent_multi_database_employee_allocation_system": _validate_agent_multi_database_employee_allocation_system,
    "_validate_agent_vip_client_projects_cross_reference": _validate_agent_vip_client_projects_cross_reference,
    "_validate_agent_lead_conversion_contact_enrichment": _validate_agent_lead_conversion_contact_enrichment,
    "_validate_agent_team_skills_matrix_system": _validate_agent_team_skills_matrix_system,
    "_validate_agent_sprint_retrospective_dashboard_creation": _validate_agent_sprint_retrospective_dashboard_creation,
    "_validate_agent_sprint_retrospective_tracker_inline": _validate_agent_sprint_retrospective_tracker_inline,
    "_validate_agent_sprint_backlog_task_prioritization": _validate_agent_sprint_backlog_task_prioritization,
    "_validate_agent_project_deadline_alerts": _validate_agent_project_deadline_alerts,
    "_validate_agent_crm_engagement_tracking": _validate_agent_crm_engagement_tracking,
    "_validate_agent_critical_tasks_summary_report": _validate_agent_critical_tasks_summary_report,
    "_validate_agent_vendor_performance_procurement": _validate_agent_vendor_performance_procurement,
    "_validate_agent_overdue_tasks_impact_analysis": _validate_agent_overdue_tasks_impact_analysis,
    "_validate_agent_inventory_reorder_management_system": _validate_agent_inventory_reorder_management_system,
    "_validate_agent_sprint_velocity_tracker_analysis": _validate_agent_sprint_velocity_tracker_analysis,
    "_validate_agent_code_repository_java_backend_references": _validate_agent_code_repository_java_backend_references,
    "_validate_agent_project_budget_expense_tracking": _validate_agent_project_budget_expense_tracking,
    "_validate_agent_contact_cleanup_engagement_reporting": _validate_agent_contact_cleanup_engagement_reporting,
    "_validate_agent_employee_directory_vip_account_managers": _validate_agent_employee_directory_vip_account_managers,
    "_validate_agent_sprint_retrospective_analysis_database": _validate_agent_sprint_retrospective_analysis_database,
    "_validate_agent_project_deadline_risk_matrix": _validate_agent_project_deadline_risk_matrix,
    "_validate_agent_team_workload_overdue_analysis": _validate_agent_team_workload_overdue_analysis,
    "_validate_agent_procurement_dashboard_purchase_orders": _validate_agent_procurement_dashboard_purchase_orders,
    "_validate_overdue_task_analysis": _validate_overdue_task_analysis,
    "_validate_project_resource_allocation": _validate_project_resource_allocation,
    "_validate_vip_client_projects": _validate_vip_client_projects,
    "_validate_contact_analytics_dashboard": _validate_contact_analytics_dashboard,
    "_validate_engineering_resource_allocation_2": _validate_engineering_resource_allocation_2,
    "_validate_department_budget_summary": _validate_department_budget_summary,
    "_validate_resource_allocation_database_2": _validate_resource_allocation_database_2,
    "_validate_budget_vs_progress_analysis": _validate_budget_vs_progress_analysis,
    "_validate_team_velocity_tracker": _validate_team_velocity_tracker,
    "_validate_sprint_retrospective": _validate_sprint_retrospective,
    "_validate_task_tracker_schema_updates": _validate_task_tracker_schema_updates,
    "_validate_work_allocation_matrix": _validate_work_allocation_matrix,
    "_validate_contact_analysis_report": _validate_contact_analysis_report,
    "_validate_database_integrity_report": _validate_database_integrity_report,
    "_validate_project_expenses_tracking": _validate_project_expenses_tracking,
    "_validate_sprint_performance_tracker_db": _validate_sprint_performance_tracker_db,
    "_validate_database_sync_scripts": _validate_database_sync_scripts,
    "_validate_low_stock_alert": _validate_low_stock_alert,
    "_validate_quarterly_business_review_db": _validate_quarterly_business_review_db,
    "_validate_q1_performance_dashboard": _validate_q1_performance_dashboard,
    "_validate_workflow_automation_tracker": _validate_workflow_automation_tracker,
    "_validate_engineering_sync_report": _validate_engineering_sync_report,
    "_validate_sprint_velocity_tracking": _validate_sprint_velocity_tracking,
    "_validate_project_deadline_alert": _validate_project_deadline_alert,
    "_validate_cross_project_dependencies": _validate_cross_project_dependencies,
    "_validate_task_tracker_sprint_burndown": _validate_task_tracker_sprint_burndown,
    "_validate_employee_performance_review": _validate_employee_performance_review,
    "_validate_lead_scoring_matrix": _validate_lead_scoring_matrix,
    "_validate_quarterly_business_review_page": _validate_quarterly_business_review_page,
    "_validate_financial_overview": _validate_financial_overview,
    "_validate_development_time_tracking": _validate_development_time_tracking,
    "_validate_sprint_retrospective_system": _validate_sprint_retrospective_system,
    "_validate_client_project_assignments": _validate_client_project_assignments,
    "_validate_cross_functional_dependencies": _validate_cross_functional_dependencies,
    "_validate_sprint_performance_tracker_v2": _validate_sprint_performance_tracker_v2,
    "_validate_resource_allocation_matrix": _validate_resource_allocation_matrix,
    "_validate_sprint_capacity_planning_2": _validate_sprint_capacity_planning_2,
    "_validate_department_budget_analysis": _validate_department_budget_analysis,
    "_validate_team_workload_balance": _validate_team_workload_balance,
    "_validate_inventory_command_center": _validate_inventory_command_center,
    "_validate_project_resource_allocation_tasks": _validate_project_resource_allocation_tasks,
    "_validate_contact_relationship_mapper": _validate_contact_relationship_mapper,
    "_validate_task_dependency_tracking": _validate_task_dependency_tracking,
    "_validate_project_health_monitoring": _validate_project_health_monitoring,
    "_validate_agent_sprint_performance_tracker": _validate_agent_sprint_performance_tracker,
    "_validate_agent_high_priority_task_report": _validate_agent_high_priority_task_report,
    "_validate_agent_sprint_tracker_database": _validate_agent_sprint_tracker_database,
    "_validate_agent_sprint_performance_metrics": _validate_agent_sprint_performance_metrics,
    "_validate_agent_sprint_progress_dashboard": _validate_agent_sprint_progress_dashboard,
    "_validate_agent_cross_database_task_project_sync": _validate_agent_cross_database_task_project_sync,
    "_validate_agent_sprint_velocity_tracker": _validate_agent_sprint_velocity_tracker,
    "_validate_agent_inventory_low_stock_reorder_tracker": _validate_agent_inventory_low_stock_reorder_tracker,
    "_validate_agent_resource_allocation_dashboard": _validate_agent_resource_allocation_dashboard,
    "_validate_agent_employee_performance_review_database": _validate_agent_employee_performance_review_database,
    "_validate_agent_sprint_planning_inline_database": _validate_agent_sprint_planning_inline_database,
    "_validate_agent_bug_management_system": _validate_agent_bug_management_system,
    "_validate_agent_sprint_effort_analysis": _validate_agent_sprint_effort_analysis,
    "_validate_agent_inventory_procurement_merge": _validate_agent_inventory_procurement_merge,
    "_validate_agent_sprint_planning_system": _validate_agent_sprint_planning_system,
    "_validate_agent_executive_project_dashboard_2": _validate_agent_executive_project_dashboard_2,
    "_validate_agent_meeting_scheduler_database": _validate_agent_meeting_scheduler_database,
    "_validate_agent_client_engagement_tracker": _validate_agent_client_engagement_tracker,
    "_validate_agent_automated_inventory_reordering": _validate_agent_automated_inventory_reordering,
    "_validate_agent_vip_client_migration": _validate_agent_vip_client_migration,
    "_validate_agent_data_quality_audit_system": _validate_agent_data_quality_audit_system,
    "_validate_agent_q4_task_migration": _validate_agent_q4_task_migration,
    "_validate_agent_automated_expense_tracking": _validate_agent_automated_expense_tracking,
    "_validate_agent_team_productivity_metrics": _validate_agent_team_productivity_metrics,
    "_validate_agent_high_value_contacts_dashboard": _validate_agent_high_value_contacts_dashboard,
    "_validate_agent_vip_client_followup": _validate_agent_vip_client_followup,
    "_validate_agent_onboarding_progress_tracker_2": _validate_agent_onboarding_progress_tracker_2,
    "_validate_agent_department_budget_analysis_2": _validate_agent_department_budget_analysis_2,
    "_validate_agent_task_escalation_system": _validate_agent_task_escalation_system,
    "_validate_agent_vip_client_engagement_tracker": _validate_agent_vip_client_engagement_tracker,
    "_validate_agent_sprint_retrospective_report": _validate_agent_sprint_retrospective_report,
    "_validate_agent_purchase_order_system": _validate_agent_purchase_order_system,
    "_validate_agent_sprint_performance_dashboard": _validate_agent_sprint_performance_dashboard,
    "_validate_agent_q1_task_analysis": _validate_agent_q1_task_analysis,
    "_validate_agent_sprint_planning_q1_2026": _validate_agent_sprint_planning_q1_2026,
    "_validate_agent_cross_database_contacts_sync": _validate_agent_cross_database_contacts_sync,
    "_validate_agent_quarterly_expenses_inventory_sync": _validate_agent_quarterly_expenses_inventory_sync,
    "_validate_agent_project_portfolio_inventory_sync": _validate_agent_project_portfolio_inventory_sync,
    "_validate_agent_department_budget_aggregation": _validate_agent_department_budget_aggregation,
    "_validate_agent_project_health_monitor_2": _validate_agent_project_health_monitor_2,
    "_validate_agent_sprint_velocity_tracking_system": _validate_agent_sprint_velocity_tracking_system,
    "_validate_agent_sprint_performance_metrics_inline": _validate_agent_sprint_performance_metrics_inline,
    "_validate_agent_inventory_forecasting_system": _validate_agent_inventory_forecasting_system,
    "_validate_getting_started_task_tracker_feature_sync": _validate_getting_started_task_tracker_feature_sync,
    "_validate_vip_client_projects_report": _validate_vip_client_projects_report,
    "_validate_contact_engagement_dashboard_2": _validate_contact_engagement_dashboard_2,
    "_validate_bug_tracker_statistics_dashboard": _validate_bug_tracker_statistics_dashboard,
    "_validate_inventory_analysis_system": _validate_inventory_analysis_system,
    "_validate_task_dependency_tracker": _validate_task_dependency_tracker,
    "_validate_cross_database_financial_analysis": _validate_cross_database_financial_analysis,
    "_validate_contact_scoring_segmentation": _validate_contact_scoring_segmentation,
    "_validate_weekly_standup_content_insertion": _validate_weekly_standup_content_insertion,
    "_validate_weekly_sprint_report": _validate_weekly_sprint_report,
    "_validate_q1_planning_hub": _validate_q1_planning_hub,
    "_validate_guideline_compliance_checklist": _validate_guideline_compliance_checklist,
    "_validate_executive_dashboard_q1_2026": _validate_executive_dashboard_q1_2026,
    "_validate_financial_audit_summary_report": _validate_financial_audit_summary_report,
    "_validate_inventory_low_stock_alert": _validate_inventory_low_stock_alert,
    "_validate_project_milestones_tracker": _validate_project_milestones_tracker,
    "_validate_sprint_workload_analysis": _validate_sprint_workload_analysis,
    "_validate_prd_technical_requirements": _validate_prd_technical_requirements,
    "_validate_vip_client_engagement_plan": _validate_vip_client_engagement_plan,
    "_validate_financial_dashboard_system": _validate_financial_dashboard_system,
    "_validate_blocked_tasks_alert": _validate_blocked_tasks_alert,
}
