CogniPrep has two separate vocabularies for "what kind of work is this", and they collided in the most boring possible way: they share a word.
The first is the onboarding taxonomy. Twenty sector families and 121 roles, written in the words a candidate uses about themselves, because they are typing their job title out of a job advert. "Technology & Software", "Engineering & Manufacturing", "Banking & Finance".
The second is the practice content sector, and there are exactly four of them:
export type Sector = 'engineering' | 'finance' | 'corporate' | 'public-sector';
Four, because of a claim in the module header that the whole content strategy rests on:
Sectors are the scaling unit for personalisation across the app: employers hiring for the same discipline assess candidates in largely the same way, so content and marking rubrics live at sector level rather than per-employer.
That is what makes 132 employer guides affordable. The guides are per employer, because what a company's process looks like is specific to that company, and you can see one at the Rolls-Royce guide which is careful to say the vendor behind its aptitude stage is reported and medium confidence rather than confirmed. The practice content underneath them is not per employer. You can count it on the assessment centre page, which lists the scenario sets by sector with the numbers in them.
So one taxonomy has 20 families and the other has 4 sectors, and something has to map between them.
The word that means the opposite thing
case 'technology':
return 'engineering';
Read that twice. The onboarding family called technology maps to the practice sector called engineering, and the onboarding family called engineering does not.
The comment in the source is blunt about why:
The two taxonomies share a word but not a meaning, which is the trap here. Practice 'engineering' is the technical tech repertoire, software, data, architecture, so the onboarding family that feeds it is 'technology' ("Technology & Software"), NOT the identically named onboarding 'engineering' ("Engineering & Manufacturing": mechanical, civil, aerospace). Matching on the shared word is exactly backwards: it hands a civil engineer the system-design and model-validation content and marks a software engineer against the corporate values/strengths rubric.
This is the failure mode of a well-meaning one-liner. return onboardingSectorId as Sector compiles if the union members happen to overlap, a quick test passes because 'finance' maps to 'finance' and 'public-sector' maps to 'public-sector', and the thing it gets wrong is the two cases where the strings agree and the meanings do not.
String equality between two namespaces is a coincidence, not a relation. If two taxonomies are authored by different parts of the product, the mapping between them is a function, even when the strings look like they would do.
The two cases that deliberately return nothing
// Listed rather than left to fall through, so the absence of a mapping
// reads as a decision and not an omission someone should "fix" back to
// 'engineering' on the strength of the shared name.
case 'engineering':
case 'energy':
return null;
default:
return null;
Those two cases are functionally identical to the default. They exist so the next person to read the switch can tell the difference between "we thought about this one" and "we never got to it". A default: return null alone would make physical engineering look like an oversight, and the obvious fix for an oversight is the wrong answer.
The reasoning for why 'corporate' is not a safe fallback is worth quoting too, because "put the leftovers in the general bucket" is the instinct:
Neither sector actually fits them: 'engineering' is now software/data content, and 'corporate' is the big-4 strengths pattern rather than the competency-plus-technical interview a Rolls-Royce or Arup scheme runs.
So a mechanical engineer gets no preselection, and null is a first-class value for the callers rather than an error:
Callers treat null as a first-class "General" option, a mixed session drawn from the untagged question pool, with every sector still one tap away, so no preselection is honest, while a confident wrong one reads as the product having misunderstood the answer.
That last clause is the product argument in one line. A personalisation that is quietly absent costs the user one tap. A personalisation that is confidently wrong costs their trust in every other thing the app has inferred about them, including the ones that were right.
Why it maps sectors and not roles
The callers all hold the role id as well, so a role-level mapping was available and was not taken:
Deliberately sector-level, not role-level, even though callers hold the role id too: the technology family is technical throughout, and the roles that fit the content least (product management, UX) still share its domain vocabulary, so a per-role exception list would buy less than it costs.
A 121-entry mapping needs maintaining every time the taxonomy grows, and every entry is a chance to be subtly wrong. A 20-entry one with two honest holes in it does not. The exceptions you skip are the ones where being approximately right is already fine: a product manager getting technical scenarios recognises the vocabulary even where the task is not their day job.
Where the four sectors actually land
The same four values are read by the exercise dashboard, the interview flow's question pools and its marking profiles, the interview session API, the guide blocks and the public assessment centre page. One SECTOR_META record, with a label, a blurb and an explicit display order, feeds all of them, so the words a sector is described in cannot drift between the marketing page and the dashboard.
The public surfaces are the easy way to see the split: the sector sets on the assessment centre page are content written per sector, and the mock interview page is the other consumer of the same four values. The 132 employer guides sit on top as the per-company layer, and none of them own any content of their own.
Top comments (0)