CMGM Competency Management Module

Competency Management (CMGM)

Bridge between appraisal outcomes and skill profiles — turning KRA scores into career development roadmaps

7
Database Tables
24
API Endpoints
3
Module Integrations
2
Dapr Topics

Vision

CMGM is the integration layer that bridges two existing modules — PERM (KRA appraisal scores) and SkillManagement (employee skill levels) — to produce actionable employee competency profiles and career development plans. After every appraisal cycle finalization, profiles are automatically regenerated via a Dapr event subscription, keeping career data current without manual HR intervention.

Business Value

Data-Driven Career Dev

Career recommendations based on actual skill levels and appraisal scores — not manager opinion alone

Auto-Updated Profiles

Competency profiles regenerate automatically after every finalized appraisal via Dapr subscription

Targeted TNA Enrichment

Skill gaps identified by CMGM enrich PERM's TNA requests with specific skill-level training needs

Behavioral Anchors

Assessors and employees get concrete behavioral indicators per proficiency level — from CompetencyManager reference

Key Concepts Glossary

TermDefinition
Competency FrameworkA named grouping of KRA-to-Skill mappings for a role family (e.g., "Software Engineering Track", "Sales Track"). One framework can serve multiple career paths.
KRA-Skill MapThe bridge record: "KRA X maps to Skill Y at proficiency level Z". Links appraisal KRA performance to concrete skill standards.
Behavioral AnchorDescriptive text explaining what "proficiency level 3 of Java" looks like in practice. Includes observable behavioral indicators as JSON array.
Proficiency Level (1-5)Matches MSKILLLEVEL.SLNO from SkillManagement: 1=Beginner, 2=Basic, 3=Intermediate, 4=Advanced, 5=Expert.
Career PathA defined progression from CurrentRole to TargetRole within a framework. Has TypicalMonthsRequired and a list of required skills at minimum proficiency levels.
Employee Competency ProfileSnapshot generated on-demand (or auto on appraisal finalization): compares employee's current skills vs. framework requirements. Stores OverallCompetencyScore and SkillGapsJson.
OverallCompetencyScorePercentage of framework requirements met: (metCount / totalCount) × 100.
ReadinessPercentPer-career-path score: what % of path requirements does the employee currently meet.
Skill Dev Plan (TSKILLDEVPLAN)Individual/Team/Dept plan to address a specific skill gap. Has TrainingMethod, EstimatedHours, TrainerId, and completion tracking.
KSABO / SFIA / ESCOIndustry competency taxonomy references used to seed anchor text and map job roles. Not tables — reference frameworks used during master data setup.

Module Boundary — CMGM vs SkillManagement

SkillManagement Owns

  • MSKILL master (skill definitions)
  • MSKILLLEVEL (1-5 proficiency definitions)
  • MEMPLOYEESKILL (current employee skill levels)
  • MROLERESOURCE (role skill requirements)
  • Skill assessment forms and approvals
  • Skill matrix views

CMGM Owns

  • MCOMPETENCYFRAMEWORK (grouping of KRA→Skill maps)
  • MCOMPETENCYKRAMAP (KRA↔Skill bridge with expected level)
  • MCOMPETENCYANCHOR (behavioral indicators per level)
  • MCAREERPATH + MCAREERPATHREQUIREMENT
  • TEMPLOYEECOMPETENCYPROFILE (snapshots)
  • TSKILLDEVPLAN (development plans)
Rule: CMGM reads SkillManagement via injected DAL interfaces (IEmployeeSkillProfileBLL, IRoleSkillRequirementBLL). CMGM never writes to SkillManagement tables. Skill levels are updated only by SkillManagement's own assessment workflows.

Database Schema — Framework Tables

MCOMPETENCYFRAMEWORK

ColumnTypeNotes
FrameworkIdint PK
Code / Name / DescriptionnvarcharUnique Code per tenant
Statussmallint0=Inactive, 1=Active
TenantIdintIndex: (TenantId, Status). Unique: (Code, TenantId)

MCOMPETENCYKRAMAP — KRA ↔ Skill Bridge

ColumnTypeNotes
CompetencyKRAMapIdint PK
FrameworkIdint FK MCOMPETENCYFRAMEWORK
KRAIdint FK MKRAFrom PERM master
SkillIdint FK MSKILLFrom SkillManagement
ExpectedProficiencyLeveltinyint1-5; matches MSKILLLEVEL.SLNO
IsMandatorybitMandatory gaps trigger immediate dev plan recommendation
SortOrdersmallint
TenantIdintUnique: (FrameworkId, KRAId, SkillId, TenantId)

MCOMPETENCYANCHOR — Behavioral Anchors

ColumnTypeNotes
AnchorIdint PK
SkillIdint FK MSKILL
ProficiencyLeveltinyint1-5; Unique: (SkillId, ProficiencyLevel, TenantId)
AnchorTextnvarchar(max)Summary behavioral statement (1-2 sentences)
BehaviorIndicatorsJsonnvarchar(max) nullableJSON array of observable behavioral indicators
TenantIdintIndex: (SkillId, TenantId)

Career Path Tables

MCAREERPATH

ColumnTypeNotes
CareerPathId, PathCode, PathNameint / nvarcharUnique PathCode per tenant
FrameworkIdint FK
CurrentRoleId / TargetRoleIdint FK MJOBROLEFrom → To role progression
TypicalMonthsRequiredint nullableEstimated time to qualify for target role
Statussmallint0=Inactive, 1=Active
TenantIdintIndexes: (FrameworkId, TenantId, Status), (CurrentRoleId, TenantId)

MCAREERPATHREQUIREMENT

ColumnTypeNotes
CareerPathReqIdint PK
CareerPathIdint FK MCAREERPATH
SkillIdint FK MSKILLUnique: (CareerPathId, SkillId, TenantId)
MinProficiencyLeveltinyintMinimum level to qualify for this path
IsMandatorybitMandatory gaps block career path readiness
SortOrdersmallint

Profile & Development Plan Tables

TEMPLOYEECOMPETENCYPROFILE

ColumnTypeNotes
CompetencyProfileIdint PK
EmployeeId / FrameworkIdintUpsert by (EmployeeId, FrameworkId, TenantId)
CareerPathId / TargetRoleIdint-1 = not selected
GeneratedOndatetimeTimestamp of last generation
OverallCompetencyScoredecimal(5,2)(metCount / totalCount) × 100
SkillGapsJsonnvarchar(max)JSON: [{SkillId, SkillName, CurrentLevel, RequiredLevel, IsMandatory}]
Statussmallint0=Draft, 1=Published
TenantIdintIndexes: (EmployeeId, TenantId, Status), (FrameworkId, TenantId)

TSKILLDEVPLAN — Skill Development Plans

ColumnTypeNotes
DevPlanIdint PK
PlanTypesmallint0=Individual, 1=Team, 2=Department
TargetIdintEmployeeId / TeamId / DeptId based on PlanType
SkillId / CurrentProficiencyLevel / TargetProficiencyLevelint / tinyint
Prioritysmallint0=Low 1=Medium 2=High 3=Critical
TrainingMethodsmallint0=Classroom, 1=OnTheJob, 2=Online, 3=Mentoring, 4=Coaching
TargetDate / EstimatedHours / ActualHoursdate / intActualHours set on CompleteSkillDevPlan
TrainerIdint-1 = Unassigned
CompletionNotesnvarchar nullableRequired on completion
Statussmallint0=Planned 1=InProgress 2=Completed 3=OnHold

Framework Setup Flow

Create Framework
→
Define KRA↔Skill Maps
with expected levels
→
Add Behavioral Anchors
per skill per level
→
Define Career Paths
Current→Target role
→
Set Path Requirements
skills + min levels

GenerateCompetencyProfile — Core Algorithm

① Load Employee Skill Levels

SkillManagementDAL → MEMPLOYEESKILL WHERE EmployeeId=@emp AND ProfileStatus=1: returns {SkillId, CurrentLevNo} for all assessed skills

② Load KRA-Skill Mappings

CMGMDAL → MCOMPETENCYKRAMAP WHERE FrameworkId=@fw: returns all {KRAId, SkillId, ExpectedProficiencyLevel, IsMandatory}

③ Load Latest KRA Scores

PERMDAL → GET_LATEST_KRA_SCORES_BY_EMPLOYEE: CTE selects top 1 finalized appraisal → ranks KRA scores preferring Manager eval over Self eval. If no finalized appraisal, all KRA scores = 0.

④ Evaluate Each Mapping (skillMet logic)

skillMet = (empCurrentLevel ≥ expectedLevel) OR (kraScore ≥ 60 AND empCurrentLevel ≥ expectedLevel - 1)
Second condition: a strong KRA score (≥60%) with level-1 gap is considered "developing-to-met" — reduces false negatives for high performers whose skill assessments lag behind their actual performance.

⑤ Compute Score & Build Gaps

OverallCompetencyScore = (metCount / totalCount) × 100
Collect SkillGapsJson array for all !skillMet entries.

⑥ Upsert Profile

INSERT or UPDATE TEMPLOYEECOMPETENCYPROFILE by (EmployeeId, FrameworkId). Publish GENERATECMGMPROFILEEVENTTYPEID event log. Cache invalidation.

⑦ Publish Dapr (if gaps)

If gaps.Any(): publish cmgm.skillgap.identified → PERM subscribes to enrich TTNAREQUEST with skill-specific training detail.
Always publish: cmgm.profile.generated → ESS career dashboard refresh.

Key SQL — GetLatestKRAScoresByEmployee (added to PERMDAL)

-- AppraisalEvalQB.GET_LATEST_KRA_SCORES_BY_EMPLOYEE
WITH LatestAppraisal AS (
    SELECT TOP 1 APPRAISALID, FINALPERCENTAGE
    FROM   TAPPRAISAL
    WHERE  EMPLOYEEID = @EmployeeId AND STATUS = 4 AND TENANTID = @TenantId
    ORDER  BY APPRAISALID DESC
),
RankedEvals AS (
    SELECT tek.KRAID, tek.KRASCORE, te.EVALTYPE,
           ROW_NUMBER() OVER (
               PARTITION BY tek.KRAID
               ORDER BY CASE te.EVALTYPE WHEN 1 THEN 0 ELSE 1 END  -- Manager(1) preferred
           ) AS Rn
    FROM   TAPPRAISALEVALKRA tek
    INNER JOIN TAPPRAISALEVAL te ON te.APPRAISALEVALID = tek.APPRAISALEVALID
               AND te.APPRAISALID = (SELECT APPRAISALID FROM LatestAppraisal)
)
SELECT  re.KRAID AS KRAId,  k.KRANAME AS KRAName,
        re.KRASCORE AS KRAScore,
        la.FINALPERCENTAGE AS OverallScore,
        la.APPRAISALID AS AppraisalId
FROM    RankedEvals re
INNER JOIN MKRA k ON k.KRAID = re.KRAID AND k.TENANTID = @TenantId
CROSS JOIN LatestAppraisal la
WHERE   re.Rn = 1

GetCareerPathRecommendations (ESS Algorithm)

// CompetencyProfileBLL.GetCareerPathRecommendationsAsync
// EmployeeId ALWAYS from login.UserId — never from request param

1. Get employee CurrentRoleId from MEMPLOYEE
2. Load all active MCAREERPATH WHERE CurrentRoleId = employee's role AND TenantId = @TenantId
3. For each path:
   a. Load MCAREERPATHREQUIREMENT for path
   b. metCount = skills where employee.CurrentLevNo >= req.MinProficiencyLevel
   c. mandatoryGapCount = mandatory reqs where employee.CurrentLevNo < MinLevel
   d. ReadinessPercent = (metCount / totalReqs) * 100
4. Sort by ReadinessPercent DESC (closest-to-qualifying first)
5. Return List<CareerPathMatchDTO> with ReadinessPercent + MandatoryGapCount + TopGaps

Dapr Integration Flow

Subscribe: perm.appraisal.finalized

CMGMSL registers a Dapr MVC subscriber. On receiving this event: auto-triggers ICompetencyProfileBLL.GenerateCompetencyProfileAsync(employeeId, defaultFrameworkId, login, ct)

Route: POST /cmgm/subscribe/appraisal-finalized

Publish: cmgm.skillgap.identified

Published when profile generation finds gaps. Payload: {EmployeeId, FrameworkId, TenantId, Gaps: [{KRAId, SkillId, GapLevel}]}

PERM subscribes → inserts TTNAREQUEST rows with KRAId and skill-specific detail

// PERM subscriber (in PERMSL) — enriches TNA from CMGM skill gaps
[ApiController][Route("subscribe")]
public class CMGMSubscriberController : ControllerBase
{
    [HttpPost("skill-gap-identified")]
    [Topic("pubsub", "cmgm.skillgap.identified")]
    public async Task<IActionResult> OnSkillGapIdentified(
        [FromBody] SkillGapIdentifiedEvent evt, CancellationToken ct)
    {
        foreach (var gap in evt.Gaps.Where(g => g.KRAId > 0))
        {
            await _tnaBll.CreateTNAFromSkillGapAsync(
                evt.EmployeeId, gap.KRAId, gap.SkillId, gap.GapLevel, ct);
        }
        return Ok();
    }
}

Skill Dev Plan Lifecycle

Planned (0)
Created from gap analysis
→
InProgress (1)
Training underway
→
Completed (2)
ActualHours + Notes required
— OR from Planned/InProgress —
OnHold (3)
→
InProgress (1)
CompleteSkillDevPlan validates Status = 0 or 1 before completing. ActualHours is required — cannot complete without recording time invested.

PERMDAL Additions for CMGM

CMGM reads KRA scores directly from PERMDAL (not via PERMBLL) to avoid a circular BLL reference:

// PERMDAL/DTOs/EmployeeKRAScoreDTO.cs  (new DTO added to PERMDAL)
public sealed class EmployeeKRAScoreDTO {
    public int     KRAId        { get; set; }
    public string? KRAName      { get; set; }
    public decimal KRAScore     { get; set; }   // manager eval preferred; falls back to self eval
    public decimal OverallScore { get; set; }   // TAPPRAISAL.FinalPercentage from latest finalized
    public int     AppraisalId  { get; set; }
}

// Added to IAppraisalEvalDAL interface:
Task<IEnumerable<EmployeeKRAScoreDTO>> GetLatestKRAScoresByEmployeeAsync(
    int employeeId, LoginDTO login, CancellationToken ct);

// CMGMBLL.csproj ProjectReference:
// <ProjectReference Include="..\..\PERM\PERMDAL\PERMDAL.csproj" />    ← DAL only, not BLL
// <ProjectReference Include="..\..\SkillManagement\SkillManagementDAL\SkillManagementDAL.csproj" />
Architectural Rule: CMGMBLL references PERMDAL (not PERMBLL) and SkillManagementDAL (not SkillManagementBLL). This prevents circular BLL-to-BLL dependencies. CMGM reads data from both modules at the DAL level only.

API Endpoints Reference (24 total)

Framework (4)

MethodRouteCache
GET/Competency/Framework/GetCompetencyFrameworkListCLIENT_LEVEL 10 min
GET/Competency/Framework/GetCompetencyFramework {FrameworkId}CLIENT_LEVEL
POST/Competency/Framework/SaveCompetencyFrameworknull (invalidates)
DELETE/Competency/Framework/DeleteCompetencyFramework {FrameworkId}null — guard: ValidationException if active KRAMaps exist

KRA Map (3)

MethodRouteNotes
GET/Competency/KRAMap/GetKRAMap {FrameworkId}CLIENT_LEVEL 10 min — returns all mappings with joined KRAName + SkillName + LevelLabel
POST/Competency/KRAMap/SaveKRAMapBulk: DELETE existing for framework + INSERT new batch in transaction
DELETE/Competency/KRAMap/DeleteKRAMap {CompetencyKRAMapId}Single row delete

Anchors (3)

MethodRouteCache
GET/Competency/Anchor/GetAnchorsBySkill {SkillId}CLIENT_LEVEL 30 min — all 5 levels for a skill
POST/Competency/Anchor/SaveAnchornull — guard: unique (SkillId, ProficiencyLevel)
DELETE/Competency/Anchor/DeleteAnchor {AnchorId}null

Career Path (7)

MethodRouteCache
GET/Competency/CareerPath/GetCareerPathList {FrameworkId?}CLIENT_LEVEL 10 min
POST/Competency/CareerPath/SaveCareerPathnull
DELETE/Competency/CareerPath/DeleteCareerPath {CareerPathId}null
GET/Competency/CareerPath/GetCareerPathRequirements {CareerPathId}CLIENT_LEVEL 10 min
POST/Competency/CareerPath/SaveCareerPathRequirementnull
DELETE/Competency/CareerPath/DeleteCareerPathRequirement {CareerPathReqId}null

Profile (4)

MethodRouteNotesCache
GET/Competency/Profile/GetCompetencyProfile {EmployeeId}Latest published profileUSER_LEVEL 5 min
POST/Competency/Profile/GenerateCompetencyProfile {EmployeeId}On-demand generation; triggers full algorithmnull
GET/Competency/Profile/GetSkillGapAnalysis {EmployeeId, CareerPathId}Live gap vs specific path; includes anchor text per gapNOT_REQUIRED
GET/Competency/Profile/GetCareerPathRecommendationsESS: EmployeeId from login.UserId only; sorted by ReadinessPercent DESCUSER_LEVEL 5 min

Development Plans (5)

MethodRouteCache
GET/Competency/Development/GetSkillDevPlanList {EmployeeId?, PlanType?, Status?}USER_LEVEL 3 min
POST/Competency/Development/SaveSkillDevPlannull
POST/Competency/Development/CompleteSkillDevPlan {DevPlanId, ActualHours, CompletionNotes}null — validates Status=0 or 1
DELETE/Competency/Development/DeleteSkillDevPlan {DevPlanId}null — only Planned/OnHold deletable

Sample Scenarios

Scenario A — Software Engineering Framework & KRA Map

KRA (PERM)Mapped SkillExpected LevelMandatory
Technical Excellence (KRAId: 5)Backend Development3 — IntermediateYes
Technical Excellence (KRAId: 5)System Design3 — IntermediateYes
Customer Focus (KRAId: 8)Communication2 — BasicNo
Delivery Quality (KRAId: 9)Code Review3 — IntermediateYes
Leadership (KRAId: 12)Team Leadership2 — BasicNo

Scenario B — Behavioral Anchors for Code Review Skill

LevelAnchor TextSample Indicators
L1 BeginnerCan identify obvious style issues; understands basic PR processReviews formatting conventions; asks for clarification
L2 BasicReviews for correctness; catches common bugs and edge casesIdentifies off-by-one errors; checks null handling
L3 IntermediateReviews for design patterns, performance, security; constructive feedbackQuestions architectural choices; flags SQL injection risks
L4 AdvancedEstablishes team review standards; mentors reviewers; architectural feedbackWrites team review guidelines; coaches juniors on review craft
L5 ExpertDefines org-wide review culture; recognized externally as authoritySpeaks at conferences; authors industry review frameworks

Scenario C — Employee Competency Profile (John Smith, EmployeeId: 201)

60%
Overall Score
2 Mandatory Gaps

John Smith — FY2026 Appraisal: 78%

Framework: Software Engineering Track · Target: Senior Dev → Tech Lead

SkillCurrent LevelRequiredGapMandatoryStatus
Backend DevelopmentL3L3—Yes✓ Met
System DesignL2L31 levelYes✗ Gap
CommunicationL3L2—No✓ Met
Code ReviewL3L41 levelYes✗ Gap
Team LeadershipL1L21 levelNo⚠ Gap
OverallCompetencyScore = 2 met / 5 total × 100 = 60% (Backend + Communication met). System Design and Code Review have mandatory gaps — auto-generates TNA via Dapr.

Scenario D — Career Path Recommendations

PathFrom → ToReadinessMandatory GapsMonths
Engineering TrackSenior Dev → Tech Lead60%2 (Code Review L4, System Design L3)18
Architecture TrackSenior Dev → Solutions Architect40%3 (System Design L4, Cloud L3, Stakeholder Mgmt L2)24
Management TrackSenior Dev → Engineering Manager35%4 (Leadership L3, Hiring L2, P&L L2, Conflict Mgmt L2)30

Employee is closest to Tech Lead path. Recommended dev plan: Code Review mentoring (40h) + System Design course (20h).

Scenario E — Skill Dev Plan Created from Gap

POST /Competency/Development/SaveSkillDevPlan
{
  "PlanType": 0,                        // Individual
  "TargetId": 201,                      // John Smith
  "SkillId": 45,                        // Code Review
  "CurrentProficiencyLevel": 3,
  "TargetProficiencyLevel": 4,
  "Priority": 2,                        // High
  "TrainingMethod": 3,                  // Mentoring
  "TargetDate": "2026-09-30",
  "EstimatedHours": 40,
  "TrainerId": 305                      // Senior architect as mentor
}

// Later: complete the plan
POST /Competency/Development/CompleteSkillDevPlan
{ "DevPlanId": 501, "ActualHours": 38, "CompletionNotes": "John completed all 8 mentoring sessions. Ready to conduct independent reviews." }
// Status → 2, ActualHours: 38, CompletionNotes populated

Integration Map

DirectionMechanismWhat Flows
PERM → CMGMDapr: perm.appraisal.finalizedTriggers auto-generation of TEMPLOYEECOMPETENCYPROFILE for the finalized employee
CMGM reads PERMProjectReference to PERMDALIAppraisalEvalDAL.GetLatestKRAScoresByEmployee — KRA scores for profile generation
CMGM reads SkillMgmtProjectReference to SkillManagementDALMEMPLOYEESKILL current levels via IEmployeeSkillProfileBLL injection
CMGM → PERM TNADapr: cmgm.skillgap.identifiedGaps payload → PERM inserts TTNAREQUEST rows with skill-specific training context
CMGM → ESSDapr: cmgm.profile.generatedESS career dashboard cache invalidation and refresh trigger

Service Configuration

SettingValue
Port5125
Proxy prefix/cmgm/
SignalRNone — no real-time push needed for CMGM
Assembly scanningCMGMBLL + CMGMDAL in CMGMSL/Program.cs
CMGMBLL.csproj referencesCMGMDAL + PERMDAL + SkillManagementDAL (DAL-only cross-module)
Dapr subscriber routePOST /cmgm/subscribe/appraisal-finalized

Caching Strategy

DataLevelTTLInvalidation
MCOMPETENCYFRAMEWORK listCLIENT_LEVEL10 minSave, Delete
MCOMPETENCYKRAMAP per frameworkCLIENT_LEVEL10 minSaveKRAMap, DeleteKRAMap
MCOMPETENCYANCHOR per skillCLIENT_LEVEL30 minSaveAnchor, DeleteAnchor
MCAREERPATH listCLIENT_LEVEL10 minSave, Delete
MCAREERPATHREQUIREMENT per pathCLIENT_LEVEL10 minSave, Delete
TEMPLOYEECOMPETENCYPROFILEUSER_LEVEL5 minGenerateCompetencyProfile
GetSkillGapAnalysisNOT_REQUIRED—Always live — gap data changes with skill assessments
GetCareerPathRecommendationsUSER_LEVEL5 minGenerateCompetencyProfile
TSKILLDEVPLAN per employeeUSER_LEVEL3 minSave, Complete, Delete