Skip to Content
Internal docs are powered by Nextra Docs Theme.
SystemsPlatformAPI2Rails to API2 migrationMigration inventory

September 11 QA evaluation backfill delta

ENG-3036 transfers the five-job QAEvaluationBackfill family (PreviewJob, ConfirmJob, QueueReplacementJob, BackdateJob, ReconcileJob) to a native API2 qa-evaluation-backfill BullMQ worker with business logic under src/lib/qa-evaluation-backfill (coverage period, plan builder, run/run-agent/ request writers, preview, confirm, replacement, backdate, reconcile, owner gate). Rails keeps the admin create/confirm/backdate endpoints, the daily report preview queuer, and the run/request model callbacks only as compatibility enqueuers through QAEvaluationBackfill::Api2Dispatcher (POST /internal/qa_evaluation_backfills/enqueue, 202-only acceptance, no GoodJob fallback). The five job classes are retained as the gate-off rollback/manual recovery path behind the default-off PostHog organization gate qa_evaluation_backfill_api2_owner. Request lifecycle side effects and post-backfill reporting keep their existing owners. Not deployed; see the QA evaluation backfill runbook.

September 11 demo-seed and fixture cron retirement delta

ENG-3015 retires the two remaining 02:00 UTC GoodJob crons without porting them to API2. LoadFixturesJob only upserts repo-defined system fixtures (data sources, permissions, roles, dashboard widget types, frameworks), which change exclusively with a deploy; SystemFixtureLoader now runs them from the Flightcontrol db:prepare pre-deploy step under a database advisory lock, in production and staging only (LOAD_SYSTEM_FIXTURES_ON_DB_PREPARE overrides). A failed deploy-time load is logged and reported to Sentry but never fails the deploy and never enqueues a GoodJob; the failure means fixtures were not loaded, and an operator re-runs bin/rails fixtures:load by hand. The retained LoadFixturesJob (bounded retries, single-run concurrency) is an explicit manual or rollback path only and is never enqueued automatically. SeedDemoDataJob is demo-only development/sales data with no customer-facing schedule requirement, so its daily run is retired; the job remains available on demand through bin/rails demo:seed, Dev::DemoDataSeeder, or a console enqueue. Both cron entries stay defined but disabled by default; rollback is re-enabling them in the GoodJob dashboard.

The same PR also disables the hourly refresh_conversation_filters entry as scheduler hygiene. It names RefreshConversationFiltersJob, a class that does not exist on main (the real classes are the no-op placeholder RefreshAllConversationFiltersJob and the per-filter RefreshConversationFilterJob, both unchanged), and API2’s 15-minute BullMQ scheduler already owns evaluation-schedule scanning. No runnable production behavior is retired by that change; the entry stays defined, disabled by default, so the config keeps a rollback record.

September 10 provider field-definition sync delta

ENG-3010 moves the 02:00 UTC SyncProviderFieldsJob sweep and its per-source SyncProviderFieldsForOrganizationDataSourceJob fan-out to the API2 provider-fields-sync BullMQ queue. The 24-hour CloudWatch census ending 22:48 UTC recorded 12 US and 6 EU per-source attempts plus one sweep per region. API2 keeps the daily cadence, scans the same kept Zendesk, Freshdesk, and Intercom sources without a tenant, deduplicates per source, reuses the existing provider clients and credential loaders, and upserts Rails field rows on (organization_data_source_id, external_id) with per-category and per-field error isolation. The Rails cron stays defined but disabled; the two job classes are a recovery-only shim behind pause_api2_provider_fields_sync. Deployment evidence and the two-tick regional bake remain open; see the package runbook.

September 10 summary and expiry ownership delta

ENG-3000 moves three recurring normal-path classes out of GoodJob. Conversation#summarize_later now sends the existing deterministic summary request directly to API2, with SummarizeConversationJob retained only as a delivery fallback and rolling-deploy drain path. The two hourly expiry jobs now share their Rails business logic with an authenticated synchronous endpoint; API2 owns the :05 UTC BullMQ scheduler, retry budget and Flipper pause switch, while the Rails crons are disabled by default for non-racing rollback.

The pre-change 24-hour census recorded 48 US summary attempts / 47 unique and 56 EU summary executions, plus 17 executions of each expiry class in each region. Runtime ownership remains pending until the candidate deploys, the first scheduled BullMQ sweep succeeds in both regions, and new normal-path GoodJob rows stop. The retained Rails classes and endpoint are recovery surfaces, not evidence of normal-path Rails ownership.

September 10 Rulebase UI API2-read delta

ENG-2554 bakes the globally enabled API2 settings-read rollouts into rulebase-ui. Twenty Rails-client importer files leave the source/test census (311 to 291), including 19 runtime importers (300 to 281), and all 24 references to the now-unconditional rollout helpers are removed. Assignment queues, complaint categories, review notes, QA evaluation requests, tags, digest-email reads, evaluation settings, knowledge-base reads, macros, pinned conversations, redaction-rule reads, scorecard alerts, SLA policies, team sampling and work schedules now have one API2 read path.

Rails-backed mutations in digest emails and redaction rules remain explicit; this package does not imply API2 write ownership. Production-retirement credit requires the attribution dashboard to emit first_party_client:rulebase-ui traffic after deployment and then show the removed endpoints absent through a complete observation window. See the UI API2 read cutover runbook.

September 10 QA statistics read delta

The rulebase-ui QA overview and dashboard score cards now read GET /qa_agent_evaluations/statistics from API2 instead of Rails. Production attribution first indexed this controller as one of the UI’s two visible Rails read families on September 10 (two sampled events; the extrapolated volume was used only for prioritization). API2 reuses the existing QA evaluation policy scope and list-filter SQL, and preserves Rails’ organization-local day boundaries, previous-period calculation, effective/active/completed scope, four-decimal floor semantics, conversation-vs-work-item dates, and the agent, team, channel, customer, workspace, and Zendesk brand filters.

This removes one runtime Rails-client importer from the September 10 main snapshot: 311 source/test importers and 300 runtime importers remain on this branch. Combined with ENG-2554’s pending read-fallback removal, the projected post-merge census is 291 source/test and 281 runtime importers. Deployment and production attribution evidence remain open.

September 10 Beam Rails-client removal delta

ENG-2971 removes Beam’s complete local Rails API client surface. The eight runtime importers, VITE_API_BASE_URL, generated Rails OpenAPI schema hook, openapi-fetch dependency, and obsolete build-copy-into-Rails deployment script are deleted. Session bootstrap/login, current organization, conversation list/detail, Jira request types and Jira draft create/update/submit now use API2 exclusively.

Two compatibility paths close the gaps that previously forced Beam back to Rails. POST /conversations/import finds and imports the Zendesk ticket across the tenant’s active Zendesk sources, creates its conversation thread and starts the existing native provider sync with a deterministic run id. Editing a Rails-backed pending Jira draft migrates it to one deterministic API2 draft, maps legacy request-type field ids by provider metadata, cancels the legacy pending row, and submits through API2; retries reuse the same native draft and cannot create a second Jira ticket. See the Beam Rails-client retirement runbook.

The remaining first-party Rails client census is entirely in rulebase-ui: 312 source/test files still import the local Rails client on the September 10 main snapshot. Deployment and live Beam smoke evidence remain open.

Complaint Slack thread follow-up

ComplaintSlackThreadFollowUpJob is migrating to native API2/BullMQ through the complaint-slack-follow-up queue. The organization-scoped PostHog gate defaults off; handoff occurs after the outermost transaction commits, API2 awaits deterministic queue insertion, and outbound delivery claims prevent duplicate legacy-thread replies. API2-owned alerts remain webhook-owned.

The 2026-09-09 baseline was 4 US executions/hour and 0 EU executions/hour, all successful. Verification requires 24 clean hours in both US and EU with zero normal-path GoodJob rows, no duplicate parent alerts or replies, tenant isolation, and Slack ok: false treated as failure. Roll back with the PostHog flag; definitive API2 enqueue rejection uses the retained GoodJob job. Owner: Platform Engineering. Created 2026-09-09; review/remove by 2026-12-08.

title: Rails to API2 migration inventory description: Current retirement census and historical migration accounting for Rails to API2. type: reference status: current owner: engineering systems:

  • api2
  • rails

Migration inventory

September 13 Knowledge Library write and Rails-client delta

ENG-3048 moves Knowledge Library document create/update/duplicate/tag/discard/ manual sync and source create/update/delete/sync from Rails to API2 and removes the Rails client, generated schema types, VITE_API_BASE_URL/api.rulebase.co runtime URLs and indirect Rails wrappers from the ten Knowledge Library boundary files. API2 owns the callback-equivalent effects; the BullMQ handoff goes through a durable outbox committed with the write (api2.knowledge_library_write_effects, swept every minute) and creates are idempotent per Idempotency-Key, with no GoodJob bridge or Rails fallback.

The source/test Rails-client importer census drops from 240 to 233 files and the runtime census drops from 229 to 222; Beam remains at zero. Production retirement credit for the Rails Knowledge Library document/source/import controllers requires the server-first PR to be live in both regions before the separate UI cutover PR merges, then a clean, meaningful first-party Sentry window; source changes alone do not retire them. See the Knowledge Library Rails-client retirement runbook.

September 10 Knowledge Library document-read delta

ENG-3003 moves the Knowledge Library document list and detail reads from Rails to API2. API2 now owns the tenant-scoped visible-document scope, pagination, recursive folder filtering, source/document-kind, Zendesk locale and brand filters, title/article search, full detail payload, and filtered previous/next navigation. The existing Rails create, update, discard, sync, import, and image write paths remain explicit owners.

The source/test Rails-client importer census drops from 291 to 289 files and the runtime census drops from 281 to 279; Beam remains at zero. Production retirement credit requires the API2 route to deploy before the UI and Sentry attribution to show the Rails document index/show transactions absent through a complete observation window. See the Knowledge Library document-read runbook.

September 9 Rulebase webhook forwarding delta

ENG-2958 removes ForwardRulebaseWebhookJob from the normal path. The job was a pure GoodJob bridge: it rebuilt {eventType, organizationId, conversation} and POSTed to API2 /webhooks/rulebase. Its only live producer is ConversationAssignment#forward_assignment_webhook_to_api2_later (conversation_assigned), reached from Conversation#assign_to_account! and all of its callers (assignment controllers, Slack interactions, bulk-assign tools, assignment rules, the assignment queue, and assign_to_qa). Production rolling-hour evidence on 2026-09-09: 3 executions in the US, 0 in the EU, all successful.

After the outermost Rails transaction commits (ActiveRecord.after_all_transactions_commit, the PR #11667 transaction-visibility rule), the producer hands the event to Api2RulebaseWebhookEventEnqueuer, which POSTs {eventId, event} to API2’s new POST /internal/rulebase_webhook_events/enqueue with 1s/3s bounded timeouts. API2 durably enqueues a rulebase-webhook-events BullMQ job before answering 202, pinned by jobId = rulebase_webhook_<event_id> (conversation_assignment_<id>), so a retried hand-off dedupes onto the already-accepted job instead of duplicating downstream effects. A proven pre-enqueue rejection (4xx — auth and payload validation run before the queue add) falls back to the retained ForwardRulebaseWebhookJob; an ambiguous 5xx or transport failure retries the same event_id through EnqueueApi2RulebaseWebhookEventJob (polynomial backoff, 10 attempts). Nothing is enqueued before commit or after rollback, and Rails performs no downstream business work synchronously. The org-scoped PostHog flag api2_rulebase_webhook_events (safe state off) restores the GoodJob producer exactly when off. /webhooks/rulebase and the BullMQ worker now share processRulebaseWebhookEvent, preserving the existing event semantics. Deployment evidence and the zero-new-GoodJob bake remain open; see the rollout gate and the traffic baseline.

September 9 report row collection delta

QAEvaluationReport::CollectRowsJob moves into the existing API2 report flow. Production evidence from the rolling hour 2026-09-09T19:56–20:56Z: 70 executions in the US and 34 in the EU (104 total), all completed, zero errors — a scheduled 20:00 UTC burst, not steady traffic.

The per-organization BullMQ job and the cron tick both collect rows through one dispatcher: the per-organization PostHog gate api2_daily_agent_evaluation_report_collect (safe state off) selects the native collector, and the Rails POST /internal/daily_agent_evaluation_reports/collect endpoint remains the rollback path behind the flag’s off state. The native path runs inside the org job — bounded by the worker’s concurrency: 3, inside a single transaction with statement_timeout = 5min matching the GoodJob budget — and creates zero CollectRowsJob rows. A native failure is not retried through Rails inside the same attempt: BullMQ retries replay the same path, and flipping the flag off restores the Rails collector for every subsequent attempt, so a mid-incident flag flip cannot race a second engine or double collection. Employee.evaluable.human scoping, scorecard/ticket-scope matching, AI-eligibility decisions, frozen-vs-sampling skip filtering, and the org-local period boundaries are ported verbatim (including the ENG-2939 correlated NOT EXISTS frozen-skip probe). Deployment evidence, parity sampling, and the zero-new-rows bake remain open; see the routine GoodJob workloads runbook and rollout gates.

September 9 XCALLY upload completion delta

ENG-2948 removes the last live normal-path CompleteConversationSyncJob producer: the XCALLY upload processor’s GoodJob::Batch on_success callback. Production evidence before the change (rolling 12-hour window on 2026-09-09) showed ~765 executions in the US from organization 25 / data source xcally and zero in the EU; every fresh XCALLY completion callback belonged to a batch whose sole child was ActiveStorage::AnalyzeJob. The wait on the analysis job was verified incidental (the batch was added to move the recording attach outside the transaction; no completion step reads analyzed blob metadata), so ActiveStorage analysis stays in Rails, decoupled from completion. After the attach is durably committed, Xcally#process claims a durable outbound_delivery_claims key (conversation-upload/sync-completion/<id>) and enqueues through POST /internal/provider_conversation_sync_completions/enqueue, which tenant-checks the conversation and adds a provider-conversation-sync-completion BullMQ job pinned by sync_run_id (xcally-upload-<id>, retained 7 days post-completion). The claim is the single-execution identity shared by the API2 hand-off and the GoodJob fallback: a proven API2 rejection takes the GoodJob batch and marks the claim, while an ambiguous transport failure leaves it unresolved for manual reconciliation — never an automatic second dispatch. The api2_conversation_upload_sync_completion flag’s off state restores the legacy batch exactly. Jira callbacks are untouched: two existed before the latest Rails deploy, none after. Deployment evidence and the zero-new-GoodJob bake remain open; see the routine GoodJob workloads runbook.

September 9 dispute Jira resolution and QA feedback enqueue delta

API2 now owns the hourly Disputes::ResolveDoneJiraIssuesJob sweep end-to-end: the dispute-jira-resolution-tick BullMQ scheduler keeps the 15 * * * * UTC cadence, lists open disputes linked to done Jira issues with the same predicate, and fans out one durable dispute job per row that resolves the dispute natively (pure SQL — no Rails call). The Rails cron stays defined but disabled; Flipper pause_api2_dispute_jira_resolutions is checked at tick and execution time and provides the non-racing revert. Per-dispute failures now raise through BullMQ retries and Sentry instead of the retired job’s log-only in-loop rescue. See the dispute Jira resolution runbook.

The evaluation-level feedback generators — GenerateQAAgentEvaluationExpandedCommentJob and GenerateCriterionFeedbackForDifferencesJob — plus the per-criterion generator they fan out to (GenerateQAAgentEvaluationCriterionFeedbackJob) run fully natively in API2: Rails producers enqueue via POST /internal/qa_feedback_generation/enqueue onto the qa-feedback-generation queue, and the prompt building (a Blueprinter-equivalent render layer, verbatim prompt constants, KB/message mention enrichment), the OpenAI call, and the expanded_comment writes all live under src/lib/qa-feedback-generation/ — no Rails /internal/* endpoints. generate_*_later falls back to the retained GoodJob classes on any enqueue failure or under the good_job_qa_feedback_generation Flipper flag. GenerateExpandedCommentJob (comment-level) remains deferred; see the QA post-processing runbook. Deployment evidence and the 24-hour zero-new-rows assertion remain open for all four classes.

September 9 QA post-processing and citation-repair delta

API2 now owns two of the QA post-processing families that remained on GoodJob after the native QA runner cutover: criterion-result chunk/embedding sync (SyncQAAgentEvaluationScorecardCriterionResultChunksJob, 85 executions in the EU+US 12-hour window ending 2026-09-09 10:13 UTC) and the daily organization citation review/repair scan (ReviewAICheckCitationsJob plus ReviewAICheckCitationsForOrganizationJob, 54 executions but ~37 minutes of Rails worker time). The criterion-result after_commit hook now hands the sync to POST /internal/qa_criterion_result_chunk_syncs/enqueue and only falls back to GoodJob on a non-2xx/transport failure; the review_ai_check_citations cron stays defined but disabled by default while the qa-citation-review-tick BullMQ scheduler runs the same 0 5 * * * yesterday cadence. Both job families keep exact prompt/model/settings, candidate SQL, PartLink repair semantics, org-local date bounds, score recalculation order and Slack output; organization scans checkpoint per scorecard result and Slack is claim-guarded.

GenerateQAAgentEvaluationCriterionFeedbackJob and GenerateExpandedCommentJob stay on GoodJob: their prompts embed full Blueprinter renders with Nokogiri-based mention enrichment and cannot be ported with verified byte-exact parity yet. The evaluation-level pair (GenerateQAAgentEvaluationExpandedCommentJob, GenerateCriterionFeedbackForDifferencesJob) later moved its enqueue path to API2 with execution kept in Rails; see the September 9 dispute Jira resolution and QA feedback enqueue delta. ComplaintSlackThreadFollowUpJob is unrelated code and is tracked separately. Deployment evidence and the 24-hour zero-new-rows assertion remain open; see the QA post-processing runbook.

September 9 Zendesk daily reconciliation delta

ENG-2931 moves the 00:00 UTC SyncZendeskTicketsJob sweep and its per-ticket ImportZendeskConversationJob fan-out to the API2 zendesk-ticket-reconciliation BullMQ queue. The rolling 12-hour EU+US window ending 10:13 UTC showed 5,029 executions for 2,738 unique import jobs, 2,291 of them rate-limit retries, all from the midnight sweep. API2 keeps the daily cadence as the webhook safety net, checkpoints last_synced_at only after each page’s tickets are durably queued, deduplicates per source and ticket, and delays a throttled ticket for Retry-After instead of consuming a retry. The Rails cron stays defined but disabled; the two job classes are a recovery-only shim behind pause_api2_zendesk_ticket_reconciliation. Deployment evidence and the two-tick regional bake remain open; see the package runbook.

September 9 relationship construction delta

Moves the two normal-path GoodJob producers of relationship construction to API2 BullMQ hand-offs behind per-organization PostHog gates (api2_conversation_relationships, api2_conversation_part_relationships). Conversation subject/create commits and ConversationFieldValue string commits enqueue one provider-conversation-field-value-link job carrying a per-commit change key; reference-bearing ConversationPart creates enqueue the three part-keyed link jobs (conversation references, Jira issues, Aircall calls). The API2 workers re-read persisted state at execution time, so retries, concurrent commits, and replayed deliveries converge on the same thread items without duplicating links or skipping recomputation. The PROVIDER_SYNC_NATIVE_EFFECTS hand-back stays GoodJob-only (via_api2: false), keeping the manifest rollback layered underneath the producer flags. Deployment evidence, the zero-new-rows gate, and the observation queries are in the relationship construction runbook.

September 9 Knowledge Library sync delta

The Rails Knowledge Library synchronization pipeline — daily source scan, per-source provider sync (Zendesk, Notion, URL, Confluence, API-key Scribe), document sync, crawl start and per-URL crawl, chunking, embeddings and search indexing — moves to API2 BullMQ ownership. The rolling 12-hour window ending September 9 ~10:13 UTC showed SyncKnowledgeBaseDocumentJob at 2,629 executions for 158 unique jobs with 2,488 US GoodJob throttle failures, plus 113 SyncKnowledgeBaseDocumentChunksJob executions and residual SyncKnowledgeBaseSources/Source, StartKnowledgeBaseCrawl and CrawlKnowledgeBaseUrl rows. API2 registers one daily scheduler, a knowledge-base-sync worker that runs native TypeScript ports of every provider path (Zendesk, Notion, URL crawl, Confluence, API-key Scribe) against the Rails-owned tables, and a native knowledge-base-chunks worker that reuses the Intercom/Scribe chunk and search-index writers. Rails keeps only the ActiveStorage image-rehosting endpoint; its provider classes remain solely as the GoodJob rollback path. Provider windows are enforced as Redis fixed windows that delay rather than fail jobs; identities are deterministic per source/document/crawl URL. Every Rails producer (source sync_later, document content/source callbacks, crawl scheduling) hands off through KnowledgeBaseSyncDispatch, which treats only API2 202 as durable acceptance and otherwise falls back to the retained GoodJob jobs. The sync_knowledge_base_sources cron is disabled by default and Flipper pause_api2_knowledge_base_sync is the kill switch. Intercom and session-token Scribe keep their existing API2 owners; API2-native agent/workflow knowledge tables are untouched. Deployment evidence and the zero-new-rows assertion remain open; see the Knowledge Library sync runbook.

September 9 weekly agent performance reports delta

Moves the weekly agent performance report pipeline from Rails/GoodJob to native API2/BullMQ ownership: the daily 08:00 UTC generation scheduler, the Monday 09:00 UTC preview/auto-send scheduler, per-organization fan-out, report lifecycle, LLM content generation, citation persistence, and email delivery all execute in api2 against Postgres — no Rails /internal/* domain endpoints. Production evidence before cutover (EU+US rolling 12h ending ~2026-09-09 10:13 UTC) showed GenerateWeeklyAgentPerformanceReportsForOrganizationJob at 53 executions and GenerateAgentPerformanceReportContentJob at about 52; with the top-level scheduler the theme was ~107 executions. The Rails crons stay listed but paused via enabled_by_default: false under the pause_api2_agent_performance_reports kill switch; generate_later, admin actions, and rake tasks enqueue through Api2AgentPerformanceReportEnqueuer. Deployment evidence and clean-bake gates remain open; see the weekly agent performance reports runbook.

September 9 retired simulation runtime delta

ENG-2926 removes simulation’s daily due-soon GoodJob cron and every model callback that created assignment, completion, or per-assignment reminder work. The four legacy job classes remain as successful no-op tombstones so already queued and delayed rows can deserialize and drain without email, tenant scans, or retries. Simulation and reminder records remain intact for history.

This is an approved product retirement, not an API2 parity port. The remaining simulation UI routes, Rails HTTP actions, Action Cable channels, generation and report jobs are separate removal edges and receive no completion credit here.

September 9 routine GoodJob workload delta

ENG-2920 removes four routine workload families from the normal-path GoodJob surface: SyncJobs::SyncUnit21Job (10-minute cron, ~83 retained EU rows), ConversationUploads::CheckNewUploadsJob plus the ProcessDirectUploadJob path (30-minute poll, ~27 retained EU rows plus earlier ~725 US daily executions), SendDailyAgentEvaluationReportJob and SendDailyAgentEvaluationReportForOrganizationJob (04:00/20:00 UTC, ~37 retained EU rows), and FixCounterCachesJob (3-hourly, ~53 retained EU rows). API2 registers five deterministic BullMQ schedulers and four workers on the same cadences; Rails work runs only through narrow /internal/* endpoints. The five cron entries stay in good_job.rb but are disabled by default and named pause_api2_* kill switches provide the non-racing revert. Producer rewires cover upload create, admin report triggers, post-backfill notification, and employee-merge repair. Deployment evidence and a zero-new-rows assertion remain open; see the routine workload runbook.

September 8 GoodJob traffic delta

The comparable production baseline now shows an 87.3% reduction in average Rails worker executions and a 91.1% reduction in the median between August 1–7 and September 1–7. An exact selected-day comparison fell from 1,301,257 unique jobs on August 4 to 94,093 on September 7, a 92.8% reduction. September 7’s higher executions-per-job ratio came from a Zendesk retry storm, not renewed underlying workload volume.

The retained September 8 GoodJob snapshot is concentrated in Zendesk ticket handoffs (30,182 combined), Jira webhooks (5,554), CX risk (2,759), dispute acknowledgement (2,520), legacy Zendesk events (1,005), and conversation completion (877). Active Storage analysis, CloudWatch publication, autoscaling, and heartbeat jobs are Rails-operational residue and do not set application migration priority. The full class table, measurement caveats, historical series, and ordered retirement plan are in the GoodJob traffic baseline.

Zendesk/Jira ingress and dispute acknowledgement are active implementation handoffs. Conversation completion residue follows those overlapping provider changes; CX risk follows stable completion ownership because it carries broader review/evaluation state transitions. No handoff receives code-ready, production, or retirement credit until its PR, regional deployment, real work, and observation gate pass.

September 7 native WorkOS event processing delta

ENG-2858 removes one ten-minute Rails GoodJob cron and registers one deterministic API2 maintenance scheduler. The API2 task requests the same 17 event types as Rails, preserves raw WorkOS payload keys, persists and applies a page in order through native handlers, and advances the existing regional cursor only after the page completes. Five bounded attempts replace the recurring job’s GoodJob retry ownership.

The native effects cover organization bootstrap and Grant identity, account create/update, membership/employee lifecycle, invitation acceptance, permission/role create/update/delete and system audit records. No normal event path calls Rails. The Rails job and payload classes remain only for queued work and revision rollback. Regional actual-event evidence and a 24-hour bake remain.

September 7 headless-auth ownership delta

ENG-2842 transfers 12 public headless WorkOS method/path contracts and their first-party auth client from Rails to API2: the feature probe, CSRF setup, method/Magic Auth selection, Magic Auth verification, password login/signup, email-verification verify/resend/cancel and password-reset request/confirm. API2 reuses the shared Rails session table and native hosted-login identity and membership transaction; it adds no schema object, cron, callback or queue.

The Rails actions remain deployed for rollback and therefore stay in the retirement inventory until regional actual-workflow and 24-hour observation pass. Hosted AuthKit is the flag-off fallback. Remaining AUTH scope includes direct invite-email callbacks, WorkOS webhook identity/role events, external token contracts and final Rails action/client cleanup. See the headless-auth runbook.

September 6 configurable Rails-binding delta

The four remaining Rails model bindings now use a validated per-relation map instead of hard-coded api2 table names. With RULEBASE_API2_PUBLIC_RELATIONS unset, Rails continues reading api2.bot_providers, api2.screening_searches, api2.status_incidents and api2.work_items. Cutover operators can select each relation independently after its reviewed public object exists; the status-incident organization join uses the same selection mechanism and the collision-safe public.api2_organizations target.

The agent-conversations ACL diagnostic is also dual-name compatible. The regenerated census covers 14,391 tracked files, 517 candidates and 2,229 exact references; 734 are active runtime/operational references, including 177 ordinary writes. Executable blockers fall from 14 to nine, all intentionally retained in the guarded work-item-resource retirement command. Production cutover readiness remains false: no public relation is selected by default and no schema, role, grant, data or queue change is included.

September 6 WorkOS authorization reconciliation delta

ENG-2815 removes the Rails recurring owner for the 03:15 UTC WorkOS authorization import and registers one deterministic API2 maintenance scheduler. API2 now imports assignable non-widget permissions, global environment roles and tenant-matched organization roles with durable mappings, collision-safe keys and additive role permissions. Scoped failures are isolated but fail the aggregate BullMQ attempt for bounded retry.

This removes one configured Rails cron only after deployment; the Rails job and service remain for queued work, remote-write callers and rollback. WorkOS event processing and role/permission writes initiated by local CRUD still prevent a full AUTH closure. No production credit is recorded until an actual regional run and 24-hour observation pass.

September 6 regional capture delta

The manifest is reconciled against fresh ~15:00 UTC production captures from both regions (US PostgreSQL 15.17, EU 15.14), taken via ECS exec. It adds the three live ENG-2775 tables (api2.bot_topic_escalation_analyses, bot_topic_escalation_reasons, bot_topic_runs) and their four foreign keys, and restores the two live, validated fk_qa*_work_item keys the morning refresh dropped as stale — the checked-in structure dump lacks them because the Rails migration adds them conditionally. Totals: 197 tables, 12 sequences, 719 indexes, 373 affected foreign keys per region; agent views unchanged at 78 US/77 EU. The four api2.work_item_resource* tables and nine foreign keys are planned but not yet applied in production, so live captures show 193 tables and 364 foreign keys. Live and reviewed manifest collisions are 28 table/26 index because legacy public.conversation_assignment_intervals was dropped in production. The full structure rehearsal normalizes that one known stale dump object before checking the manifest. Identity bridges are clean in both regions, and the assignment routine matches immutable migration0336 in both. The September 7 writer scan covers 14,437 tracked files, 523 candidates and 2,268 exact references; 756 active runtime/operational references, 177 ordinary writes and nine executable blockers, all in the separately gated screening resource retirement script. Production readiness is unchanged (false).

September 6 current-main schema delta

The checked ENG-2682 manifest now covers source revision 0a076d5d639 rather than projecting the September 2 catalog indefinitely. It adds 14 native tables, 16 new foreign keys, removes two no-longer-present QA-to-work-item foreign keys, and records the newly colliding conversation-assignment table, sequence and primary key. Current source totals are 194 tables, 12 sequences, 716 indexes, 367 affected foreign keys, 29 table-name collisions and 27 index-name collisions. The 78 US/77 EU agent-view plan is unchanged.

The local PostgreSQL structure rehearsal moves and reverses all 193 tables in the checked Rails structure, 76 restored agent views and 12 sequences while preserving 1,451 total foreign keys. It validates the exact 367-FK manifest and the reviewed assignment-routine signature before any movement. The new Drizzle-owned QA result-import table is planned and reported as absent from that structure snapshot. The refreshed source scan covers 14,329 tracked files, 510 candidates and 2,168 exact references; 716 are active runtime/operational references, including 166 ordinary writes. It now reports 23 executable blockers and 180 relations without an active literal. Nine new blockers are the catalog/lock/drop statements in the already-guarded four-table screening-resource retirement command; execute that only after all old workers drain. Production catalogs and permissions have not been inferred from this source refresh.

September 5 evaluation-schedule ownership delta

ENG-2808  removes the normal 15-minute GoodJob cron and gives API2 the bounded due-schedule scan plus deterministic per-window BullMQ delivery. An immutable organization, schedule, run date, and expected window cross the temporary Rails boundary. Rails locks and rechecks that expected window before creating a batch, so stale or ambiguous retries cannot advance a newly edited schedule. The Rails jobs remain available for code-revert rollback. No production credit is recorded until regional deployment and actual-window observation pass.

September 4 runtime writer delta

ENG-2682 adds a reproducible literal scan over 271 non-index relations and the api2.rebuild_conversation_assignment_intervals routine in the schema manifest. The checked result covers 14,232 tracked files, 499 candidate files and 2,029 exact references. Runtime and operational ownership accounts for 619 references and 149 ordinary writes. Fourteen references need a cutover rewrite or wrapper: four Rails model bindings, three operational/API2 dynamic bindings, six assignment-routine caller references and one ACL probe. No active named-constraint or direct-sequence caller was found. The result keeps comments, tests, historical migrations and documentation separately classified rather than treating them as deployable writers.

The September 6 dual-schema compatibility package replaces those six direct routine references with one resolver per runtime. Both prefer the future public signature and fall back to api2; the generated inventory classifies those paired references as compatibility resolvers rather than executable cutover blockers. The wrapper remains required until the move and rollback window complete.

Absence from this literal scan is not absence from production. Stored or dynamically assembled SQL, external clients, regional ACL/default grants, identity bridges and lock/load budgets remain required. The fresh September 4 regional export could not run because AWS SSO expired.

The September 2 census is complete for source revision 1baf2de49054e46b80b1ab2f7e130a916b426964: 1,041 Rails route records, 414 literal Rails callers, 262 job/support files and 40 configured cron entries, with raw callers, callback effects, external contracts, schema objects and operational dependencies assigned. The detailed exports and package register are in migration-planning/2026-09-02/ in the coordinated planning worktree. Maintain incremental deltas from current main; source coverage does not certify runtime retirement.

Of 24 inventoried domains, simulation is complete by Chidi’s product-retirement decision and 23 remain in migration scope. The retired bucket contains 69 route records, nine literal callers, ten job files and one reminder cron; these are cleanup work under OPS/REALTIME by September 11, not feature-porting work. Shared call analysis/call scripts/assessments, phishing candidates and native coaching sessions remain retained. Source totals do not shrink until code is removed.

The September 15 target and foundation priorities replace the original one-month target and 8–10 weighted-points/week goal. Dates are milestones, never reasons to wait. Pull native auth, QA stages/failure and schema rehearsal forward within the three-package limit. Measure completed contract/caller/scheduler/ callback/schema/runtime gates and observed delivery time. The historical 64%/84% model below is frozen and is not a complete-program percentage or planning denominator; no new weighted score is implied by this census.

Incremental AUTH ownership: ENG-2741, ENG-2751 and ENG-2758

DELETE /accounts/sign_out and two UI callers merged through #11073; regional configuration and deployment are verified, while actual-event/24-hour observation remains open. Existing-session validation and session retention are separate prior packages.

ENG-2751 transferred the existing-member sidebar switch from the Rails WorkOS entry route to POST /session/organization. It validates the current persisted session, local target membership and WorkOS user/organization response, renews the cookie atomically, preserves pending-status/name healing, and clears the UI cache before navigation. The two sidebar placements share one native action. ENG-2758 adds hosted AuthKit entry/callback server compatibility with private pre-auth sessions, state/PKCE checks, Ruby-compatible WorkOS sealing, account resolution and atomic cookie rotation. Its server is deployed, both regional API2 callback URLs are registered in WorkOS, and the first-party login, signup, password-reset and regional-auth callers are staged for API2 with Rails-compatible enterprise SSO selection. Headless login, direct invitations, new membership/employee provisioning, WorkOS event/role/membership schedules and external token contracts remain open. See the switch runbook and hosted-login runbook.

ENG-2809 transfers only the daily WorkOS membership reconciliation schedule and execution to API2/BullMQ. It keeps the existing statuses, descending 100-row pagination, 1,000-page ceiling, kept-membership identity, tenant mismatch guard, role lookup/import, assignable-permission filter, and per-organization isolation. Partial failures fail the BullMQ attempt so successful idempotent organizations can repeat while transient failures retry. WorkOS webhook/event processing, authorization’s separate 03:15 role/permission sweep, invitation provisioning, and other identity lifecycle paths remain Rails dependencies. See the membership reconciliation runbook.

Work-schedule mutation preparation

ENG-2742 implements three native mutation routes, retaining timezone labels, field conditions, synced-edit restrictions, tenant/source isolation and soft discard. Shared Rails/API2 locking protects default selection. The three UI mutation helpers are deployed through API2 in both regional Workers. Actual mutation events and 24-hour observation remain open. No full SETTINGS closure is claimed yet.

Native existing-session validation package

ENG-2730 removes API2 authentication’s Rails session and organization-metadata HTTP lookups. Existing cookies, public identity/session tables and native membership renderers remain compatible. Logout, rotation, password-salt changes, membership revocation and permission/scope changes are checked before cache reuse. Login, organization switching and WorkOS lifecycle remain explicit AUTH dependencies; this package does not close that domain. Session trimming is transferred by ENG-2738 below.

Native session retention package

ENG-2738 removes the recurring Rails trim_sessions cron and cleanup execution. API2 preserves the strict creation-age cutoff (30 days; 12 hours in staging), 1,000-row transactions, durable retry cursors and fixed scan ceiling. No schema change or manual production cleanup is included. Existing session creation, WorkOS/logout and old queued Rails jobs remain explicit dependencies. The bounded code package still needs deployment and actual-midnight/24-hour evidence. Contract: rulebase-web/rulebase-api2/docs/session-retention.md.

QA lifecycle and schema preparation packages

ENG-2682’s full repository-structure rehearsal covers all 178 native tables and 76 agent views present in the dump, with OID/column/FK/index/sequence/ACL checks and a lossless reverse move. It explicitly lists the six production manifest objects absent from the dump; synthetic reader grants do not certify production ACLs. Consolidation remains In Progress with no production objects moved.

The expanded full-structure writer matrix covers 323 unique indexes on 178 tables, including 18 partial indexes. Ordinary DML and inferred conflicts plan through old/new bindings and rollback. It separately proves 192 named-constraint alias failures, 26 renamed constraint targets and 11 disappearing sequence names. This is compatibility preparation, not runtime ownership or production parity.

A separate ENG-2682 routine rehearsal restores exact migrations 0326 and 0336 against synthetic public input tables, then applies deployable migration0344. The source body matches fresh September 7 captures in both regions. It proves actual interval upserts, historical team/employee changes, cleanup, handling totals, preserved table/sequence/primary-key/function identities, old-view DML, both routine signatures, compatibility removal and reverse writes. Rails and API2 callers already prefer the public signature and fall back to api2; alias retirement remains explicit. The broader schema manifest gates are unchanged.

ENG-2731 now routes kept related conversations for Intercom, Zendesk, Freshdesk, Jira, Outreach and xCally through API2’s existing provider-sync engine. One record and stable sync-run ID are checkpointed per request lease; provider errors and missing sources remain best-effort, while tenant/source mismatches fail closed. Unsupported, discarded and timestamp-incompatible records retain the Rails stage primitive until their production census is closed. The Rails-unavailable preparation gate remains open; legacy GoodJob behavior is preserved.

ENG-2731 removes API2’s authoritative Rails /advance and /fail calls. Native SQL owns the request stage ledger and terminal state. Relationship detection, normal related-provider refresh, mark_prep_complete, and bounded conversation-part and attachment captions are native; explicit execute_stage and finalize_failure adapters preserve eligibility/sampling, evaluation creation, unsupported-source fallback, and remaining result effects. ENG-2885 adds native bounded selection and progress for conversation-part image captions while retaining their Rails context/result endpoints. ENG-2887 directly owns attachment cache reuse, provider execution, terminal status persistence and redaction tombstones. Live billing stays unchanged. This reduces ownership dependencies but does not close QA or certify operation with Rails HTTP unavailable.

The result-finalization dispatch package moves success, skip and exhausted-run delivery behind a separate deterministic BullMQ queue. Finalizer retries no longer rerun inference, and conflicting payload reuse fails closed. ENG-2801 then makes API2 the gated result-tree writer: tenant-qualified context resolution, criterion/check persistence, parent and scorecard aggregation, and the result-run claim commit in one transaction. A durable API2 ownership marker keeps retries on the same writer across flag changes or callback failures. Rails retains only agent/request completion, sampling, notification and billing effects during this slice, so native finalization parity is still incomplete.

ENG-2682’s checked manifest covers 180 native tables, 12 sequences and 353 affected foreign keys per region, 28 colliding table names, 26 colliding index names and 78 US/77 EU agent views. The US-only view remains unresolved. Local representative rehearsal passes; production table placement and application bindings have not changed. Full restoration/writer/mapping gates remain open.

Native retention package

ENG-2723 removes the recurring Rails connection-log cleanup path: daily scan, per-source threshold, bounded deletion and retry continuations run in API2. Retain 1,000 newest rows, including discarded connections and tenants without API2 mappings. Rails log writers/readers, shared public tables, old queued jobs and legacy class remain. Code-ready is not deployed or baked; no additional points. Package contract: rulebase-web/rulebase-api2/docs/connection-log-retention.md.

Historical weighting model — superseded for planning

CategoryWeightCurrent completionContributionBasis
UI and API reads20%75%15.0Most settings and directory reads are baked; workspace search is deployed and manual exports are code-ready
Writes and user actions15%45%6.8Several API2 actions exist, but Rails still owns a material write surface
Ingestion, provider sync, and webhooks20%85%17.0Zendesk and Intercom are globally on; selection flags and residual callbacks remain
Async analysis and evaluation20%65%13.0Translation and transcription moved; QA and analysis ownership remains mixed
Schedules, exports, and recovery10%60%6.0QA recovery and some exports moved; remaining Rails schedules need a complete census
Auth, public API, and admin10%45%4.5Session/bootstrap reads moved, with mixed Rails ownership elsewhere
Rails dependency and flag cleanup5%25%1.3Several bake-ins are ready, but callbacks and temporary controls remain
Total100%63.6% → 64%Provisional until the route/job census is complete

Retain this dated model for historical interpretation only. Record current ownership and scope changes in the progress log.

The production-verified score remains 64%. Including the deployed residual transcription, QA sampling recovery, workspace-search, manual-export, and user-requested QA changes; the recently merged Jira, QA-stage transcription, post-sync image-captioning, caption-resumability, and Zendesk webhook-execution packages plus the merged automated-QA repair, Tickets export, Zendesk-recovery, and sales-call credit-reconciliation stacks, the portfolio is 84% code-ready. Tickets’ three points, Zendesk recovery’s one point, and sales-call reconciliation’s one point are already included. Freshdesk and ENG-2707 add no new weighting or production credit. Production credit waits for each exact revision to pass its regional observation window. The EU result-callback recovery is deployed, ten US and nine EU ticket-closed requests completed afterward, and no matching post-deploy Sentry event appeared. Automated expansion still requires the full clean window and explicit per-organization stale-row repair.

September 2, 19:04 UTC audit

Production remains 64%, code-ready remains 84%, and both deltas are zero (36 production / 16 code-ready points remain). ENG-2707 is deployed, but EU provider wait still measured 5,703 with 16 active jobs and a 230-minute oldest sampled wait. US had no provider wait. A new Freshdesk null-metadata regression blocks its bake credit; its repair receives no migration points. US has one mapped source; EU has one unmapped legacy source, so the new scheduler provides no EU coverage until that mapping/dependency is resolved. The program-wide stop used by this audit was superseded later on September 2: only packages with demonstrated incident dependencies are rollout-gated. The 8–10-points/week target and one-month finish remain at risk.

Fresh regional retained GoodJob runtime ranks EU QA preparation at 134,762 seconds, US evaluation checks/completion polls at 149,633 seconds, and summary work at 21,620 seconds combined. These include retries and deferrals; use the Sentry equal-day comparison in the progress log for trend evidence. Direct transcription already has an API2 owner and is not a new migration candidate. The next three domains remain automated QA repair/invariant/ticket-closed ownership, summary/completion callbacks, and the synchronous endpoint census.

September 2, 15:05 UTC audit

Production remains 64%, code-ready remains 84%, and both deltas are zero for this run (36 production points / 16 code-ready points remain). ENG-2707 is a regression mitigation and receives no migration credit. The inventory has recorded no verified production gain since the August 30 baseline: the 8–10-points/week target and one-month finish are at risk. Confidence is low until EU sync stabilizes and the census closes the denominator.

Live GoodJob retained executions rank EU QA preparation first (12,296 runs / 96,045 seconds), then Zendesk webhook processing (42,608 / 17,946 seconds). US evaluation checks plus completion polls consumed 64,739 seconds across 18,398 retained runs. Counts include retries/concurrency deferrals and are not unique workload counts. GoodJob retention is incomplete for day-over-day comparison; Sentry’s sampled equal 24-hour windows provide the baseline.

Residual direct transcription is already migrated and no longer the leading ready workload: EU retained only four Rails executions / 86 seconds today, versus 2,667 sampled Rails spans / 55,407 seconds in the preceding Sentry day. Do not count a second ownership transfer for that existing work.

Landing reconciliation at 17:13 UTC: Tickets #10945, Zendesk recovery #10946, sales-call credit reconciliation #10949, and Freshdesk #11009 are now merged. ENG-2707 is the remaining PR in this serial preparation queue. These status updates add no migration points and include no new production audit; the regional observations above remain the recorded 15:05 UTC snapshot. No net-new package is safety-cleared by this preparation. Remaining domains below still need specific parity or repair gates.

Detailed inventory

SurfaceTrafficStateRails path / dependencyAPI2 replacementGate or fallbackEvidence and next action
Intercom conversation importHigh: 1.80M Rails runs and 1.12B ms in the seven days before cutoverBaked into API2ImportIntercomConversationJob remains only for immediate API2 delivery failureBullMQ Intercom conversation importEnqueue failure falls back to GoodJob; PostHog selection branch removedPR #10714 , PR #10757 ; verify the recovery bake window, then delete the orphaned flag record
Intercom ticket importMediumBaked into API2ImportIntercomTicketJob remains only for immediate API2 delivery failureBullMQ Intercom ticket importEnqueue failure falls back to GoodJob; PostHog selection branch removedPR #10717 , PR #10757 ; verify the recovery bake window, then delete the orphaned flag record
Intercom webhook receipt and routingHigh; 59 US attempts / 52 unique legacy jobs and zero EU attempts in the 24 hours ending September 10 at 16:04 UTCBaked processing; direct provider ingress code landed; configured Rails fallback removal in review under ENG-2998Rails remains the configured provider ingress and forwards to each regional API2 service; ENG-2998 makes configured delivery failures return 503 for provider retry instead of creating a Rails GoodJobAPI2 /webhooks/intercom owns conversation, ticket, and deletion events, including nested ticket IDs on ticket-part notifications; ENG-2800 adds external HMAC ingress plus ordered US-to-EU API2 fanoutRevert ENG-2998 to restore the Rails fallback; the Rails provider URL remains the broader ingress rollbackPR #10718 , PR #10720 , PR #10757 , ENG-2800 , and ENG-2998 ; deploy ENG-2998 in both regions and require no new ProcessIntercomWebhookEventJob before changing the Intercom destination
Intercom drift reconciliationHigh in US recovery; normally lowAPI2 replacement deployed; coverage guard in progressRails ImportIntercomConversationsJob and its 15-minute GoodJob cron are removedPer-source BullMQ reconciliation with 150-item pages, a 50-page run bound, durable page cursors, and API2 organization eligibilityExisting Rails watermark plus five-minute overlapPR #10759 , PR #10888 , and ENG-2605 ; mapped sources advanced through the September 1 audit with zero duplicate keys, while three legacy regional sources without API2 organizations exposed a coverage gap and retry amplification that the source-selection guard removes
Jira issue reconciliationMedium; bursty during provider incidentsAPI2 replacement code-readyRails recurring basic-auth scan, dispute discovery, stale linked-issue refresh jobs, and their GoodJob crons are removedBounded BullMQ source scans fan out coalesced per-issue jobs for metadata, comments, service-desk fields, conversations, and disputesExisting Rails watermarks with 24-hour overlap; BullMQ retry and rate limitingENG-2253 ; deploy, confirm all three schedulers and watermarks advance in both regions, and verify no new SyncJiraIssueJob executions
Provider daily field-definition sync (Zendesk, Freshdesk, Intercom)Low: 12 US / 6 EU per-source attempts and one sweep per region in the 24h ending September 10 22:48 UTCCode-ready; production verification pendingsync_provider_fields cron kept but disabled; SyncProviderFieldsJob and SyncProviderFieldsForOrganizationDataSourceJob are a recovery-only shimBullMQ daily 02:00 UTC scan -> deduplicated per-source job -> per-category provider read and idempotent field upsert on (organization_data_source_id, external_id)Flipper pause_api2_provider_fields_sync (checked at scan and source); re-enable the Rails cron only while pausedENG-3010  and the package runbook; observe two 02:00 UTC ticks per region with every kept source completed, advancing last_synced_at, zero new Rails job rows, and no duplicate field identities before removing the shim
Zendesk and Jira webhook ingressHigh; all provider deliveries currently terminate on Rails, which synchronously forwards to API2 — the September 8 residue snapshot still counted 5,554 Jira webhook and 1,005 legacy Zendesk GoodJob executionsDirect provider ingress code landed; provider repoint pendingWebhooksController#zendesk/#jira remain the registered provider destinations, authenticate the provider, and forward raw bodies plus routing headers to API2 /internal/webhooks/*API2 POST /webhooks/zendesk verifies the shared registration bearer token (ZENDESK_WEBHOOK_BEARER_TOKEN) and POST /webhooks/jira verifies the per-delivery HS256 JWT (issuer JIRA_CLIENT_ID, context.cloudId resolving kept sources); both share the legacy-webhook-ingestion dedupe ids, eight-attempt retries, and ignored-event rulesFlipper legacy_webhook_rails_processing restores GoodJob processing; provider URLs revert to Rails on cutover rollbackENG-2967 ; provision ZENDESK_WEBHOOK_BEARER_TOKEN into the API2 Infisical environments, confirm API2 JIRA_CLIENT_ID/JIRA_CLIENT_SECRET match the OAuth app signing webhook JWTs, verify the exact revision in both regions, then obtain explicit authorization to repoint each Zendesk subscription endpoint and Jira connection webhook URL before removing Rails ingress; see the rollout gate
Zendesk coalesced webhook ticket executionHigh: 57,333 Rails runs and 8.1 worker-hours in the latest Sentry dayCode-ready; production verification pendingRails receives webhooks, coalesces durable event rows, and remains the terminal delivery/processing fallbackBullMQ fetches/imports the ticket, enqueues source-owned provider sync, and atomically consumes the event snapshotDeterministic snapshot run ID; Rails fallback after API2 retry exhaustion; no new rollout flagPR #10960  and ENG-2692 ; deploy in both regions, verify throughput and event-row drain for 24 hours, then remove the Rails normal-path implementation
Zendesk daily ticket reconciliation and importsMedium: 2,738 import jobs / 5,029 executions per rolling 12h, all from the 00:00 UTC sweepCode-ready; production verification pendingsync_zendesk_tickets cron kept but disabled; SyncZendeskTicketsJob and ImportZendeskConversationJob are a recovery-only shimBullMQ daily scan -> per-source incremental export with monotonic last_synced_at checkpoints -> deduplicated per-ticket import and source-owned provider syncFlipper pause_api2_zendesk_ticket_reconciliation (checked at every job kind); re-enable the Rails cron only while pausedENG-2931  and the package runbook; observe two midnight ticks per region with advancing watermarks, zero new Rails import rows, and an empty ticket failed set before removing the shim
Zendesk stuck-conversation recoveryMedium; six-hour bounded sweepCode-readyRails job class is retained for code-revert rollback; its GoodJob cron is removedAPI2 BullMQ scheduler scans active Zendesk sources and enqueues the existing source-owned provider-sync queueOne-hour import grace, 500 rows per source, provider-sync deduplication, and code revertENG-2684 ; excludes discarded sources and organizations without an API2 mapping before enqueue. Verify four scheduled ticks in both regions with no Rails duplicate sweep, no new unmapped-organization errors from this producer, bounded cap warnings, and no stuck backlog before removing the retained Rails class
Zendesk source-owned conversation syncHighGlobally enabled; one source configuration blockedSyncConversationJob remains the delivery-error and invalid-source fallbackAPI2 provider conversation sync and native completionSupported, source-matched, configured sources use API2; enqueue failure falls back once to Rails; mismatched or missing-subdomain sources bypass API2Zendesk runbook, PR #10741 . The reported 2026-09-09 rolling-hour baseline was 4 US executions, 0 EU, all successful. The four rows were not independently classified because the regional GoodJob census was unavailable; the durable guard prevents the known missing-subdomain and source-mismatch retry paths while the tenant-scoped repair remains read-only
Other supported provider conversation syncMediumAPI2 defaultResidual Rails completion callbacks and provider-specific behaviorAPI2 provider syncRails callback only for effects API2 has not claimedPR #10716 ; inventory remaining callback effect keys
Conversation-level call transcriptionHighBaked into API2; native post-transcription continuation in reviewTranscribeConversationJob and the broad provider-analysis/completion endpoints remain code-revert compatibility pathsAPI2 transcription flow, eligible call/image selection, deterministic BullMQ fan-out/fan-in, transcript rebuild, and translation enqueueImmediate native translation delivery failure requests the retained GoodJob fallback; Rails retains narrow CX-risk dispatchPR #10707 , PR #10733 , PR #10739 , PR #10741 , and ENG-2803  / PR #11223 . ENG-2803 receives no production credit until the exact revision passes regional queue, transcript, translation, CX-risk, fallback, and Rails-traffic observation gates
Direct call-detail transcriptionHigh in EU: 1,986 runs and 39,130 seconds in the pre-cutover 12 hoursDeployed; production verification pendingTranscribeConversationPartCallDetailJob remains the disabled-gate and immediate API2 delivery fallbackStandalone job on the existing BullMQ conversation-transcription call queuePostHog api2_conversation_transcription; deterministic ID; GoodJob fallbackPR #10913  deployed to both regions. Latest retained API2 windows had 986 US and 157 EU successful jobs with no new failure or backlog; three Rails 404 fallbacks occurred during rollout with none later. Complete the 24-hour bake before crediting two weighted points
Post-sync conversation image captioningMedium: 1,333 sampled Rails spans and 6.1 provider-hours in the latest Sentry dayCode-ready behind the transcription rolloutRails retains authoritative image discovery, prompts, persistence, post-sync callback, and the provider job for fallbackDedicated BullMQ caption queue calls gpt-5-mini, redacts derived text, persists through Rails, and joins transcription fan-inPostHog api2_conversation_transcription; deterministic part-revision ID; explicit rejection falls back to GoodJobENG-2689  preserves five-image and 1,000-token limits, minimal reasoning, flex processing, stale-write rejection, tenant checks, and batch-success semantics. The sampled workload rose from 667 spans / 1.8 provider-hours / 9.6-second p95 in the prior day to 1,333 / 6.1 hours / 27.3 seconds, with zero measured failures in either window. Production credit waits for clean US/EU queue, persistence, fallback, and fan-in evidence
Conversation translationMediumBaked into API2Rails translation method and retry bridge remain for rollback/drainAPI2 translation route and BullMQ workerBest-effort delivery failure can enqueue the Rails bridgeTranslation migration; remove the bridge only after its documented drain condition
Topic/customer-issue assignmentMediumImplemented; bake state to verifyRails persistence and callbacks remain involvedAPI2 assignment queue and workerOperational retry pathTopic assignment migration; verify production ownership and cleanup state
QA sampling recoveryMediumDeployed; production verification pendingRails data remains authoritative; duplicate scheduling and terminal-request fan-out are removed by the bakeAPI2 recovery scheduling and executionBullMQ recovery and starvation safeguardsPR #10920  is deployed; verify fresh regional completions for 24 hours, then delete api2_qa_sampling_recovery
Settings, roles, membership, org-structure, scorecard, and related readsMediumBaked into UI; flag cleanup pendingRails endpoints remain availableAPI2 read routesSeveral active PostHog flags no longer have code referencesPR #10558 ; delete confirmed orphaned flags after audit
Workspace searchMediumDeployed; production verification pendingRails command-menu multisearch has been removedOne API2 workspace-search request owns every Command-K entity resultPostHog api2_workspace_search is orphaned until the deployed bake passes verificationUntargeted 100% rollout since August 21; PR #10921  and ENG-2674  remove the Rails/work-item/company fallback fan-out. Revert the revision if post-deploy search or Sentry checks fail; delete the PostHog record only after both regions pass the window
Manual exportsMediumEvaluations/All deployed; Tickets code-readyRails Tickets endpoint and job remain only for code-revert rollbackAPI2 export queue and worker for Evaluations, All interactions, and the classic combined AI/Auditor Tickets workbookPostHog api2_manual_exports is orphaned until the deployed bake verifiesENG-2677  / PR #10925  removed the selection branch; ENG-2683  / PR #10945  moves Tickets after 10/10 workbook-parity cases passed. Require fresh US/EU Tickets workbooks and a clean 24-hour export-run window before removing the Rails rollback path
Conversation-QA result finalizationHighProduction ownership gate passed; native import and completion enabled in both regionsRails effects remain the rollback path until the separate bake-in removalAPI2 atomically validates/imports the result tree, records completion ownership, completes terminal evaluation/request state and queues billing/summary effectsBoth PostHog flags fail closed; accepted retries keep their recorded owner after flag changesThe gate passed September 8 at 22:01 UTC with native markers for every traffic-bearing production customer, no marker conflict, duplicate result, retained finalization failure, or repeated effect. Keep the durable marker, API2 worker, Rails effects endpoint, and rollback flags until the bake-in change lands. See the completion runbook
QA evaluation request processingHighProduction ownership gate passed for the complete retained customer cohortRails GoodJob remains the immediate-delivery fallback and owns ticket-closed traffic for organizations where the gate is disabledAPI2 BullMQ drives native preparation for user-requested, admin, scheduled, and gated ticket-closed requestsOnly ticket-closed traffic reads Flipper api2_qa_evaluation_request_processing; other normal traffic is revision-selectedThe exact actor sets are US organizations 19, 25, and 38, plus EU organizations 33, 38, and 41. The gate passed September 8 at 22:01 UTC with no overdue or duplicate active request and no new terminal queue failure. Kuda request 5863910 was a recovered deployment-time stall before PRs #11508/#11512 and did not recur after the corrected workers were live
Sales-call compliance pipelineMediumCode-ready ownership and credit reconciliationRails retains non-sales-call post-sync analysis, API2 persistence callbacks, and the reconciliation job class for rollbackAPI2 owns transcription, holistic compliance, and six-hour credit reconciliation for every eligible sales callSelection branch removed; both packages use code-revert rollbackUS produced 70 snapshots from 70 distinct API2 result runs in 24 hours with no fresh queue failure; EU has no eligible traffic. PR #10932  makes future regional sales calls API2-owned. ENG-2687  replaces the GoodJob schedule with a bounded, tenant-scoped BullMQ maintenance task and requires clean regional scheduler, usage, and counter evidence before receiving production credit
Evaluation schedule executionScheduled; 15-minute global scanCode-ready; production verification pendingRails retains batch construction and the legacy scheduler/job implementation for code-revert rollbackAPI2 performs the bounded due scan and deterministic BullMQ per-window deliveryExpected-window fencing plus Rails row lock makes replay and schedule edits safeENG-2808 ; deploy in both regions, verify exactly one scheduler, observe real windows and no duplicate batches for 24 hours, then plan native batch construction
Evaluation-check executionHigh; LLM-backed complaint, customer-issue and compliance checksProduction ownership gate passed; all three native-body flags enabled in both regionsRails retains each check body as the disabled/unavailable-flag fallback and keeps Evaluations::RunEvaluationCheckJob for immediate-delivery/code-revert fallbackAPI2 owns deterministic run delivery, five-attempt retries, complaint/customer-issue/compliance bodies, post-commit effects and a separate eight-attempt terminal-failure queuePer-type PostHog flags fail closed; run and failure share a per-check advisory lock; terminal/missing checks no-op; exhausted jobs are retainedThe gate passed September 8 at 22:01 UTC with no new terminal run/failure-queue job, duplicate active AI evaluation, or newly stranded CX-risk snapshot. Retain the per-type flags and Rails executor until the separate bake-in removal. See the package runbook
Remaining Rails endpoints, jobs, and callbacksUnknownCensus requiredRails controllers, GoodJob classes, cron/scheduler entries, and API2-to-Rails internal callbacksTo be assignedNone until a bounded migration is designedDaily automation ranks the remaining surface by production workload and customer impact

CX-risk snapshot completion fan-in

The September 2 audit attributed part of 149,633 seconds of US evaluation-check and completion runtime to the Rails polling family. ENG-2249 is code-ready: BullMQ owns deterministic completion events, retry, deduplication, and calls to the retained Rails finalizer. Rails still owns the final transaction and keeps Evaluations::CheckCompletionJob as the immediate-delivery/code-revert fallback. There is no rollout flag; a stable snapshot-state key and dedicated per-snapshot advisory lock make overlap idempotent. Deploy both services, observe real regional traffic for 24 hours, and remove the normal GoodJob path only after no stranded, duplicate, or failed finalizations are found. See the package runbook.

Next work packages

Freshdesk recurring imports: ENG-2696

The code-ready package in ENG-2696  replaces ImportFreshdeskTicketsJob’s 15-minute cron with a BullMQ source scan and independently retryable ticket imports. Rails historical/manual imports and the old job classes remain recovery dependencies; native Freshdesk sync already belongs to API2.

Source scans select active connections with API2 organization mappings, stream 100-ticket pages, and stop at 300 pages without advancing a capped watermark. A durable fixed window survives initial failures and later scheduler ticks. Only source codes 1, 3, 7, and 10 are imported. Malformed ticket IDs are logged without payloads and do not block later valid tickets; queue delivery errors retain the window. Imports preserve the null external-type identity, existing sync-owned fields, conversation threads, and facet invalidation.

The shared queue starts with concurrency 2 and a global limit of 15 jobs per minute, prioritizing source scans over ticket imports. Check actual regional backlog and throughput before deployment; the retained historical importer is not a substitute for verifying that every active source has an API2 mapping.

The aggregate remains 64% production / 84% code-ready, with 36 production and 16 code-ready points remaining. No extra weighting is assigned without recalibrating the shared workload denominator. This run does not claim new production evidence or count earlier merged packages again.

Five-stream packages after September 2 acceleration

StreamCurrent bounded packageNext package and dependency
ReliabilityENG-2696 #11026 merged/deployed; US watermark 23:00 UTC, EU coverage and 24-hour bake pendingHistorical recovery after 17:00 UTC and EU org/source 31/25 coverage require specific approval
QA/evaluationsENG-2720 : native hourly credit reconciliation, including ledger insertion and mirror confirmationDurable conversation-QA handoff; schedule engine; existing ENG-2680 repair/invariant gates remain separate
Provider/post-syncENG-2803 : native provider post-transcription continuation in PR #11223 CX-risk dispatch ownership after continuation bakes; external provider ingress remains separately gated
HTTP/clientENG-2722 : four tag mutations with first-party routing, auth and tenant parityWork-schedule writes, then owned custom-field definitions
Platform/retirementENG-2723 native daily connection-log retention prepared; reproducible census still needs dependency classificationAuth/public/admin/operational cutover; public-only schema expand/contract across both api2 and agent_api

The user now caps concurrent workstreams at six. Rotate the five logical streams within that limit including review/deployment waits. Independent implementation continues while dependent provider expansion is gated. No new point allocation or production credit is claimed for these packages until implementation and the measured denominator support it.

The 23:04 UTC targeted query found zero eligible overdue QA credit mirrors in each region. EU org 41 had 27 billed evaluations in the prior 24 hours, all mapped and mirrored; US had none. This proves current observed billing coverage only. Preserve unmapped/bad rows for retry and require actual workload evidence during bake.

The synchronous transcription inside QAEvaluationRequest::StageProcessor#transcribe_calls is removed from the API2-owned path by ENG-2686. Its delayed continuation must deploy and pass the regional observation window before receiving two production points. The sales-call credit reconciliation in ENG-2687 similarly receives its one point only after the BullMQ scheduler and database effects verify in both regions. Post-sync image captioning in ENG-2689 receives two points only after the existing transcription rollout proves provider-result parity, stale-write and redaction safety, bounded fallback, and successful post-sync fan-in in both regions.

Status definitions

  • Not started: Rails owns the production path and no parity-ready API2 path exists.
  • Implemented behind a gate: API2 code exists but has no meaningful production rollout.
  • Partially rolled out: selected organizations or a percentage use API2.
  • Globally enabled; baking: API2 owns production, but a temporary selection gate and legacy path still exist.
  • Baked into API2: the selection branch is removed; only intentional resilience or data-ownership dependencies remain.
  • Blocked: a named correctness, reliability, or operational issue prevents rollout.
  • Intentionally Rails: the dependency is documented with an owner and exit condition or accepted as part of the final architecture.
Last updated on