@forwardimpact/map 0.11.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.
- package/LICENSE +201 -0
- package/README.md +67 -0
- package/bin/fit-map.js +287 -0
- package/examples/behaviours/_index.yaml +8 -0
- package/examples/behaviours/outcome_ownership.yaml +43 -0
- package/examples/behaviours/polymathic_knowledge.yaml +41 -0
- package/examples/behaviours/precise_communication.yaml +39 -0
- package/examples/behaviours/relentless_curiosity.yaml +37 -0
- package/examples/behaviours/systems_thinking.yaml +40 -0
- package/examples/capabilities/_index.yaml +8 -0
- package/examples/capabilities/business.yaml +205 -0
- package/examples/capabilities/delivery.yaml +1001 -0
- package/examples/capabilities/people.yaml +68 -0
- package/examples/capabilities/reliability.yaml +349 -0
- package/examples/capabilities/scale.yaml +1672 -0
- package/examples/copilot-setup-steps.yaml +25 -0
- package/examples/devcontainer.yaml +21 -0
- package/examples/disciplines/_index.yaml +6 -0
- package/examples/disciplines/data_engineering.yaml +68 -0
- package/examples/disciplines/engineering_management.yaml +61 -0
- package/examples/disciplines/software_engineering.yaml +68 -0
- package/examples/drivers.yaml +202 -0
- package/examples/framework.yaml +73 -0
- package/examples/levels.yaml +115 -0
- package/examples/questions/behaviours/outcome_ownership.yaml +228 -0
- package/examples/questions/behaviours/polymathic_knowledge.yaml +275 -0
- package/examples/questions/behaviours/precise_communication.yaml +248 -0
- package/examples/questions/behaviours/relentless_curiosity.yaml +248 -0
- package/examples/questions/behaviours/systems_thinking.yaml +238 -0
- package/examples/questions/capabilities/business.yaml +107 -0
- package/examples/questions/capabilities/delivery.yaml +101 -0
- package/examples/questions/capabilities/people.yaml +106 -0
- package/examples/questions/capabilities/reliability.yaml +105 -0
- package/examples/questions/capabilities/scale.yaml +104 -0
- package/examples/questions/skills/architecture_design.yaml +115 -0
- package/examples/questions/skills/cloud_platforms.yaml +105 -0
- package/examples/questions/skills/code_quality.yaml +162 -0
- package/examples/questions/skills/data_modeling.yaml +107 -0
- package/examples/questions/skills/devops.yaml +111 -0
- package/examples/questions/skills/full_stack_development.yaml +118 -0
- package/examples/questions/skills/sre_practices.yaml +113 -0
- package/examples/questions/skills/stakeholder_management.yaml +116 -0
- package/examples/questions/skills/team_collaboration.yaml +106 -0
- package/examples/questions/skills/technical_writing.yaml +110 -0
- package/examples/self-assessments.yaml +64 -0
- package/examples/stages.yaml +191 -0
- package/examples/tracks/_index.yaml +5 -0
- package/examples/tracks/platform.yaml +47 -0
- package/examples/tracks/sre.yaml +46 -0
- package/examples/vscode-settings.yaml +21 -0
- package/package.json +49 -0
- package/schema/json/behaviour-questions.schema.json +95 -0
- package/schema/json/behaviour.schema.json +73 -0
- package/schema/json/capability-questions.schema.json +95 -0
- package/schema/json/capability.schema.json +229 -0
- package/schema/json/defs.schema.json +132 -0
- package/schema/json/discipline.schema.json +123 -0
- package/schema/json/drivers.schema.json +48 -0
- package/schema/json/framework.schema.json +68 -0
- package/schema/json/levels.schema.json +121 -0
- package/schema/json/self-assessments.schema.json +52 -0
- package/schema/json/skill-questions.schema.json +83 -0
- package/schema/json/stages.schema.json +88 -0
- package/schema/json/track.schema.json +95 -0
- package/schema/rdf/behaviour-questions.ttl +128 -0
- package/schema/rdf/behaviour.ttl +130 -0
- package/schema/rdf/capability.ttl +466 -0
- package/schema/rdf/defs.ttl +396 -0
- package/schema/rdf/discipline.ttl +313 -0
- package/schema/rdf/drivers.ttl +84 -0
- package/schema/rdf/framework.ttl +166 -0
- package/schema/rdf/levels.ttl +357 -0
- package/schema/rdf/self-assessments.ttl +147 -0
- package/schema/rdf/skill-questions.ttl +155 -0
- package/schema/rdf/stages.ttl +166 -0
- package/schema/rdf/track.ttl +225 -0
- package/src/index-generator.js +65 -0
- package/src/index.js +44 -0
- package/src/levels.js +553 -0
- package/src/loader.js +608 -0
- package/src/modifiers.js +23 -0
- package/src/schema-validation.js +438 -0
- package/src/validation.js +2136 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Directory Index Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates _index.yaml files for browser-based directory discovery.
|
|
5
|
+
* These files list all entity files in a directory.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readdir, writeFile } from "fs/promises";
|
|
9
|
+
import { join, basename } from "path";
|
|
10
|
+
import { stringify as stringifyYaml } from "yaml";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generate _index.yaml for a directory
|
|
14
|
+
* @param {string} dir - Directory path
|
|
15
|
+
* @returns {Promise<string[]>} List of file IDs included
|
|
16
|
+
*/
|
|
17
|
+
export async function generateDirIndex(dir) {
|
|
18
|
+
const files = await readdir(dir);
|
|
19
|
+
const yamlFiles = files.filter(
|
|
20
|
+
(f) => f.endsWith(".yaml") && !f.startsWith("_"),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const fileIds = yamlFiles.map((f) => basename(f, ".yaml")).sort();
|
|
24
|
+
|
|
25
|
+
const content = stringifyYaml(
|
|
26
|
+
{
|
|
27
|
+
// Auto-generated index for browser loading
|
|
28
|
+
// Do not edit manually - regenerate with: npx pathway --generate-index
|
|
29
|
+
files: fileIds,
|
|
30
|
+
},
|
|
31
|
+
{ lineWidth: 0 },
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Add header comment
|
|
35
|
+
const output = `# Auto-generated index for browser loading
|
|
36
|
+
# Do not edit manually - regenerate with: npx pathway --generate-index
|
|
37
|
+
${content}`;
|
|
38
|
+
|
|
39
|
+
await writeFile(join(dir, "_index.yaml"), output, "utf-8");
|
|
40
|
+
|
|
41
|
+
return fileIds;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generate all index files for the data directory
|
|
46
|
+
* @param {string} dataDir - Path to the data directory
|
|
47
|
+
* @returns {Promise<Object>} Summary of generated indexes
|
|
48
|
+
*/
|
|
49
|
+
export async function generateAllIndexes(dataDir) {
|
|
50
|
+
const directories = ["behaviours", "disciplines", "tracks", "capabilities"];
|
|
51
|
+
|
|
52
|
+
const results = {};
|
|
53
|
+
|
|
54
|
+
for (const dir of directories) {
|
|
55
|
+
const fullPath = join(dataDir, dir);
|
|
56
|
+
try {
|
|
57
|
+
const files = await generateDirIndex(fullPath);
|
|
58
|
+
results[dir] = files;
|
|
59
|
+
} catch (err) {
|
|
60
|
+
results[dir] = { error: err.message };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return results;
|
|
65
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @forwardimpact/map
|
|
3
|
+
*
|
|
4
|
+
* Data model definitions, validation, and loading for Engineering Pathway.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Data loading
|
|
8
|
+
export {
|
|
9
|
+
loadAllData,
|
|
10
|
+
loadYamlFile,
|
|
11
|
+
createDataLoader,
|
|
12
|
+
loadFrameworkConfig,
|
|
13
|
+
loadQuestionFolder,
|
|
14
|
+
loadQuestionBankFromFolder,
|
|
15
|
+
loadSelfAssessments,
|
|
16
|
+
loadExampleData,
|
|
17
|
+
loadAndValidate,
|
|
18
|
+
loadAgentData,
|
|
19
|
+
loadSkillsWithAgentData,
|
|
20
|
+
} from "./loader.js";
|
|
21
|
+
|
|
22
|
+
// Referential integrity validation
|
|
23
|
+
export {
|
|
24
|
+
validateAllData,
|
|
25
|
+
validateQuestionBank,
|
|
26
|
+
validateSelfAssessment,
|
|
27
|
+
validateAgentData,
|
|
28
|
+
} from "./validation.js";
|
|
29
|
+
|
|
30
|
+
// Schema-based validation
|
|
31
|
+
export {
|
|
32
|
+
validateDataDirectory,
|
|
33
|
+
validateReferentialIntegrity,
|
|
34
|
+
runSchemaValidation,
|
|
35
|
+
} from "./schema-validation.js";
|
|
36
|
+
|
|
37
|
+
// Index generation
|
|
38
|
+
export { generateAllIndexes, generateDirIndex } from "./index-generator.js";
|
|
39
|
+
|
|
40
|
+
// Type constants and helpers
|
|
41
|
+
export * from "./levels.js";
|
|
42
|
+
|
|
43
|
+
// Capability validation helper
|
|
44
|
+
export { isCapability } from "./modifiers.js";
|
package/src/levels.js
ADDED
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engineering Pathway Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* This module defines all data structures used in the engineering pathway.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Skill proficiencies from lowest to highest proficiency
|
|
9
|
+
* @readonly
|
|
10
|
+
* @enum {string}
|
|
11
|
+
*/
|
|
12
|
+
export const SkillProficiency = {
|
|
13
|
+
AWARENESS: "awareness",
|
|
14
|
+
FOUNDATIONAL: "foundational",
|
|
15
|
+
WORKING: "working",
|
|
16
|
+
PRACTITIONER: "practitioner",
|
|
17
|
+
EXPERT: "expert",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Ordered array of skill proficiencies for comparison/clamping
|
|
22
|
+
* @type {string[]}
|
|
23
|
+
*/
|
|
24
|
+
export const SKILL_PROFICIENCY_ORDER = [
|
|
25
|
+
SkillProficiency.AWARENESS,
|
|
26
|
+
SkillProficiency.FOUNDATIONAL,
|
|
27
|
+
SkillProficiency.WORKING,
|
|
28
|
+
SkillProficiency.PRACTITIONER,
|
|
29
|
+
SkillProficiency.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
|
+
// Data-driven Stage Functions
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// Stage ordering is derived from loaded stage data, not hardcoded.
|
|
61
|
+
// Use getStageOrder(stages) to get stage IDs in lifecycle order.
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get ordered stage IDs from loaded stage data
|
|
65
|
+
*
|
|
66
|
+
* Stages are defined in stages.yaml and their array order IS the
|
|
67
|
+
* canonical lifecycle order. This function extracts IDs preserving
|
|
68
|
+
* that order, similar to getCapabilityOrder for capabilities.
|
|
69
|
+
*
|
|
70
|
+
* @param {Object[]} stages - Loaded stages array from stages.yaml
|
|
71
|
+
* @returns {string[]} Stage IDs in lifecycle order
|
|
72
|
+
*/
|
|
73
|
+
export function getStageOrder(stages) {
|
|
74
|
+
return stages.map((s) => s.id);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Skill capabilities (what capability area)
|
|
79
|
+
* @readonly
|
|
80
|
+
* @enum {string}
|
|
81
|
+
*/
|
|
82
|
+
export const Capability = {
|
|
83
|
+
DELIVERY: "delivery",
|
|
84
|
+
SCALE: "scale",
|
|
85
|
+
RELIABILITY: "reliability",
|
|
86
|
+
DATA: "data",
|
|
87
|
+
AI: "ai",
|
|
88
|
+
ML: "ml",
|
|
89
|
+
PROCESS: "process",
|
|
90
|
+
BUSINESS: "business",
|
|
91
|
+
PEOPLE: "people",
|
|
92
|
+
DOCUMENTATION: "documentation",
|
|
93
|
+
PRODUCT: "product",
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// Data-driven Capability Functions
|
|
98
|
+
// ============================================================================
|
|
99
|
+
// These functions work with loaded capability data for responsibility derivation
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get capability metadata from loaded capability data
|
|
103
|
+
* @param {Object[]} capabilities - Loaded capabilities array
|
|
104
|
+
* @param {string} capabilityId - The capability ID to look up
|
|
105
|
+
* @returns {Object|undefined} The capability object or undefined
|
|
106
|
+
*/
|
|
107
|
+
export function getCapabilityById(capabilities, capabilityId) {
|
|
108
|
+
return capabilities.find((c) => c.id === capabilityId);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get ordered capability IDs from loaded capability data
|
|
113
|
+
* @param {Object[]} capabilities - Loaded capabilities array
|
|
114
|
+
* @returns {string[]} Capability IDs in display order
|
|
115
|
+
*/
|
|
116
|
+
export function getCapabilityOrder(capabilities) {
|
|
117
|
+
return [...capabilities]
|
|
118
|
+
.sort((a, b) => (a.ordinalRank || 0) - (b.ordinalRank || 0))
|
|
119
|
+
.map((c) => c.id);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Group skills by capability in display order
|
|
124
|
+
* @param {import('./levels.js').Skill[]} skills - Array of skills to group
|
|
125
|
+
* @param {Object[]} capabilities - Loaded capabilities array for ordering
|
|
126
|
+
* @returns {Object<string, import('./levels.js').Skill[]>} Object with capabilities as keys (in display order)
|
|
127
|
+
*/
|
|
128
|
+
export function groupSkillsByCapability(skills, capabilities) {
|
|
129
|
+
const capabilityOrder = getCapabilityOrder(capabilities);
|
|
130
|
+
const result = {};
|
|
131
|
+
|
|
132
|
+
// Initialize all capabilities in display order (ensures consistent key order)
|
|
133
|
+
for (const capability of capabilityOrder) {
|
|
134
|
+
result[capability] = [];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Populate with skills
|
|
138
|
+
for (const skill of skills) {
|
|
139
|
+
if (result[skill.capability]) {
|
|
140
|
+
result[skill.capability].push(skill);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Remove empty capabilities and sort skills within each capability by name
|
|
145
|
+
for (const capability of Object.keys(result)) {
|
|
146
|
+
if (result[capability].length === 0) {
|
|
147
|
+
delete result[capability];
|
|
148
|
+
} else {
|
|
149
|
+
result[capability].sort((a, b) => a.name.localeCompare(b.name));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get emoji for a capability from loaded capability data
|
|
158
|
+
* @param {Object[]} capabilities - Loaded capabilities array
|
|
159
|
+
* @param {string} capabilityId - The capability ID
|
|
160
|
+
* @returns {string} The emoji or default "💡"
|
|
161
|
+
*/
|
|
162
|
+
export function getCapabilityEmoji(capabilities, capabilityId) {
|
|
163
|
+
const capability = getCapabilityById(capabilities, capabilityId);
|
|
164
|
+
return capability?.emojiIcon || "💡";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get responsibility statement for a capability at a specific skill proficiency
|
|
169
|
+
*
|
|
170
|
+
* Uses professionalResponsibilities for professional disciplines and
|
|
171
|
+
* managementResponsibilities for management disciplines.
|
|
172
|
+
*
|
|
173
|
+
* @param {Object[]} capabilities - Loaded capabilities array
|
|
174
|
+
* @param {string} capabilityId - The capability ID
|
|
175
|
+
* @param {string} level - The skill proficiency (awareness, foundational, working, practitioner, expert)
|
|
176
|
+
* @param {Object} [discipline] - Optional discipline to determine which responsibilities to use
|
|
177
|
+
* @param {boolean} [discipline.isManagement] - Whether this is a management discipline
|
|
178
|
+
* @returns {string|undefined} The responsibility statement or undefined
|
|
179
|
+
*/
|
|
180
|
+
export function getCapabilityResponsibility(
|
|
181
|
+
capabilities,
|
|
182
|
+
capabilityId,
|
|
183
|
+
level,
|
|
184
|
+
discipline,
|
|
185
|
+
) {
|
|
186
|
+
const capability = getCapabilityById(capabilities, capabilityId);
|
|
187
|
+
const responsibilityKey = discipline?.isManagement
|
|
188
|
+
? "managementResponsibilities"
|
|
189
|
+
: "professionalResponsibilities";
|
|
190
|
+
return capability?.[responsibilityKey]?.[level];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Skill type within a discipline
|
|
195
|
+
* @readonly
|
|
196
|
+
* @enum {string}
|
|
197
|
+
*/
|
|
198
|
+
export const SkillType = {
|
|
199
|
+
PRIMARY: "primary",
|
|
200
|
+
SECONDARY: "secondary",
|
|
201
|
+
BROAD: "broad",
|
|
202
|
+
TRACK: "track",
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* @typedef {Object} LevelDescription
|
|
207
|
+
* @property {string} level - The level identifier
|
|
208
|
+
* @property {string} description - Description of what this level means
|
|
209
|
+
*/
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @typedef {Object} Skill
|
|
213
|
+
* @property {string} id - Unique identifier
|
|
214
|
+
* @property {string} name - Display name
|
|
215
|
+
* @property {string} capability - One of Capability values
|
|
216
|
+
* @property {string} description - General description of the skill
|
|
217
|
+
* @property {Object<string, string>} proficiencyDescriptions - Description for each skill proficiency
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* @typedef {Object} Behaviour
|
|
222
|
+
* @property {string} id - Unique identifier
|
|
223
|
+
* @property {string} name - Display name
|
|
224
|
+
* @property {string} description - General description of the behaviour
|
|
225
|
+
* @property {Object<string, string>} maturityDescriptions - Description for each maturity level
|
|
226
|
+
*/
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @typedef {Object} Driver
|
|
230
|
+
* @property {string} id - Unique identifier
|
|
231
|
+
* @property {string} name - Display name
|
|
232
|
+
* @property {string} description - Description of the organizational outcome
|
|
233
|
+
* @property {string[]} contributingSkills - Array of skill IDs that support this driver
|
|
234
|
+
* @property {string[]} contributingBehaviours - Array of behaviour IDs that support this driver
|
|
235
|
+
*/
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @typedef {Object} Discipline
|
|
239
|
+
* @property {string} id - Unique identifier
|
|
240
|
+
* @property {string} specialization - Display name for the field (e.g., "Software Engineering")
|
|
241
|
+
* @property {string} roleTitle - Display name for a person in this role (e.g., "Software Engineer")
|
|
242
|
+
* @property {string} [name] - Legacy display name (deprecated, use specialization/roleTitle)
|
|
243
|
+
* @property {string} description - Description of the discipline
|
|
244
|
+
* @property {Array<string|null>} validTracks - Valid track configurations. null = allow trackless (generalist), string = track ID
|
|
245
|
+
* @property {string[]} coreSkills - Skill IDs requiring deep expertise (Practitioner/Expert)
|
|
246
|
+
* @property {string[]} supportingSkills - Skill IDs requiring solid competence (Working/Practitioner)
|
|
247
|
+
* @property {string[]} broadSkills - Skill IDs requiring awareness (Awareness/Foundational)
|
|
248
|
+
* @property {Object<string, number>} behaviourModifiers - Map of behaviour ID to modifier (+1, 0, -1)
|
|
249
|
+
*/
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @typedef {Object} AssessmentWeights
|
|
253
|
+
* @property {number} skillWeight - Weight for skill matching (0.0-1.0)
|
|
254
|
+
* @property {number} behaviourWeight - Weight for behaviour matching (0.0-1.0)
|
|
255
|
+
*/
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* @typedef {Object} Track
|
|
259
|
+
* @property {string} id - Unique identifier
|
|
260
|
+
* @property {string} name - Display name
|
|
261
|
+
* @property {string} description - Description of the track focus
|
|
262
|
+
* @property {Object<string, number>} skillModifiers - Map of capability/skill ID to level modifier (positive or negative integer)
|
|
263
|
+
* @property {Object<string, number>} behaviourModifiers - Map of behaviour ID to maturity modifier (positive or negative integer)
|
|
264
|
+
* @property {AssessmentWeights} [assessmentWeights] - Optional custom weights for job matching
|
|
265
|
+
* @property {string} [minLevel] - Optional minimum level ID this track is valid for
|
|
266
|
+
*/
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* @typedef {Object} LevelSkillProficiencies
|
|
270
|
+
* @property {string} primary - Base skill proficiency for primary skills
|
|
271
|
+
* @property {string} secondary - Base skill proficiency for secondary skills
|
|
272
|
+
* @property {string} broad - Base skill proficiency for broad skills
|
|
273
|
+
*/
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @typedef {Object} LevelExpectations
|
|
277
|
+
* @property {string} impactScope - Expected scope of work/impact
|
|
278
|
+
* @property {string} autonomyExpectation - Expected level of autonomy
|
|
279
|
+
* @property {string} influenceScope - Expected sphere of influence
|
|
280
|
+
* @property {string} complexityHandled - Expected complexity of work handled
|
|
281
|
+
*/
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @typedef {Object} BreadthCriteria
|
|
285
|
+
* @property {number} [practitioner] - Minimum number of skills at Practitioner level
|
|
286
|
+
* @property {number} [expert] - Minimum number of skills at Expert level
|
|
287
|
+
*/
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* @typedef {Object} Level
|
|
291
|
+
* @property {string} id - Unique identifier
|
|
292
|
+
* @property {string} professionalTitle - Display name for professional/IC track (e.g., "Level I", "Staff")
|
|
293
|
+
* @property {string} managementTitle - Display name for management track (e.g., "Associate", "Director")
|
|
294
|
+
* @property {string} [name] - Legacy display name (deprecated, use professionalTitle/managementTitle)
|
|
295
|
+
* @property {string} [typicalExperienceRange] - Typical years of experience range (e.g., "0-2", "20+")
|
|
296
|
+
* @property {number} ordinalRank - Numeric level for ordering (higher = more senior)
|
|
297
|
+
* @property {LevelSkillProficiencies} baseSkillProficiencies - Base skill proficiencies by skill type
|
|
298
|
+
* @property {string} baseBehaviourMaturity - Base behaviour maturity level
|
|
299
|
+
* @property {LevelExpectations} expectations - Role expectations
|
|
300
|
+
* @property {BreadthCriteria} [breadthCriteria] - For senior levels, breadth requirements
|
|
301
|
+
*/
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @typedef {Object} SkillMatrixEntry
|
|
305
|
+
* @property {string} skillId - The skill ID
|
|
306
|
+
* @property {string} skillName - The skill name
|
|
307
|
+
* @property {string} capability - The skill capability
|
|
308
|
+
* @property {string} type - The skill type (primary/secondary/broad)
|
|
309
|
+
* @property {string} proficiency - The derived skill proficiency
|
|
310
|
+
* @property {string} proficiencyDescription - Description for this proficiency
|
|
311
|
+
*/
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* @typedef {Object} BehaviourProfileEntry
|
|
315
|
+
* @property {string} behaviourId - The behaviour ID
|
|
316
|
+
* @property {string} behaviourName - The behaviour name
|
|
317
|
+
* @property {string} maturity - The derived maturity level
|
|
318
|
+
* @property {string} maturityDescription - Description for this maturity level
|
|
319
|
+
|
|
320
|
+
*/
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* @typedef {Object} JobDefinition
|
|
324
|
+
* @property {string} id - Generated job ID (discipline_level_track)
|
|
325
|
+
* @property {string} title - Generated job title
|
|
326
|
+
* @property {Discipline} discipline - Reference to the discipline
|
|
327
|
+
* @property {Level} level - Reference to the level
|
|
328
|
+
* @property {Track} track - Reference to the track
|
|
329
|
+
* @property {SkillMatrixEntry[]} skillMatrix - Complete derived skill matrix
|
|
330
|
+
* @property {BehaviourProfileEntry[]} behaviourProfile - Complete derived behaviour profile
|
|
331
|
+
* @property {LevelExpectations} expectations - Level-level expectations
|
|
332
|
+
*/
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* @typedef {Object} Question
|
|
336
|
+
* @property {string} id - Unique identifier
|
|
337
|
+
* @property {string} text - The question text
|
|
338
|
+
* @property {string} type - Question type (technical, situational, behavioural)
|
|
339
|
+
* @property {string[]} [followUps] - Optional follow-up questions
|
|
340
|
+
* @property {string[]} [lookingFor] - What good answers should include
|
|
341
|
+
* @property {number} [expectedDurationMinutes] - Estimated time to ask and answer
|
|
342
|
+
*/
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @typedef {Object<string, Question[]>} LevelQuestions - Questions organized by level
|
|
346
|
+
*/
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* @typedef {Object} RoleTypeQuestions
|
|
350
|
+
* @property {LevelQuestions} [professionalQuestions] - Questions for professional/IC roles
|
|
351
|
+
* @property {LevelQuestions} [managementQuestions] - Questions for management roles
|
|
352
|
+
*/
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* @typedef {Object} QuestionBank
|
|
356
|
+
* @property {Object<string, RoleTypeQuestions>} skillProficiencies - Questions by skill ID, then by role type (professional/management), then by level
|
|
357
|
+
* @property {Object<string, RoleTypeQuestions>} behaviourMaturities - Questions by behaviour ID, then by role type, then by maturity
|
|
358
|
+
* @property {Object<string, RoleTypeQuestions>} [capabilityLevels] - Questions by capability ID, then by role type, then by level
|
|
359
|
+
*/
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* @typedef {Object} SelfAssessment
|
|
363
|
+
* @property {string} [id] - Optional identifier
|
|
364
|
+
* @property {Object<string, string>} skills - Map of skill ID to self-assessed level
|
|
365
|
+
* @property {Object<string, string>} behaviours - Map of behaviour ID to self-assessed maturity
|
|
366
|
+
* @property {Object} [expectations] - Optional self-assessment of scope/autonomy/influence
|
|
367
|
+
* @property {string} [expectations.scope] - Self-assessed scope
|
|
368
|
+
* @property {string} [expectations.autonomy] - Self-assessed autonomy
|
|
369
|
+
* @property {string} [expectations.influence] - Self-assessed influence
|
|
370
|
+
*/
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* @typedef {Object} MatchGap
|
|
374
|
+
* @property {string} id - Skill or behaviour ID
|
|
375
|
+
* @property {string} name - Skill or behaviour name
|
|
376
|
+
* @property {string} type - 'skill' or 'behaviour'
|
|
377
|
+
* @property {string} current - Current level
|
|
378
|
+
* @property {string} required - Required level
|
|
379
|
+
* @property {number} gap - Numeric gap (positive means below requirement)
|
|
380
|
+
*/
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* @typedef {Object} MatchAnalysis
|
|
384
|
+
* @property {number} overallScore - Combined weighted score (0-1)
|
|
385
|
+
* @property {number} skillScore - Skill match score (0-1)
|
|
386
|
+
* @property {number} behaviourScore - Behaviour match score (0-1)
|
|
387
|
+
* @property {MatchingWeights} weightsUsed - The weights used in calculation
|
|
388
|
+
* @property {MatchGap[]} gaps - Array of gaps where requirements not met
|
|
389
|
+
* @property {MatchTierInfo} tier - Match tier classification
|
|
390
|
+
* @property {MatchGap[]} priorityGaps - Top 3 gaps by severity for focused development
|
|
391
|
+
* @property {number} [expectationsScore] - For senior roles, expectations match score
|
|
392
|
+
*/
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* @typedef {Object} JobMatch
|
|
396
|
+
* @property {JobDefinition} job - The matched job
|
|
397
|
+
* @property {MatchAnalysis} analysis - Match analysis details
|
|
398
|
+
*/
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* @typedef {Object} DevelopmentItem
|
|
402
|
+
* @property {string} id - Skill or behaviour ID
|
|
403
|
+
* @property {string} name - Skill or behaviour name
|
|
404
|
+
* @property {string} type - 'skill' or 'behaviour'
|
|
405
|
+
* @property {string} currentLevel - Current level
|
|
406
|
+
* @property {string} targetLevel - Target level for the job
|
|
407
|
+
* @property {number} priority - Priority score (higher = more important)
|
|
408
|
+
* @property {string} rationale - Why this development is important
|
|
409
|
+
*/
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* @typedef {Object} DevelopmentPath
|
|
413
|
+
* @property {JobDefinition} targetJob - The target job
|
|
414
|
+
* @property {DevelopmentItem[]} items - Prioritized development items
|
|
415
|
+
* @property {number} estimatedReadiness - Current readiness score (0-1)
|
|
416
|
+
*/
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* @typedef {Object} DriverCoverage
|
|
420
|
+
* @property {string} driverId - The driver ID
|
|
421
|
+
* @property {string} driverName - The driver name
|
|
422
|
+
* @property {number} skillCoverage - Percentage of linked skills at Working+ (0-1)
|
|
423
|
+
* @property {number} behaviourCoverage - Percentage of linked behaviours at Practicing+ (0-1)
|
|
424
|
+
* @property {number} overallScore - Weighted average of skill and behaviour coverage
|
|
425
|
+
* @property {string[]} coveredSkills - Skills that meet the threshold
|
|
426
|
+
* @property {string[]} coveredBehaviours - Behaviours that meet the threshold
|
|
427
|
+
* @property {string[]} missingSkills - Skills below threshold
|
|
428
|
+
* @property {string[]} missingBehaviours - Behaviours below threshold
|
|
429
|
+
*/
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* @typedef {Object} InterviewQuestion
|
|
433
|
+
* @property {Question} question - The question details
|
|
434
|
+
* @property {string} targetId - The skill or behaviour ID this assesses
|
|
435
|
+
* @property {string} targetName - The skill or behaviour name
|
|
436
|
+
* @property {string} targetType - 'skill' or 'behaviour'
|
|
437
|
+
* @property {string} targetLevel - The level this question assesses
|
|
438
|
+
* @property {number} priority - Priority in the interview (higher = ask first)
|
|
439
|
+
*/
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* @typedef {Object} InterviewGuide
|
|
443
|
+
* @property {JobDefinition} job - The job being interviewed for
|
|
444
|
+
* @property {InterviewQuestion[]} questions - Ordered list of questions
|
|
445
|
+
* @property {number} estimatedMinutes - Total estimated time
|
|
446
|
+
* @property {Object} coverage - Coverage summary
|
|
447
|
+
* @property {string[]} coverage.skills - Skills covered
|
|
448
|
+
* @property {string[]} coverage.behaviours - Behaviours covered
|
|
449
|
+
*/
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* @typedef {Object} ValidationError
|
|
453
|
+
* @property {string} type - Error type identifier
|
|
454
|
+
* @property {string} message - Human-readable error message
|
|
455
|
+
* @property {string} [path] - Path to the invalid data
|
|
456
|
+
* @property {*} [value] - The invalid value
|
|
457
|
+
*/
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* @typedef {Object} ValidationWarning
|
|
461
|
+
* @property {string} type - Warning type identifier
|
|
462
|
+
* @property {string} message - Human-readable warning message
|
|
463
|
+
* @property {string} [path] - Path to the concerning data
|
|
464
|
+
*/
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* @typedef {Object} ValidationResult
|
|
468
|
+
* @property {boolean} valid - Whether validation passed
|
|
469
|
+
* @property {ValidationError[]} errors - Array of validation errors
|
|
470
|
+
* @property {ValidationWarning[]} warnings - Array of validation warnings
|
|
471
|
+
*/
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* @typedef {Object} JobValidationRules
|
|
475
|
+
* @property {Array<{discipline: string, level?: string, track?: string}>} [invalidCombinations] - Invalid combinations
|
|
476
|
+
* @property {Object<string, string[]>} [validTracksByDiscipline] - Valid tracks per discipline
|
|
477
|
+
*/
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Helper function to get skill proficiency index
|
|
481
|
+
* @param {string} level - The skill proficiency
|
|
482
|
+
* @returns {number} The index (0-4), or -1 if invalid
|
|
483
|
+
*/
|
|
484
|
+
export function getSkillProficiencyIndex(level) {
|
|
485
|
+
return SKILL_PROFICIENCY_ORDER.indexOf(level);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Helper function to get behaviour maturity index
|
|
490
|
+
* @param {string} maturity - The maturity level
|
|
491
|
+
* @returns {number} The index (0-3), or -1 if invalid
|
|
492
|
+
*/
|
|
493
|
+
export function getBehaviourMaturityIndex(maturity) {
|
|
494
|
+
return BEHAVIOUR_MATURITY_ORDER.indexOf(maturity);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Clamp a skill proficiency index to valid range
|
|
499
|
+
* @param {number} index - The index to clamp
|
|
500
|
+
* @returns {string} The clamped skill proficiency
|
|
501
|
+
*/
|
|
502
|
+
export function clampSkillProficiency(index) {
|
|
503
|
+
const clampedIndex = Math.max(
|
|
504
|
+
0,
|
|
505
|
+
Math.min(SKILL_PROFICIENCY_ORDER.length - 1, index),
|
|
506
|
+
);
|
|
507
|
+
return SKILL_PROFICIENCY_ORDER[clampedIndex];
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Clamp a behaviour maturity index to valid range
|
|
512
|
+
* @param {number} index - The index to clamp
|
|
513
|
+
* @returns {string} The clamped maturity level
|
|
514
|
+
*/
|
|
515
|
+
export function clampBehaviourMaturity(index) {
|
|
516
|
+
const clampedIndex = Math.max(
|
|
517
|
+
0,
|
|
518
|
+
Math.min(BEHAVIOUR_MATURITY_ORDER.length - 1, index),
|
|
519
|
+
);
|
|
520
|
+
return BEHAVIOUR_MATURITY_ORDER[clampedIndex];
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Check if a skill proficiency meets or exceeds a requirement
|
|
525
|
+
* @param {string} actual - The actual skill proficiency
|
|
526
|
+
* @param {string} required - The required skill proficiency
|
|
527
|
+
* @returns {boolean} True if actual meets or exceeds required
|
|
528
|
+
*/
|
|
529
|
+
export function skillProficiencyMeetsRequirement(actual, required) {
|
|
530
|
+
return getSkillProficiencyIndex(actual) >= getSkillProficiencyIndex(required);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Check if a behaviour maturity meets or exceeds a requirement
|
|
535
|
+
* @param {string} actual - The actual maturity level
|
|
536
|
+
* @param {string} required - The required maturity level
|
|
537
|
+
* @returns {boolean} True if actual meets or exceeds required
|
|
538
|
+
*/
|
|
539
|
+
export function behaviourMaturityMeetsRequirement(actual, required) {
|
|
540
|
+
return (
|
|
541
|
+
getBehaviourMaturityIndex(actual) >= getBehaviourMaturityIndex(required)
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Get emoji for a concept from framework data
|
|
547
|
+
* @param {Object} framework - Framework object loaded from framework.yaml
|
|
548
|
+
* @param {string} concept - The concept type: 'driver', 'skill', 'behaviour', 'discipline', 'level', or 'track'
|
|
549
|
+
* @returns {string} The emoji for the concept or default "💡"
|
|
550
|
+
*/
|
|
551
|
+
export function getConceptEmoji(framework, concept) {
|
|
552
|
+
return framework?.entityDefinitions?.[concept]?.emojiIcon || "💡";
|
|
553
|
+
}
|