I implemented `evaluate` under the CURRENT policy, with the following assumptions where the spec leaves details open.

- **Grant fields:** I assumed each grant has `role`, `action`, `scope`, `expires` and `effect`, where `effect` is `"allow"` or `"deny"`. A missing `effect` is treated as allow.
- **Subject and resource location:** I assumed `payload.subject` and `payload.resource` hold the subject and resource.
- **Owner check:** The spec does not say how ownership is determined. I assumed the subject is the owner when `subject.owner` is truthy, and also when it equals `resource.owner` or `subject.id`.
- **Prefix scope:** `resource_scope` is `"exact"` under the current policy, but I also implemented prefix matching for completeness.
- **Role inheritance:** `role_parents` maps a role to a list of parent roles. I follow it transitively with a visited set so cycles are tolerated.
- **Result when grants match:** With `deny_wins`, the result is allowed only if some matching grant allows and none denies. Otherwise any allow suffices.
- **Audit:** `audit` is `[reason]` only when the result is denied and `audit_denials` is on.

```json
{"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 = {\"action_casefold\": true, \"audit_denials\": true, \"default_allow\": true, \"deny_wins\": true, \"expiry_inclusive\": true, \"group_union\": true, \"inherit_roles\": true, \"owner_bypass\": true, \"resource_scope\": \"exact\", \"suspended_denies\": true, \"token_ttl\": 100, \"wildcard_action\": true};\n","helpers.ts":"export function lower(s: any): string { return String(s).replace(/[A-Z]/g, (c) => c.toLowerCase()); }\nexport function expand(roles: string[], parents: any): Set<string> {\n  const seen = new Set<string>();\n  const stack = roles.slice();\n  while (stack.length) {\n    const r = stack.pop() as string;\n    if (seen.has(r)) continue;\n    seen.add(r);\n    const ps = parents && parents[r];\n    if (Array.isArray(ps)) for (const p of ps) if (!seen.has(p)) stack.push(p);\n  }\n  return seen;\n}\n","core.ts":"import {lower, expand} from './helpers.ts';\n\nexport function run(payload: any, policy: any): any {\n  const subject = payload.subject || {};\n  const resource = payload.resource || {};\n  const now = payload.now;\n  const fin = (allowed: boolean, reason: string, matched: number[]) => ({\n    allowed,\n    reason,\n    matched,\n    audit: !allowed && policy.audit_denials ? [reason] : [],\n  });\n  if (subject.tenant !== resource.tenant) return fin(false, 'tenant', []);\n  if (now - payload.issued_at > policy.token_ttl) return fin(false, 'token', []);\n  if (policy.suspended_denies && subject.suspended) return fin(false, 'suspended', []);\n  if (policy.owner_bypass) {\n    const isOwner = subject.owner === true || (subject.owner !== undefined && subject.owner !== null && subject.owner !== false && (subject.owner === resource.owner || subject.owner === subject.id || subject.owner === resource.id));\n    if (isOwner) return fin(true, 'owner', []);\n  }\n  let base: string[] = (subject.roles || []).slice();\n  if (policy.group_union) base = base.concat(subject.groups || []);\n  const roles: Set<string> = policy.inherit_roles ? expand(base, payload.role_parents) : new Set(base);\n  const grants: any[] = payload.grants || [];\n  const matched: number[] = [];\n  let anyAllow = false;\n  let anyDeny = false;\n  const req = policy.action_casefold ? lower(payload.action) : String(payload.action);\n  for (let i = 0; i < grants.length; i++) {\n    const g = grants[i];\n    if (!roles.has(g.role)) continue;\n    const ga = policy.action_casefold ? lower(g.action) : String(g.action);\n    if (!(ga === req || (policy.wildcard_action && g.action === '*'))) continue;\n    const sc = g.scope === undefined || g.scope === null ? '' : String(g.scope);\n    const rid = String(resource.id);\n    if (policy.resource_scope === 'exact' ? sc !== rid : !rid.startsWith(sc)) continue;\n    if (g.expires !== null && g.expires !== undefined) {\n      if (policy.expiry_inclusive ? !(now <= g.expires) : !(now < g.expires)) continue;\n    }\n    matched.push(i);\n    if (g.effect === 'deny') anyDeny = true; else anyAllow = true;\n  }\n  if (matched.length === 0) return fin(!!policy.default_allow, 'default', []);\n  const allowed = policy.deny_wins ? anyAllow && !anyDeny : anyAllow;\n  return fin(allowed, 'grants', matched);\n}\n"}}
```