Build the weekly capability index
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.
Build the weekly capability index
From: Integrations / Research Tooling Vendor: SourceWell Surface: polling Category: build · Track: python · Tier: 3
Context
Brightwater’s researchers spend their Monday morning answering the same question — “who have we got who can do X?” — and today they answer it by grepping a spreadsheet somebody exports by hand. We want the index built for them, weekly, from SourceWell plus the skills feed we buy from Ledgerfield Analytics.
SourceWell is the system of record for who exists and for the skill
vocabulary. Ledgerfield’s feed is the system of record for who can do what,
and nothing else: it is a classifier’s output, dropped into
input/ledgerfield_skills.json every Sunday night. Their own format note is
in docs/ledgerfield-skills-extract.md — it is Ledgerfield’s document about
Ledgerfield’s file, and they last revised it in 2025.
The index goes out to the whole research desk, so a skill nobody can be found under is a week of people not being contacted.
Full vendor documentation is in docs/ — start at docs/index.md.
What we need
The test harness runs your code exactly as follows — this command is the contract:
python -m skill_index
# -> writes the output artifacts listed below
The harness invokes that command more than once against the same mounted output directory, the way the Sunday scheduler does.
- One entry per skill SourceWell holds. Each carries the
nameSourceWell holds for it now and astatusoflive, orretiredwhere SourceWell has retired it. Retired skills stay in the index — the desk still wants to know who used to be findable under them. - Each skill entry lists the
sw_idof every person who carries that skill, ordered by id, and amember_count. A skill nobody carries still gets an entry, with an empty list. - One entry per person SourceWell holds and has not retired, carrying their
nm, every skill they carry ordered by tag id, and askill_count. People SourceWell has retired are not published, in either section. countsreportsskills_live,skills_retired,skills_without_membersandpeople_published.- We pay SourceWell per call, so keep each pass economical.
Ledgerfield’s feed is the only source for who carries what. Everything else — who exists, what the skills are called, which of them are still in use — comes from SourceWell.
Output artifacts
output/capability_index.json—{"skills": [...], "people": [...], "counts": {...}}. Eachskillsentry carriestag_id,name,status,membersandmember_count, ordered bytag_id. Eachpeopleentry carriessw_id,nm,skillsandskill_count, ordered bysw_id.output/skills_report.csv— header rowtag_id,name,status,member_countplus one line per skill, empty string where a value does not apply.
Environment
| Variable | Meaning |
|---|---|
VENDOR_BASE_URL | Vendor sandbox base URL (e.g. http://sourcewell:8000) |
OUTPUT_DIR | Directory where output artifacts land (defaults to ./output) |
SW_API_KEY | Vendor 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
Every skill SourceWell holds is in the index under the name SourceWell uses for it, every published person’s skills are all of the skills Ledgerfield credits them with, the report reconciles with the index, and next week’s pass costs no more than this one.
Graded checks (118)
Names of every assertion the grader runs for this task. Per-attempt pass/fail and details are on each trajectory page.
- sw217_index_run1_completed
- sw217_sections_have_exact_cardinality_and_order
- sw217_index_covers_every_skill_sourcewell_holds
- sw217_index_covers_every_person_sourcewell_publishes
- sw217_skill_roster_tag_0001
- sw217_skill_roster_tag_0002
- sw217_skill_roster_tag_0003
- sw217_skill_roster_tag_0004
- sw217_skill_roster_tag_0005
- sw217_skill_roster_tag_0006
- sw217_skill_roster_tag_0007
- sw217_skill_roster_tag_0008
- sw217_skill_roster_tag_0009
- sw217_skill_roster_tag_0010
- sw217_skill_roster_tag_0011
- sw217_skill_roster_tag_0012
- sw217_skill_roster_tag_0013
- sw217_skill_roster_tag_0014
- sw217_skill_roster_tag_0015
- sw217_skill_roster_tag_0016
- sw217_skill_roster_tag_0017
- sw217_skill_roster_tag_0018
- sw217_skill_roster_tag_0019
- sw217_skill_roster_tag_0020
- sw217_skill_roster_tag_0021
- sw217_skill_roster_tag_0022
- sw217_skill_roster_tag_0023
- sw217_skill_roster_tag_0024
- sw217_skill_roster_tag_0025
- sw217_skill_roster_tag_0026
- sw217_skill_roster_tag_0027
- sw217_skill_roster_tag_0028
- sw217_skill_roster_tag_0029
- sw217_skill_roster_tag_0030
- sw217_skill_roster_tag_0900
- sw217_skill_roster_tag_0901
- sw217_skill_label_tag_0001
- sw217_skill_label_tag_0002
- sw217_skill_label_tag_0003
- sw217_skill_label_tag_0004
- sw217_skill_label_tag_0005
- sw217_skill_label_tag_0006
- sw217_skill_label_tag_0007
- sw217_skill_label_tag_0008
- sw217_skill_label_tag_0009
- sw217_skill_label_tag_0010
- sw217_skill_label_tag_0011
- sw217_skill_label_tag_0012
- sw217_skill_label_tag_0013
- sw217_skill_label_tag_0014
- sw217_skill_label_tag_0015
- sw217_skill_label_tag_0016
- sw217_skill_label_tag_0017
- sw217_skill_label_tag_0018
- sw217_skill_label_tag_0019
- sw217_skill_label_tag_0020
- sw217_skill_label_tag_0021
- sw217_skill_label_tag_0022
- sw217_skill_label_tag_0023
- sw217_skill_label_tag_0024
- and 58 more