feat(backend): split X connector sync into its own Cloud Run Job (#9298) - #11183
feat(backend): split X connector sync into its own Cloud Run Job (#9298)#11183aryanorastar wants to merge 18 commits into
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
4 issues found across 24 files
Confidence score: 2/5
- In
backend/utils/other/jobs.py, deploying the notifications job beforex-connector-sync-joband its Scheduler trigger exist can halt X incremental sync, leaving connected users with stale data; gate rollout on verified job/scheduler creation or enforce dependency ordering in deployment automation. - In
.github/workflows/gcp_x_connector_sync_job.yml, the unconditionalgcloud scheduler jobs describe "$SCHEDULER_JOB"check makes the first workflow run fail whenx-connector-sync-6hhas not been created yet, which can block initial provisioning—add a create-if-missing/bootstrap path or conditional validation. - In
backend/scripts/scaffold_cloud_run_job.sh, name validation currently allows trailing hyphens and overlong names, so scaffolding appears successful but latergcloud run jobsdeployment fails; validate the full Cloud Run naming regex and 1–63 length constraints up front. - In
backend/scripts/validate_x_connector_sync_scheduler.py(and duplicated tests), near-copyingvalidate_memory_maintenance_schedulerincreases drift risk when one validator evolves and the other does not; extract shared scheduler-validation logic to a common helper and keep only job-specific constants in each script.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="backend/utils/other/jobs.py">
<violation number="1" location="backend/utils/other/jobs.py:6">
P1: Deploying the updated notifications job before `x-connector-sync-job` and its Scheduler trigger are provisioned stops all X incremental sync, leaving connected users' data stale. Please make creation/verification of `x-connector-sync-6h` a prerequisite for this handoff, or retain the old invocation until the new job is live.</violation>
</file>
<file name="backend/scripts/scaffold_cloud_run_job.sh">
<violation number="1" location="backend/scripts/scaffold_cloud_run_job.sh:52">
P2: The input check accepts trailing-hyphen and overlong Cloud Run job names, causing scaffolding to succeed before the eventual `gcloud run jobs` deployment rejects the resource name. Validate the full 1–63 character name grammar before writing the stubs.</violation>
</file>
<file name="backend/scripts/validate_x_connector_sync_scheduler.py">
<violation number="1" location="backend/scripts/validate_x_connector_sync_scheduler.py:45">
P2: This new scheduler validator is a near-verbatim copy of `backend/scripts/validate_memory_maintenance_scheduler.py` (only the schedule constant and docstring differ), and the test file duplicates `test_validate_memory_maintenance_scheduler.py`. Because both are deploy-critical gates, any future contract change (e.g., adding retry config, a second target field, or a different OAuth setting) has to be applied to two independent copies and can silently drift. Per the repo's own guidance on shared primitives, consider extracting a common scheduler-contract validator (e.g., a shared `SchedulerContract` + `validate_scheduler_state(state, contract, *, schedule, display_name)`) and having the memory and X connector entrypoints reuse it, instead of maintaining two parallel implementations.</violation>
</file>
<file name=".github/workflows/gcp_x_connector_sync_job.yml">
<violation number="1" location=".github/workflows/gcp_x_connector_sync_job.yml:140">
P2: The first run of this workflow will fail at 'Validate 6h Scheduler trigger': the step runs `gcloud scheduler jobs describe "$SCHEDULER_JOB"` unconditionally, but this PR does not create the `x-connector-sync-6h` scheduler (per the PR description, maintainers wire it via runbook). On the initial deploy the scheduler does not exist, so `describe` returns NOT_FOUND and the whole deploy job reports failed right after the Cloud Run job was successfully deployed. The operations team ends up with a red first deploy and a manual pre-step ordering that isn't encoded anywhere in the workflow. Consider making the scheduler validation resilient to a missing scheduler on first bootstrap (e.g., skip with a warning when `describe` returns NOT_FOUND), or add an explicit scheduler-create/bootstrap step so the first deploy isn't guaranteed to fail.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
|
||
| async def start_job(): | ||
| # Notification | ||
| # Notification / daily summary only. X connector sync lives on x-connector-sync-job. |
There was a problem hiding this comment.
P1: Deploying the updated notifications job before x-connector-sync-job and its Scheduler trigger are provisioned stops all X incremental sync, leaving connected users' data stale. Please make creation/verification of x-connector-sync-6h a prerequisite for this handoff, or retain the old invocation until the new job is live.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/utils/other/jobs.py, line 6:
<comment>Deploying the updated notifications job before `x-connector-sync-job` and its Scheduler trigger are provisioned stops all X incremental sync, leaving connected users' data stale. Please make creation/verification of `x-connector-sync-6h` a prerequisite for this handoff, or retain the old invocation until the new job is live.</comment>
<file context>
@@ -1,13 +1,8 @@
async def start_job():
- # Notification
+ # Notification / daily summary only. X connector sync lives on x-connector-sync-job.
if should_run_daily_notification_job():
await start_cron_notification_job()
</file context>
Git-on-my-level
left a comment
There was a problem hiding this comment.
Thanks for pulling the X connector sync out into its own Cloud Run Job. The direction is good: the new entrypoint keeps the job single-purpose, the workflow includes a read-only Scheduler contract check, and the tests cover the notifications-job isolation and selected-job runtime-env rendering.
I do think this needs changes before merge because the new job does not appear to receive the full runtime contract needed by run_x_sync_job():
backend/utils/x_connector.pyrefreshes OAuth access tokens viaX_OAUTH_CLIENT_ID,X_OAUTH_CLIENT_SECRET, andX_OAUTH_REDIRECT_URI, then falls back toutils.social.get_twitter_timeline(), which readsRAPID_API_HOST/RAPID_API_KEYat import time.- The new
cloud_run.jobs.x-connector-sync-jobbindings inbackend/deploy/runtime_env*.yamlmovePINECONE_*over, but do not bind the X OAuth or RapidAPI env/secrets to the new job. - That means the dedicated job can refresh neither expired OAuth tokens nor the RapidAPI fallback path; connected users whose access tokens expire would silently degrade to failed/stale X syncs even though the Scheduler and Cloud Run Job are healthy.
Please add the X OAuth and RapidAPI bindings needed by the code path to x-connector-sync-job (and keep any secrets off notifications-job if it no longer needs them), then extend the runtime-env/render tests to assert the new job carries those values and the notifications job does not retain X-sync-only bindings.
Agent-instruction impact: this PR also updates backend/AGENTS.md and adds docs/doc/developer/backend/cloud_run_jobs_checklist.md, both of which guide coding/review agents working in this repo. The guidance is directionally safe and useful (one periodic domain per Cloud Run Job; do not hitchhike unrelated work onto notifications-job), but because it changes future agent behavior and deploy scaffolding expectations, I’d still want maintainer review on the final wording once the runtime contract issue above is fixed.
by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.
Address David CR on BasedHardware#11183: the dedicated sync job must refresh OAuth tokens and use RapidAPI fallback. Also tighten scaffold/scheduler gates. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@Git-on-my-level addressed your runtime-contract CR in Runtime contract (your blocker)
Also fixed while here (cubic P1s aligned with the same surface)
VerificationFocused unit suite green in Ready for re-pass when you have a moment. Agent-doc wording ( |
|
@Git-on-my-level gentle bump for re-pass. Head |
Git-on-my-level
left a comment
There was a problem hiding this comment.
Thanks for the follow-up. The original runtime-contract blocker is addressed now: x-connector-sync-job receives the X OAuth/RapidAPI bindings needed by run_x_sync_job(), and the tests now assert those bindings stay off notifications-job.
I still need to request one deploy-readiness change before this can merge:
backend/modal/x_connector_sync_job.pycallsasyncio.run(run_x_sync_job())and then exits 0 regardless of the returned summary. Inbackend/utils/x_connector.py, the job catches a global user-listing failure and returns{'users': 0, 'synced': 0, 'new_posts': 0}, and per-user failures are logged/ignored. As a standalone Cloud Run Job triggered every 6h, that means credential/firestore/network breakage can look like a successful Scheduler execution, so the new dedicated sync job can silently stop syncing users while Cloud Run/Scheduler appear healthy.
Please make the job surface failure to Cloud Run for at least global/listing failures, and preferably include enough failed-user accounting in the runner/entrypoint to raise when the scheduled run could not complete its sync contract. The nearby memory-maintenance-job pattern of inspecting the summary and raising on errors is a good reference.
Agent-instruction impact: this PR updates backend/AGENTS.md and adds docs/doc/developer/backend/cloud_run_jobs_checklist.md, which will guide future coding/review agents toward one periodic domain per Cloud Run Job and away from adding unrelated work to notifications-job. That guidance looks safe and useful, but because it changes future agent behavior and deploy scaffolding expectations, it should still get human maintainer sign-off with the workflow/deploy changes.
by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.
|
@Git-on-my-level addressed the deploy-readiness CR on Failure surface (your blocker) — mirrors memory-maintenance-job:
Tests: Ready for re-pass when you have a moment. Agent-doc wording unchanged from prior head. |
Split mangled x-connector render assertions into their own test (NameError on jobs), and black-format the failure-surface files at line-length 120. Failure-Class: none Co-authored-by: Cursor <cursoragent@cursor.com>
|
CI was red on
Focused local: Author-side clear again once CI goes green — ready for re-pass on the failure-surface behavior when you can. |
Git-on-my-level
left a comment
There was a problem hiding this comment.
Thanks for the follow-up — the two earlier blockers I raised look addressed on this head:
backend/deploy/runtime_env.yaml/_base.yamlnow givex-connector-sync-jobthe X OAuth, RapidAPI, Pinecone, Firebase, encryption, and stage/project bindings it needs, whilenotifications-jobno longer carries the X-sync-only env/secrets.backend/modal/x_connector_sync_job.pynow callsraise_if_x_sync_job_failed(summary)afterasyncio.run(run_x_sync_job()), andbackend/utils/x_connector.pynow reports listing errors and all-user failures through the summary instead of letting Cloud Run/Scheduler see a healthy no-op..github/workflows/gcp_x_connector_sync_job.ymluses the checked-out SHA for image tags, deploys the dedicated job, and gates success on a read-only Scheduler describe + validator.backend/utils/other/jobs.pyis now limited to notifications/daily summary work, which matches the split.backend/AGENTS.mdanddocs/doc/developer/backend/cloud_run_jobs_checklist.mddo change future agent/coding guidance: they steer agents toward one periodic domain per Cloud Run Job and away from hitchhiking new domains ontonotifications-job. That guidance looks safe and aligned with this fix, but it is agent-behavior/deploy guidance and should still get maintainer sign-off with the workflow change.
I found one remaining deploy-contract issue that should be fixed before merge:
backend/testing/workflow_contracts.jsonadds the invariant thatx-connector-sync-jobdeploys fail unless the exact enabled 6h OAuth Scheduler trigger targets the deployed job, and it registerstests/unit/test_validate_x_connector_sync_scheduler.py. However, thebackend_cloud_run_deploy.sourceslist still only includes the memory-maintenance workflow/validator (.github/workflows/gcp_memory_maintenance_job.yml,.github/workflows/gcp_memory_maintenance_job_auto_dev.yml, andbackend/scripts/validate_memory_maintenance_scheduler.py). It does not include.github/workflows/gcp_x_connector_sync_job.ymlorbackend/scripts/validate_x_connector_sync_scheduler.py, which are the actual enforcement files for the new invariant. That leaves the source-closure/diff-scoped contract mapping blind to future edits of this deploy-critical gate. Please add those two files to thebackend_cloud_run_deploy.sourceslist alongside the existing memory-maintenance entries.
Given this touches workflow/deploy behavior and agent-facing docs, I’m leaving the existing workflow-review / needs-maintainer-review labels in place for maintainer sign-off after the source-closure fix.
by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.
Resolved on current head: the X connector job now has the required runtime bindings and surfaces sync failures to Cloud Run; current review tracks a separate workflow-contract source-closure issue.
Addressed review @936f3ede4056@Git-on-my-level Source-closure blocker fixed.
alongside the existing memory-maintenance entries, so diff-scoped/source-closure mapping tracks the 6h Scheduler invariant enforcement files (not only the registered test). Also merged latest Ready for re-pass / dismiss CR when CI is green. |
Git-on-my-level
left a comment
There was a problem hiding this comment.
Thanks for the updates — I re-reviewed this head and the prior blocker looks resolved.
What I checked:
backend/testing/workflow_contracts.jsonnow includes both.github/workflows/gcp_x_connector_sync_job.ymlandbackend/scripts/validate_x_connector_sync_scheduler.pyin the backend Cloud Run deploy source closure, so the new Scheduler/deploy invariant is no longer blind to the actual enforcement files..github/workflows/gcp_x_connector_sync_job.ymldefines a dedicated manual deploy forx-connector-sync-job, tags from the checked-out commit, renders only the selected job env, and fails the deploy if the existingx-connector-sync-6hScheduler contract does not match the job.backend/deploy/runtime_env.yamlplus_base.yaml/overlays separate the X OAuth/RapidAPI/Pinecone bindings ontox-connector-sync-jobwhile keepingnotifications-jobscoped to notification/daily-summary runtime bindings.backend/modal/x_connector_sync_job.pyis a thin Firebase-initializing entrypoint that always runsrun_x_sync_job()and callsraise_if_x_sync_job_failed(summary), so Cloud Run/Scheduler see a non-zero failure when the sync contract is broken.backend/utils/x_connector.pyremoves the old hour-modulo gate, reports listing failures througherrors, counts per-user failures, and fails only on global/listing or all-user failure while still allowing partial progress.backend/utils/other/jobs.pyno longer imports or invokes X sync, which matches the ownership split away fromnotifications-job.backend/scripts/validate_x_connector_sync_scheduler.pyis read-only and validates the exact Scheduler name, 6h cadence, target URI, OAuth service account, and enabled state without creating or mutating cloud resources.backend/AGENTS.mdanddocs/doc/developer/backend/cloud_run_jobs_checklist.mddo change agent-facing repo guidance: future coding/review agents will be steered toward one periodic domain per Cloud Run Job and away from adding unrelated jobs tonotifications-job. That guidance looks safe and consistent with this PR, but it is still maintainer-facing agent/deploy guidance.
Given the workflow/deploy surface and the agent-instruction docs, I’m not formally approving; this should still get human maintainer sign-off for the rollout plan and Scheduler resource creation. Technically, I do not see a remaining code blocker on this head, and I’m dismissing my older change request because the source-closure concern is now addressed.
by AI on behalf of David — leaving this for human maintainer review because it changes deploy workflow behavior and agent-facing backend guidance.
Resolved on 936f3ed: backend_cloud_run_deploy now includes gcp_x_connector_sync_job.yml and validate_x_connector_sync_scheduler.py in source closure.
|
@Git-on-my-level @kodjima33 need human response Author-clear on
Branch protection still waits on code-owner review from @Git-on-my-level (workflow/deploy) plus write-access APPROVE. Please APPROVE when the rollout plan is acceptable; Scheduler job creation + first deploy via |
|
@Git-on-my-level @kodjima33 workflow-review ping: current head is MERGEABLE with all checks green; the only remaining gate is write-access/code-owner approval for the X connector Cloud Run Job workflow. Please approve or state an explicit blocker. |
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Refreshed onto current @Git-on-my-level workflow CO when ready. |
Co-authored-by: Cursor <cursoragent@cursor.com>
Address David CR on BasedHardware#11183: the dedicated sync job must refresh OAuth tokens and use RapidAPI fallback. Also tighten scaffold/scheduler gates. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the Cloud Run Job service map concise and format the scheduler test with the pinned Black version. Failure-Class: none Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Keep generated scaffold guidance descriptive without creating untracked TODO markers that fail repository metadata checks. Failure-Class: none Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
Surface listing failures and total per-user failure to Cloud Run via a non-zero exit (memory-maintenance pattern) so Scheduler cannot look healthy while X sync is broken. Failure-Class: none Co-authored-by: Cursor <cursoragent@cursor.com>
Split mangled x-connector render assertions into their own test (NameError on jobs), and black-format the failure-surface files at line-length 120. Failure-Class: none Co-authored-by: Cursor <cursoragent@cursor.com>
Include gcp_x_connector_sync_job.yml and validate_x_connector_sync_scheduler.py in backend_cloud_run_deploy.sources so source-closure tracks the 6h Scheduler invariant. Co-authored-by: Cursor <cursoragent@cursor.com>
a51ce21 to
d4787f5
Compare
|
tip refreshed onto main ( |
Scheduled X extraction Flex bindings and the gateway verify/probe now live on the dedicated sync job; notifications-job no longer carries the leftover gateway/Flex surface. Recomposed runtime_env.yaml for Hygiene. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@Git-on-my-level tip after
Local: focused runtime-env/x-job suite green; |
|
Conflicts with current main resolved on tip Still current and still author-side clear. Verified against |
…sync-job # Conflicts: # backend/AGENTS.md # backend/tests/unit/test_notifications_job_orchestrator.py # backend/utils/other/jobs.py
|
Author-side work is complete. I refreshed the branch onto current The red check came from Current-main verification: 32/32 PR preflight checks passed. The branch is mergeable and CI is rerunning. The remaining items are maintainer-owned: CODEOWNERS approval, then the documented first job/Scheduler deployment after merge. |
Failure-Class: none
Git-on-my-level
left a comment
There was a problem hiding this comment.
Thanks for the continued updates — the split itself remains in good shape. This pass surfaced one real blocker on head b16d822a and one piece of good news about CI.
Blocker: scheduled X extraction silently dropped the gateway Flex routing
While you were iterating, main gained PromotionFlexRunRouter in scheduled X sync (a052a90f, "Expand shared Flex switch to scheduled memory work", Aug 16 06:08 UTC) — main's run_x_sync_job() constructs the router and passes background_flex= to sync_x_for_user(), so extraction runs through the x_memory_extraction_flex gateway lane when the shared switch is on.
Your rewritten backend/utils/x_connector.py::run_x_sync_job() (kept through the later main merges) never constructs the router, so extraction_llm is always None and scheduled extraction always takes the legacy get_llm('memories') direct path. That path only routes through the gateway with OMI_LLM_GATEWAY_FEATURE_MODE=gateway, which is not part of this job's contract.
The problem is that the PR's own deploy contract still requires Flex on this job, so the code and the contract now disagree:
backend/deploy/runtime_env/_base.yaml+ composedruntime_env.yamlsetOMI_BACKGROUND_FLEX_CAPABLE: 'true'and bindOMI_LLM_GATEWAY_URL/OMI_LLM_GATEWAY_SERVICE_TOKENonx-connector-sync-jobbackend/scripts/runtime_env_validation/manifest.pyenforces both, with the rationale "so the shared live flag covers scheduled X extraction".github/workflows/gcp_x_connector_sync_job.ymlruns the gateway serving gate and probes theomi:auto:x-memory-extraction-flexVPC lane before deploy
For comparison, the job this PR clones — memory-maintenance-job — constructs PromotionFlexRunRouter(force_enabled=...) in its cron, so the capability flag there matches code that actually reads it.
Could you either restore router construction in run_x_sync_job() (preferred, preserves current prod behavior), or, if dropping Flex routing from X extraction is intended, strip the capability flag, gateway token/URL bindings, the manifest.py rule, and the Flex-lane probe in the same change so the contract describes what the code does? As-is, the deploy guarantees something the runtime never uses, and the flag OMI_BACKGROUND_FLEX_CAPABLE (which promotion_flex_capable() reads) becomes dead env on this job.
CI: the red "Backend unit suite" is not yours
The failure is pyright reportUnusedImport ×2 at backend/utils/stt/streaming.py:37 (SafeSonioxSocket, process_audio_soniox) — a file this PR doesn't touch. Main introduced it in 7c900a93 (Aug 27 14:25 UTC), your branch merged main at 17:13, and main already fixed it in 2af0832 (#12084, 18:18 UTC). Main's own unit runs failed on the same window and pass from 2af0832 onward. A main refresh should clear it without any change here. (I checked the pytest portion of the run — it passed.)
What still looks good on this head
backend/modal/x_connector_sync_job.py— thin entrypoint, always runsrun_x_sync_job(), exits non-zero viaraise_if_x_sync_job_failed(summary);backend/utils/x_connector.pynow surfaces listing failures and all-user failure instead of a silent healthy no-op.backend/utils/other/jobs.py— clean removal of the X import/gate; no danglingshould_run_x_sync_jobreferences anywhere.backend/deploy/runtime_env.yaml+_base.yaml/overlays — the ownership split is correct end-to-end: notifications-job keeps only the secrets its remaining code reads (I verifiedutils/other/notifications.pyand the materialization-health check use no Pinecone/gateway/X env), and all X OAuth/RapidAPI/Pinecone/Flex bindings move to the new job.backend/scripts/validate_x_connector_sync_scheduler.py— read-only, validates the exact 6h/OAuth-SA/target contract; the workflow only validates (never creates) the Scheduler job.backend/scripts/scaffold_cloud_run_job.sh— dry-run by default, creates no GCP/Secret/Scheduler resources; template references all resolve..github/workflows/gcp_x_connector_sync_job.yml— dispatch-only, environment-gated, image smoke-verified before push, deploy concurrency lock registered.
Agent-facing docs (flagged for maintainers)
backend/AGENTS.md and docs/doc/developer/backend/cloud_run_jobs_checklist.md change agent guidance: future coding/review agents will be steered toward one periodic domain per Cloud Run Job, cloning the landed templates, and away from adding unrelated work to notifications-job. I checked the wording against the tree — it's accurate and matches issue #9298's intent — but it is durable agent-facing policy from an outside contributor, so a maintainer should consciously accept it.
Leaving for human maintainer review: the Flex routing decision above (restore vs. deliberately strip the contract), and post-merge rollout — the Scheduler SA + x-connector-sync-6h creation and the ordered deploy in the runbook can't be verified from code.
by AI on behalf of David — requesting changes for the Flex routing/contract mismatch on the scheduled X sync path; needs a maintainer decision on restore-vs-strip before the new job owns prod sync.
…sync-job # Conflicts: # backend/runtime_images.json # backend/tests/unit/test_render_backend_runtime_env.py
Restore the job-level PromotionFlexRunRouter that the Cloud Run split dropped, pass one shared run router to every connected user, and preserve deliberate PromotionFlexDeferred outcomes without counting them as user failures.\n\nThe behavioral regression test drives run_x_sync_job through an injected registry, asserts the router is constructed with the job start time and reused for every user, and proves a capacity deferral leaves failed=0.\n\nVerification:\n- focused X scheduler suites: 16 passed\n- X/runtime/deployment contract suites: 162 passed\n- full backend suite exercised; X suites passed, while five files identical to origin/main remain red on unrelated current-main STT/test-isolation/inventory failures\n\nFailure-Class: none
|
@Git-on-my-level Resolved the Flex-routing blocker in What changed
Regression proofAdded a behavioral test (not a source-string tripwire) that runs the real
Verification on the updated head:
No physical-device test applies: this is the backend scheduled Cloud Run path. Live Scheduler/deploy verification remains the explicit post-merge maintainer step already listed in the PR. |
|
@Git-on-my-level @undivisible CI follow-up: the three remaining red checks are reproducible on the current
I isolated the main-wide repair in #12339. Proof on that branch:
The X-specific suites on this PR remain green, including the shared per-run Flex router regression added in |
Summary
notifications-jobonto a dedicatedx-connector-sync-job(entrypoint, Dockerfile, runtime-env contract, deploy workflow).x-connector-sync-6h); the entrypoint no longer uses hour-modulo gating.PromotionFlexRunRouterper job run, reuse it for every connected user, and leave intentional Flex capacity deferrals pending without counting them as sync failures.Fixes #9298
Why the Flex follow-up matters
While this PR was open, main added the shared Flex router to scheduled X memory extraction. The job split retained the Flex-aware per-user implementation but its rewritten scheduler boundary stopped passing the router, which made scheduled extraction silently use the legacy standard lane. The latest commit restores the current production routing contract without weakening this PR's new Cloud Run failure surface.
Verification
run_x_sync_job()through two registry users and proves:sync_x_for_user()call;PromotionFlexDeferreduser remains retryable and does not incrementfailed.backend/test.shsweep exercised all 992 test files; both X suites passed. Five unrelated current-main files remain red and are byte-identical toorigin/mainin this branch (test_legacy_memory_surface_inventory.py,test_modulate_stt.py,test_streaming_deepgram_backoff.py,test_async_app_integrations.py,test_async_realtime_integrations_offload.py).x-connector-sync-job.gcp_x_connector_sync_job.yml.Failure class
Failure-Class: none
The change is an ownership split plus a review-time regression repair; it is not a shipped recurring failure-class instance.
Product invariants affected
notifications-job.Test plan