@forwardimpact/pathway 0.1.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 (227) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +104 -0
  3. package/app/commands/agent.js +430 -0
  4. package/app/commands/behaviour.js +61 -0
  5. package/app/commands/command-factory.js +211 -0
  6. package/app/commands/discipline.js +58 -0
  7. package/app/commands/driver.js +94 -0
  8. package/app/commands/grade.js +60 -0
  9. package/app/commands/index.js +20 -0
  10. package/app/commands/init.js +67 -0
  11. package/app/commands/interview.js +68 -0
  12. package/app/commands/job.js +157 -0
  13. package/app/commands/progress.js +77 -0
  14. package/app/commands/questions.js +179 -0
  15. package/app/commands/serve.js +143 -0
  16. package/app/commands/site.js +121 -0
  17. package/app/commands/skill.js +76 -0
  18. package/app/commands/stage.js +129 -0
  19. package/app/commands/track.js +70 -0
  20. package/app/components/action-buttons.js +66 -0
  21. package/app/components/behaviour-profile.js +53 -0
  22. package/app/components/builder.js +341 -0
  23. package/app/components/card.js +98 -0
  24. package/app/components/checklist.js +145 -0
  25. package/app/components/comparison-radar.js +237 -0
  26. package/app/components/detail.js +230 -0
  27. package/app/components/error-page.js +72 -0
  28. package/app/components/grid.js +109 -0
  29. package/app/components/list.js +120 -0
  30. package/app/components/modifier-table.js +142 -0
  31. package/app/components/nav.js +64 -0
  32. package/app/components/progression-table.js +320 -0
  33. package/app/components/radar-chart.js +102 -0
  34. package/app/components/skill-matrix.js +97 -0
  35. package/app/css/base.css +56 -0
  36. package/app/css/bundles/app.css +40 -0
  37. package/app/css/bundles/handout.css +43 -0
  38. package/app/css/bundles/slides.css +40 -0
  39. package/app/css/components/badges.css +215 -0
  40. package/app/css/components/buttons.css +101 -0
  41. package/app/css/components/forms.css +105 -0
  42. package/app/css/components/layout.css +209 -0
  43. package/app/css/components/nav.css +166 -0
  44. package/app/css/components/progress.css +166 -0
  45. package/app/css/components/states.css +82 -0
  46. package/app/css/components/surfaces.css +243 -0
  47. package/app/css/components/tables.css +362 -0
  48. package/app/css/components/typography.css +122 -0
  49. package/app/css/components/utilities.css +41 -0
  50. package/app/css/pages/agent-builder.css +391 -0
  51. package/app/css/pages/assessment-results.css +453 -0
  52. package/app/css/pages/detail.css +59 -0
  53. package/app/css/pages/interview-builder.css +148 -0
  54. package/app/css/pages/job-builder.css +134 -0
  55. package/app/css/pages/landing.css +92 -0
  56. package/app/css/pages/lifecycle.css +118 -0
  57. package/app/css/pages/progress-builder.css +274 -0
  58. package/app/css/pages/self-assessment.css +502 -0
  59. package/app/css/reset.css +50 -0
  60. package/app/css/tokens.css +153 -0
  61. package/app/css/views/handout.css +30 -0
  62. package/app/css/views/print.css +608 -0
  63. package/app/css/views/slide-animations.css +113 -0
  64. package/app/css/views/slide-base.css +330 -0
  65. package/app/css/views/slide-sections.css +597 -0
  66. package/app/css/views/slide-tables.css +275 -0
  67. package/app/formatters/agent/dom.js +540 -0
  68. package/app/formatters/agent/profile.js +133 -0
  69. package/app/formatters/agent/skill.js +58 -0
  70. package/app/formatters/behaviour/dom.js +91 -0
  71. package/app/formatters/behaviour/markdown.js +54 -0
  72. package/app/formatters/behaviour/shared.js +64 -0
  73. package/app/formatters/discipline/dom.js +187 -0
  74. package/app/formatters/discipline/markdown.js +87 -0
  75. package/app/formatters/discipline/shared.js +131 -0
  76. package/app/formatters/driver/dom.js +103 -0
  77. package/app/formatters/driver/shared.js +92 -0
  78. package/app/formatters/grade/dom.js +208 -0
  79. package/app/formatters/grade/markdown.js +94 -0
  80. package/app/formatters/grade/shared.js +86 -0
  81. package/app/formatters/index.js +50 -0
  82. package/app/formatters/interview/dom.js +97 -0
  83. package/app/formatters/interview/markdown.js +66 -0
  84. package/app/formatters/interview/shared.js +332 -0
  85. package/app/formatters/job/description.js +176 -0
  86. package/app/formatters/job/dom.js +411 -0
  87. package/app/formatters/job/markdown.js +102 -0
  88. package/app/formatters/progress/dom.js +135 -0
  89. package/app/formatters/progress/markdown.js +86 -0
  90. package/app/formatters/progress/shared.js +339 -0
  91. package/app/formatters/questions/json.js +43 -0
  92. package/app/formatters/questions/markdown.js +303 -0
  93. package/app/formatters/questions/shared.js +274 -0
  94. package/app/formatters/questions/yaml.js +76 -0
  95. package/app/formatters/shared.js +71 -0
  96. package/app/formatters/skill/dom.js +168 -0
  97. package/app/formatters/skill/markdown.js +109 -0
  98. package/app/formatters/skill/shared.js +125 -0
  99. package/app/formatters/stage/dom.js +135 -0
  100. package/app/formatters/stage/index.js +12 -0
  101. package/app/formatters/stage/shared.js +111 -0
  102. package/app/formatters/track/dom.js +128 -0
  103. package/app/formatters/track/markdown.js +105 -0
  104. package/app/formatters/track/shared.js +181 -0
  105. package/app/handout-main.js +421 -0
  106. package/app/handout.html +21 -0
  107. package/app/index.html +59 -0
  108. package/app/lib/card-mappers.js +173 -0
  109. package/app/lib/cli-output.js +270 -0
  110. package/app/lib/error-boundary.js +70 -0
  111. package/app/lib/errors.js +49 -0
  112. package/app/lib/form-controls.js +47 -0
  113. package/app/lib/job-cache.js +86 -0
  114. package/app/lib/markdown.js +114 -0
  115. package/app/lib/radar.js +866 -0
  116. package/app/lib/reactive.js +77 -0
  117. package/app/lib/render.js +212 -0
  118. package/app/lib/router-core.js +160 -0
  119. package/app/lib/router-pages.js +16 -0
  120. package/app/lib/router-slides.js +202 -0
  121. package/app/lib/state.js +148 -0
  122. package/app/lib/utils.js +14 -0
  123. package/app/lib/yaml-loader.js +327 -0
  124. package/app/main.js +213 -0
  125. package/app/model/agent.js +702 -0
  126. package/app/model/checklist.js +137 -0
  127. package/app/model/derivation.js +699 -0
  128. package/app/model/index-generator.js +71 -0
  129. package/app/model/interview.js +539 -0
  130. package/app/model/job.js +222 -0
  131. package/app/model/levels.js +591 -0
  132. package/app/model/loader.js +564 -0
  133. package/app/model/matching.js +858 -0
  134. package/app/model/modifiers.js +158 -0
  135. package/app/model/profile.js +266 -0
  136. package/app/model/progression.js +507 -0
  137. package/app/model/validation.js +1385 -0
  138. package/app/pages/agent-builder.js +823 -0
  139. package/app/pages/assessment-results.js +507 -0
  140. package/app/pages/behaviour.js +70 -0
  141. package/app/pages/discipline.js +71 -0
  142. package/app/pages/driver.js +106 -0
  143. package/app/pages/grade.js +117 -0
  144. package/app/pages/interview-builder.js +50 -0
  145. package/app/pages/interview.js +304 -0
  146. package/app/pages/job-builder.js +50 -0
  147. package/app/pages/job.js +58 -0
  148. package/app/pages/landing.js +305 -0
  149. package/app/pages/progress-builder.js +58 -0
  150. package/app/pages/progress.js +495 -0
  151. package/app/pages/self-assessment.js +729 -0
  152. package/app/pages/skill.js +113 -0
  153. package/app/pages/stage.js +231 -0
  154. package/app/pages/track.js +69 -0
  155. package/app/slide-main.js +360 -0
  156. package/app/slides/behaviour.js +38 -0
  157. package/app/slides/chapter.js +82 -0
  158. package/app/slides/discipline.js +40 -0
  159. package/app/slides/driver.js +39 -0
  160. package/app/slides/grade.js +32 -0
  161. package/app/slides/index.js +198 -0
  162. package/app/slides/interview.js +58 -0
  163. package/app/slides/job.js +55 -0
  164. package/app/slides/overview.js +126 -0
  165. package/app/slides/progress.js +83 -0
  166. package/app/slides/skill.js +40 -0
  167. package/app/slides/track.js +39 -0
  168. package/app/slides.html +56 -0
  169. package/app/types.js +147 -0
  170. package/bin/pathway.js +489 -0
  171. package/examples/agents/.claude/skills/architecture-design/SKILL.md +88 -0
  172. package/examples/agents/.claude/skills/cloud-platforms/SKILL.md +90 -0
  173. package/examples/agents/.claude/skills/code-quality-review/SKILL.md +67 -0
  174. package/examples/agents/.claude/skills/data-modeling/SKILL.md +99 -0
  175. package/examples/agents/.claude/skills/developer-experience/SKILL.md +99 -0
  176. package/examples/agents/.claude/skills/devops-cicd/SKILL.md +96 -0
  177. package/examples/agents/.claude/skills/full-stack-development/SKILL.md +90 -0
  178. package/examples/agents/.claude/skills/knowledge-management/SKILL.md +100 -0
  179. package/examples/agents/.claude/skills/pattern-generalization/SKILL.md +102 -0
  180. package/examples/agents/.claude/skills/sre-practices/SKILL.md +117 -0
  181. package/examples/agents/.claude/skills/technical-debt-management/SKILL.md +123 -0
  182. package/examples/agents/.claude/skills/technical-writing/SKILL.md +129 -0
  183. package/examples/agents/.github/agents/se-platform-code.agent.md +181 -0
  184. package/examples/agents/.github/agents/se-platform-plan.agent.md +178 -0
  185. package/examples/agents/.github/agents/se-platform-review.agent.md +113 -0
  186. package/examples/agents/.vscode/settings.json +8 -0
  187. package/examples/behaviours/_index.yaml +8 -0
  188. package/examples/behaviours/outcome_ownership.yaml +44 -0
  189. package/examples/behaviours/polymathic_knowledge.yaml +42 -0
  190. package/examples/behaviours/precise_communication.yaml +40 -0
  191. package/examples/behaviours/relentless_curiosity.yaml +38 -0
  192. package/examples/behaviours/systems_thinking.yaml +41 -0
  193. package/examples/capabilities/_index.yaml +8 -0
  194. package/examples/capabilities/business.yaml +251 -0
  195. package/examples/capabilities/delivery.yaml +352 -0
  196. package/examples/capabilities/people.yaml +100 -0
  197. package/examples/capabilities/reliability.yaml +318 -0
  198. package/examples/capabilities/scale.yaml +394 -0
  199. package/examples/disciplines/_index.yaml +5 -0
  200. package/examples/disciplines/data_engineering.yaml +76 -0
  201. package/examples/disciplines/software_engineering.yaml +76 -0
  202. package/examples/drivers.yaml +205 -0
  203. package/examples/framework.yaml +58 -0
  204. package/examples/grades.yaml +118 -0
  205. package/examples/questions/behaviours/outcome_ownership.yaml +52 -0
  206. package/examples/questions/behaviours/polymathic_knowledge.yaml +48 -0
  207. package/examples/questions/behaviours/precise_communication.yaml +55 -0
  208. package/examples/questions/behaviours/relentless_curiosity.yaml +51 -0
  209. package/examples/questions/behaviours/systems_thinking.yaml +53 -0
  210. package/examples/questions/skills/architecture_design.yaml +54 -0
  211. package/examples/questions/skills/cloud_platforms.yaml +48 -0
  212. package/examples/questions/skills/code_quality.yaml +49 -0
  213. package/examples/questions/skills/data_modeling.yaml +46 -0
  214. package/examples/questions/skills/devops.yaml +47 -0
  215. package/examples/questions/skills/full_stack_development.yaml +48 -0
  216. package/examples/questions/skills/sre_practices.yaml +44 -0
  217. package/examples/questions/skills/stakeholder_management.yaml +49 -0
  218. package/examples/questions/skills/team_collaboration.yaml +43 -0
  219. package/examples/questions/skills/technical_writing.yaml +43 -0
  220. package/examples/self-assessments.yaml +66 -0
  221. package/examples/stages.yaml +76 -0
  222. package/examples/tracks/_index.yaml +6 -0
  223. package/examples/tracks/manager.yaml +53 -0
  224. package/examples/tracks/platform.yaml +54 -0
  225. package/examples/tracks/sre.yaml +58 -0
  226. package/examples/vscode-settings.yaml +22 -0
  227. package/package.json +68 -0
@@ -0,0 +1,591 @@
1
+ /**
2
+ * Engineering Pathway Type Definitions
3
+ *
4
+ * This module defines all data structures used in the engineering pathway.
5
+ */
6
+
7
+ /**
8
+ * Skill levels from lowest to highest proficiency
9
+ * @readonly
10
+ * @enum {string}
11
+ */
12
+ export const SkillLevel = {
13
+ AWARENESS: "awareness",
14
+ FOUNDATIONAL: "foundational",
15
+ WORKING: "working",
16
+ PRACTITIONER: "practitioner",
17
+ EXPERT: "expert",
18
+ };
19
+
20
+ /**
21
+ * Ordered array of skill levels for comparison/clamping
22
+ * @type {string[]}
23
+ */
24
+ export const SKILL_LEVEL_ORDER = [
25
+ SkillLevel.AWARENESS,
26
+ SkillLevel.FOUNDATIONAL,
27
+ SkillLevel.WORKING,
28
+ SkillLevel.PRACTITIONER,
29
+ SkillLevel.EXPERT,
30
+ ];
31
+
32
+ /**
33
+ * Behaviour maturity levels from lowest to highest
34
+ * @readonly
35
+ * @enum {string}
36
+ */
37
+ export const BehaviourMaturity = {
38
+ EMERGING: "emerging",
39
+ DEVELOPING: "developing",
40
+ PRACTICING: "practicing",
41
+ ROLE_MODELING: "role_modeling",
42
+ EXEMPLIFYING: "exemplifying",
43
+ };
44
+
45
+ /**
46
+ * Ordered array of behaviour maturity levels for comparison/clamping
47
+ * @type {string[]}
48
+ */
49
+ export const BEHAVIOUR_MATURITY_ORDER = [
50
+ BehaviourMaturity.EMERGING,
51
+ BehaviourMaturity.DEVELOPING,
52
+ BehaviourMaturity.PRACTICING,
53
+ BehaviourMaturity.ROLE_MODELING,
54
+ BehaviourMaturity.EXEMPLIFYING,
55
+ ];
56
+
57
+ /**
58
+ * Lifecycle stages for development workflow
59
+ * @readonly
60
+ * @enum {string}
61
+ */
62
+ export const Stage = {
63
+ PLAN: "plan",
64
+ CODE: "code",
65
+ REVIEW: "review",
66
+ };
67
+
68
+ /**
69
+ * Ordered array of stages for lifecycle progression
70
+ * @type {string[]}
71
+ */
72
+ export const STAGE_ORDER = [Stage.PLAN, Stage.CODE, Stage.REVIEW];
73
+
74
+ /**
75
+ * Skill capabilities (what capability area)
76
+ * @readonly
77
+ * @enum {string}
78
+ */
79
+ export const Capability = {
80
+ DELIVERY: "delivery",
81
+ SCALE: "scale",
82
+ RELIABILITY: "reliability",
83
+ DATA: "data",
84
+ AI: "ai",
85
+ PROCESS: "process",
86
+ BUSINESS: "business",
87
+ PEOPLE: "people",
88
+ DOCUMENTATION: "documentation",
89
+ };
90
+
91
+ /**
92
+ * Ordered array of capabilities for consistent display
93
+ * Groups related capabilities logically:
94
+ * 1. Core delivery
95
+ * 2. Data & AI capabilities
96
+ * 3. Scale & reliability
97
+ * 4. People & process
98
+ * 5. Business & documentation
99
+ * @type {string[]}
100
+ */
101
+ export const CAPABILITY_ORDER = [
102
+ Capability.DELIVERY,
103
+ Capability.DATA,
104
+ Capability.AI,
105
+ Capability.SCALE,
106
+ Capability.RELIABILITY,
107
+ Capability.PEOPLE,
108
+ Capability.PROCESS,
109
+ Capability.BUSINESS,
110
+ Capability.DOCUMENTATION,
111
+ ];
112
+
113
+ /**
114
+ * Get the index of a capability in the ordered list
115
+ * @param {string} capability - The capability to look up
116
+ * @returns {number} The index (0-based), or -1 if not found
117
+ */
118
+ export function getCapabilityIndex(capability) {
119
+ return CAPABILITY_ORDER.indexOf(capability);
120
+ }
121
+
122
+ /**
123
+ * Compare two capabilities for sorting
124
+ * @param {string} a - First capability
125
+ * @param {string} b - Second capability
126
+ * @returns {number} Comparison result for sorting
127
+ */
128
+ export function compareCapabilities(a, b) {
129
+ return getCapabilityIndex(a) - getCapabilityIndex(b);
130
+ }
131
+
132
+ /**
133
+ * Sort an array of skills by capability order, then by name
134
+ * @param {import('./levels.js').Skill[]} skills - Array of skills to sort
135
+ * @returns {import('./levels.js').Skill[]} Sorted array (new array, does not mutate input)
136
+ */
137
+ export function sortSkillsByCapability(skills) {
138
+ return [...skills].sort((a, b) => {
139
+ const capabilityCompare = compareCapabilities(a.capability, b.capability);
140
+ if (capabilityCompare !== 0) return capabilityCompare;
141
+ return a.name.localeCompare(b.name);
142
+ });
143
+ }
144
+
145
+ /**
146
+ * Group skills by capability in the defined order
147
+ * @param {import('./levels.js').Skill[]} skills - Array of skills to group
148
+ * @returns {Object<string, import('./levels.js').Skill[]>} Object with capabilities as keys (in order)
149
+ */
150
+ export function groupSkillsByCapability(skills) {
151
+ const result = {};
152
+
153
+ // Initialize all capabilities in order (ensures consistent key order)
154
+ for (const capability of CAPABILITY_ORDER) {
155
+ result[capability] = [];
156
+ }
157
+
158
+ // Populate with skills
159
+ for (const skill of skills) {
160
+ if (result[skill.capability]) {
161
+ result[skill.capability].push(skill);
162
+ }
163
+ }
164
+
165
+ // Remove empty capabilities and sort skills within each capability by name
166
+ for (const capability of Object.keys(result)) {
167
+ if (result[capability].length === 0) {
168
+ delete result[capability];
169
+ } else {
170
+ result[capability].sort((a, b) => a.name.localeCompare(b.name));
171
+ }
172
+ }
173
+
174
+ return result;
175
+ }
176
+
177
+ // ============================================================================
178
+ // Data-driven Capability Functions
179
+ // ============================================================================
180
+ // These functions work with loaded capability data for responsibility derivation
181
+
182
+ /**
183
+ * Get capability metadata from loaded capability data
184
+ * @param {Object[]} capabilities - Loaded capabilities array
185
+ * @param {string} capabilityId - The capability ID to look up
186
+ * @returns {Object|undefined} The capability object or undefined
187
+ */
188
+ export function getCapabilityById(capabilities, capabilityId) {
189
+ return capabilities.find((c) => c.id === capabilityId);
190
+ }
191
+
192
+ /**
193
+ * Get ordered capability IDs from loaded capability data
194
+ * @param {Object[]} capabilities - Loaded capabilities array
195
+ * @returns {string[]} Capability IDs in display order
196
+ */
197
+ export function getCapabilityOrder(capabilities) {
198
+ return [...capabilities]
199
+ .sort((a, b) => (a.displayOrder || 0) - (b.displayOrder || 0))
200
+ .map((c) => c.id);
201
+ }
202
+
203
+ /**
204
+ * Get emoji for a capability from loaded capability data
205
+ * @param {Object[]} capabilities - Loaded capabilities array
206
+ * @param {string} capabilityId - The capability ID
207
+ * @returns {string} The emoji or default "💡"
208
+ */
209
+ export function getCapabilityEmoji(capabilities, capabilityId) {
210
+ const capability = getCapabilityById(capabilities, capabilityId);
211
+ return capability?.emoji || "💡";
212
+ }
213
+
214
+ /**
215
+ * Get responsibility statement for a capability at a specific skill level
216
+ *
217
+ * Uses professionalResponsibilities for professional tracks and
218
+ * managementResponsibilities for management tracks.
219
+ *
220
+ * @param {Object[]} capabilities - Loaded capabilities array
221
+ * @param {string} capabilityId - The capability ID
222
+ * @param {string} level - The skill level (awareness, foundational, working, practitioner, expert)
223
+ * @param {Object} [track] - Optional track to determine which responsibilities to use
224
+ * @param {boolean} [track.isManagement] - Whether this is a management track
225
+ * @returns {string|undefined} The responsibility statement or undefined
226
+ */
227
+ export function getCapabilityResponsibility(
228
+ capabilities,
229
+ capabilityId,
230
+ level,
231
+ track,
232
+ ) {
233
+ const capability = getCapabilityById(capabilities, capabilityId);
234
+ const responsibilityKey = track?.isManagement
235
+ ? "managementResponsibilities"
236
+ : "professionalResponsibilities";
237
+ return capability?.[responsibilityKey]?.[level];
238
+ }
239
+
240
+ /**
241
+ * Skill type within a discipline
242
+ * @readonly
243
+ * @enum {string}
244
+ */
245
+ export const SkillType = {
246
+ PRIMARY: "primary",
247
+ SECONDARY: "secondary",
248
+ BROAD: "broad",
249
+ TRACK: "track",
250
+ };
251
+
252
+ /**
253
+ * @typedef {Object} LevelDescription
254
+ * @property {string} level - The level identifier
255
+ * @property {string} description - Description of what this level means
256
+ */
257
+
258
+ /**
259
+ * @typedef {Object} Skill
260
+ * @property {string} id - Unique identifier
261
+ * @property {string} name - Display name
262
+ * @property {string} capability - One of Capability values
263
+ * @property {string} description - General description of the skill
264
+ * @property {Object<string, string>} levelDescriptions - Description for each skill level
265
+ */
266
+
267
+ /**
268
+ * @typedef {Object} Behaviour
269
+ * @property {string} id - Unique identifier
270
+ * @property {string} name - Display name
271
+ * @property {string} description - General description of the behaviour
272
+ * @property {Object<string, string>} maturityDescriptions - Description for each maturity level
273
+ */
274
+
275
+ /**
276
+ * @typedef {Object} Driver
277
+ * @property {string} id - Unique identifier
278
+ * @property {string} name - Display name
279
+ * @property {string} description - Description of the organizational outcome
280
+ * @property {string[]} contributingSkills - Array of skill IDs that support this driver
281
+ * @property {string[]} contributingBehaviours - Array of behaviour IDs that support this driver
282
+ */
283
+
284
+ /**
285
+ * @typedef {Object} Discipline
286
+ * @property {string} id - Unique identifier
287
+ * @property {string} specialization - Display name for the field (e.g., "Software Engineering")
288
+ * @property {string} roleTitle - Display name for a person in this role (e.g., "Software Engineer")
289
+ * @property {string} [name] - Legacy display name (deprecated, use specialization/roleTitle)
290
+ * @property {string} description - Description of the discipline
291
+ * @property {string[]} coreSkills - Skill IDs requiring deep expertise (Practitioner/Expert)
292
+ * @property {string[]} supportingSkills - Skill IDs requiring solid competence (Working/Practitioner)
293
+ * @property {string[]} broadSkills - Skill IDs requiring awareness (Awareness/Foundational)
294
+ * @property {Object<string, number>} behaviourModifiers - Map of behaviour ID to modifier (+1, 0, -1)
295
+ */
296
+
297
+ /**
298
+ * @typedef {Object} AssessmentWeights
299
+ * @property {number} skillWeight - Weight for skill matching (0.0-1.0)
300
+ * @property {number} behaviourWeight - Weight for behaviour matching (0.0-1.0)
301
+ */
302
+
303
+ /**
304
+ * @typedef {Object} Track
305
+ * @property {string} id - Unique identifier
306
+ * @property {string} name - Display name
307
+ * @property {string} description - Description of the track focus
308
+ * @property {boolean} [isProfessional=true] - Whether this is a professional/individual contributor track
309
+ * @property {boolean} [isManagement=false] - Whether this is a management/people manager track
310
+ * @property {Object<string, number>} skillModifiers - Map of skill ID to level modifier (positive or negative integer)
311
+ * @property {Object<string, number>} behaviourModifiers - Map of behaviour ID to maturity modifier (positive or negative integer)
312
+ * @property {AssessmentWeights} [assessmentWeights] - Optional custom weights for job matching
313
+ * @property {string[]} [validDisciplines] - Optional array of discipline IDs this track is valid for
314
+ * @property {string} [minGrade] - Optional minimum grade ID this track is valid for
315
+ */
316
+
317
+ /**
318
+ * @typedef {Object} GradeSkillLevels
319
+ * @property {string} primary - Base skill level for primary skills
320
+ * @property {string} secondary - Base skill level for secondary skills
321
+ * @property {string} broad - Base skill level for broad skills
322
+ */
323
+
324
+ /**
325
+ * @typedef {Object} GradeExpectations
326
+ * @property {string} impactScope - Expected scope of work/impact
327
+ * @property {string} autonomyExpectation - Expected level of autonomy
328
+ * @property {string} influenceScope - Expected sphere of influence
329
+ * @property {string} complexityHandled - Expected complexity of work handled
330
+ */
331
+
332
+ /**
333
+ * @typedef {Object} BreadthCriteria
334
+ * @property {number} [practitioner] - Minimum number of skills at Practitioner level
335
+ * @property {number} [expert] - Minimum number of skills at Expert level
336
+ */
337
+
338
+ /**
339
+ * @typedef {Object} Grade
340
+ * @property {string} id - Unique identifier
341
+ * @property {string} professionalTitle - Display name for professional/IC track (e.g., "Level I", "Staff")
342
+ * @property {string} managementTitle - Display name for management track (e.g., "Associate", "Director")
343
+ * @property {string} [name] - Legacy display name (deprecated, use professionalTitle/managementTitle)
344
+ * @property {string} [typicalExperienceRange] - Typical years of experience range (e.g., "0-2", "20+")
345
+ * @property {number} ordinalRank - Numeric level for ordering (higher = more senior)
346
+ * @property {GradeSkillLevels} baseSkillLevels - Base skill levels by skill type
347
+ * @property {string} baseBehaviourMaturity - Base behaviour maturity level
348
+ * @property {GradeExpectations} expectations - Role expectations
349
+ * @property {BreadthCriteria} [breadthCriteria] - For senior grades, breadth requirements
350
+ */
351
+
352
+ /**
353
+ * @typedef {Object} SkillMatrixEntry
354
+ * @property {string} skillId - The skill ID
355
+ * @property {string} skillName - The skill name
356
+ * @property {string} capability - The skill capability
357
+ * @property {string} type - The skill type (primary/secondary/broad)
358
+ * @property {string} level - The derived skill level
359
+ * @property {string} levelDescription - Description for this level
360
+ */
361
+
362
+ /**
363
+ * @typedef {Object} BehaviourProfileEntry
364
+ * @property {string} behaviourId - The behaviour ID
365
+ * @property {string} behaviourName - The behaviour name
366
+ * @property {string} maturity - The derived maturity level
367
+ * @property {string} maturityDescription - Description for this maturity level
368
+
369
+ */
370
+
371
+ /**
372
+ * @typedef {Object} JobDefinition
373
+ * @property {string} id - Generated job ID (discipline_grade_track)
374
+ * @property {string} title - Generated job title
375
+ * @property {Discipline} discipline - Reference to the discipline
376
+ * @property {Grade} grade - Reference to the grade
377
+ * @property {Track} track - Reference to the track
378
+ * @property {SkillMatrixEntry[]} skillMatrix - Complete derived skill matrix
379
+ * @property {BehaviourProfileEntry[]} behaviourProfile - Complete derived behaviour profile
380
+ * @property {GradeExpectations} expectations - Grade-level expectations
381
+ */
382
+
383
+ /**
384
+ * @typedef {Object} Question
385
+ * @property {string} id - Unique identifier
386
+ * @property {string} text - The question text
387
+ * @property {string} type - Question type (technical, situational, behavioural)
388
+ * @property {string[]} [followUps] - Optional follow-up questions
389
+ * @property {string[]} [lookingFor] - What good answers should include
390
+ * @property {number} [expectedDurationMinutes] - Estimated time to ask and answer
391
+ */
392
+
393
+ /**
394
+ * @typedef {Object} QuestionBank
395
+ * @property {Object<string, Object<string, Question[]>>} skillLevels - Questions by skill ID, then by level
396
+ * @property {Object<string, Object<string, Question[]>>} behaviourMaturities - Questions by behaviour ID, then by maturity
397
+ */
398
+
399
+ /**
400
+ * @typedef {Object} SelfAssessment
401
+ * @property {string} [id] - Optional identifier
402
+ * @property {Object<string, string>} skills - Map of skill ID to self-assessed level
403
+ * @property {Object<string, string>} behaviours - Map of behaviour ID to self-assessed maturity
404
+ * @property {Object} [expectations] - Optional self-assessment of scope/autonomy/influence
405
+ * @property {string} [expectations.scope] - Self-assessed scope
406
+ * @property {string} [expectations.autonomy] - Self-assessed autonomy
407
+ * @property {string} [expectations.influence] - Self-assessed influence
408
+ */
409
+
410
+ /**
411
+ * @typedef {Object} MatchGap
412
+ * @property {string} id - Skill or behaviour ID
413
+ * @property {string} name - Skill or behaviour name
414
+ * @property {string} type - 'skill' or 'behaviour'
415
+ * @property {string} current - Current level
416
+ * @property {string} required - Required level
417
+ * @property {number} gap - Numeric gap (positive means below requirement)
418
+ */
419
+
420
+ /**
421
+ * @typedef {Object} MatchAnalysis
422
+ * @property {number} overallScore - Combined weighted score (0-1)
423
+ * @property {number} skillScore - Skill match score (0-1)
424
+ * @property {number} behaviourScore - Behaviour match score (0-1)
425
+ * @property {MatchingWeights} weightsUsed - The weights used in calculation
426
+ * @property {MatchGap[]} gaps - Array of gaps where requirements not met
427
+ * @property {MatchTierInfo} tier - Match tier classification
428
+ * @property {MatchGap[]} priorityGaps - Top 3 gaps by severity for focused development
429
+ * @property {number} [expectationsScore] - For senior roles, expectations match score
430
+ */
431
+
432
+ /**
433
+ * @typedef {Object} JobMatch
434
+ * @property {JobDefinition} job - The matched job
435
+ * @property {MatchAnalysis} analysis - Match analysis details
436
+ */
437
+
438
+ /**
439
+ * @typedef {Object} DevelopmentItem
440
+ * @property {string} id - Skill or behaviour ID
441
+ * @property {string} name - Skill or behaviour name
442
+ * @property {string} type - 'skill' or 'behaviour'
443
+ * @property {string} currentLevel - Current level
444
+ * @property {string} targetLevel - Target level for the job
445
+ * @property {number} priority - Priority score (higher = more important)
446
+ * @property {string} rationale - Why this development is important
447
+ */
448
+
449
+ /**
450
+ * @typedef {Object} DevelopmentPath
451
+ * @property {JobDefinition} targetJob - The target job
452
+ * @property {DevelopmentItem[]} items - Prioritized development items
453
+ * @property {number} estimatedReadiness - Current readiness score (0-1)
454
+ */
455
+
456
+ /**
457
+ * @typedef {Object} DriverCoverage
458
+ * @property {string} driverId - The driver ID
459
+ * @property {string} driverName - The driver name
460
+ * @property {number} skillCoverage - Percentage of linked skills at Working+ (0-1)
461
+ * @property {number} behaviourCoverage - Percentage of linked behaviours at Practicing+ (0-1)
462
+ * @property {number} overallScore - Weighted average of skill and behaviour coverage
463
+ * @property {string[]} coveredSkills - Skills that meet the threshold
464
+ * @property {string[]} coveredBehaviours - Behaviours that meet the threshold
465
+ * @property {string[]} missingSkills - Skills below threshold
466
+ * @property {string[]} missingBehaviours - Behaviours below threshold
467
+ */
468
+
469
+ /**
470
+ * @typedef {Object} InterviewQuestion
471
+ * @property {Question} question - The question details
472
+ * @property {string} targetId - The skill or behaviour ID this assesses
473
+ * @property {string} targetName - The skill or behaviour name
474
+ * @property {string} targetType - 'skill' or 'behaviour'
475
+ * @property {string} targetLevel - The level this question assesses
476
+ * @property {number} priority - Priority in the interview (higher = ask first)
477
+ */
478
+
479
+ /**
480
+ * @typedef {Object} InterviewGuide
481
+ * @property {JobDefinition} job - The job being interviewed for
482
+ * @property {InterviewQuestion[]} questions - Ordered list of questions
483
+ * @property {number} estimatedMinutes - Total estimated time
484
+ * @property {Object} coverage - Coverage summary
485
+ * @property {string[]} coverage.skills - Skills covered
486
+ * @property {string[]} coverage.behaviours - Behaviours covered
487
+ */
488
+
489
+ /**
490
+ * @typedef {Object} ValidationError
491
+ * @property {string} type - Error type identifier
492
+ * @property {string} message - Human-readable error message
493
+ * @property {string} [path] - Path to the invalid data
494
+ * @property {*} [value] - The invalid value
495
+ */
496
+
497
+ /**
498
+ * @typedef {Object} ValidationWarning
499
+ * @property {string} type - Warning type identifier
500
+ * @property {string} message - Human-readable warning message
501
+ * @property {string} [path] - Path to the concerning data
502
+ */
503
+
504
+ /**
505
+ * @typedef {Object} ValidationResult
506
+ * @property {boolean} valid - Whether validation passed
507
+ * @property {ValidationError[]} errors - Array of validation errors
508
+ * @property {ValidationWarning[]} warnings - Array of validation warnings
509
+ */
510
+
511
+ /**
512
+ * @typedef {Object} JobValidationRules
513
+ * @property {Array<{discipline: string, grade?: string, track?: string}>} [invalidCombinations] - Invalid combinations
514
+ * @property {Object<string, string[]>} [validTracksByDiscipline] - Valid tracks per discipline
515
+ */
516
+
517
+ /**
518
+ * Helper function to get skill level index
519
+ * @param {string} level - The skill level
520
+ * @returns {number} The index (0-4), or -1 if invalid
521
+ */
522
+ export function getSkillLevelIndex(level) {
523
+ return SKILL_LEVEL_ORDER.indexOf(level);
524
+ }
525
+
526
+ /**
527
+ * Helper function to get behaviour maturity index
528
+ * @param {string} maturity - The maturity level
529
+ * @returns {number} The index (0-3), or -1 if invalid
530
+ */
531
+ export function getBehaviourMaturityIndex(maturity) {
532
+ return BEHAVIOUR_MATURITY_ORDER.indexOf(maturity);
533
+ }
534
+
535
+ /**
536
+ * Clamp a skill level index to valid range
537
+ * @param {number} index - The index to clamp
538
+ * @returns {string} The clamped skill level
539
+ */
540
+ export function clampSkillLevel(index) {
541
+ const clampedIndex = Math.max(
542
+ 0,
543
+ Math.min(SKILL_LEVEL_ORDER.length - 1, index),
544
+ );
545
+ return SKILL_LEVEL_ORDER[clampedIndex];
546
+ }
547
+
548
+ /**
549
+ * Clamp a behaviour maturity index to valid range
550
+ * @param {number} index - The index to clamp
551
+ * @returns {string} The clamped maturity level
552
+ */
553
+ export function clampBehaviourMaturity(index) {
554
+ const clampedIndex = Math.max(
555
+ 0,
556
+ Math.min(BEHAVIOUR_MATURITY_ORDER.length - 1, index),
557
+ );
558
+ return BEHAVIOUR_MATURITY_ORDER[clampedIndex];
559
+ }
560
+
561
+ /**
562
+ * Check if a skill level meets or exceeds a requirement
563
+ * @param {string} actual - The actual skill level
564
+ * @param {string} required - The required skill level
565
+ * @returns {boolean} True if actual meets or exceeds required
566
+ */
567
+ export function skillLevelMeetsRequirement(actual, required) {
568
+ return getSkillLevelIndex(actual) >= getSkillLevelIndex(required);
569
+ }
570
+
571
+ /**
572
+ * Check if a behaviour maturity meets or exceeds a requirement
573
+ * @param {string} actual - The actual maturity level
574
+ * @param {string} required - The required maturity level
575
+ * @returns {boolean} True if actual meets or exceeds required
576
+ */
577
+ export function behaviourMaturityMeetsRequirement(actual, required) {
578
+ return (
579
+ getBehaviourMaturityIndex(actual) >= getBehaviourMaturityIndex(required)
580
+ );
581
+ }
582
+
583
+ /**
584
+ * Get emoji for a concept from framework data
585
+ * @param {Object} framework - Framework object loaded from framework.yaml
586
+ * @param {string} concept - The concept type: 'driver', 'skill', 'behaviour', 'discipline', 'grade', or 'track'
587
+ * @returns {string} The emoji for the concept or default "💡"
588
+ */
589
+ export function getConceptEmoji(framework, concept) {
590
+ return framework?.entityDefinitions?.[concept]?.emoji || "💡";
591
+ }