@forwardimpact/pathway 0.21.0 → 0.23.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 (123) hide show
  1. package/README.md +3 -3
  2. package/bin/fit-pathway.js +22 -22
  3. package/package.json +4 -3
  4. package/src/commands/agent.js +14 -10
  5. package/src/commands/behaviour.js +11 -1
  6. package/src/commands/build.js +11 -2
  7. package/src/commands/command-factory.js +4 -2
  8. package/src/commands/dev.js +9 -2
  9. package/src/commands/discipline.js +19 -2
  10. package/src/commands/driver.js +11 -1
  11. package/src/commands/index.js +1 -1
  12. package/src/commands/init.js +1 -1
  13. package/src/commands/interview.js +8 -8
  14. package/src/commands/job.js +41 -28
  15. package/src/commands/level.js +76 -0
  16. package/src/commands/progress.js +20 -20
  17. package/src/commands/questions.js +3 -3
  18. package/src/commands/skill.js +11 -1
  19. package/src/commands/stage.js +11 -1
  20. package/src/commands/tool.js +4 -3
  21. package/src/commands/track.js +11 -1
  22. package/src/components/action-buttons.js +3 -3
  23. package/src/components/builder.js +25 -25
  24. package/src/components/card.js +8 -104
  25. package/src/components/comparison-radar.js +4 -4
  26. package/src/components/detail.js +18 -120
  27. package/src/components/error-page.js +8 -68
  28. package/src/components/grid.js +12 -106
  29. package/src/components/list.js +7 -116
  30. package/src/components/nav.js +7 -60
  31. package/src/components/radar-chart.js +3 -3
  32. package/src/components/skill-matrix.js +7 -7
  33. package/src/css/bundles/app.css +25 -21
  34. package/src/css/bundles/handout.css +33 -33
  35. package/src/css/bundles/slides.css +25 -25
  36. package/src/css/pages/landing.css +5 -5
  37. package/src/formatters/index.js +5 -5
  38. package/src/formatters/interview/shared.js +23 -23
  39. package/src/formatters/job/description.js +18 -18
  40. package/src/formatters/job/dom.js +12 -12
  41. package/src/formatters/job/markdown.js +7 -7
  42. package/src/formatters/json-ld.js +24 -24
  43. package/src/formatters/{grade → level}/dom.js +31 -27
  44. package/src/formatters/{grade → level}/markdown.js +19 -28
  45. package/src/formatters/{grade → level}/microdata.js +28 -38
  46. package/src/formatters/level/shared.js +86 -0
  47. package/src/formatters/progress/markdown.js +2 -2
  48. package/src/formatters/progress/shared.js +51 -51
  49. package/src/formatters/questions/markdown.js +8 -6
  50. package/src/formatters/questions/shared.js +7 -7
  51. package/src/formatters/skill/dom.js +4 -4
  52. package/src/formatters/skill/markdown.js +1 -1
  53. package/src/formatters/skill/microdata.js +3 -3
  54. package/src/formatters/skill/shared.js +3 -3
  55. package/src/formatters/track/shared.js +1 -1
  56. package/src/handout-main.js +12 -12
  57. package/src/handout.html +32 -13
  58. package/src/index.html +33 -14
  59. package/src/lib/card-mappers.js +16 -16
  60. package/src/lib/cli-command.js +3 -3
  61. package/src/lib/cli-output.js +2 -2
  62. package/src/lib/error-boundary.js +3 -66
  63. package/src/lib/errors.js +7 -45
  64. package/src/lib/job-cache.js +12 -12
  65. package/src/lib/markdown.js +2 -109
  66. package/src/lib/reactive.js +7 -73
  67. package/src/lib/render.js +53 -201
  68. package/src/lib/router-core.js +2 -156
  69. package/src/lib/router-pages.js +2 -11
  70. package/src/lib/router-slides.js +2 -197
  71. package/src/lib/state.js +16 -65
  72. package/src/lib/utils.js +3 -10
  73. package/src/lib/yaml-loader.js +22 -80
  74. package/src/main.js +10 -10
  75. package/src/pages/agent-builder.js +12 -12
  76. package/src/pages/assessment-results.js +28 -24
  77. package/src/pages/interview-builder.js +6 -6
  78. package/src/pages/interview.js +8 -8
  79. package/src/pages/job-builder.js +7 -7
  80. package/src/pages/job.js +8 -8
  81. package/src/pages/landing.js +8 -8
  82. package/src/pages/level.js +122 -0
  83. package/src/pages/progress-builder.js +8 -8
  84. package/src/pages/progress.js +74 -74
  85. package/src/pages/self-assessment.js +7 -7
  86. package/src/pages/skill.js +1 -1
  87. package/src/slide-main.js +23 -23
  88. package/src/slides/chapter.js +4 -4
  89. package/src/slides/index.js +11 -11
  90. package/src/slides/interview.js +2 -2
  91. package/src/slides/job.js +4 -4
  92. package/src/slides/level.js +32 -0
  93. package/src/slides/overview.js +10 -10
  94. package/src/slides/progress.js +13 -13
  95. package/src/slides.html +32 -13
  96. package/src/types.js +1 -1
  97. package/templates/job.template.md +2 -2
  98. package/src/commands/grade.js +0 -60
  99. package/src/css/base.css +0 -56
  100. package/src/css/components/badges.css +0 -232
  101. package/src/css/components/buttons.css +0 -101
  102. package/src/css/components/forms.css +0 -191
  103. package/src/css/components/layout.css +0 -218
  104. package/src/css/components/nav.css +0 -206
  105. package/src/css/components/progress.css +0 -166
  106. package/src/css/components/states.css +0 -82
  107. package/src/css/components/surfaces.css +0 -347
  108. package/src/css/components/tables.css +0 -362
  109. package/src/css/components/top-bar.css +0 -180
  110. package/src/css/components/typography.css +0 -121
  111. package/src/css/components/utilities.css +0 -41
  112. package/src/css/pages/detail.css +0 -119
  113. package/src/css/reset.css +0 -50
  114. package/src/css/tokens.css +0 -162
  115. package/src/css/views/handout.css +0 -30
  116. package/src/css/views/print.css +0 -634
  117. package/src/css/views/slide-animations.css +0 -113
  118. package/src/css/views/slide-base.css +0 -331
  119. package/src/css/views/slide-sections.css +0 -597
  120. package/src/css/views/slide-tables.css +0 -275
  121. package/src/formatters/grade/shared.js +0 -86
  122. package/src/pages/grade.js +0 -122
  123. package/src/slides/grade.js +0 -32
package/src/lib/state.js CHANGED
@@ -1,21 +1,19 @@
1
1
  /**
2
2
  * Application state management
3
+ *
4
+ * Uses generic store from @forwardimpact/libui/state
5
+ * with Pathway-specific state shape and accessors.
3
6
  */
4
7
 
5
- /**
6
- * @typedef {Object} AppState
7
- * @property {Object} data - Loaded data from YAML files
8
- * @property {Object} ui - UI state
9
- */
8
+ import { createStore } from "@forwardimpact/libui/state";
10
9
 
11
- /** @type {AppState} */
12
- const state = {
10
+ const store = createStore({
13
11
  data: {
14
12
  skills: [],
15
13
  behaviours: [],
16
14
  disciplines: [],
17
15
  tracks: [],
18
- grades: [],
16
+ levels: [],
19
17
  drivers: [],
20
18
  questions: {},
21
19
  capabilities: [],
@@ -31,52 +29,22 @@ const state = {
31
29
  behaviours: { search: "" },
32
30
  disciplines: { search: "" },
33
31
  tracks: { search: "" },
34
- grades: { search: "" },
32
+ levels: { search: "" },
35
33
  drivers: { search: "" },
36
34
  },
37
35
  },
38
- };
39
-
40
- /** @type {Set<Function>} */
41
- const listeners = new Set();
42
-
43
- /**
44
- * Get the current state
45
- * @returns {AppState}
46
- */
47
- export function getState() {
48
- return state;
49
- }
50
-
51
- /**
52
- * Get a specific path from state
53
- * @param {string} path - Dot-notation path (e.g., 'data.skills')
54
- * @returns {*}
55
- */
56
- export function getStatePath(path) {
57
- return path.split(".").reduce((obj, key) => obj?.[key], state);
58
- }
36
+ });
59
37
 
60
- /**
61
- * Update state at a specific path
62
- * @param {string} path - Dot-notation path
63
- * @param {*} value - New value
64
- */
65
- export function updateState(path, value) {
66
- const keys = path.split(".");
67
- const lastKey = keys.pop();
68
- const target = keys.reduce((obj, key) => obj[key], state);
69
- target[lastKey] = value;
70
- notifyListeners();
71
- }
38
+ export const { getState, getStatePath, updateState, subscribe } = store;
72
39
 
73
40
  /**
74
41
  * Merge data into state
75
42
  * @param {Object} data - Data to merge
76
43
  */
77
44
  export function setData(data) {
45
+ const state = getState();
78
46
  Object.assign(state.data, data, { loaded: true, error: null });
79
- notifyListeners();
47
+ updateState("data", state.data);
80
48
  }
81
49
 
82
50
  /**
@@ -84,25 +52,7 @@ export function setData(data) {
84
52
  * @param {Error} error
85
53
  */
86
54
  export function setError(error) {
87
- state.data.error = error.message;
88
- notifyListeners();
89
- }
90
-
91
- /**
92
- * Subscribe to state changes
93
- * @param {Function} listener
94
- * @returns {Function} Unsubscribe function
95
- */
96
- export function subscribe(listener) {
97
- listeners.add(listener);
98
- return () => listeners.delete(listener);
99
- }
100
-
101
- /**
102
- * Notify all listeners of state change
103
- */
104
- function notifyListeners() {
105
- listeners.forEach((listener) => listener(state));
55
+ updateState("data.error", error.message);
106
56
  }
107
57
 
108
58
  /**
@@ -112,9 +62,10 @@ function notifyListeners() {
112
62
  * @param {*} value - Filter value
113
63
  */
114
64
  export function setFilter(entity, filterKey, value) {
65
+ const state = getState();
115
66
  if (state.ui.filters[entity]) {
116
67
  state.ui.filters[entity][filterKey] = value;
117
- notifyListeners();
68
+ updateState("ui.filters", state.ui.filters);
118
69
  }
119
70
  }
120
71
 
@@ -124,7 +75,7 @@ export function setFilter(entity, filterKey, value) {
124
75
  * @returns {Object}
125
76
  */
126
77
  export function getFilters(entity) {
127
- return state.ui.filters[entity] || {};
78
+ return getState().ui.filters[entity] || {};
128
79
  }
129
80
 
130
81
  /**
@@ -140,7 +91,7 @@ export function getFilters(entity) {
140
91
  * @returns {Branding}
141
92
  */
142
93
  export function getBranding() {
143
- const { framework } = state.data;
94
+ const { framework } = getState().data;
144
95
  return {
145
96
  title: framework.title || "Engineering Pathway",
146
97
  tag: framework.tag || "#BenchTools",
package/src/lib/utils.js CHANGED
@@ -1,14 +1,7 @@
1
1
  /**
2
2
  * General utility functions
3
+ *
4
+ * Re-exports from @forwardimpact/libui/utils.
3
5
  */
4
6
 
5
- /**
6
- * Get an array of items by their IDs
7
- * @param {Array} items - Array of items with id property
8
- * @param {string[]} ids - Array of IDs to find
9
- * @returns {Array} - Found items, filtered to remove nulls
10
- */
11
- export function getItemsByIds(items, ids) {
12
- if (!ids) return [];
13
- return ids.map((id) => items.find((item) => item.id === id)).filter(Boolean);
14
- }
7
+ export { getItemsByIds } from "@forwardimpact/libui/utils";
@@ -1,56 +1,21 @@
1
1
  /**
2
2
  * Browser-compatible YAML loading
3
3
  *
4
- * Uses _index.yaml files to discover files in directory structures.
5
- * These index files are auto-generated by the CLI (npx pathway --generate-index).
4
+ * Generic utilities from @forwardimpact/libui/yaml-loader,
5
+ * plus Pathway-specific entity loaders.
6
6
  */
7
7
 
8
- import { parse as parseYaml } from "https://cdn.jsdelivr.net/npm/yaml@2.3.4/+esm";
8
+ export {
9
+ loadYamlFile,
10
+ tryLoadYamlFile,
11
+ loadDirIndex,
12
+ } from "@forwardimpact/libui/yaml-loader";
9
13
 
10
- /**
11
- * Load and parse a YAML file
12
- * @param {string} path - Path to the YAML file
13
- * @returns {Promise<*>}
14
- */
15
- export async function loadYamlFile(path) {
16
- const response = await fetch(path);
17
- if (!response.ok) {
18
- throw new Error(
19
- `Failed to load ${path}: ${response.status} ${response.statusText}`,
20
- );
21
- }
22
- const text = await response.text();
23
- return parseYaml(text);
24
- }
25
-
26
- /**
27
- * Try to load a YAML file, return null if not found
28
- * @param {string} path - Path to the YAML file
29
- * @returns {Promise<*|null>}
30
- */
31
- async function tryLoadYamlFile(path) {
32
- const response = await fetch(path);
33
- if (response.status === 404) {
34
- return null;
35
- }
36
- if (!response.ok) {
37
- throw new Error(
38
- `Failed to load ${path}: ${response.status} ${response.statusText}`,
39
- );
40
- }
41
- const text = await response.text();
42
- return parseYaml(text);
43
- }
44
-
45
- /**
46
- * Load directory index (list of file IDs)
47
- * @param {string} dir - Directory path
48
- * @returns {Promise<string[]>} Array of file IDs
49
- */
50
- async function loadDirIndex(dir) {
51
- const index = await loadYamlFile(`${dir}/_index.yaml`);
52
- return index.files || [];
53
- }
14
+ import {
15
+ loadYamlFile,
16
+ tryLoadYamlFile,
17
+ loadDirIndex,
18
+ } from "@forwardimpact/libui/yaml-loader";
54
19
 
55
20
  /**
56
21
  * Load skills from capability files
@@ -86,7 +51,7 @@ async function loadSkillsFromCapabilities(capabilitiesDir) {
86
51
  name,
87
52
  capability: capabilityId, // Add capability from parent
88
53
  description: human.description,
89
- levelDescriptions: human.levelDescriptions,
54
+ proficiencyDescriptions: human.proficiencyDescriptions,
90
55
  // Include isHumanOnly flag for agent filtering (defaults to false)
91
56
  ...(isHumanOnly && { isHumanOnly }),
92
57
  ...(agent && { agent }),
@@ -115,23 +80,18 @@ async function loadDisciplinesFromDir(disciplinesDir) {
115
80
  const disciplines = await Promise.all(
116
81
  disciplineIds.map(async (id) => {
117
82
  const content = await loadYamlFile(`${disciplinesDir}/${id}.yaml`);
118
- // Shared content at top level, role summaries under human:
119
83
  const {
120
84
  specialization,
121
85
  roleTitle,
122
- // Track constraints
123
86
  isProfessional,
124
87
  isManagement,
125
88
  validTracks,
126
- minGrade,
127
- // Shared content - now at root level
89
+ minLevel,
128
90
  description,
129
- // Structural properties (derivation inputs)
130
91
  coreSkills,
131
92
  supportingSkills,
132
93
  broadSkills,
133
94
  behaviourModifiers,
134
- // Presentation sections
135
95
  human,
136
96
  agent,
137
97
  } = content;
@@ -139,19 +99,15 @@ async function loadDisciplinesFromDir(disciplinesDir) {
139
99
  id,
140
100
  specialization,
141
101
  roleTitle,
142
- // Track constraints
143
102
  isProfessional,
144
103
  isManagement,
145
104
  validTracks,
146
- minGrade,
147
- // Shared content at top level
105
+ minLevel,
148
106
  description,
149
- // Structural properties
150
107
  coreSkills,
151
108
  supportingSkills,
152
109
  broadSkills,
153
110
  behaviourModifiers,
154
- // Human presentation content (role summaries only)
155
111
  ...human,
156
112
  ...(agent && { agent }),
157
113
  };
@@ -171,31 +127,25 @@ async function loadTracksFromDir(tracksDir) {
171
127
  const tracks = await Promise.all(
172
128
  trackIds.map(async (id) => {
173
129
  const content = await loadYamlFile(`${tracksDir}/${id}.yaml`);
174
- // Shared content at top level (no human section for tracks anymore)
175
130
  const {
176
131
  name,
177
- // Shared content - now at root level
178
132
  description,
179
133
  roleContext,
180
- // Structural properties (derivation inputs)
181
134
  skillModifiers,
182
135
  behaviourModifiers,
183
136
  matchingWeights,
184
- minGrade,
185
- // Agent section (no human section anymore for tracks)
137
+ minLevel,
186
138
  agent,
187
139
  } = content;
188
140
  return {
189
141
  id,
190
142
  name,
191
- // Shared content at top level
192
143
  description,
193
144
  roleContext,
194
- // Structural properties
195
145
  skillModifiers,
196
146
  behaviourModifiers,
197
147
  matchingWeights,
198
- minGrade,
148
+ minLevel,
199
149
  ...(agent && { agent }),
200
150
  };
201
151
  }),
@@ -214,7 +164,6 @@ async function loadBehavioursFromDir(behavioursDir) {
214
164
  const behaviours = await Promise.all(
215
165
  behaviourIds.map(async (id) => {
216
166
  const content = await loadYamlFile(`${behavioursDir}/${id}.yaml`);
217
- // Flatten human properties to top level (behaviours use human: section in YAML)
218
167
  const { name, human, agent } = content;
219
168
  return {
220
169
  id,
@@ -285,7 +234,7 @@ async function loadQuestionFolder(
285
234
  );
286
235
 
287
236
  return {
288
- skillLevels: Object.fromEntries(skillEntries),
237
+ skillProficiencies: Object.fromEntries(skillEntries),
289
238
  behaviourMaturities: Object.fromEntries(behaviourEntries),
290
239
  capabilityLevels: Object.fromEntries(capabilityEntries),
291
240
  };
@@ -297,25 +246,20 @@ async function loadQuestionFolder(
297
246
  * @returns {Promise<Object>}
298
247
  */
299
248
  export async function loadAllData(dataDir = "./data") {
300
- // Load capabilities first (skills are embedded in capabilities)
301
249
  const capabilities = await loadCapabilitiesFromDir(`${dataDir}/capabilities`);
302
-
303
- // Extract skills from capabilities
304
250
  const skills = await loadSkillsFromCapabilities(`${dataDir}/capabilities`);
305
251
 
306
- // Load remaining core data in parallel (using _index.yaml for discovery)
307
- const [drivers, behaviours, disciplines, tracks, grades, stages, framework] =
252
+ const [drivers, behaviours, disciplines, tracks, levels, stages, framework] =
308
253
  await Promise.all([
309
254
  loadYamlFile(`${dataDir}/drivers.yaml`),
310
255
  loadBehavioursFromDir(`${dataDir}/behaviours`),
311
256
  loadDisciplinesFromDir(`${dataDir}/disciplines`),
312
257
  loadTracksFromDir(`${dataDir}/tracks`),
313
- loadYamlFile(`${dataDir}/grades.yaml`),
258
+ loadYamlFile(`${dataDir}/levels.yaml`),
314
259
  loadYamlFile(`${dataDir}/stages.yaml`),
315
260
  loadYamlFile(`${dataDir}/framework.yaml`),
316
261
  ]);
317
262
 
318
- // Load questions using skill/behaviour/capability IDs
319
263
  const questions = await loadQuestionFolder(
320
264
  `${dataDir}/questions`,
321
265
  skills,
@@ -329,7 +273,7 @@ export async function loadAllData(dataDir = "./data") {
329
273
  skills,
330
274
  disciplines,
331
275
  tracks,
332
- grades,
276
+ levels,
333
277
  questions,
334
278
  capabilities,
335
279
  stages,
@@ -339,9 +283,8 @@ export async function loadAllData(dataDir = "./data") {
339
283
 
340
284
  /**
341
285
  * Load agent-specific data for browser-based agent generation
342
- * Uses co-located files where agent sections are embedded in entity files
343
286
  * @param {string} [dataDir='./data'] - Path to data directory
344
- * @returns {Promise<Object>} Agent data including disciplines, tracks, behaviours, vscodeSettings, devcontainer, copilotSetupSteps
287
+ * @returns {Promise<Object>}
345
288
  */
346
289
  export async function loadAgentDataBrowser(dataDir = "./data") {
347
290
  const [
@@ -360,7 +303,6 @@ export async function loadAgentDataBrowser(dataDir = "./data") {
360
303
  tryLoadYamlFile(`${dataDir}/copilot-setup-steps.yaml`),
361
304
  ]);
362
305
 
363
- // Extract agent sections from co-located files
364
306
  return {
365
307
  disciplines: disciplines
366
308
  .filter((d) => d.agent)
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,10 +26,10 @@ import {
26
26
  deriveStageAgent,
27
27
  generateSkillMarkdown,
28
28
  deriveAgentSkills,
29
- deriveReferenceGrade,
29
+ deriveReferenceLevel,
30
30
  deriveToolkit,
31
31
  buildAgentIndex,
32
- } from "@forwardimpact/libpathway";
32
+ } from "@forwardimpact/libskill";
33
33
  import {
34
34
  createSelectWithValue,
35
35
  createDisciplineSelect,
@@ -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
 
@@ -19,7 +19,7 @@ import { getState } from "../lib/state.js";
19
19
  import { createBadge } from "../components/card.js";
20
20
  import { formatLevel } from "../lib/render.js";
21
21
  import { getAssessmentState, resetAssessment } from "./self-assessment.js";
22
- import { findRealisticMatches } from "@forwardimpact/libpathway/matching";
22
+ import { findRealisticMatches } from "@forwardimpact/libskill/matching";
23
23
 
24
24
  /**
25
25
  * Render the assessment results page
@@ -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",