@forwardimpact/pathway 0.19.0 → 0.20.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/pathway",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "Career progression web app and CLI for exploring roles and generating agent teams",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -10,10 +10,7 @@
10
10
 
11
11
  import Mustache from "mustache";
12
12
 
13
- import {
14
- SKILL_LEVEL_ORDER,
15
- BEHAVIOUR_MATURITY_ORDER,
16
- } from "@forwardimpact/schema/levels";
13
+ import { BEHAVIOUR_MATURITY_ORDER } from "@forwardimpact/schema/levels";
17
14
  import { trimValue, trimFields } from "../shared.js";
18
15
 
19
16
  /**
@@ -84,43 +81,45 @@ function prepareJobDescriptionData({ job, discipline, grade, track }) {
84
81
  return indexB - indexA;
85
82
  });
86
83
 
87
- // Group skills by level
88
- const skillsByLevel = {};
89
- for (const skill of job.skillMatrix) {
90
- const level = skill.level || "Other";
91
- if (!skillsByLevel[level]) {
92
- skillsByLevel[level] = [];
93
- }
94
- skillsByLevel[level].push(skill);
95
- }
84
+ // Build capability skill sections at the highest skill level
85
+ let capabilitySkills = [];
86
+ const derivedResponsibilities = job.derivedResponsibilities || [];
87
+ if (derivedResponsibilities.length > 0) {
88
+ // derivedResponsibilities is sorted: highest level first, then by ordinalRank
89
+ const highestLevel = derivedResponsibilities[0].level;
96
90
 
97
- // Sort levels in reverse order (expert first, awareness last)
98
- const sortedLevels = Object.keys(skillsByLevel).sort((a, b) => {
99
- const indexA = SKILL_LEVEL_ORDER.indexOf(a.toLowerCase());
100
- const indexB = SKILL_LEVEL_ORDER.indexOf(b.toLowerCase());
101
- if (indexA === -1 && indexB === -1) return a.localeCompare(b);
102
- if (indexA === -1) return 1;
103
- if (indexB === -1) return -1;
104
- return indexB - indexA;
105
- });
91
+ // Filter responsibilities to only the highest level
92
+ const topResponsibilities = derivedResponsibilities.filter(
93
+ (r) => r.level === highestLevel,
94
+ );
106
95
 
107
- // Keep only the top 2 skill levels for job descriptions
108
- const topLevels = sortedLevels.slice(0, 2);
96
+ // Group skill matrix entries by capability at the highest level
97
+ const skillsByCapability = {};
98
+ for (const skill of job.skillMatrix) {
99
+ if (skill.level !== highestLevel) continue;
100
+ if (!skillsByCapability[skill.capability]) {
101
+ skillsByCapability[skill.capability] = [];
102
+ }
103
+ skillsByCapability[skill.capability].push(skill);
104
+ }
109
105
 
110
- // Build skill levels array for template
111
- const skillLevels = topLevels.map((level) => {
112
- const skills = skillsByLevel[level];
113
- const sortedSkills = [...skills].sort((a, b) =>
114
- (a.skillName || "").localeCompare(b.skillName || ""),
115
- );
116
- return {
117
- levelHeading: `${level.toUpperCase()}-LEVEL SKILLS`,
118
- skills: sortedSkills.map((s) => ({
119
- skillName: s.skillName,
120
- levelDescription: s.levelDescription || "",
121
- })),
122
- };
123
- });
106
+ // Build capability sections in ordinalRank order
107
+ capabilitySkills = topResponsibilities
108
+ .filter((r) => skillsByCapability[r.capability]?.length > 0)
109
+ .map((r) => {
110
+ const skills = [...skillsByCapability[r.capability]].sort((a, b) =>
111
+ (a.skillName || "").localeCompare(b.skillName || ""),
112
+ );
113
+ return {
114
+ capabilityHeading: r.capabilityName.toUpperCase(),
115
+ responsibilityDescription: r.responsibility,
116
+ skills: skills.map((s) => ({
117
+ skillName: s.skillName,
118
+ levelDescription: s.levelDescription || "",
119
+ })),
120
+ };
121
+ });
122
+ }
124
123
 
125
124
  // Build qualification summary with placeholder replacement
126
125
  const qualificationSummary =
@@ -129,9 +128,6 @@ function prepareJobDescriptionData({ job, discipline, grade, track }) {
129
128
  grade.typicalExperienceRange || "",
130
129
  ) || null;
131
130
 
132
- const responsibilities = trimFields(job.derivedResponsibilities, {
133
- responsibility: "required",
134
- });
135
131
  const behaviours = trimFields(sortedBehaviours, {
136
132
  maturityDescription: "optional",
137
133
  });
@@ -150,15 +146,14 @@ function prepareJobDescriptionData({ job, discipline, grade, track }) {
150
146
  hasTrackRoleContext: !!trimmedTrackRoleContext,
151
147
  expectationsParagraph: trimmedExpectationsParagraph,
152
148
  hasExpectationsParagraph: !!trimmedExpectationsParagraph,
153
- responsibilities,
154
- hasResponsibilities: responsibilities.length > 0,
155
149
  behaviours,
156
150
  hasBehaviours: behaviours.length > 0,
157
- skillLevels: skillLevels.map((level) => ({
158
- ...level,
159
- skills: trimFields(level.skills, { levelDescription: "optional" }),
151
+ capabilitySkills: capabilitySkills.map((cap) => ({
152
+ ...cap,
153
+ responsibilityDescription: trimValue(cap.responsibilityDescription),
154
+ skills: trimFields(cap.skills, { levelDescription: "optional" }),
160
155
  })),
161
- hasSkillLevels: skillLevels.length > 0,
156
+ hasCapabilitySkills: capabilitySkills.length > 0,
162
157
  qualificationSummary: trimmedQualificationSummary,
163
158
  hasQualificationSummary: !!trimmedQualificationSummary,
164
159
  };
@@ -16,14 +16,6 @@
16
16
 
17
17
  {{{expectationsParagraph}}}
18
18
  {{/hasExpectationsParagraph}}
19
- {{#hasResponsibilities}}
20
-
21
- ## ROLE RESPONSIBILITIES
22
-
23
- {{#responsibilities}}
24
- - **{{{capabilityName}}}:** {{{responsibility}}}
25
- {{/responsibilities}}
26
- {{/hasResponsibilities}}
27
19
  {{#hasBehaviours}}
28
20
 
29
21
  ## ROLE BEHAVIOURS
@@ -32,16 +24,20 @@
32
24
  - **{{{behaviourName}}}:** {{{maturityDescription}}}
33
25
  {{/behaviours}}
34
26
  {{/hasBehaviours}}
35
- {{#hasSkillLevels}}
36
- {{#skillLevels}}
27
+ {{#hasCapabilitySkills}}
28
+
29
+ ## ROLE RESPONSIBILITIES
30
+ {{#capabilitySkills}}
31
+
32
+ ### {{{capabilityHeading}}}
37
33
 
38
- ## {{{levelHeading}}}
34
+ {{{responsibilityDescription}}}:
39
35
 
40
36
  {{#skills}}
41
37
  - **{{{skillName}}}:** {{{levelDescription}}}
42
38
  {{/skills}}
43
- {{/skillLevels}}
44
- {{/hasSkillLevels}}
39
+ {{/capabilitySkills}}
40
+ {{/hasCapabilitySkills}}
45
41
  {{#hasQualificationSummary}}
46
42
 
47
43
  ## QUALIFICATIONS