Get the Riverside signup file onto the CrewCall roster
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.
Get the Riverside signup file onto the CrewCall roster
From: Integrations / Workforce Ops Vendor: CrewCall Surface: polling, writeback Category: build · Tier: 2
Context
Riverside Staffing covers our overflow shifts. Their portal exports everyone who
has put their name down since the last export; we get those people onto the
tenant’s CrewCall roster so dispatch can book them. The file is at
input/signups.csv and it is exactly what their portal produces — we do not get
to ask them to change it. It is one row per submission.
Workforce Ops keep their own runbook for this job at
docs/riverside-signup-runbook.md.
The rules Workforce Ops settled on
These are ours, not CrewCall’s.
- Two submissions are the same person when the last seven digits of their phone numbers match, once punctuation, spacing and any country code are ignored.
- Within one person’s submissions, the newest wins: the highest
submitted_at, and where two are identical, the lowersubmission_id. That submission is the person’s canonical record. - A blank field on the canonical submission is filled from the next-newest submission that has a value for it.
- The name comes out with the whitespace tidied and each part capitalised; the first word is the given name and everything after it is the family name.
- The email comes out lower-cased, with any
+tagbefore the@dropped. - The role comes out lower-cased with spaces written as underscores.
- Somebody whose CrewCall record has been deleted is not on the roster, and a person who is not on the roster is a new hire.
And the standing rule for every import we run: this creates people, it never edits them. Where CrewCall already holds someone, their CrewCall record is the truth and a signup form has nothing to say about it.
Full vendor documentation is in docs/ — start at docs/index.md.
What we need
The harness runs your code exactly as follows — this command is the contract:
python -m crewcall_signup_import import-signups
# -> writes the output artifact listed below
For each person the file describes, once their submissions have been collapsed into one:
- Work out whether CrewCall already holds that person.
- If it does not, sign them up with their canonical name, email, phone and role.
- If it does, leave their CrewCall record alone and record them as skipped.
Then write the report described below. Running the command a second time over the same file must not sign anybody up twice and must not touch anybody.
Output artifacts
output/dedupe_report.json—row_count,person_count,created_count,skipped_count, andpeople: one entry per person, each with theperson_key(the identity value above), thesurvivor_submission_id, the full list ofsubmission_idsthat collapsed into that person, the canonicalfirst_name,last_name,emailandrole, theoutcome(createdorskipped), and theworker_idthat person corresponds to in CrewCall.
Ordering within the file is not significant.
Environment
| Variable | Meaning |
|---|---|
VENDOR_BASE_URL | Vendor sandbox base URL (e.g. http://vendor:8000) |
CC_API_KEY | Vendor credential injected by the test harness |
INPUT_FILE | Path to the agency’s signup export |
OUTPUT_DIR | Directory where output artifacts land (defaults to ./output) |
PAGE_LIMIT | Optional batch-size override |
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 entry command exits 0, the report accounts for every submission under the right person with the right canonical values, the roster ends up holding exactly the people it held before plus the ones the file genuinely added — one record each — nobody who was already there has been modified, and a second run changes nothing.
Graded checks (102)
Names of every assertion the grader runs for this task. Per-attempt pass/fail and details are on each trajectory page.
- signup_import_completed
- dedupe_report_collapses_rows_to_people
- d1_submissions_grouped_by_person
- d2_group_survivor_row_chosen
- d3_person_values_canonicalised
- person_5550101_is_created
- person_5550101_group_exact
- person_5550101_canonical_exact
- person_5550202_is_created
- person_5550202_group_exact
- person_5550202_canonical_exact
- person_5550303_is_created
- person_5550303_group_exact
- person_5550303_canonical_exact
- person_5550404_is_created
- person_5550404_group_exact
- person_5550404_canonical_exact
- person_5550505_is_created
- person_5550505_group_exact
- person_5550505_canonical_exact
- person_5550606_is_created
- person_5550606_group_exact
- person_5550606_canonical_exact
- person_5551424_is_skipped
- person_5551424_group_exact
- person_5551424_canonical_exact
- person_5551696_is_skipped
- person_5551696_group_exact
- person_5551696_canonical_exact
- person_5552582_is_created
- person_5552582_group_exact
- person_5552582_canonical_exact
- person_5553495_is_skipped
- person_5553495_group_exact
- person_5553495_canonical_exact
- person_5557085_is_skipped
- person_5557085_group_exact
- person_5557085_canonical_exact
- person_5557800_is_skipped
- person_5557800_group_exact
- person_5557800_canonical_exact
- d6_one_create_per_new_person
- d7_existence_scan_read_whole_roster
- roster_holds_one_row_per_signed_up_person
- d8_created_person_set_exact
- d4_no_person_held_twice_upstream
- d5_created_workers_canonical_upstream
- roster_5550101_one_live_row
- person_5550101_worker_id_is_the_vendors
- roster_5550202_one_live_row
- person_5550202_worker_id_is_the_vendors
- roster_5550303_one_live_row
- person_5550303_worker_id_is_the_vendors
- roster_5550404_one_live_row
- person_5550404_worker_id_is_the_vendors
- roster_5550505_one_live_row
- person_5550505_worker_id_is_the_vendors
- roster_5550606_one_live_row
- person_5550606_worker_id_is_the_vendors
- roster_5551424_one_live_row
- and 42 more