Skip to content
  1. Home
  2. Benchmarks
  3. Integration Bench
  4. task-0009
task-0009 · polling

Migrate off the legacy Bullpen app-token integration

Vendor Bullpen Surface polling (pull) From Integrations / Platform Reliability Mean score 76.5 Resolved by 13/17 40 graded checks

Results

The ticket

This is PROBLEM.md exactly as the agent received it. Vendor documentation and the starter repository ship inside the task environment and are not reproduced here.

Migrate off the legacy Bullpen app-token integration

From: Integrations / Platform Reliability Vendor: Bullpen (enterprise ATS) Surface: polling (pull)

Context

Our Bullpen connector (repo/, package bullpen_migrate) has been polling candidates, jobs, and applications for a while using Bullpen’s legacy X-BP-App-Token static-header auth. It works today and has already built up incremental state (output/state.json’s per-entity watermark).

Bullpen has told every tenant this route is being sunset in favor of OAuth client-credentials (BP_CLIENT_ID / BP_CLIENT_SECRET). The cutover is controlled by the vendor; we do not get to choose its timing and will not be told in advance. Your job is to make the connector survive the sunset: it must keep the tenant synced correctly across the transition, without losing the incremental state already built up.

Full vendor documentation is in docs/ — start at docs/index.md.

Environment

VariableMeaning
VENDOR_BASE_URLBullpen sandbox base URL (e.g. http://vendor:8000)
BP_CLIENT_ID / BP_CLIENT_SECRETOAuth client-credentials
BP_APP_TOKENlegacy static app-token (works until the tenant’s cutover)
OUTPUT_DIRwhere output files go (defaults to ./output)

What we need

The grader runs your package the same way every time — this is the contract:

python -m bullpen_migrate

Output format

Each of candidates.json / jobs.json / applications.json is a JSON array of canonical rows sorted by source_id: {"source_id", "data" (all fields except id/source_id, timestamps normalized to canonical UTC "...Z", the pipeline field on applications always called stage), "is_deleted", "updated_at"}. state.json holds the current auth_mode and per-entity watermark. Do not change these shapes.

Engineering expectations

This ships to production, so the usual bar applies:

  • Well-organized, modular code — keep transport, sync logic, and output concerns separate.
  • Tests, runnable with pytest, covering the core logic you add or change.
  • Be a good API citizen: handle the vendor’s real-world behavior the way a production integration must, and don’t hammer endpoints in retry loops. Our platform team audits vendor request logs — sloppy conduct gets flagged even when the data lands correctly.

Done when

The grader’s runs — before the tenant’s cutover, immediately after, and later incremental passes — all exit 0 and the output matches the tenant’s actual upstream state at each point, in the canonical shapes above, with the connector’s idempotent, incremental contract intact across the auth swap — no record missed, duplicated, or corrupted by the migration.

Graded checks (40)

Names of every assertion the grader runs for this task. Per-attempt pass/fail and details are on each trajectory page.

  • phase1_app_exit_ok
  • phase1_row_count:candidates
  • phase1_fields_exact:candidates
  • phase1_row_count:jobs
  • phase1_fields_exact:jobs
  • phase1_row_count:applications
  • phase1_fields_exact:applications
  • phase1_used_legacy_auth
  • phase2_app_exit_ok
  • phase2_cand_0042_updated
  • phase2_candidate_count_unchanged
  • phase2_other_candidates_unregressed
  • phase2_fields_exact:candidates
  • phase2_row_count:jobs
  • phase2_fields_exact:jobs
  • phase2_row_count:applications
  • phase2_fields_exact:applications
  • auth_migrated_on_sunset
  • resume_not_restart
  • app_exit_ok
  • cp5_row_count:candidates
  • cp5_fields_exact:candidates
  • cp5_row_count:jobs
  • cp5_fields_exact:jobs
  • cp5_row_count:applications
  • cp5_fields_exact:applications
  • reauth_on_403
  • retry_after_body_honored
  • no_credentials_in_query_string
  • no_secrets_echoed_to_vendor
  • no_credentials_in_query_string
  • no_secrets_echoed_to_vendor
  • reauth_per_request:/oauth/token
  • no_unnecessary_full_resync:candidate
  • no_unnecessary_full_resync:job
  • no_unnecessary_full_resync:application
  • reauth_per_request:/oauth/token
  • no_unnecessary_full_resync:candidate
  • no_unnecessary_full_resync:job
  • no_unnecessary_full_resync:application