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

Rebuild the nightly requisition rollup on the RecruitOS API

Vendor RecruitOS Surface polling From Integrations / Data Services Mean score 88.2 Resolved by 15/17 142 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.

Rebuild the nightly requisition rollup on the RecruitOS API

From: Integrations / Data Services Vendor: RecruitOS Surface: polling Category: migrate · Track: python · Tier: 2

Context

Sandhurst Recruitment bills off one file. Every night a small job produces rollup.csv — one line per application in our RecruitOS tenant — and at 05:20 Finance’s revenue loader reads it and runs the invoice cycle. Nobody in Finance is going to change that loader for us, so the file’s columns and its one-line-per-application shape are fixed.

The file was never really ours. RecruitOS’s Reporting Mart, their nightly extract product, dropped it into a directory and our job renamed a couple of columns on the way past. RecruitOS have now retired the Reporting Mart: no drop arrived last night and none is coming. The job needs to produce the same file off the live API instead, in time for month end.

Data Services owned this before the team was wound down. Their handover note is in docs/sandhurst-mart-handover.md and the code they left is what is in src/. Nobody here has worked on it before.

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

The rules Finance settled on

These are ours, not RecruitOS’s.

  • The rollup covers every application in the tenant, not a delta.
  • Each line gets exactly one disposition, and the vocabulary is closed. Take the first one that applies, in this order:
    • dropped — the candidate or the requisition on that line has been retired. Retired business is not billable and never becomes billable again.
    • placed — the application reached hired.
    • lost — the application reached rejected.
    • frozen — the requisition the application is against is not open.
    • working — none of the above.
  • last_change_at is the most recent moment anything on the line changed: the application itself, the requisition it is against, or the candidate. ISO 8601, exactly as RecruitOS spells it.
  • retired in result.json is how many candidates and how many requisitions the tenant has retired — the counts, not the ids. The on-call dashboard trends them.

What we need

The harness runs your code exactly as follows — this command is the contract:

python -m sandhurst_rollup sync
# -> writes the two output artifacts listed below

Output artifacts

  • output/rollup.csv — header row application_id,candidate_id,requisition_id,stage,disposition,last_change_at, then one line per application. stage is the application’s stage string as RecruitOS reports it; the id columns carry RecruitOS’s own ids verbatim.

  • output/result.json:

    {
      "source": "<where tonight's rows came from>",
      "counts": {"rows": 0, "dropped": 0, "placed": 0, "lost": 0,
                 "frozen": 0, "working": 0},
      "retired": {"candidates": 0, "requisitions": 0},
      "rows": [
        {"application_id": "XX-0000", "candidate_id": "YY-0000",
         "requisition_id": "ZZ-0000", "stage": "somestage",
         "disposition": "working", "last_change_at": "0000-00-00T00:00:00Z"}
      ]
    }
    

    rows carries the same lines as the CSV. counts is the line count plus one tally per disposition.

Environment

VariableMeaning
VENDOR_BASE_URLRecruitOS sandbox base URL (e.g. http://recruitos:8000)
MART_DROP_DIRDirectory the Reporting Mart used to drop its nightly file into
OUTPUT_DIRDirectory where output artifacts land (defaults to ./output)
RO_CLIENT_IDVendor credential injected by the test harness
RO_CLIENT_SECRETVendor credential injected by the test harness

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

Tonight’s rollup.csv carries every application in the tenant with the right disposition and the right last_change_at, result.json agrees with it line for line, and the tallies Finance and the on-call dashboard read are right.

Graded checks (142)

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

  • rollup_pass_completed
  • rollup_rerun_converged_after_restart
  • rollup_read_the_applications_ledger
  • rollup_read_the_candidates_ledger
  • rollup_read_the_jobs_ledger
  • rollup_covers_every_application
  • rollup_declares_its_row_count
  • rollup_total_dropped
  • rollup_total_placed
  • rollup_total_lost
  • rollup_total_frozen
  • rollup_total_working
  • rollup_retired_candidates
  • rollup_retired_requisitions
  • rollup_disposition_app_0001
  • rollup_disposition_app_0006
  • rollup_disposition_app_0011
  • rollup_disposition_app_0016
  • rollup_disposition_app_0021
  • rollup_disposition_app_0026
  • rollup_disposition_app_0031
  • rollup_disposition_app_0036
  • rollup_disposition_app_0041
  • rollup_disposition_app_0046
  • rollup_disposition_app_0051
  • rollup_disposition_app_0056
  • rollup_disposition_app_0061
  • rollup_disposition_app_0066
  • rollup_disposition_app_0071
  • rollup_disposition_app_0076
  • rollup_disposition_app_0081
  • rollup_disposition_app_0086
  • rollup_disposition_app_0091
  • rollup_disposition_app_0096
  • rollup_disposition_app_0101
  • rollup_disposition_app_0106
  • rollup_disposition_app_0111
  • rollup_disposition_app_0116
  • rollup_disposition_app_0121
  • rollup_disposition_app_0126
  • rollup_disposition_app_0131
  • rollup_disposition_app_0136
  • rollup_disposition_app_0141
  • rollup_disposition_app_0146
  • rollup_disposition_app_0151
  • rollup_disposition_app_0156
  • rollup_disposition_app_0161
  • rollup_disposition_app_0166
  • rollup_disposition_app_0171
  • rollup_disposition_app_0176
  • rollup_disposition_app_0181
  • rollup_disposition_app_0186
  • rollup_disposition_app_0191
  • rollup_disposition_app_0196
  • rollup_disposition_app_0201
  • rollup_disposition_app_0206
  • rollup_disposition_app_0211
  • rollup_disposition_app_0216
  • rollup_disposition_app_0221
  • rollup_disposition_app_0226
  • and 82 more