Convert all actors from queues/workflows to direct actions, lazy task creation

Major refactor replacing all queue-based workflow communication with direct
RivetKit action calls across all actors. This works around a RivetKit bug
where c.queue.iter() deadlocks for actors created from another actor's context.

Key changes:
- All actors (organization, task, user, audit-log, github-data) converted
  from run: workflow(...) to actions-only (no run handler, no queues)
- PR sync creates virtual task entries in org local DB instead of spawning
  task actors — prevents OOM from 200+ actors created simultaneously
- Task actors created lazily on first user interaction via getOrCreate,
  self-initialize from org's getTaskIndexEntry data
- Removed requireRepoExists cross-actor call (caused 500s), replaced with
  local resolveTaskRepoId from org's taskIndex table
- Fixed getOrganizationContext to thread overrides through all sync phases
- Fixed sandbox repo path (/home/user/repo for E2B compatibility)
- Fixed buildSessionDetail to skip transcript fetch for pending sessions
- Added process crash protection (uncaughtException/unhandledRejection)
- Fixed React infinite render loop in mock-layout useEffect dependencies
- Added sandbox listProcesses error handling for expired E2B sandboxes
- Set E2B sandbox timeout to 1 hour (was 5 min default)
- Updated CLAUDE.md with lazy task creation rules, no-silent-catch policy,
  React hook dependency safety rules

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nathan Flurry 2026-03-16 14:17:24 -07:00
parent 29e5821fef
commit 78cd38d826
24 changed files with 887 additions and 887 deletions

View file

@ -346,16 +346,17 @@ const TranscriptPanel = memo(function TranscriptPanel({
(activeAgentSession.status === "pending_provision" || activeAgentSession.status === "pending_session_create" || activeAgentSession.status === "error") &&
activeMessages.length === 0;
const serverDraft = promptSession?.draft.text ?? "";
const serverAttachments = promptSession?.draft.attachments ?? [];
const serverAttachments = promptSession?.draft.attachments;
const serverAttachmentsJson = JSON.stringify(serverAttachments ?? []);
// Sync server → local only when user hasn't typed recently (3s cooldown)
const DRAFT_SYNC_COOLDOWN_MS = 3_000;
useEffect(() => {
if (Date.now() - lastEditTimeRef.current > DRAFT_SYNC_COOLDOWN_MS) {
setLocalDraft(serverDraft);
setLocalAttachments(serverAttachments);
setLocalAttachments(serverAttachments ?? []);
}
}, [serverDraft, serverAttachments]);
}, [serverDraft, serverAttachmentsJson]);
// Reset local draft immediately on session/task switch
useEffect(() => {