The heartbeat path (canRunAgent → startAgentHeartbeatRun) only checked
isAgentPendingApproval (hire gate), completely bypassing plan_review,
draft_review, strategy_review and other blocking approval types. Agents
in Co-Founder mode continued running despite pending approvals.
Root cause: task-runner.prepareTaskRun had the full getBlockingApproval
check, but heartbeat runs never went through prepareTaskRun — they
created runs directly, skipping the approval gate entirely.
Three fixes:
- heartbeat.canRunAgent: add getBlockingApproval(companyId, agentId, null)
for agent-level blocking approvals
- heartbeat.startAgentHeartbeatRun: add getBlockingApproval with task id
after task resolution, before run creation
- task-runner.resumeQueuedRuns: add getBlockingApproval check so app
restart does not dispatch runs past pending approvals
Also replaced the hardcoded type list in hasPendingReviewApprovalForAgent
with a dynamic join on approval_policies.blocks_execution, eliminating
the sync risk when new blocking approval types are added.
Win release host's wrangler OAuth is logged into a different Cloudflare
account than the R2-enabled one, so `wrangler r2 bucket info` returns
"Please enable R2 through the Cloudflare Dashboard" and release-local-winlinux
fails before any upload. The S3 API credentials (R2_ACCESS_KEY_ID /
R2_SECRET_ACCESS_KEY) carry their own account scope and are the path
we already use for every upload and purge, so when they are set we can
short-circuit the wrangler bucket-info/create and dev-url dance entirely.
Also requires R2_PUBLIC_BASE_URL in .env.release.local when S3 mode is
on, since the wrangler-based dev-url fallback is unreachable in that
mode.