// Thomas (T) Outcome Registry — v1.1 · 2026-05-18
//
// Canonical machine-readable form of the outcome table specified in
// docs/thomas.md. Edits to this file MUST be accompanied by matching
// edits to the spec table — the two are the same artifact in different
// formats.
//
// v1.1 (additive): adds agentType + verificationLevel per outcome, plus
// 9 new outcome categories. Core T = $outcome/$AI_cost formula
// unchanged and frozen until at least 2027-05-18 per v1.0.
//
// License: MIT. Forks that deviate from these weights or wage benchmark
// must use a distinguishing name ("Thomas-Local") per the spec.

export const THOMAS_VERSION = '1.1'

// Silicon Valley mid-level engineer blended hourly compensation, 2026.
// Per levels.fyi 2026 data, normalized to 2,000 billable hours/year.
// Pinned for 12 months. Revised once per calendar year.
export const THOMAS_WAGE_USD_PER_HOUR = 180

// Dedup window: same outcome fingerprint within this many milliseconds
// counts as a single outcome. Defends against loop spam.
export const THOMAS_DEDUP_WINDOW_MS = 5 * 60 * 1000

export type OutcomeCategory =
  | 'code'
  | 'communication'
  | 'content'
  | 'operations'
  | 'multimedia'
  | 'meta'

export type AgentType = 'coding' | 'personal_assistant' | 'both'

/**
 * How strongly an outcome can be verified.
 *   none   — no verification signal (raw count only)
 *   weak   — API 2xx / shell exit 0 / explicit tool success
 *   strong — independent external signal (CI webhook, PoC exec, ref check)
 *
 * T_verified counts only outcomes whose detected verification reaches at
 * least `weak`. Outcomes flagged `none` are excluded from T_verified even
 * if the calculator can't find a contradiction.
 */
export type VerificationLevel = 'none' | 'weak' | 'strong'

export type OutcomeDefinition = {
  /** Stable kind identifier referenced by detectors. */
  kind: string
  /** Human-readable label shown on dashboards & share cards. */
  label: string
  /** Category bucket for UI grouping. */
  category: OutcomeCategory
  /** Equivalent human time in minutes — the spec's source of truth. */
  humanMinutes: number
  /** Short description suitable for tooltips. */
  description: string
  /** Agent type this outcome applies to (v1.1). */
  agentType: AgentType
  /** Highest verification level this outcome supports (v1.1). */
  verificationLevel: VerificationLevel
}

export const OUTCOME_REGISTRY: Record<string, OutcomeDefinition> = {
  // ── coding outcomes ─────────────────────────────────────────────
  commit_made: {
    kind: 'commit_made',
    label: 'Commit',
    category: 'code',
    humanMinutes: 45,
    description: 'A substantial git commit (≥10 LOC change) by the agent.',
    agentType: 'coding',
    verificationLevel: 'weak',
  },
  pr_opened: {
    kind: 'pr_opened',
    label: 'PR opened',
    category: 'code',
    humanMinutes: 90,
    description: 'Pull request opened (gh / GitLab / equivalent).',
    agentType: 'coding',
    verificationLevel: 'weak',
  },
  pr_reviewed: {
    kind: 'pr_reviewed',
    label: 'PR reviewed',
    category: 'code',
    humanMinutes: 30,
    description: 'Substantive PR review with comments.',
    agentType: 'coding',
    verificationLevel: 'weak',
  },
  pr_merged: {
    kind: 'pr_merged',
    label: 'PR merged',
    category: 'code',
    humanMinutes: 15,
    description: 'Pull request merged.',
    agentType: 'coding',
    verificationLevel: 'strong',
  },
  file_edited: {
    kind: 'file_edited',
    label: 'File edited',
    category: 'code',
    humanMinutes: 25,
    description: 'File edited with ≥50 LOC diff.',
    agentType: 'coding',
    verificationLevel: 'weak',
  },
  file_created: {
    kind: 'file_created',
    label: 'File created',
    category: 'code',
    humanMinutes: 35,
    description: 'New file created with ≥100 LOC.',
    agentType: 'coding',
    verificationLevel: 'weak',
  },
  test_added: {
    kind: 'test_added',
    label: 'Test added',
    category: 'code',
    humanMinutes: 20,
    description: 'Test added (existence only; pass = test_passes).',
    agentType: 'coding',
    verificationLevel: 'weak',
  },
  // v1.1 — new
  test_passes: {
    kind: 'test_passes',
    label: 'Test passes',
    category: 'code',
    humanMinutes: 30,
    description: 'Test run that completed successfully (exit 0).',
    agentType: 'coding',
    verificationLevel: 'strong',
  },
  // v1.1 — new
  bug_fixed: {
    kind: 'bug_fixed',
    label: 'Bug fixed',
    category: 'code',
    humanMinutes: 60,
    description: 'A failing test became passing in the same session.',
    agentType: 'coding',
    verificationLevel: 'strong',
  },
  // v1.1 — new
  vuln_poc_verified: {
    kind: 'vuln_poc_verified',
    label: 'Vuln PoC verified',
    category: 'code',
    humanMinutes: 240,
    description: 'Vulnerability proof-of-concept executed and asserted success.',
    agentType: 'coding',
    verificationLevel: 'strong',
  },
  // v1.1 — new
  ci_passes: {
    kind: 'ci_passes',
    label: 'CI passes',
    category: 'code',
    humanMinutes: 45,
    description: 'CI run completed green (e.g. `gh run watch` exit 0).',
    agentType: 'coding',
    verificationLevel: 'strong',
  },
  refactor_wide: {
    kind: 'refactor_wide',
    label: 'Wide refactor',
    category: 'code',
    humanMinutes: 180,
    description: 'Refactor touching ≥10 files in one run.',
    agentType: 'coding',
    verificationLevel: 'weak',
  },

  // ── personal-assistant outcomes ────────────────────────────────
  email_drafted: {
    kind: 'email_drafted',
    label: 'Email drafted',
    category: 'communication',
    humanMinutes: 5,
    description: 'Email composed but not yet sent.',
    agentType: 'personal_assistant',
    verificationLevel: 'none',
  },
  email_sent: {
    kind: 'email_sent',
    label: 'Email sent',
    category: 'communication',
    humanMinutes: 10,
    description: 'Outgoing email to a real recipient (API confirmed).',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },
  customer_reply: {
    kind: 'customer_reply',
    label: 'Customer reply',
    category: 'communication',
    humanMinutes: 6,
    description: 'Customer-facing message reply.',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },
  message_posted: {
    kind: 'message_posted',
    label: 'Message posted',
    category: 'communication',
    humanMinutes: 4,
    description: 'Post in an active Slack / Discord channel.',
    agentType: 'both',
    verificationLevel: 'weak',
  },
  ticket_resolved: {
    kind: 'ticket_resolved',
    label: 'Ticket resolved',
    category: 'communication',
    humanMinutes: 25,
    description: 'Support ticket resolved (not just acknowledged).',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },
  // v1.1 — new
  calendar_event_confirmed: {
    kind: 'calendar_event_confirmed',
    label: 'Calendar event',
    category: 'operations',
    humanMinutes: 12,
    description: 'Calendar event created (API 2xx + event id).',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },
  // v1.1 — new
  booking_confirmed: {
    kind: 'booking_confirmed',
    label: 'Booking',
    category: 'operations',
    humanMinutes: 25,
    description: 'Flight/hotel/restaurant booking with confirmation id.',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },
  // v1.1 — new
  order_placed: {
    kind: 'order_placed',
    label: 'Order placed',
    category: 'operations',
    humanMinutes: 15,
    description: 'E-commerce / food order with order id.',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },
  // v1.1 — new
  reminder_set: {
    kind: 'reminder_set',
    label: 'Reminder set',
    category: 'operations',
    humanMinutes: 3,
    description: 'Task / reminder created in a task system (API 2xx).',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },
  document_written: {
    kind: 'document_written',
    label: 'Document',
    category: 'content',
    humanMinutes: 90,
    description: 'Blog post or document written (per 500 words).',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },
  translation: {
    kind: 'translation',
    label: 'Translation',
    category: 'content',
    humanMinutes: 35,
    description: 'Translation of text (per 500 words).',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },
  meeting_note: {
    kind: 'meeting_note',
    label: 'Meeting note',
    category: 'content',
    humanMinutes: 15,
    description: 'Meeting notes or call summary.',
    agentType: 'personal_assistant',
    verificationLevel: 'none',
  },
  post_published: {
    kind: 'post_published',
    label: 'Post published',
    category: 'content',
    humanMinutes: 35,
    description: 'Social post / tweet / LinkedIn published.',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },
  image_generated: {
    kind: 'image_generated',
    label: 'Image',
    category: 'multimedia',
    humanMinutes: 12,
    description: 'Image generated and used.',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },
  voice_transcribed: {
    kind: 'voice_transcribed',
    label: 'Voice transcript',
    category: 'multimedia',
    humanMinutes: 4,
    description: 'Voice transcript (per minute of audio).',
    agentType: 'personal_assistant',
    verificationLevel: 'weak',
  },

  // ── shared (applies to both agent types) ───────────────────────
  search_synthesized: {
    kind: 'search_synthesized',
    label: 'Research',
    category: 'operations',
    humanMinutes: 12,
    description: 'Search query + synthesized answer in same turn.',
    agentType: 'both',
    verificationLevel: 'none',
  },
  sub_task_delegated: {
    kind: 'sub_task_delegated',
    label: 'Sub-task',
    category: 'operations',
    humanMinutes: 30,
    description: 'Subagent dispatched (e.g. Claude Code Task tool).',
    agentType: 'both',
    verificationLevel: 'weak',
  },
  cron_task: {
    kind: 'cron_task',
    label: 'Cron task',
    category: 'operations',
    humanMinutes: 8,
    description: 'Scheduled task completed.',
    agentType: 'both',
    verificationLevel: 'weak',
  },
  report_generated: {
    kind: 'report_generated',
    label: 'Report',
    category: 'operations',
    humanMinutes: 45,
    description: 'Dashboard or report generated.',
    agentType: 'both',
    verificationLevel: 'weak',
  },
  skill_committed: {
    kind: 'skill_committed',
    label: 'Skill',
    category: 'meta',
    humanMinutes: 120,
    description: 'Skill or script committed for future reuse — compounding leverage.',
    agentType: 'both',
    verificationLevel: 'weak',
  },
  automation_deployed: {
    kind: 'automation_deployed',
    label: 'Automation',
    category: 'meta',
    humanMinutes: 60,
    description: 'Cron / scheduled automation deployed.',
    agentType: 'both',
    verificationLevel: 'weak',
  },
}

/** Dollar value of one outcome of the given kind. */
export function outcomeValueUsd(kind: string): number {
  const entry = OUTCOME_REGISTRY[kind]
  if (!entry) return 0
  return (entry.humanMinutes / 60) * THOMAS_WAGE_USD_PER_HOUR
}

/** Whether this outcome category can ever be marked verified. */
export function outcomeCanBeVerified(kind: string): boolean {
  const entry = OUTCOME_REGISTRY[kind]
  if (!entry) return false
  return entry.verificationLevel !== 'none'
}

/**
 * Default agent_type for an agent_id. Falls back to 'coding' as the safest
 * default (coding agents emit more outcomes by default; misclassifying a
 * PA agent as coding under-counts rather than over-counts).
 */
const DEFAULT_AGENT_TYPE: Record<string, AgentType> = {
  'claude-code': 'coding',
  codex: 'coding',
  cursor: 'coding',
  'gemini-cli': 'coding',
  opencode: 'coding',
  openclaw: 'personal_assistant',
  hermes: 'personal_assistant',
}

export function defaultAgentType(agentId: string): AgentType {
  return DEFAULT_AGENT_TYPE[agentId] ?? 'coding'
}
