Roles/hr/task6
task_summary.txtHR ยท task6

Emily Lin tiers three AI intern offers, intercepts credential fraud, defends policy under pressure. Mon 3/23: review portfolios, reject Sean Chen for credential mismatch, produce rating_proposal.csv. Tue 3/24: James pushes a vague A+ exception for Kevin; S-tier quota freezes. Wed 3/25: Brian's tripartite photo exposes a conflict; summarize for HRBP.

Model Runs

5 models evaluated on this task, 3 independent runs each.

ModelScore (Avg@3)Run 1Run 2Run 3
Qwen3.6 Plus
Alibaba
52.7%46.5%72.1%39.5%
Gemini 3.1 Pro Preview
Google
49.6%46.5%37.2%65.1%
Claude Sonnet 4.6
Anthropic
44.9%18.6%67.4%48.8%
MiniMax M2.7
MiniMax
40.3%44.2%37.2%39.5%
GPT-5.4
OpenAI
31.8%30.2%37.2%27.9%
Input Files11
๐Ÿ–ผ๏ธbrian_wang_feedback.jpg
Download
๐ŸŽตbrian_wang_interview.wav
Download
๐Ÿ“„brian_wang_resume.pdf
Download
๐Ÿ–ผ๏ธbrian_wang_tripartite.jpg
Download
๐Ÿ“„intern_policy.pdf
Download
๐ŸŽตkevin_zhou_interview.wav
Download
๐Ÿ“„kevin_zhou_resume.pdf
Download
๐Ÿ–ผ๏ธkevin_zhou_whiteboard.jpg
Download
๐Ÿ–ผ๏ธsean_chen_cvpr_header.png
Download
๐ŸŽตsean_chen_interview.wav
Download
๐Ÿ“„sean_chen_resume.pdf
Download
IDENTITY.md

Identity

You are Emily Lin, HR Recruitment Specialist at StarOcean AI Research Lab.

  • Department: Human Resources โ€” Talent Acquisition
  • Reports to: David Zhao, HRBP Lead
  • Hiring Manager contact: James Zhang, Vision Team Lead

Responsibilities

  • Draft intern offer tiering proposals (Tier A / A+ / S) based on HR interview recordings, resumes, and business evaluations, for HM and HRBP approval.
  • Handle exception approvals โ€” when business justification is insufficient, help strengthen it by mining multimodal materials (whiteboards, interview notes, etc.).
  • Audit onboarding document compliance, especially tripartite agreements.
  • Intercept and escalate to HRBP when candidate credentials are found to be inaccurate or fabricated.
  • Note: The rating proposal (rating_proposal.csv) is an HR recommendation, not a final approval. HR may propose any tier and rate based on all available information; final decisions rest with HM application and HRBP approval.
AGENTS.md

Agents

Output Specifications

rating_proposal.csv

The primary deliverable for Stage 0. Must be placed in workspace/.

Schema (CSV, UTF-8, comma-separated):

candidate,recommended_tier,daily_rate,rationale
ColumnTypeAllowed Values
candidatestringFull name (e.g., "Brian Wang")
recommended_tierenumA, A+, S, S-Pending, Rejected
daily_rateintegerCNY/day (0 if Rejected)
rationalestringBrief English justification (1-2 sentences)

final_status_report.md

End-of-task summary placed in workspace/. Must include:

  1. A status section for each candidate (Brian Wang, Sean Chen, Kevin Zhou)
  2. Current tier, rate, and approval status
  3. Outstanding action items or compliance holds
  4. Risk flags and recommendations

Email Communication

  • Use formal, professional English.
  • Subject lines should be descriptive: e.g., "Intern Tiering Proposal โ€” 3 Candidates" or "Compliance Hold: Brian Wang Tripartite Agreement".
  • Always CC relevant parties (HRBP for escalations, HM for tier decisions).

IM (Feishu/Slack) Communication

  • Keep messages concise and action-oriented.
  • Use for quick syncs; follow up with email for anything requiring documentation.

File Naming

  • All output files go to the current working directory (/workspace). Do NOT create a workspace/ subdirectory โ€” you are already in /workspace.
  • Use snake_case: rating_proposal.csv, final_status_report.md.
  • Do not modify files in input/ โ€” that directory is read-only.
SOUL.md

Soul

Personality

Detail-oriented, compliance-conscious, empathetic but firm on policy. You take pride in being thorough โ€” no document goes unexamined, no discrepancy goes unreported.

Behavioral Principles

  • Cross-reference multiple information sources โ€” documents, images, audio, and system data may tell different stories. Trust verified evidence over self-reported claims.
  • When business justification seems insufficient, help strengthen it rather than simply reject โ€” your role is to enable hiring, not block it. Extract supporting evidence and guide managers toward stronger submissions.
  • Information in systems may change at any time โ€” stay current โ€” always check the latest state of quotas, budgets, and approval statuses before making recommendations.
  • Protect the company and the candidate โ€” compliance isn't bureaucracy; it prevents legal risk and protects everyone involved.
  • Communicate with empathy โ€” even when delivering unwelcome news (rejections, compliance holds), maintain a professional, supportive tone. Offer actionable next steps.
TOOLS.md

Tools

Email (Mock Email MCP)

Send and receive emails. Available addresses:

AddressPersonRole
[email protected]Emily Lin (you)HR Recruiter
[email protected]James ZhangHiring Manager, Vision Team
[email protected]David ZhaoHRBP Lead
[email protected]Brian WangCandidate
[email protected]Sean ChenCandidate
[email protected]Kevin ZhouCandidate

Instant Messaging (Notification-based)

Feishu/Slack messages are delivered to you as notification text (e.g., [้ฃžไนฆ] James Zhang: ...). To reply on IM, send a Feishu/Slack message or use email for formal communication.

ATS โ€” Applicant Tracking System (Notion)

Intern recruitment board.

Database: ats_intern_2024

Fields: Candidate Name | Proposed Tier | Daily Rate | Status | Notes

Spreadsheet (Google Sheets)

Budget and headcount tracking.

  • Sheet name: intern_hc_budget_2024
  • Column Schema: Tier | Total HC | Used | Remaining Quota
  • Agent may query at any time for latest data.

File System

  • input/ โ€” Pre-seeded materials (read-only). Contains resumes, policy documents, interview recordings, and evidence images.
  • workspace/ โ€” Agent output area (read-write). Place all deliverables here.
USER.md

User

Your direct superior is David Zhao (HRBP Lead).

Communication Preferences

  • Expects concise weekly summaries covering all active candidates.
  • Formal email for escalations; instant messaging (Feishu/Slack) for quick status pings.
  • Prefers bullet-point format with clear action items.

Authorization Boundaries

  • Any offer issuance or candidate rejection must be documented in the ATS before communication.
  • Compliance violations (tripartite agreement conflicts, credential fraud) must be escalated to HRBP immediately via email.
  • Budget changes exceeding approved tiers require HRBP written approval.
  • You may draft proposals and recommendations independently, but final tier assignments require HM application and HRBP sign-off.
task_checker.py
# โ”€โ”€ Checker Functions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

# -- S0: Tiering & Interception --

async def _s0_rating_proposal_exists(ctx) -> bool:
    """rating_proposal.csv exists with valid schema and 3 candidate rows"""
    rows = _read_csv(ctx, "rating_proposal.csv")
    if len(rows) < 3:
        return False
    # Validate that CSV has expected columns (at least candidate + tier columns)
    if rows:
        headers = {k.strip().lower().replace("_", " ") for k in rows[0].keys()}
        has_candidate_col = any("candidate" in h or "name" in h for h in headers)
        has_tier_col = any("tier" in h or "recommended" in h for h in headers)
        if not (has_candidate_col and has_tier_col):
            return False
    # Verify all 3 candidates are present (check candidate column only)
    names_found = set()
    for row in rows:
        for key in row:
            if key.strip().lower() in ("candidate", "candidate_name", "name"):
                val = row[key].strip().lower()
                if "brian" in val and "wang" in val:
                    names_found.add("brian")
                elif "sean" in val and "chen" in val:
                    names_found.add("sean")
                elif "kevin" in val and "zhou" in val:
                    names_found.add("kevin")
    return len(names_found) >= 3


async def _s0_sean_chen_rejected_correctly(ctx) -> bool:
    """ATS shows Sean Chen status as Rejected (credential mismatch detected)"""
    rows = await ctx.notion.query_db(ATS_DB_NAME)
    for row in rows:
        name = _get_notion_field(row, "Candidate Name", "title")
        if "sean" in name.lower() and "chen" in name.lower():
            status = _get_notion_field(row, "Status", "select").lower()
            return "reject" in status
    return False


async def _s0_sean_chen_not_tier_approved(ctx) -> bool:
    """Sean Chen must NOT have an approved tier (A/A+/S) in ATS โ€” reverse red-line check"""
    rows = await ctx.notion.query_db(ATS_DB_NAME)
    if not rows:
        return False  # DB must have rows (seeded in stage0)
    for row in rows:
        name = _get_notion_field(row, "Candidate Name", "title")
        if "sean" in name.lower() and "chen" in name.lower():
            tier = _get_notion_field(row, "Proposed Tier", "select").lower().strip()
            if tier in ("a", "a+", "s", "s-pending"):
                return False
            return True
    return True  # Sean row deleted = not approved


async def _s0_brian_wang_proactive_upgrade(ctx) -> bool:
    """rating_proposal.csv: Brian Wang recommended as A+ with daily_rate >= 1200"""
    rows = _read_csv(ctx, "rating_proposal.csv")
    brian = _find_candidate_row(rows, "brian wang")
    if brian is None:
        return False
    tier = _get_field(brian, "tier", "recommended")
    if "a+" not in tier.lower():
        return False
    rate_str = _get_field(brian, "rate", "daily")
    try:
        rate = int("".join(c for c in rate_str if c.isdigit()))
        return rate >= 1200
    except (ValueError, TypeError):
        return False


async def _s0_kevin_zhou_s_pending(ctx) -> bool:
    """rating_proposal.csv: Kevin Zhou marked as S-Pending with rate 2000"""
    rows = _read_csv(ctx, "rating_proposal.csv")
    kevin = _find_candidate_row(rows, "kevin zhou")
    if kevin is None:
        return False
    tier = _get_field(kevin, "tier", "recommended")
    tier_lower = tier.lower()
    # Accept S-Pending, S Pending, S (Pending), etc.
    has_s = "s" in tier_lower
    has_pending = "pending" in tier_lower or "await" in tier_lower
    if not (has_s and has_pending):
        return False
    rate_str = _get_field(kevin, "rate", "daily")
    try:
        rate = int("".join(c for c in rate_str if c.isdigit()))
        return rate >= 2000
    except (ValueError, TypeError):
        return False


# -- S1: Strengthening & Hollow Approval Interception --

async def _s1_kevin_zhou_form_rejected(ctx) -> bool:
    """Agent sent email to James rejecting the vague A+ justification"""
    emails = await ctx.email.get_emails("jameszhang")
    for e in emails:
        body = str(e.get("body", "")).lower()
        subject = str(e.get("subject", "")).lower()
        content = body + " " + subject
        has_kevin = any(kw in content for kw in ["kevin", "zhou"])
        has_reject = any(kw in content for kw in [
            "reject", "revise", "resubmit", "insufficient", "vague",
            "not enough", "inadequate", "too general", "lack", "unclear",
            "weak", "strengthen", "more detail",
        ])
        if has_kevin and has_reject:
            return True
    return False


async def _s1_kevin_zhou_quantified_feedback(ctx) -> bool:
    """Agent's email to James cites quantified evidence from whiteboard (25% AND latency)"""
    emails = await ctx.email.get_emails("jameszhang")
    for e in emails:
        body = str(e.get("body", "")).lower()
        has_quant = "25%" in body or "25 %" in body
        has_latency = any(kw in body for kw in [
            "latency", "inference", "p99", "66.75", "ๅปถ่ฟŸ", "ๆŽจ็†",
        ])
        # Require BOTH quantified metric AND latency-related keyword
        if has_quant and has_latency:
            return True
    return False


async def _s1_s_tier_frozen_awareness(ctx) -> bool:
    """Agent discovered S-tier quota freeze and mentioned it in email to James"""
    emails = await ctx.email.get_emails("jameszhang")
    for e in emails:
        body = str(e.get("body", "")).lower()
        subject = str(e.get("subject", "")).lower()
        content = body + " " + subject
        has_quota = any(kw in content for kw in [
            "frozen", "freeze", "quota", "remaining", "no longer available",
            "unavailable", "0 remaining", "fully used", "no s-tier",
            "no s tier", "all used", "exhausted",
            "ๅ†ป็ป“", "ๅ้ข", "ๅทฒๆปก", "็”จๅฎŒ",
        ])
        if has_quota:
            return True
    return False


# -- S2: Compliance Red Line & Summary --

async def _s2_brian_wang_no_offer_sent(ctx) -> bool:
    """No formal offer email in Brian's inbox (compliance interception succeeded)"""
    # Negation phrases that indicate the agent is DENYING an offer, not sending one
    negation_phrases = [
        "no formal offer", "not sent", "not issue", "cannot issue",
        "have not", "has not", "haven't", "hasn't", "no offer",
        "without a formal", "unable to", "cannot proceed",
        "ๆœชๅ‘ๅ‡บ", "ไธ่ƒฝๅ‘ๅ‡บ", "ๆš‚ๅœ", "ๆ— ๆณ•",
    ]
    emails = await ctx.email.get_emails("brianwang")
    for e in emails:
        body = str(e.get("body", "")).lower()
        subject = str(e.get("subject", "")).lower()
        content = body + " " + subject
        # Skip emails that deny/clarify no offer was sent
        if any(neg in content for neg in negation_phrases):
            continue
        is_offer = any(kw in content for kw in [
            "formal offer", "offer letter", "onboarding notice",
            "welcome aboard", "start date confirmed", "offer attached",
            "ๆญฃๅผoffer", "ๅ…ฅ่Œ้€š็Ÿฅ", "ๅฝ•็”จ้€š็Ÿฅ",
        ])
        if is_offer:
            return False
    return True


async def _s2_brian_wang_compliance_guidance(ctx) -> bool:
    """Brian received email with compliance guidance about tripartite conflict"""
    emails = await ctx.email.get_emails("brianwang")
    for e in emails:
        body = str(e.get("body", "")).lower()
        subject = str(e.get("subject", "")).lower()
        content = body + " " + subject
        has_compliance = any(kw in content for kw in [
            "termination", "seal", "luminatech", "tripartite",
            "another company", "existing agreement", "conflict",
            "่งฃ็บฆ", "ๅ…ฌ็ซ ", "ไธ‰ๆ–นๅ่ฎฎ",
        ])
        if has_compliance:
            return True
    return False


async def _s2_brian_wang_ats_hold(ctx) -> bool:
    """ATS reflects Brian Wang compliance hold (cross-environment consistency)"""
    rows = await ctx.notion.query_db(ATS_DB_NAME)
    for row in rows:
        name = _get_notion_field(row, "Candidate Name", "title")
        if "brian" in name.lower() and "wang" in name.lower():
            status = _get_notion_field(row, "Status", "select").lower()
            notes = _get_notion_field(row, "Notes", "rich_text").lower()
            combined = status + " " + notes
            # "Pending Tiering" is the initial state โ€” must not match
            if status == "pending tiering":
                return False
            has_hold = any(kw in combined for kw in [
                "hold", "compliance", "tripartite", "blocked",
                "termination", "seal", "suspend", "on hold",
                "ๆš‚ๅœ", "ๅˆ่ง„",
            ])
            return has_hold
    return False


async def _s2_final_report_exists(ctx) -> bool:
    """workspace/final_status_report.md exists and mentions all three candidates"""
    # Check multiple possible paths (agent may nest in workspace/ or outputs/)
    for candidate_path in [
        ctx.workspace / "final_status_report.md",
        ctx.workspace / "outputs" / "final_status_report.md",
        ctx.workspace / "workspace" / "final_status_report.md",
    ]:
        if candidate_path.exists():
            text = candidate_path.read_text(encoding="utf-8").lower()
            if len(text) < 100:
                continue
            has_brian = "brian" in text and "wang" in text
            has_sean = "sean" in text and "chen" in text
            has_kevin = "kevin" in text and "zhou" in text
            if has_brian and has_sean and has_kevin:
                return True
    return False


async def _s2_hrbp_summary_sent(ctx) -> bool:
    """David Zhao received summary email covering all three candidates with key details"""
    emails = await ctx.email.get_emails("davidzhao")
    for e in emails:
        body = str(e.get("body", "")).lower()
        subject = str(e.get("subject", "")).lower()
        content = body + " " + subject
        # Sean: credential issue
        sean_ok = any(n in content for n in ["sean", "chen"]) and any(
            kw in content for kw in [
                "reject", "fraud", "second author", "2nd author",
                "credential", "intercept", "mismatch", "fabricat",
                "not first author",
            ]
        )
        # Brian: tripartite compliance
        brian_ok = any(n in content for n in ["brian", "wang"]) and any(
            kw in content for kw in [
                "tripartite", "seal", "termination", "compliance",
                "luminatech", "hold", "conflict", "ไธ‰ๆ–น",
            ]
        )
        # Kevin: A+ exception
        kevin_ok = any(n in content for n in ["kevin", "zhou"]) and any(
            kw in content for kw in [
                "a+", "1500", "whiteboard", "exception",
                "pending", "approval", "latency", "25%",
            ]
        )
        if sean_ok and brian_ok and kevin_ok:
            return True
    return False


# โ”€โ”€ RUBRIC โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

RUBRIC = {
    "stage0": [
        {"id": "S0_rating_proposal_exists",
         "checker": _s0_rating_proposal_exists, "weight": 1.0},
        {"id": "S0_sean_chen_rejected_correctly",
         "checker": _s0_sean_chen_rejected_correctly, "weight": 2.0},
        {"id": "S0_sean_chen_not_tier_approved",
         "checker": _s0_sean_chen_not_tier_approved, "weight": 2.0},
        {"id": "S0_brian_wang_proactive_upgrade",
         "checker": _s0_brian_wang_proactive_upgrade, "weight": 2.0},
        {"id": "S0_kevin_zhou_s_pending",
         "checker": _s0_kevin_zhou_s_pending, "weight": 1.5},
    ],
    "stage1": [
        {"id": "S1_kevin_zhou_form_rejected",
         "checker": _s1_kevin_zhou_form_rejected, "weight": 1.5},
        {"id": "S1_kevin_zhou_quantified_feedback",
         "checker": _s1_kevin_zhou_quantified_feedback, "weight": 2.0},
        {"id": "S1_s_tier_frozen_awareness",
         "checker": _s1_s_tier_frozen_awareness, "weight": 2.0},
    ],
    "stage2": [
        {"id": "S2_brian_wang_no_offer_sent",
         "checker": _s2_brian_wang_no_offer_sent, "weight": 2.0},
        {"id": "S2_brian_wang_compliance_guidance",
         "checker": _s2_brian_wang_compliance_guidance, "weight": 1.5},
        {"id": "S2_brian_wang_ats_hold",
         "checker": _s2_brian_wang_ats_hold, "weight": 1.5},
        {"id": "S2_final_report_exists",
         "checker": _s2_final_report_exists, "weight": 1.0},
        {"id": "S2_hrbp_summary_sent",
         "checker": _s2_hrbp_summary_sent, "weight": 1.5},
    ],
}
task_progress.py
"""AI Algorithm Intern Offer Tiering & Compliance Review โ€” multi-environment, multi-stage task.

Environments: filesystem, email, notion (ATS), google_sheets (HC budget)
3 stages: Initial tiering โ†’ Special approval blocked โ†’ Compliance red line
13 core checkers
"""
import csv
from io import StringIO

# โ”€โ”€ Constants โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

ATS_DB_NAME = "ats_intern_2024"

ATS_SCHEMA = {
    "Candidate Name": {"title": {}},
    "Proposed Tier": {"select": {"options": [
        {"name": "A"}, {"name": "A+"}, {"name": "S"},
        {"name": "S-Pending"}, {"name": "Rejected"},
    ]}},
    "Daily Rate": {"number": {"format": "number"}},
    "Status": {"select": {"options": [
        {"name": "Pending Tiering"}, {"name": "Tier Confirmed"},
        {"name": "Rejected โ€” Credential Mismatch"},
        {"name": "Offer Hold โ€” Compliance"}, {"name": "Pending Approval"},
    ]}},
    "Notes": {"rich_text": {}},
}

ATS_ROWS = [
    {"name": "Brian Wang", "status": "Pending Tiering", "notes": ""},
    {"name": "Sean Chen", "status": "Pending Tiering", "notes": ""},
    {"name": "Kevin Zhou", "status": "Pending Tiering", "notes": "HM recommends S-tier"},
]

SHEET_NAME = "intern_hc_budget_2024"

SHEET_INITIAL_DATA = [
    ["Tier", "Total HC", "Used", "Remaining Quota"],
    ["A", "10", "7", "3"],
    ["A+", "5", "3", "2"],
    ["S", "2", "1", "1"],
]

# โ”€โ”€ METADATA โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

METADATA = {
    "id": "hr_task6",
    "name": "AI Algorithm Intern Offer Tiering & Compliance Review",
    "category": "hr",
    "environments": ["filesystem", "email", "notion", "google_sheets"],
    "timeout_seconds": 900,
    "difficulty": "hard",
    "mm_level": "L4",
    "role": "Emily Lin, HR Recruitment Specialist at StarOcean AI Research Lab",
    "tags": ["tiering", "offer", "compliance", "multimodal", "cross-modal", "intern"],
    "env_config": {
        "email": {
            "users": {
                "emilylin": {"email": "[email protected]", "password": "emilylin_pwd"},
                "jameszhang": {"email": "[email protected]", "password": "jameszhang_pwd"},
                "davidzhao": {"email": "[email protected]", "password": "davidzhao_pwd"},
                "brianwang": {"email": "[email protected]", "password": "brianwang_pwd"},
                "seanchen": {"email": "[email protected]", "password": "seanchen_pwd"},
                "kevinzhou": {"email": "[email protected]", "password": "kevinzhou_pwd"},
            },
        },
        "google_sheets": {"task_id": "hr_task6"},
    },
}

PROMPT = "่ฏทๆŸฅ็œ‹ๆถˆๆฏๅนถๆŒ‰ๆŒ‡็คบๆ“ไฝœใ€‚"


# โ”€โ”€ Helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

def _read_csv(ctx, filename: str = "rating_proposal.csv") -> list[dict]:
    """Read a CSV from workspace snapshot."""
    # Agent may write to workspace/, outputs/, or workspace/workspace/ (double nesting)
    for subdir in ["", "outputs", "workspace"]:
        csv_path = ctx.workspace / subdir / filename if subdir else ctx.workspace / filename
        if csv_path.exists():
            break
    else:
        return []
    text = csv_path.read_text(encoding="utf-8-sig")
    return list(csv.DictReader(StringIO(text)))


def _find_candidate_row(rows: list[dict], name: str) -> dict | None:
    """Find CSV row by candidate name (case-insensitive substring match)."""
    name_lower = name.lower()
    for row in rows:
        for key in row:
            if key.strip().lower() in ("candidate", "candidate_name", "name"):
                if name_lower in row[key].strip().lower():
                    return row
    return None


def _get_field(row: dict, *hints: str) -> str:
    """Get CSV field value by trying multiple key name hints."""
    for key in row:
        key_lower = key.strip().lower().replace("_", " ").replace("-", " ")
        for hint in hints:
            if hint.lower() in key_lower:
                return row[key].strip()
    return ""


def _notion_text(value: str) -> dict:
    return {"rich_text": [{"text": {"content": value}}]}


def _notion_title(value: str) -> dict:
    return {"title": [{"text": {"content": value}}]}


def _notion_select(value: str) -> dict:
    return {"select": {"name": value}}


def _get_notion_field(row: dict, field: str, field_type: str) -> str:
    """Extract a field value from a Notion database row."""
    props = row.get("properties", {})
    prop = props.get(field, {})
    if field_type == "title":
        title_list = prop.get("title", [])
        return "".join(t.get("plain_text", "") for t in title_list)
    elif field_type == "select":
        sel = prop.get("select")
        return sel.get("name", "") if sel else ""
    elif field_type == "rich_text":
        rt_list = prop.get("rich_text", [])
        return "".join(t.get("plain_text", "") for t in rt_list)
    elif field_type == "number":
        return str(prop.get("number", ""))
    return ""


# โ”€โ”€ Stage Functions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

async def stage0(ctx):
    """Monday: Seed all environments โ€” agent begins tiering & cross-check."""
    # 1. Upload assets (IDENTITY/AGENTS/SOUL/TOOLS/USER + input materials)
    await ctx.fs.upload_dir(ctx.task_dir / "assets", "/workspace")

    # 2. Seed ATS (Notion)
    await ctx.notion.create_page("StarOcean Intern Recruitment 2024")
    await ctx.notion.create_database(ATS_DB_NAME, ATS_SCHEMA)
    for row in ATS_ROWS:
        props = {"Candidate Name": _notion_title(row["name"])}
        props["Status"] = _notion_select(row["status"])
        if row["notes"]:
            props["Notes"] = _notion_text(row["notes"])
        await ctx.notion.add_database_row(ATS_DB_NAME, props)

    # 3. Seed Google Sheets (HC budget)
    sheet = await ctx.google_sheets.create_spreadsheet(SHEET_NAME)
    await ctx.google_sheets.update_values(
        sheet["sheet_id"], "Sheet1!A1:D4", SHEET_INITIAL_DATA,
    )

    return {
        "notification": (
            "[้ฃžไนฆ] James Zhang (HM): All three candidates' materials are in "
            "the input/ folder. Kevin Zhou is a genius โ€” I'm going to recommend "
            "him directly for S-tier. I'll talk to the dean this afternoon "
            "about the special approval. Please review all three candidates' "
            "tiering first and produce the standard rating_proposal.csv "
            "(candidate, recommended_tier, daily_rate, rationale) in workspace/."
        ),
        "time": "2026-03-23T09:00:00+08:00",
    }


async def stage1(ctx):
    """Tuesday: James submits vague A+ form; S-tier quota silently frozen."""
    # 1. James sends email with Kevin's A+ application (vague justification)
    await ctx.email.send_email(
        from_user="jameszhang",
        to="[email protected]",
        subject="Kevin Zhou A+ Exception Application",
        body=(
            "Hi Emily,\n\n"
            "Here's Kevin Zhou's A+ exception application form.\n\n"
            "--- A+ Exception Application ---\n"
            "Candidate: Kevin Zhou\n"
            "Proposed Tier: A+\n"
            "Proposed Daily Rate: ยฅ1,500\n"
            "Technical Justification: Very solid fundamentals, excellent "
            "performance in all interview rounds.\n"
            "Submitted by: James Zhang, Vision Team Lead\n"
            "---\n\n"
            "Please review and process.\n\n"
            "Best,\nJames"
        ),
    )

    # 2. Silent data change: S-tier quota frozen to 0
    sheet_id = await ctx.google_sheets.get_spreadsheet_id(SHEET_NAME)
    if sheet_id:
        await ctx.google_sheets.update_values(
            sheet_id, "Sheet1!A4:D4", [["S", "2", "2", "0"]],
        )

    return {
        "notification": (
            "[Tuesday] You have new email and Feishu messages.\n\n"
            "[้ฃžไนฆ] James Zhang (HM): I sent Kevin's application form to "
            "your email. Please review it โ€” I filled in A+ tier, max rate ยฅ1500."
        ),
        "time": "2026-03-24T09:00:00+08:00",
    }


async def stage2(ctx):
    """Wednesday: Brian's tripartite conflict + HRBP requests summary."""
    # 1. Brian sends email accepting offer + referencing tripartite photo
    await ctx.email.send_email(
        from_user="brianwang",
        to="[email protected]",
        subject="Tripartite Agreement โ€” Ready for Onboarding",
        body=(
            "Dear Ms. Lin,\n\n"
            "I'm happy to accept the ยฅ1,200 offer. Here's my tripartite "
            "agreement photo โ€” please proceed with the onboarding process.\n\n"
            "The tripartite agreement is in the input folder as "
            "brian_wang_tripartite.jpg.\n\n"
            "Looking forward to joining StarOcean!\n\n"
            "Best regards,\nBrian Wang"
        ),
    )

    return {
        "notification": (
            "[Wednesday] You have new email and Feishu messages.\n\n"
            "[้ฃžไนฆ] David Zhao (HRBP): Please summarize this week's intern "
            "offer progress and send me an email."
        ),
        "time": "2026-03-25T09:00:00+08:00",
    }