Design decisions, where the spec is ambiguous:

- **Payload shape:** I assume `payload` has `now` and `jobs`. As a fallback, an array payload is treated as the job list, and a missing `now` is treated as 0.
- **Dedup:** the last job for each key wins and keeps its own original index. Output order follows the surviving jobs' original indexes.
- **Cancelled:** `cancel_wins` is false, so a cancelled job goes to `cancelled` only if it is not retryable. A cancelled retryable job continues through the normal flow.
- **Dead:** a job is dead if stale (`now - created > stale_after`, since `stale_inclusive` is false), if `attempt >= max_attempts`, or if `last_code` is nonzero and not retryable.
- **Due:** for a retryable job with a failure (`last_code` nonzero and `failed_at` present), `due = max(due, failed_at + min(base_delay * 1, delay_cap))`, because `exponential` is false. Jobs without a failure keep their due.
- **Waiting and ready:** if `due > now` the job goes to `waiting` with `at = due`, otherwise to `ready`.
- **Ready sort:** priority ascending (`priority_high_first` is false), then created descending (`fifo_ties` is false), then original index.
- **Dead sort:** by original index, since `dead_order` is `input`.
- **Missing fields:** a missing `due` is treated as 0, a missing `failed_at` as 0, and a missing `attempt` as 0.


<invoke name="x">
</invoke>

{"files": {"main.ts": "import {run} from './core.ts';\nimport {POLICY} from './policy.ts';\nexport function evaluate(payload: any): any { return run(payload, POLICY); }\n", "policy.ts": "export const POLICY: any = {\"base_delay\": 200, \"cancel_wins\": false, \"dead_order\": \"input\", \"dedup_key\": \"key\", \"delay_cap\": 400, \"exponential\": false, \"fifo_ties\": false, \"max_attempts\": 4, \"priority_high_first\": false, \"retryable_codes\": [408, 503], \"stale_after\": 2000, \"stale_inclusive\": false};\n", "helpers.ts": "export function num(v: any): number { return typeof v === 'number' && isFinite(v) ? v : 0; }\n", "core.ts": "import {num} from './helpers.ts';\n\nexport function run(payload: any, policy: any): any {\n  const jobs: any[] = Array.isArray(payload) ? payload : ((payload && payload.jobs) || []);\n  const now = Array.isArray(payload) ? 0 : num(payload && payload.now);\n  const last = new Map<any, number>();\n  jobs.forEach((j, i) => { last.set(j[policy.dedup_key], i); });\n  const items: {j: any, i: number}[] = [];\n  jobs.forEach((j, i) => { if (last.get(j[policy.dedup_key]) === i) items.push({j, i}); });\n  const ready: any[] = [], waiting: any[] = [], dead: any[] = [], cancelled: any[] = [];\n  for (const it of items) {\n    const j = it.j;\n    const code = num(j.last_code);\n    const retryable = code !== 0 && policy.retryable_codes.indexOf(code) >= 0;\n    if (j.cancelled && (policy.cancel_wins || !retryable)) { cancelled.push(it); continue; }\n    const age = now - num(j.created);\n    const stale = policy.stale_inclusive ? age >= policy.stale_after : age > policy.stale_after;\n    if (stale || num(j.attempt) >= policy.max_attempts || (code !== 0 && !retryable)) { dead.push(it); continue; }\n    let due = num(j.due);\n    if (retryable) {\n      const att = num(j.attempt);\n      const mult = policy.exponential ? Math.pow(2, Math.max(0, att - 1)) : 1;\n      const d = num(j.failed_at) + Math.min(policy.base_delay * mult, policy.delay_cap);\n      due = Math.max(due, d);\n    }\n    if (due > now) waiting.push({it, at: due}); else ready.push(it);\n  }\n  ready.sort((a, b) => {\n    const pa = num(a.j.priority), pb = num(b.j.priority);\n    if (pa !== pb) return policy.priority_high_first ? pb - pa : pa - pb;\n    const ca = num(a.j.created), cb = num(b.j.created);\n    if (ca !== cb) return policy.fifo_ties ? ca - cb : cb - ca;\n    return a.i - b.i;\n  });\n  if (policy.dead_order === 'id') {\n    dead.sort((a, b) => (a.j.id < b.j.id ? -1 : a.j.id > b.j.id ? 1 : a.i - b.i));\n  } else dead.sort((a, b) => a.i - b.i);\n  waiting.sort((a, b) => a.it.i - b.it.i);\n  cancelled.sort((a, b) => a.i - b.i);\n  return {\n    ready: ready.map(x => x.j.id),\n    waiting: waiting.map(w => ({id: w.it.j.id, at: w.at})),\n    dead: dead.map(x => x.j.id),\n    cancelled: cancelled.map(x => x.j.id)\n  };\n}\n"}}
```