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
| Category | Weight | Current completion | Contribution | Basis |
|---|---|---|---|---|
| UI and API reads | 20% | 75% | 15.0 | Most settings and directory reads are baked; workspace search is deployed and manual exports are code-ready |
| Writes and user actions | 15% | 45% | 6.8 | Several API2 actions exist, but Rails still owns a material write surface |
| Ingestion, provider sync, and webhooks | 20% | 85% | 17.0 | Zendesk and Intercom are globally on; selection flags and residual callbacks remain |
| Async analysis and evaluation | 20% | 65% | 13.0 | Translation and transcription moved; QA and analysis ownership remains mixed |
| Schedules, exports, and recovery | 10% | 60% | 6.0 | QA recovery and some exports moved; remaining Rails schedules need a complete census |
| Auth, public API, and admin | 10% | 45% | 4.5 | Session/bootstrap reads moved, with mixed Rails ownership elsewhere |
| Rails dependency and flag cleanup | 5% | 25% | 1.3 | Several bake-ins are ready, but callbacks and temporary controls remain |
| Total | 100% | 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
| Surface | Traffic | State | Rails path / dependency | API2 replacement | Gate or fallback | Evidence and next action |
|---|---|---|---|---|---|---|
| Intercom conversation import | High: 1.80M Rails runs and 1.12B ms in the seven days before cutover | Baked into API2 | ImportIntercomConversationJob remains only for immediate API2 delivery failure | BullMQ Intercom conversation import | Enqueue failure falls back to GoodJob; PostHog selection branch removed | PR #10714 , PR #10757 ; verify the recovery bake window, then delete the orphaned flag record |
| Intercom ticket import | Medium | Baked into API2 | ImportIntercomTicketJob remains only for immediate API2 delivery failure | BullMQ Intercom ticket import | Enqueue failure falls back to GoodJob; PostHog selection branch removed | PR #10717 , PR #10757 ; verify the recovery bake window, then delete the orphaned flag record |
| Intercom webhook receipt and routing | High; 59 US attempts / 52 unique legacy jobs and zero EU attempts in the 24 hours ending September 10 at 16:04 UTC | Baked processing; direct provider ingress code landed; configured Rails fallback removal in review under ENG-2998 | Rails 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 GoodJob | API2 /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 fanout | Revert ENG-2998 to restore the Rails fallback; the Rails provider URL remains the broader ingress rollback | PR #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 reconciliation | High in US recovery; normally low | API2 replacement deployed; coverage guard in progress | Rails ImportIntercomConversationsJob and its 15-minute GoodJob cron are removed | Per-source BullMQ reconciliation with 150-item pages, a 50-page run bound, durable page cursors, and API2 organization eligibility | Existing Rails watermark plus five-minute overlap | PR #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 reconciliation | Medium; bursty during provider incidents | API2 replacement code-ready | Rails recurring basic-auth scan, dispute discovery, stale linked-issue refresh jobs, and their GoodJob crons are removed | Bounded BullMQ source scans fan out coalesced per-issue jobs for metadata, comments, service-desk fields, conversations, and disputes | Existing Rails watermarks with 24-hour overlap; BullMQ retry and rate limiting | ENG-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 UTC | Code-ready; production verification pending | sync_provider_fields cron kept but disabled; SyncProviderFieldsJob and SyncProviderFieldsForOrganizationDataSourceJob are a recovery-only shim | BullMQ 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 paused | ENG-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 ingress | High; 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 executions | Direct provider ingress code landed; provider repoint pending | WebhooksController#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 rules | Flipper legacy_webhook_rails_processing restores GoodJob processing; provider URLs revert to Rails on cutover rollback | ENG-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 execution | High: 57,333 Rails runs and 8.1 worker-hours in the latest Sentry day | Code-ready; production verification pending | Rails receives webhooks, coalesces durable event rows, and remains the terminal delivery/processing fallback | BullMQ fetches/imports the ticket, enqueues source-owned provider sync, and atomically consumes the event snapshot | Deterministic snapshot run ID; Rails fallback after API2 retry exhaustion; no new rollout flag | PR #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 imports | Medium: 2,738 import jobs / 5,029 executions per rolling 12h, all from the 00:00 UTC sweep | Code-ready; production verification pending | sync_zendesk_tickets cron kept but disabled; SyncZendeskTicketsJob and ImportZendeskConversationJob are a recovery-only shim | BullMQ daily scan -> per-source incremental export with monotonic last_synced_at checkpoints -> deduplicated per-ticket import and source-owned provider sync | Flipper pause_api2_zendesk_ticket_reconciliation (checked at every job kind); re-enable the Rails cron only while paused | ENG-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 recovery | Medium; six-hour bounded sweep | Code-ready | Rails job class is retained for code-revert rollback; its GoodJob cron is removed | API2 BullMQ scheduler scans active Zendesk sources and enqueues the existing source-owned provider-sync queue | One-hour import grace, 500 rows per source, provider-sync deduplication, and code revert | ENG-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 sync | High | Globally enabled; one source configuration blocked | SyncConversationJob remains the delivery-error and invalid-source fallback | API2 provider conversation sync and native completion | Supported, source-matched, configured sources use API2; enqueue failure falls back once to Rails; mismatched or missing-subdomain sources bypass API2 | Zendesk 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 sync | Medium | API2 default | Residual Rails completion callbacks and provider-specific behavior | API2 provider sync | Rails callback only for effects API2 has not claimed | PR #10716 ; inventory remaining callback effect keys |
| Conversation-level call transcription | High | Baked into API2; native post-transcription continuation in review | TranscribeConversationJob and the broad provider-analysis/completion endpoints remain code-revert compatibility paths | API2 transcription flow, eligible call/image selection, deterministic BullMQ fan-out/fan-in, transcript rebuild, and translation enqueue | Immediate native translation delivery failure requests the retained GoodJob fallback; Rails retains narrow CX-risk dispatch | PR #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 transcription | High in EU: 1,986 runs and 39,130 seconds in the pre-cutover 12 hours | Deployed; production verification pending | TranscribeConversationPartCallDetailJob remains the disabled-gate and immediate API2 delivery fallback | Standalone job on the existing BullMQ conversation-transcription call queue | PostHog api2_conversation_transcription; deterministic ID; GoodJob fallback | PR #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 captioning | Medium: 1,333 sampled Rails spans and 6.1 provider-hours in the latest Sentry day | Code-ready behind the transcription rollout | Rails retains authoritative image discovery, prompts, persistence, post-sync callback, and the provider job for fallback | Dedicated BullMQ caption queue calls gpt-5-mini, redacts derived text, persists through Rails, and joins transcription fan-in | PostHog api2_conversation_transcription; deterministic part-revision ID; explicit rejection falls back to GoodJob | ENG-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 translation | Medium | Baked into API2 | Rails translation method and retry bridge remain for rollback/drain | API2 translation route and BullMQ worker | Best-effort delivery failure can enqueue the Rails bridge | Translation migration; remove the bridge only after its documented drain condition |
| Topic/customer-issue assignment | Medium | Implemented; bake state to verify | Rails persistence and callbacks remain involved | API2 assignment queue and worker | Operational retry path | Topic assignment migration; verify production ownership and cleanup state |
| QA sampling recovery | Medium | Deployed; production verification pending | Rails data remains authoritative; duplicate scheduling and terminal-request fan-out are removed by the bake | API2 recovery scheduling and execution | BullMQ recovery and starvation safeguards | PR #10920 is deployed; verify fresh regional completions for 24 hours, then delete api2_qa_sampling_recovery |
| Settings, roles, membership, org-structure, scorecard, and related reads | Medium | Baked into UI; flag cleanup pending | Rails endpoints remain available | API2 read routes | Several active PostHog flags no longer have code references | PR #10558 ; delete confirmed orphaned flags after audit |
| Workspace search | Medium | Deployed; production verification pending | Rails command-menu multisearch has been removed | One API2 workspace-search request owns every Command-K entity result | PostHog api2_workspace_search is orphaned until the deployed bake passes verification | Untargeted 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 exports | Medium | Evaluations/All deployed; Tickets code-ready | Rails Tickets endpoint and job remain only for code-revert rollback | API2 export queue and worker for Evaluations, All interactions, and the classic combined AI/Auditor Tickets workbook | PostHog api2_manual_exports is orphaned until the deployed bake verifies | ENG-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 finalization | High | Production ownership gate passed; native import and completion enabled in both regions | Rails effects remain the rollback path until the separate bake-in removal | API2 atomically validates/imports the result tree, records completion ownership, completes terminal evaluation/request state and queues billing/summary effects | Both PostHog flags fail closed; accepted retries keep their recorded owner after flag changes | The 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 processing | High | Production ownership gate passed for the complete retained customer cohort | Rails GoodJob remains the immediate-delivery fallback and owns ticket-closed traffic for organizations where the gate is disabled | API2 BullMQ drives native preparation for user-requested, admin, scheduled, and gated ticket-closed requests | Only ticket-closed traffic reads Flipper api2_qa_evaluation_request_processing; other normal traffic is revision-selected | The 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 pipeline | Medium | Code-ready ownership and credit reconciliation | Rails retains non-sales-call post-sync analysis, API2 persistence callbacks, and the reconciliation job class for rollback | API2 owns transcription, holistic compliance, and six-hour credit reconciliation for every eligible sales call | Selection branch removed; both packages use code-revert rollback | US 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 execution | Scheduled; 15-minute global scan | Code-ready; production verification pending | Rails retains batch construction and the legacy scheduler/job implementation for code-revert rollback | API2 performs the bounded due scan and deterministic BullMQ per-window delivery | Expected-window fencing plus Rails row lock makes replay and schedule edits safe | ENG-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 execution | High; LLM-backed complaint, customer-issue and compliance checks | Production ownership gate passed; all three native-body flags enabled in both regions | Rails retains each check body as the disabled/unavailable-flag fallback and keeps Evaluations::RunEvaluationCheckJob for immediate-delivery/code-revert fallback | API2 owns deterministic run delivery, five-attempt retries, complaint/customer-issue/compliance bodies, post-commit effects and a separate eight-attempt terminal-failure queue | Per-type PostHog flags fail closed; run and failure share a per-check advisory lock; terminal/missing checks no-op; exhausted jobs are retained | The 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 callbacks | Unknown | Census required | Rails controllers, GoodJob classes, cron/scheduler entries, and API2-to-Rails internal callbacks | To be assigned | None until a bounded migration is designed | Daily 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
| Stream | Current bounded package | Next package and dependency |
|---|---|---|
| Reliability | ENG-2696 #11026 merged/deployed; US watermark 23:00 UTC, EU coverage and 24-hour bake pending | Historical recovery after 17:00 UTC and EU org/source 31/25 coverage require specific approval |
| QA/evaluations | ENG-2720 : native hourly credit reconciliation, including ledger insertion and mirror confirmation | Durable conversation-QA handoff; schedule engine; existing ENG-2680 repair/invariant gates remain separate |
| Provider/post-sync | ENG-2803 : native provider post-transcription continuation in PR #11223 | CX-risk dispatch ownership after continuation bakes; external provider ingress remains separately gated |
| HTTP/client | ENG-2722 : four tag mutations with first-party routing, auth and tenant parity | Work-schedule writes, then owned custom-field definitions |
| Platform/retirement | ENG-2723 native daily connection-log retention prepared; reproducible census still needs dependency classification | Auth/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.