@forwardimpact/pathway 0.21.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +3 -3
  2. package/bin/fit-pathway.js +22 -22
  3. package/package.json +3 -3
  4. package/src/commands/agent.js +7 -7
  5. package/src/commands/index.js +1 -1
  6. package/src/commands/init.js +1 -1
  7. package/src/commands/interview.js +8 -8
  8. package/src/commands/job.js +19 -19
  9. package/src/commands/level.js +60 -0
  10. package/src/commands/progress.js +20 -20
  11. package/src/commands/questions.js +3 -3
  12. package/src/components/action-buttons.js +3 -3
  13. package/src/components/builder.js +25 -25
  14. package/src/components/comparison-radar.js +3 -3
  15. package/src/components/detail.js +2 -2
  16. package/src/components/grid.js +1 -1
  17. package/src/components/radar-chart.js +3 -3
  18. package/src/components/skill-matrix.js +7 -7
  19. package/src/css/pages/landing.css +5 -5
  20. package/src/formatters/index.js +5 -5
  21. package/src/formatters/interview/shared.js +20 -20
  22. package/src/formatters/job/description.js +17 -17
  23. package/src/formatters/job/dom.js +12 -12
  24. package/src/formatters/job/markdown.js +7 -7
  25. package/src/formatters/json-ld.js +24 -24
  26. package/src/formatters/{grade → level}/dom.js +31 -27
  27. package/src/formatters/{grade → level}/markdown.js +19 -28
  28. package/src/formatters/{grade → level}/microdata.js +28 -38
  29. package/src/formatters/level/shared.js +86 -0
  30. package/src/formatters/progress/markdown.js +2 -2
  31. package/src/formatters/progress/shared.js +48 -48
  32. package/src/formatters/questions/markdown.js +8 -6
  33. package/src/formatters/questions/shared.js +7 -7
  34. package/src/formatters/skill/dom.js +4 -4
  35. package/src/formatters/skill/markdown.js +1 -1
  36. package/src/formatters/skill/microdata.js +3 -3
  37. package/src/formatters/skill/shared.js +2 -2
  38. package/src/handout-main.js +12 -12
  39. package/src/index.html +1 -1
  40. package/src/lib/card-mappers.js +16 -16
  41. package/src/lib/cli-command.js +3 -3
  42. package/src/lib/cli-output.js +2 -2
  43. package/src/lib/job-cache.js +11 -11
  44. package/src/lib/render.js +5 -5
  45. package/src/lib/state.js +2 -2
  46. package/src/lib/yaml-loader.js +9 -9
  47. package/src/main.js +10 -10
  48. package/src/pages/agent-builder.js +11 -11
  49. package/src/pages/assessment-results.js +27 -23
  50. package/src/pages/interview-builder.js +6 -6
  51. package/src/pages/interview.js +8 -8
  52. package/src/pages/job-builder.js +6 -6
  53. package/src/pages/job.js +7 -7
  54. package/src/pages/landing.js +8 -8
  55. package/src/pages/level.js +122 -0
  56. package/src/pages/progress-builder.js +8 -8
  57. package/src/pages/progress.js +74 -74
  58. package/src/pages/self-assessment.js +7 -7
  59. package/src/slide-main.js +22 -22
  60. package/src/slides/chapter.js +4 -4
  61. package/src/slides/index.js +10 -10
  62. package/src/slides/interview.js +2 -2
  63. package/src/slides/job.js +3 -3
  64. package/src/slides/level.js +32 -0
  65. package/src/slides/overview.js +9 -9
  66. package/src/slides/progress.js +13 -13
  67. package/src/types.js +1 -1
  68. package/templates/job.template.md +2 -2
  69. package/src/commands/grade.js +0 -60
  70. package/src/formatters/grade/shared.js +0 -86
  71. package/src/pages/grade.js +0 -122
  72. package/src/slides/grade.js +0 -32
@@ -13,22 +13,22 @@ const cache = new Map();
13
13
  /**
14
14
  * Build a consistent cache key from job parameters
15
15
  * @param {string} disciplineId
16
- * @param {string} gradeId
16
+ * @param {string} levelId
17
17
  * @param {string} [trackId] - Optional track ID
18
18
  * @returns {string}
19
19
  */
20
- export function buildJobKey(disciplineId, gradeId, trackId = null) {
20
+ export function buildJobKey(disciplineId, levelId, trackId = null) {
21
21
  if (trackId) {
22
- return `${disciplineId}_${gradeId}_${trackId}`;
22
+ return `${disciplineId}_${levelId}_${trackId}`;
23
23
  }
24
- return `${disciplineId}_${gradeId}`;
24
+ return `${disciplineId}_${levelId}`;
25
25
  }
26
26
 
27
27
  /**
28
28
  * Get or create a cached job definition
29
29
  * @param {Object} params
30
30
  * @param {Object} params.discipline
31
- * @param {Object} params.grade
31
+ * @param {Object} params.level
32
32
  * @param {Object} [params.track] - Optional track
33
33
  * @param {Array} params.skills
34
34
  * @param {Array} params.behaviours
@@ -37,18 +37,18 @@ export function buildJobKey(disciplineId, gradeId, trackId = null) {
37
37
  */
38
38
  export function getOrCreateJob({
39
39
  discipline,
40
- grade,
40
+ level,
41
41
  track = null,
42
42
  skills,
43
43
  behaviours,
44
44
  capabilities,
45
45
  }) {
46
- const key = buildJobKey(discipline.id, grade.id, track?.id);
46
+ const key = buildJobKey(discipline.id, level.id, track?.id);
47
47
 
48
48
  if (!cache.has(key)) {
49
49
  const job = deriveJob({
50
50
  discipline,
51
- grade,
51
+ level,
52
52
  track,
53
53
  skills,
54
54
  behaviours,
@@ -73,11 +73,11 @@ export function clearCache() {
73
73
  /**
74
74
  * Invalidate a specific job from the cache
75
75
  * @param {string} disciplineId
76
- * @param {string} gradeId
76
+ * @param {string} levelId
77
77
  * @param {string} [trackId] - Optional track ID
78
78
  */
79
- export function invalidateCachedJob(disciplineId, gradeId, trackId = null) {
80
- cache.delete(buildJobKey(disciplineId, gradeId, trackId));
79
+ export function invalidateCachedJob(disciplineId, levelId, trackId = null) {
80
+ cache.delete(buildJobKey(disciplineId, levelId, trackId));
81
81
  }
82
82
 
83
83
  /**
package/src/lib/render.js CHANGED
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import {
6
- SKILL_LEVEL_ORDER,
6
+ SKILL_PROFICIENCY_ORDER,
7
7
  BEHAVIOUR_MATURITY_ORDER,
8
8
  } from "@forwardimpact/map/levels";
9
9
 
@@ -190,7 +190,7 @@ export function showError(message) {
190
190
  }
191
191
 
192
192
  /**
193
- * Format a skill level or behaviour maturity for display
193
+ * Format a skill proficiency or behaviour maturity for display
194
194
  * Handles both snake_case and camelCase
195
195
  * @param {string} value - The level/maturity value
196
196
  * @returns {string}
@@ -205,12 +205,12 @@ export function formatLevel(value) {
205
205
  }
206
206
 
207
207
  /**
208
- * Get the index for a skill level (1-5)
208
+ * Get the index for a skill proficiency (1-5)
209
209
  * @param {string} level
210
210
  * @returns {number}
211
211
  */
212
- export function getSkillLevelIndex(level) {
213
- return SKILL_LEVEL_ORDER.indexOf(level) + 1;
212
+ export function getSkillProficiencyIndex(level) {
213
+ return SKILL_PROFICIENCY_ORDER.indexOf(level) + 1;
214
214
  }
215
215
 
216
216
  /**
package/src/lib/state.js CHANGED
@@ -15,7 +15,7 @@ const state = {
15
15
  behaviours: [],
16
16
  disciplines: [],
17
17
  tracks: [],
18
- grades: [],
18
+ levels: [],
19
19
  drivers: [],
20
20
  questions: {},
21
21
  capabilities: [],
@@ -31,7 +31,7 @@ const state = {
31
31
  behaviours: { search: "" },
32
32
  disciplines: { search: "" },
33
33
  tracks: { search: "" },
34
- grades: { search: "" },
34
+ levels: { search: "" },
35
35
  drivers: { search: "" },
36
36
  },
37
37
  },
@@ -86,7 +86,7 @@ async function loadSkillsFromCapabilities(capabilitiesDir) {
86
86
  name,
87
87
  capability: capabilityId, // Add capability from parent
88
88
  description: human.description,
89
- levelDescriptions: human.levelDescriptions,
89
+ proficiencyDescriptions: human.proficiencyDescriptions,
90
90
  // Include isHumanOnly flag for agent filtering (defaults to false)
91
91
  ...(isHumanOnly && { isHumanOnly }),
92
92
  ...(agent && { agent }),
@@ -123,7 +123,7 @@ async function loadDisciplinesFromDir(disciplinesDir) {
123
123
  isProfessional,
124
124
  isManagement,
125
125
  validTracks,
126
- minGrade,
126
+ minLevel,
127
127
  // Shared content - now at root level
128
128
  description,
129
129
  // Structural properties (derivation inputs)
@@ -143,7 +143,7 @@ async function loadDisciplinesFromDir(disciplinesDir) {
143
143
  isProfessional,
144
144
  isManagement,
145
145
  validTracks,
146
- minGrade,
146
+ minLevel,
147
147
  // Shared content at top level
148
148
  description,
149
149
  // Structural properties
@@ -181,7 +181,7 @@ async function loadTracksFromDir(tracksDir) {
181
181
  skillModifiers,
182
182
  behaviourModifiers,
183
183
  matchingWeights,
184
- minGrade,
184
+ minLevel,
185
185
  // Agent section (no human section anymore for tracks)
186
186
  agent,
187
187
  } = content;
@@ -195,7 +195,7 @@ async function loadTracksFromDir(tracksDir) {
195
195
  skillModifiers,
196
196
  behaviourModifiers,
197
197
  matchingWeights,
198
- minGrade,
198
+ minLevel,
199
199
  ...(agent && { agent }),
200
200
  };
201
201
  }),
@@ -285,7 +285,7 @@ async function loadQuestionFolder(
285
285
  );
286
286
 
287
287
  return {
288
- skillLevels: Object.fromEntries(skillEntries),
288
+ skillProficiencies: Object.fromEntries(skillEntries),
289
289
  behaviourMaturities: Object.fromEntries(behaviourEntries),
290
290
  capabilityLevels: Object.fromEntries(capabilityEntries),
291
291
  };
@@ -304,13 +304,13 @@ export async function loadAllData(dataDir = "./data") {
304
304
  const skills = await loadSkillsFromCapabilities(`${dataDir}/capabilities`);
305
305
 
306
306
  // Load remaining core data in parallel (using _index.yaml for discovery)
307
- const [drivers, behaviours, disciplines, tracks, grades, stages, framework] =
307
+ const [drivers, behaviours, disciplines, tracks, levels, stages, framework] =
308
308
  await Promise.all([
309
309
  loadYamlFile(`${dataDir}/drivers.yaml`),
310
310
  loadBehavioursFromDir(`${dataDir}/behaviours`),
311
311
  loadDisciplinesFromDir(`${dataDir}/disciplines`),
312
312
  loadTracksFromDir(`${dataDir}/tracks`),
313
- loadYamlFile(`${dataDir}/grades.yaml`),
313
+ loadYamlFile(`${dataDir}/levels.yaml`),
314
314
  loadYamlFile(`${dataDir}/stages.yaml`),
315
315
  loadYamlFile(`${dataDir}/framework.yaml`),
316
316
  ]);
@@ -329,7 +329,7 @@ export async function loadAllData(dataDir = "./data") {
329
329
  skills,
330
330
  disciplines,
331
331
  tracks,
332
- grades,
332
+ levels,
333
333
  questions,
334
334
  capabilities,
335
335
  stages,
package/src/main.js CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  renderDisciplineDetail,
26
26
  } from "./pages/discipline.js";
27
27
  import { renderTracksList, renderTrackDetail } from "./pages/track.js";
28
- import { renderGradesList, renderGradeDetail } from "./pages/grade.js";
28
+ import { renderLevelsList, renderLevelDetail } from "./pages/level.js";
29
29
  import { renderDriversList, renderDriverDetail } from "./pages/driver.js";
30
30
  import { renderStagesList, renderStageDetail } from "./pages/stage.js";
31
31
  import { renderToolsList } from "./pages/tool.js";
@@ -99,9 +99,9 @@ function setupRoutes() {
99
99
  router.on("/track", renderTracksList);
100
100
  router.on("/track/:id", renderTrackDetail);
101
101
 
102
- // Grade
103
- router.on("/grade", renderGradesList);
104
- router.on("/grade/:id", renderGradeDetail);
102
+ // Level
103
+ router.on("/level", renderLevelsList);
104
+ router.on("/level/:id", renderLevelDetail);
105
105
 
106
106
  // Driver
107
107
  router.on("/driver", renderDriversList);
@@ -116,18 +116,18 @@ function setupRoutes() {
116
116
 
117
117
  // Job builder
118
118
  router.on("/job-builder", renderJobBuilder);
119
- router.on("/job/:discipline/:grade/:track", renderJobDetail);
120
- router.on("/job/:discipline/:grade", renderJobDetail);
119
+ router.on("/job/:discipline/:level/:track", renderJobDetail);
120
+ router.on("/job/:discipline/:level", renderJobDetail);
121
121
 
122
122
  // Interview prep
123
123
  router.on("/interview-prep", renderInterviewPrep);
124
- router.on("/interview/:discipline/:grade/:track", renderInterviewDetail);
125
- router.on("/interview/:discipline/:grade", renderInterviewDetail);
124
+ router.on("/interview/:discipline/:level/:track", renderInterviewDetail);
125
+ router.on("/interview/:discipline/:level", renderInterviewDetail);
126
126
 
127
127
  // Career progress
128
128
  router.on("/career-progress", renderCareerProgress);
129
- router.on("/progress/:discipline/:grade/:track", renderProgressDetail);
130
- router.on("/progress/:discipline/:grade", renderProgressDetail);
129
+ router.on("/progress/:discipline/:level/:track", renderProgressDetail);
130
+ router.on("/progress/:discipline/:level", renderProgressDetail);
131
131
 
132
132
  // Self-assessment
133
133
  router.on("/self-assessment", renderSelfAssessment);
@@ -26,7 +26,7 @@ import {
26
26
  deriveStageAgent,
27
27
  generateSkillMarkdown,
28
28
  deriveAgentSkills,
29
- deriveReferenceGrade,
29
+ deriveReferenceLevel,
30
30
  deriveToolkit,
31
31
  buildAgentIndex,
32
32
  } from "@forwardimpact/libpathway";
@@ -256,8 +256,8 @@ export async function renderAgentBuilder() {
256
256
  return;
257
257
  }
258
258
 
259
- // Get reference grade for derivation
260
- const grade = deriveReferenceGrade(data.grades);
259
+ // Get reference level for derivation
260
+ const level = deriveReferenceLevel(data.levels);
261
261
 
262
262
  // Build agent index for all valid combinations
263
263
  const agentIndex = buildAgentIndex({
@@ -274,7 +274,7 @@ export async function renderAgentBuilder() {
274
274
  humanTrack,
275
275
  agentDiscipline,
276
276
  agentTrack,
277
- grade,
277
+ level,
278
278
  stages,
279
279
  skills: data.skills,
280
280
  behaviours: data.behaviours,
@@ -451,7 +451,7 @@ function createAllStagesPreview(context) {
451
451
  humanTrack,
452
452
  agentDiscipline,
453
453
  agentTrack,
454
- grade,
454
+ level,
455
455
  stages,
456
456
  skills,
457
457
  behaviours,
@@ -468,7 +468,7 @@ function createAllStagesPreview(context) {
468
468
  discipline: humanDiscipline,
469
469
  track: humanTrack,
470
470
  stage,
471
- grade,
471
+ level,
472
472
  skills,
473
473
  behaviours,
474
474
  agentBehaviours,
@@ -481,7 +481,7 @@ function createAllStagesPreview(context) {
481
481
  discipline: humanDiscipline,
482
482
  track: humanTrack,
483
483
  stage,
484
- grade,
484
+ level,
485
485
  skills,
486
486
  behaviours,
487
487
  agentBehaviours,
@@ -498,7 +498,7 @@ function createAllStagesPreview(context) {
498
498
  const derivedSkills = deriveAgentSkills({
499
499
  discipline: humanDiscipline,
500
500
  track: humanTrack,
501
- grade,
501
+ level,
502
502
  skills,
503
503
  });
504
504
 
@@ -594,7 +594,7 @@ function createSingleStagePreview(context, stage) {
594
594
  humanTrack,
595
595
  agentDiscipline,
596
596
  agentTrack,
597
- grade,
597
+ level,
598
598
  skills,
599
599
  behaviours,
600
600
  agentBehaviours,
@@ -609,7 +609,7 @@ function createSingleStagePreview(context, stage) {
609
609
  discipline: humanDiscipline,
610
610
  track: humanTrack,
611
611
  stage,
612
- grade,
612
+ level,
613
613
  skills,
614
614
  behaviours,
615
615
  agentBehaviours,
@@ -623,7 +623,7 @@ function createSingleStagePreview(context, stage) {
623
623
  const derivedSkills = deriveAgentSkills({
624
624
  discipline: humanDiscipline,
625
625
  track: humanTrack,
626
- grade,
626
+ level,
627
627
  skills,
628
628
  });
629
629
 
@@ -45,14 +45,14 @@ export function renderAssessmentResults() {
45
45
  };
46
46
 
47
47
  // Find matching jobs with realistic scoring
48
- const { matches, matchesByTier, estimatedGrade } = findRealisticMatches({
48
+ const { matches, matchesByTier, estimatedLevel } = findRealisticMatches({
49
49
  selfAssessment,
50
50
  disciplines: data.disciplines,
51
- grades: data.grades,
51
+ levels: data.levels,
52
52
  tracks: data.tracks,
53
53
  skills: data.skills,
54
54
  behaviours: data.behaviours,
55
- filterByGrade: false, // Show all grades but group by tier
55
+ filterByLevel: false, // Show all levels but group by tier
56
56
  topN: 20,
57
57
  });
58
58
 
@@ -73,7 +73,7 @@ export function renderAssessmentResults() {
73
73
  ),
74
74
 
75
75
  // Summary stats
76
- createSummaryStats(assessmentState, data, estimatedGrade),
76
+ createSummaryStats(assessmentState, data, estimatedLevel),
77
77
 
78
78
  // Top matches grouped by tier
79
79
  createMatchesSection(matches, matchesByTier, selfAssessment, data),
@@ -139,28 +139,28 @@ function renderNoAssessment() {
139
139
  * Create summary statistics section
140
140
  * @param {Object} assessmentState - Current assessment state
141
141
  * @param {Object} data - App data
142
- * @param {{grade: Object, confidence: number}} estimatedGrade - Estimated best-fit grade
142
+ * @param {{level: Object, confidence: number}} estimatedLevel - Estimated best-fit level
143
143
  * @returns {HTMLElement}
144
144
  */
145
- function createSummaryStats(assessmentState, data, estimatedGrade) {
145
+ function createSummaryStats(assessmentState, data, estimatedLevel) {
146
146
  const skillCount = Object.keys(assessmentState.skills).length;
147
147
  const behaviourCount = Object.keys(assessmentState.behaviours).length;
148
148
 
149
149
  // Calculate average levels
150
- const avgSkillLevel = calculateAverageLevel(
150
+ const avgSkillProficiency = calculateAverageLevel(
151
151
  Object.values(assessmentState.skills),
152
152
  ["awareness", "foundational", "working", "practitioner", "expert"],
153
153
  );
154
154
 
155
- // Get grade name based on track (default to professional)
156
- const gradeName =
157
- estimatedGrade.grade.professionalTitle ||
158
- estimatedGrade.grade.name ||
159
- estimatedGrade.grade.id;
155
+ // Get level name based on track (default to professional)
156
+ const levelName =
157
+ estimatedLevel.level.professionalTitle ||
158
+ estimatedLevel.level.name ||
159
+ estimatedLevel.level.id;
160
160
  const confidenceLabel =
161
- estimatedGrade.confidence >= 0.7
161
+ estimatedLevel.confidence >= 0.7
162
162
  ? "High"
163
- : estimatedGrade.confidence >= 0.4
163
+ : estimatedLevel.confidence >= 0.4
164
164
  ? "Medium"
165
165
  : "Low";
166
166
 
@@ -179,8 +179,12 @@ function createSummaryStats(assessmentState, data, estimatedGrade) {
179
179
  `of ${data.behaviours.length} Behaviours`,
180
180
  "🧠",
181
181
  ),
182
- createStatBox(formatLevel(avgSkillLevel), "Avg Skill Level", "💡"),
183
- createStatBox(gradeName, `Estimated Level (${confidenceLabel})`, "🎯"),
182
+ createStatBox(
183
+ formatLevel(avgSkillProficiency),
184
+ "Avg Skill Proficiency",
185
+ "💡",
186
+ ),
187
+ createStatBox(levelName, `Estimated Level (${confidenceLabel})`, "🎯"),
184
188
  ),
185
189
  );
186
190
  }
@@ -380,8 +384,8 @@ function createMatchCard(match, _index, _selfAssessment, _data) {
380
384
  a(
381
385
  {
382
386
  href: job.track
383
- ? `#/job/${job.discipline.id}/${job.grade.id}/${job.track.id}`
384
- : `#/job/${job.discipline.id}/${job.grade.id}`,
387
+ ? `#/job/${job.discipline.id}/${job.level.id}/${job.track.id}`
388
+ : `#/job/${job.discipline.id}/${job.level.id}`,
385
389
  },
386
390
  job.title,
387
391
  ),
@@ -389,7 +393,7 @@ function createMatchCard(match, _index, _selfAssessment, _data) {
389
393
  div(
390
394
  { className: "match-badges" },
391
395
  createBadge(job.discipline.name, "default"),
392
- createBadge(job.grade.name, "secondary"),
396
+ createBadge(job.level.name, "secondary"),
393
397
  job.track && createBadge(job.track.name, "broad"),
394
398
  ),
395
399
  ),
@@ -440,8 +444,8 @@ function createMatchCard(match, _index, _selfAssessment, _data) {
440
444
  a(
441
445
  {
442
446
  href: job.track
443
- ? `#/job/${job.discipline.id}/${job.grade.id}/${job.track.id}`
444
- : `#/job/${job.discipline.id}/${job.grade.id}`,
447
+ ? `#/job/${job.discipline.id}/${job.level.id}/${job.track.id}`
448
+ : `#/job/${job.discipline.id}/${job.level.id}`,
445
449
  className: "btn btn-secondary btn-sm",
446
450
  },
447
451
  "View Job Details",
@@ -449,8 +453,8 @@ function createMatchCard(match, _index, _selfAssessment, _data) {
449
453
  a(
450
454
  {
451
455
  href: job.track
452
- ? `#/interview/${job.discipline.id}/${job.grade.id}/${job.track.id}`
453
- : `#/interview/${job.discipline.id}/${job.grade.id}`,
456
+ ? `#/interview/${job.discipline.id}/${job.level.id}/${job.track.id}`
457
+ : `#/interview/${job.discipline.id}/${job.level.id}`,
454
458
  className: "btn btn-secondary btn-sm",
455
459
  },
456
460
  "Interview Prep",
@@ -17,7 +17,7 @@ export function renderInterviewPrep() {
17
17
  createBuilder({
18
18
  title: "Interview Prep",
19
19
  description:
20
- "Select a discipline, track, and grade to generate tailored interview questions " +
20
+ "Select a discipline, track, and level to generate tailored interview questions " +
21
21
  "based on the role's skill requirements and expected behaviours.",
22
22
  formTitle: "Select Role",
23
23
  emptyPreviewText: "Select all three components to preview the interview.",
@@ -26,21 +26,21 @@ export function renderInterviewPrep() {
26
26
  prepareInterviewBuilderPreview({
27
27
  ...selection,
28
28
  behaviourCount: data.behaviours.length,
29
- grades: data.grades,
29
+ levels: data.levels,
30
30
  }),
31
31
  detailPath: (sel) =>
32
32
  sel.track
33
- ? `/interview/${sel.discipline}/${sel.grade}/${sel.track}`
34
- : `/interview/${sel.discipline}/${sel.grade}`,
33
+ ? `/interview/${sel.discipline}/${sel.level}/${sel.track}`
34
+ : `/interview/${sel.discipline}/${sel.level}`,
35
35
  renderPreview: createStandardPreview,
36
36
  helpItems: [
37
37
  {
38
38
  label: "Role Selection",
39
- text: "Choose a discipline, track, and grade to define the target role for the interview.",
39
+ text: "Choose a discipline, track, and level to define the target role for the interview.",
40
40
  },
41
41
  {
42
42
  label: "Skill Questions",
43
- text: "Questions are generated based on the required skill levels for the role.",
43
+ text: "Questions are generated based on the required skill proficiencies for the role.",
44
44
  },
45
45
  {
46
46
  label: "Behaviour Questions",
@@ -32,18 +32,18 @@ import {
32
32
  * @param {Object} params - Route params
33
33
  */
34
34
  export function renderInterviewDetail(params) {
35
- const { discipline: disciplineId, grade: gradeId, track: trackId } = params;
35
+ const { discipline: disciplineId, level: levelId, track: trackId } = params;
36
36
  const { data } = getState();
37
37
 
38
38
  // Find the components
39
39
  const discipline = data.disciplines.find((d) => d.id === disciplineId);
40
- const grade = data.grades.find((g) => g.id === gradeId);
40
+ const level = data.levels.find((g) => g.id === levelId);
41
41
  const track = trackId ? data.tracks.find((t) => t.id === trackId) : null;
42
42
 
43
- if (!discipline || !grade) {
43
+ if (!discipline || !level) {
44
44
  renderError({
45
45
  title: "Interview Not Found",
46
- message: "Invalid combination. Discipline or grade not found.",
46
+ message: "Invalid combination. Discipline or level not found.",
47
47
  backPath: "/interview-prep",
48
48
  backText: "← Back to Interview Prep",
49
49
  });
@@ -64,7 +64,7 @@ export function renderInterviewDetail(params) {
64
64
  // Use formatter shared module to get all interview types
65
65
  const interviewsView = prepareAllInterviews({
66
66
  discipline,
67
- grade,
67
+ level,
68
68
  track,
69
69
  skills: data.skills,
70
70
  behaviours: data.behaviours,
@@ -74,7 +74,7 @@ export function renderInterviewDetail(params) {
74
74
  if (!interviewsView) {
75
75
  renderError({
76
76
  title: "Invalid Combination",
77
- message: "This discipline, track, and grade combination is not valid.",
77
+ message: "This discipline, track, and level combination is not valid.",
78
78
  backPath: "/interview-prep",
79
79
  backText: "← Back to Interview Prep",
80
80
  });
@@ -100,8 +100,8 @@ export function renderInterviewDetail(params) {
100
100
  ),
101
101
  " × ",
102
102
  a(
103
- { href: `#/grade/${interviewsView.gradeId}` },
104
- interviewsView.gradeId,
103
+ { href: `#/level/${interviewsView.levelId}` },
104
+ interviewsView.levelId,
105
105
  ),
106
106
  " × ",
107
107
  a(
@@ -17,7 +17,7 @@ export function renderJobBuilder() {
17
17
  createBuilder({
18
18
  title: "Job Builder",
19
19
  description:
20
- "Combine a discipline, track, and grade to generate a complete job definition " +
20
+ "Combine a discipline, track, and level to generate a complete job definition " +
21
21
  "with skill matrix and behaviour profile.",
22
22
  formTitle: "Select Components",
23
23
  emptyPreviewText:
@@ -27,12 +27,12 @@ export function renderJobBuilder() {
27
27
  prepareJobBuilderPreview({
28
28
  ...selection,
29
29
  behaviourCount: data.behaviours.length,
30
- grades: data.grades,
30
+ levels: data.levels,
31
31
  }),
32
32
  detailPath: (sel) =>
33
33
  sel.track
34
- ? `/job/${sel.discipline}/${sel.grade}/${sel.track}`
35
- : `/job/${sel.discipline}/${sel.grade}`,
34
+ ? `/job/${sel.discipline}/${sel.level}/${sel.track}`
35
+ : `/job/${sel.discipline}/${sel.level}`,
36
36
  renderPreview: createStandardPreview,
37
37
  helpItems: [
38
38
  {
@@ -40,8 +40,8 @@ export function renderJobBuilder() {
40
40
  text: "Defines the T-shaped skill profile with primary, secondary, and broad skills.",
41
41
  },
42
42
  {
43
- label: "Grade",
44
- text: "Sets base skill levels and behaviour maturity expectations for career level.",
43
+ label: "Level",
44
+ text: "Sets base skill proficiencies and behaviour maturity expectations for career level.",
45
45
  },
46
46
  {
47
47
  label: "Track",
package/src/pages/job.js CHANGED
@@ -28,18 +28,18 @@ async function getJobTemplate() {
28
28
  * @param {Object} params - Route params
29
29
  */
30
30
  export async function renderJobDetail(params) {
31
- const { discipline: disciplineId, grade: gradeId, track: trackId } = params;
31
+ const { discipline: disciplineId, level: levelId, track: trackId } = params;
32
32
  const { data } = getState();
33
33
 
34
34
  // Find the components
35
35
  const discipline = data.disciplines.find((d) => d.id === disciplineId);
36
- const grade = data.grades.find((g) => g.id === gradeId);
36
+ const level = data.levels.find((g) => g.id === levelId);
37
37
  const track = trackId ? data.tracks.find((t) => t.id === trackId) : null;
38
38
 
39
- if (!discipline || !grade) {
39
+ if (!discipline || !level) {
40
40
  renderError({
41
41
  title: "Job Not Found",
42
- message: "Invalid job combination. Discipline or grade not found.",
42
+ message: "Invalid job combination. Discipline or level not found.",
43
43
  backPath: "/job-builder",
44
44
  backText: "← Back to Job Builder",
45
45
  });
@@ -60,7 +60,7 @@ export async function renderJobDetail(params) {
60
60
  // Use formatter shared module to get job detail view
61
61
  const jobView = prepareJobDetail({
62
62
  discipline,
63
- grade,
63
+ level,
64
64
  track,
65
65
  skills: data.skills,
66
66
  behaviours: data.behaviours,
@@ -72,7 +72,7 @@ export async function renderJobDetail(params) {
72
72
  if (!jobView) {
73
73
  renderError({
74
74
  title: "Invalid Combination",
75
- message: "This discipline, track, and grade combination is not valid.",
75
+ message: "This discipline, track, and level combination is not valid.",
76
76
  backPath: "/job-builder",
77
77
  backText: "← Back to Job Builder",
78
78
  });
@@ -89,6 +89,6 @@ export async function renderJobDetail(params) {
89
89
 
90
90
  // Load template and format
91
91
  const jobTemplate = await getJobTemplate();
92
- const page = jobToDOM(jobView, { discipline, grade, track, jobTemplate });
92
+ const page = jobToDOM(jobView, { discipline, level, track, jobTemplate });
93
93
  render(page);
94
94
  }
@@ -105,9 +105,9 @@ export function renderLanding() {
105
105
  href: "/discipline",
106
106
  }),
107
107
  createStatCard({
108
- value: data.grades.length,
109
- label: "Grades",
110
- href: "/grade",
108
+ value: data.levels.length,
109
+ label: "Levels",
110
+ href: "/level",
111
111
  }),
112
112
  createStatCard({
113
113
  value: data.tracks.length,
@@ -169,9 +169,9 @@ export function renderLanding() {
169
169
  "/discipline",
170
170
  ),
171
171
  createQuickLinkCard(
172
- `${getConceptEmoji(framework, "grade")} ${framework.entityDefinitions.grade.title}`,
173
- `${data.grades.length} ${framework.entityDefinitions.grade.title.toLowerCase()} — ${framework.entityDefinitions.grade.description.trim().split("\n")[0]}`,
174
- "/grade",
172
+ `${getConceptEmoji(framework, "level")} ${framework.entityDefinitions.level.title}`,
173
+ `${data.levels.length} ${framework.entityDefinitions.level.title.toLowerCase()} — ${framework.entityDefinitions.level.description.trim().split("\n")[0]}`,
174
+ "/level",
175
175
  ),
176
176
  createQuickLinkCard(
177
177
  `${getConceptEmoji(framework, "track")} ${framework.entityDefinitions.track.title}`,
@@ -220,7 +220,7 @@ export function renderLanding() {
220
220
  h2({}, "🔨 Build a Job Definition"),
221
221
  p(
222
222
  {},
223
- "Combine a discipline, track, and grade to generate a complete job definition " +
223
+ "Combine a discipline, track, and level to generate a complete job definition " +
224
224
  "with skill matrices and behaviour profiles.",
225
225
  ),
226
226
  div(
@@ -256,7 +256,7 @@ export function renderLanding() {
256
256
  h2({}, "📈 Plan Your Career"),
257
257
  p(
258
258
  {},
259
- "Visualize your progression to the next grade and compare expectations " +
259
+ "Visualize your progression to the next level and compare expectations " +
260
260
  "across different tracks to plan your career development.",
261
261
  ),
262
262
  div(