Skills Mapping — technical guide
This implementation reference is for support, integration and engineering teams. It traces the employee portfolio, library search, ratings, job comparison and manager inspection. It separates those paths from framework editing, review answers and AI label matching. User instructions are in the functional guide; tenant controls are in the configuration guide.
All implementation statements below are Verified in code unless marked otherwise. Local snapshots are listed in Evidence and limitations. They do not establish the deployed version.
Architecture and responsibilities
The shared data node represents entities mapped to the same business tables, including userskill; it is not evidence that the inspected deployments share a database connection. The search-service URL is configurable. AIhub implements a compatible skills-library capability, but the actual upstream selected by the live legacy tenant was Not verified.
| Component | Responsibility and boundary |
|---|---|
Legacy Customer/ProfileBundle | Profile rendering, self-rating handlers, motivation updates, deletion entry point and analysis data. |
Legacy Main/ConfigBundle/Controller/SkillLibraryController.php | Library page, grouped search and selected-skill submission for personal/company contexts. |
SkillSearcher, SkillLibraryUrlManager, ObservatorySkillPersister | Search input, service selection, ranked result resolution and copying observatory skills into company scope. |
SkillQuotaService, SkillDeletionService, LockedSkillSourceService | Outside-job quota and protected-removal/source rules. |
Legacy manager ProspectBundle | Employee profile and role-conditioned skill section. |
Nextgen UserSkillDTO, repository and resource controller | User-skill CRUD contracts, current-company scope, visibility and filters. |
| Nextgen review skill renderer | Skill answers inside a review; not a standalone portfolio or Analysis page. |
AIhub src/skills_library | Resolves free-text labels to ranked skill candidates. It does not render the legacy map. |
Observed in application: Chrome exposed legacy personal profile/library and manager employee-detail routes on 23 October 2026. Targeted nextgen front-end searches found review/configuration skill components, without establishing a replacement Skills Mapping page. A modern shell alone is not evidence of a redesigned feature implementation.
Request and data flow
Library search
- The employee opens the profile library route
skills/library/{hash}under the profile routing context. The controller requires authentication and resolves the target customer. An unknown hash redirects to the workspace home; another employee's page has additional role checks. - The library template sends an XHR POST to route name
main_config_skill_suggestion, passingtextandcustomer. The company variant usesmain_config_skill_suggestionboand can pass repository restrictions. SkillSearcher::buildApiInput()combines configured observatory IDs with explicitly selected repositories, or public company repositories when none were selected. It supplies locale, result limit and configured collection name.SkillLibraryUrlManagerselects the default or alternate AI library URL. AI mode can prioritize public customer repositories, main first, and request semantic deduplication.- Returned skill IDs are sorted by descending score and loaded locally. Observatory skills can be copied into the target company's catalogue. Results outside that company are discarded; title/repository keys deduplicate the display.
- The controller groups results by skill group/type and repository origin and renders the result HTML.
Illustrative outbound body, with synthetic repository IDs and deployment values omitted:
{
"search_query": "communication",
"repositories_id": [101, 202],
"language": "fr",
"results_number": 40,
"repositories_id_prio": null
}
The real body also contains the injected collection_name. When priority repositories exist in AI mode, repositories_id_prio becomes an ordered list and threshold_deduplicate is 0.95. The result contract consumed by legacy includes skill_id, score, skill text and repository metadata. Similarity scores rank search candidates; they are not employee mastery ratings.
Important lifecycle detail: library search can persist observatory catalogue copies and API call logs. It is not technically free of database side effects even when the employee never submits a portfolio selection. The documentation inspection performed a search and did not deliberately add any user skills.
Selected-skill submission
The library template keeps selected IDs and their ratings in the page, then posts to main_config_add_skills (/addSkills in its route file; verify import prefixes when integrating). The submitted form structure is illustrated with synthetic IDs:
customer=123
data[0][skillId]=456
data[0][level]=3
data[0][motivation]=4
The leading spaces above are for display only; fields are form keys, not a JSON request. The controller also recognizes locked and managerLevel in its skill-property structure; their presence is not a general authorization guarantee.
The inspected action checks authentication, customer existence, matching setting/company and whether the actor is self, direct manager or the implementation's master-school category. It requires a nonempty data array, validates the outside-job quota and skips existing user-skill links. Existing skills are assigned through SkillImportService, flushed and followed by UserSkillAddedEvent. A separate creation branch exists; that branch was not exercised and is not a guarantee that every free-text suggestion can be saved.
The browser checks mastery and, unless hidden by fo_no_display_motivation_add_skill, motivation before posting. Success displays a dialog and returns to profile skills. Writes were Not verified in application. The implementation flushes inside the loop: do not promise an atomic multi-skill save or assume a failed request changed nothing.
Profile ratings and analysis
Legacy route names customer_profil_updatenote and customer_profile_motivationskill map to changeNoteSkillsAction() and motivationAction(). The former accepts skills_id, skills_note and a type such as niveau; the latter accepts skill and note. These use user-skill link IDs, distinct from catalogue skill IDs.
Handlers update stored ratings and return JSON used to refresh ratings and chart data. They also contain experience-skill synchronization branches; do not assume a profile edit affects only a single visual row. Ownership checks differ between branches, so the inspected self-service path is not a complete authorization audit.
Bubble construction excludes the resume-language marker and includes only skills with positive mastery and motivation. It preserves actual values separately from chart positions and offsets coincident points by quarter steps. Inspected handlers split quadrants at 2.5, with green/blue/orange/pink colors matching the observed legend. Long labels may be abbreviated as (...). These details explain display artifacts; they do not change stored proficiency.
analysis-tab.html.twig gates the job comparison with module_analysespider, can hide its job selector, and hides the bubble area when the dataset is empty. Job comparison and the motivation map consume different scopes. The dynamic comparator's complete classification algorithm and five-level bubble behavior were Not verified; use the displayed legend rather than extrapolating numeric thresholds.
Removal and downstream updates
Employee bulk deletion validates request shape, normalizes submitted link IDs and calls SkillDeletionService scoped to the current user. It excludes primary/alternative-job skills as configured and marker = locked, removes eligible links and emits UserSkillDeletedEvent. A successful response does not prove any record was removed.
UserSkillEntityListener dispatches UserSkillChangedEvent after persistence/removal. DatabaseActivitySubscriber also handles changes, including rating events; PlatformActivitySubscriber contains message-broker subscriptions. This establishes downstream event integration, not guaranteed matching freshness, delivery timing or exactly-once processing. These paths were not executed as part of the review.
Data model and API contracts
| Data | Representation |
|---|---|
| Catalogue skill | Skill entity with translations, group/type and repository membership. |
| Employee skill | userskill link: user, skill, nullable note, motivation, managerNiveau, source and lock marker, among other fields. |
| Expected level | Job–skill relationship, separate from employee self/mastery and manager ratings. |
| Source and protection | Imported source and job membership influence presentation/filtering; the lock marker is a separate property. |
| Review skill answer | Review-specific answer structure; it is not interchangeable with a user-skill resource update. |
Nextgen config/routes.yaml imports V1 routing under /api/v1. src/Http/Api/Routing/V1/user_skill.yaml defines:
| Method | Path | Permission |
|---|---|---|
| GET | /api/v1/user/skill or /api/v1/user/{userId}/skill | read-user-skill |
| GET | /api/v1/user/{userId}/skill/{skillId} | read-user-skill |
| POST | /api/v1/user/skill or /api/v1/user/{userId}/skill | create-user-skill |
| PUT / PATCH | /api/v1/user/{userId}/skill/{skillId} | update-user-skill |
| DELETE | /api/v1/user/{userId}/skill/{skillId} | delete-user-skill |
Self variants and /by-code/{skillCode} variants exist for show/update/delete. Nextgen {skillId} filters s.id, the catalogue skill ID, not the userskill link ID used by the legacy rating handlers. Delete routes explicitly set soft_deletable: false; they were not called during inspection.
UserSkillDTO exposes skillId or alternative skillCode, currentLevel, motivationLevel, translated read fields and timestamps. The metadata-aware serializer uses camel-case-to-snake-case conversion. An illustrative nextgen creation payload is:
{
"skill_id": 456,
"current_level": 3,
"motivation_level": 4
}
This is a source-grounded example, not a captured successful request. The DTO checks company-scoped referenced entities and user/skill uniqueness. currentLevel maps to note and uses a dynamic range from 1 to the current company scale; the inline description “1 to 5” is less precise than the actual validator. Motivation permits 0–4, while the nullable fields and legacy map eligibility are separate concerns. The listener fills the current user when no user ID is provided and resolves an alternative skill code.
List filtering and visibility
UserSkillRepository::findUserSkills() constrains the skill company and target user, adds translations and applies visibility filters when querying another user. V1 lists sort by current level ascending and support level, source, title, type and approval filters.
The route's only_approved filter defaults to true. Its implementation selects us.source IS NULL; false instead selects cv or linkedin sources. False is therefore a reverse selection, not “include every source”. The skill_source filter classifies job versus profile by membership in the current primary job's skills, rather than directly reading the source string. These rules can explain differences between API lists and a legacy portfolio.
The V2 UserSkillController::skillOfReviewed() adapts GET list/detail calls for a review's target employee, configured through review routing and participation checks. Nextgen front-end Skills/api/skills.api.ts writes review answers through /api/v2/user-review/{userReviewId}/answer/skill/{questionId} and its answer-ID variant. Those calls should not be documented as direct employee-portfolio CRUD.
Configuration, authorization and privacy
The configuration guide indexes the exact switches, scale and quota conditions. Legacy UI restrictions, library submission authorization and nextgen resource permissions are separate mechanisms; none should be inferred solely from the presence of a button.
The local nextgen entity maps to userskill, as does the legacy model. This is evidence of schema compatibility at that table boundary, not proof of deployment synchronization or identical side effects. No automatic UI migration or feature-switch precedence between generations was established.
Treat employee mastery, motivation, manager levels and imported profile content as restricted personnel data. Use synthetic API examples and authorized populations. Redact profile hashes, names and portraits in shared evidence. Actual upstream addresses, credentials and deployment collection names are deliberately omitted here. Legacy search persists request/response API call logs; access and retention for those records require the platform's operational policy.
Runtime dependencies, failure modes and observability
Legacy requires Symfony/Doctrine persistence, configured library service URLs, catalogue repositories and the JavaScript/chart assets used by the profile. AIhub's independently inspected search service supports MongoDB/PostgreSQL retrieval; query_service.py and its local documentation describe backend selection and ranking. The tenant's actual backend, provider and network configuration were Not verified. Nextgen job attachment uses UserSkillService batches and post-create events; do not assume that process supplies assessed levels.
| Failure / discrepancy | Evidence and diagnostic focus |
|---|---|
| Library unavailable | SkillLibraryUrlManager rejects missing URL configuration; check selected mode and runtime dependencies without sharing secrets. |
| Empty or incomplete search | Locale, repository/observatory selection, ranked limit, local ID resolution and company checks can reduce results. |
| Search-service transport error | SkillSearcher logs cURL failures and throws; successful transport records ApiCallLog timing and request/response. A blank result is not proof of service health. |
| Addition rejected | Controller can return 401 for authentication, 403 for company/actor restrictions, 400 for empty input or quota, and 500 for unexpected failures. |
| Partial save after error | Loop-level flushes mean request success/failure cannot be treated as an all-or-nothing transaction guarantee. Reconcile actual links before retrying. |
| UI lock differs from API outcome | Check the specific endpoint and service; template gating is not a uniform write policy. |
| Map omits a skill | Positive mastery and motivation are required; source marker, job scope and missing data also matter. |
| API list differs from profile | Verify company/population scope, translations, primary-job source classification and approval filter. |
| Downstream score appears stale | Trace relevant user-skill events and message handling; no completion-time guarantee was established. |
No live API probes, failure injection, destructive actions or runtime tests were performed. These are diagnostic locations established by source inspection.
Extension points
Keep catalogue selection and locale/repository policy in the search boundary; add-skill authorization and quota enforcement belong in server-side services. A new skill source needs review across the source field, API approval filters, lock rules, chart eligibility and downstream events. A scale change needs comparison/rating validation in every consuming path, especially hard-coded legacy quadrant logic. These are Inferred engineering considerations, not implementation changes delivered by this documentation task.
Evidence and limitations
Repository paths are relative to the named checkout under the local GitHub workspace. Revisions identify inspected source snapshots only.
| Repository / revision | Primary evidence |
|---|---|
smartmobility-legacy — 9ecdb0db43 | src/Customer/ProfileBundle/Controller/DefaultController.php, Resources/config/routing.yml, Resources/views/Default/includes/{competences-tab,competences-table,analysis-tab,analysisV2}.html.twig; Services/SkillSearcher.php. |
| Same legacy checkout | src/Main/ConfigBundle/Controller/SkillLibraryController.php, Resources/config/routing/skillslibrary.yml, Resources/views/skillsLibrary/index_customer.html.twig; src/Service/SkillLibraryUrlManager.php; src/Service/Skills/{SkillQuotaService,SkillDeletionService,LockedSkillSourceService}.php. |
| Same legacy checkout | src/Company/ProspectBundle/Services/ViewSection/ProfileSection/SkillSection.php; src/EventListener/{UserSkillEntityListener,DatabaseActivitySubscriber}.php; src/EventSubscriber/PlatformActivitySubscriber.php; src/Main/ConfigBundle/{Entity/Setting.php,Form/Type/SchoolsettingType.php}. |
smartmobility-nextgen-back — 00beda02c | config/routes.yaml, config/services/http.php, src/Http/Api/Routing/V1/user_skill.yaml, Routing/V2/review.yaml, Controller/V2/UserSkillController.php; src/Domain/V1/User/DTO/UserSkillDTO.php; src/Domain/V2/Review/Validator/DynamicSkillRangeValidator.php; persistence entity/repository/listener for UserSkill; src/Infrastructure/Service/User/UserSkillService.php. |
smartmobility-nextgen-front — d14017a5 | src/modules/ModuleConfig/Campaigns/modules/Reviews/modules/FormRender/components/renderers/Skills/api/skills.api.ts; src/modules/CompanySettings/BasicConfiguration/components/Performance/EvaluationSkillConfiguration.vue. These establish review-related behavior, not a replacement mapping page. |
aihub — 757dd2678 | docs/en/skills_library/index.md, src/skills_library/services/query_service.py. Search capability inspected; no live routing link from this tenant to this checkout established. |
Observed in application: 23 October 2026, authenticated Chrome test session, employee and manager workspaces; screenshots are in the functional guide. Personal details and profile identifiers are redacted; relevant areas are outlined without cropping the application window.
Not verified: source-to-deployment revision match, every persona, saved ratings/additions/removals, five-level chart behavior, live API response envelopes, all authorization branches, observatory-copy effects from the performed search, downstream delivery and matching refresh timing. No application repository or running configuration was modified by this documentation work.
Functional-guide evidence
These implementation and product references support the functional guide. Local source behavior does not establish which revision or optional features are deployed. Application observations remain distinct from code and documentary evidence.
- Observed in application: 23 October 2026, authenticated Chrome session in the test tenant; personal and manager workspaces; profile skills, library initial/search states, job comparison, motivation map and manager skills table. Screenshots retain the full application window with privacy redactions and purple outlines.
- Verified in code: legacy
src/Customer/ProfileBundle/Resources/views/Default/includes/{competences-tab,competences-table,analysis-tab}.html.twig,Controller/DefaultController.php,src/Main/ConfigBundle/Controller/SkillLibraryController.php, library templates, andsrc/Service/Skills/{SkillQuotaService,SkillDeletionService,LockedSkillSourceService}.php. Nextgen evidence is indexed in the technical guide. - Inferred: the suggested development conversations in the quadrant table are usage guidance, not automated platform decisions.
- Not verified: successful writes, deletion, endorsement requests, production configuration values, exhaustive role coverage, five-level chart behavior, campaign completion and equivalence between the local checkout and deployed code. No ratings, skills or settings were deliberately changed.
Journey verification — 26 October 2026
Observed in application: PS Testing 2026, Jim Carter. The newly added MS Project row retained 2/4 ratings after reload, then retained changed 3/4 ratings after another reload. Removing only that test addition and reloading removed it from the complete rendered list; existing skills were unchanged. Locked-job skill mutation and manager-side writes were not exercised.
Last updated on 3 November 2026