@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,158 @@
1
+ /**
2
+ * Skill Modifier Expansion Functions
3
+ *
4
+ * This module provides pure functions for expanding capability-based skill modifiers
5
+ * to individual skill modifiers. Tracks define modifiers by capability only
6
+ * (e.g., "delivery: 1", "scale: -1") - individual skill modifiers are not allowed.
7
+ */
8
+
9
+ import { CAPABILITY_ORDER } from "./levels.js";
10
+
11
+ /**
12
+ * Valid skill capability names for modifier expansion
13
+ * @type {Set<string>}
14
+ */
15
+ const VALID_CAPABILITIES = new Set(CAPABILITY_ORDER);
16
+
17
+ /**
18
+ * Check if a key is a skill capability
19
+ * @param {string} key - The key to check
20
+ * @returns {boolean} True if the key is a valid skill capability
21
+ */
22
+ export function isCapability(key) {
23
+ return VALID_CAPABILITIES.has(key);
24
+ }
25
+
26
+ /**
27
+ * Get skills by capability from a skills array
28
+ * @param {import('./levels.js').Skill[]} skills - Array of all skills
29
+ * @param {string} capability - The capability to filter by
30
+ * @returns {import('./levels.js').Skill[]} Skills in the specified capability
31
+ */
32
+ export function getSkillsByCapability(skills, capability) {
33
+ return skills.filter((skill) => skill.capability === capability);
34
+ }
35
+
36
+ /**
37
+ * Build a map of capability to skill IDs
38
+ * @param {import('./levels.js').Skill[]} skills - Array of all skills
39
+ * @returns {Object<string, string[]>} Map of capability to array of skill IDs
40
+ */
41
+ export function buildCapabilityToSkillsMap(skills) {
42
+ const capabilityMap = {};
43
+
44
+ for (const capability of VALID_CAPABILITIES) {
45
+ capabilityMap[capability] = [];
46
+ }
47
+
48
+ for (const skill of skills) {
49
+ if (skill.capability && capabilityMap[skill.capability]) {
50
+ capabilityMap[skill.capability].push(skill.id);
51
+ }
52
+ }
53
+
54
+ return capabilityMap;
55
+ }
56
+
57
+ /**
58
+ * Expand capability-based skill modifiers to individual skill modifiers
59
+ *
60
+ * Takes a skillModifiers object containing capability-based modifiers only
61
+ * (e.g., { delivery: 1, scale: -1 }). Individual skill modifiers are not allowed.
62
+ *
63
+ * Returns an object with individual skill modifiers expanded from capabilities.
64
+ *
65
+ * @param {Object<string, number>} skillModifiers - The capability skill modifiers
66
+ * @param {import('./levels.js').Skill[]} skills - Array of all skills (for capability lookup)
67
+ * @returns {Object<string, number>} Expanded skill modifiers with individual skill IDs
68
+ */
69
+ export function expandSkillModifiers(skillModifiers, skills) {
70
+ if (!skillModifiers) {
71
+ return {};
72
+ }
73
+
74
+ const capabilityMap = buildCapabilityToSkillsMap(skills);
75
+ const expanded = {};
76
+
77
+ // Expand capability modifiers to individual skills
78
+ for (const [key, modifier] of Object.entries(skillModifiers)) {
79
+ if (isCapability(key)) {
80
+ // This is a capability - expand to all skills in that capability
81
+ const skillIds = capabilityMap[key] || [];
82
+ for (const skillId of skillIds) {
83
+ expanded[skillId] = modifier;
84
+ }
85
+ }
86
+ // Non-capability keys are ignored (validation should catch these)
87
+ }
88
+
89
+ return expanded;
90
+ }
91
+
92
+ /**
93
+ * Extract capability modifiers from a skillModifiers object
94
+ * @param {Object<string, number>} skillModifiers - The skill modifiers
95
+ * @returns {Object<string, number>} Only the capability-based modifiers
96
+ */
97
+ export function extractCapabilityModifiers(skillModifiers) {
98
+ if (!skillModifiers) {
99
+ return {};
100
+ }
101
+
102
+ const result = {};
103
+ for (const [key, modifier] of Object.entries(skillModifiers)) {
104
+ if (isCapability(key)) {
105
+ result[key] = modifier;
106
+ }
107
+ }
108
+ return result;
109
+ }
110
+
111
+ /**
112
+ * Extract individual skill modifiers from a skillModifiers object
113
+ * @param {Object<string, number>} skillModifiers - The skill modifiers
114
+ * @returns {Object<string, number>} Only the individual skill modifiers
115
+ */
116
+ export function extractIndividualModifiers(skillModifiers) {
117
+ if (!skillModifiers) {
118
+ return {};
119
+ }
120
+
121
+ const result = {};
122
+ for (const [key, modifier] of Object.entries(skillModifiers)) {
123
+ if (!isCapability(key)) {
124
+ result[key] = modifier;
125
+ }
126
+ }
127
+ return result;
128
+ }
129
+
130
+ /**
131
+ * Get the effective skill modifier for a specific skill
132
+ *
133
+ * Looks up the capability modifier for the skill's capability.
134
+ * Returns 0 if no modifier applies.
135
+ *
136
+ * @param {string} skillId - The skill ID to get modifier for
137
+ * @param {Object<string, number>} skillModifiers - The capability skill modifiers
138
+ * @param {import('./levels.js').Skill[]} skills - Array of all skills
139
+ * @returns {number} The effective modifier for this skill
140
+ */
141
+ export function resolveSkillModifier(skillId, skillModifiers, skills) {
142
+ if (!skillModifiers) {
143
+ return 0;
144
+ }
145
+
146
+ // Find the skill's capability
147
+ const skill = skills.find((s) => s.id === skillId);
148
+ if (!skill || !skill.capability) {
149
+ return 0;
150
+ }
151
+
152
+ // Check for capability modifier
153
+ if (skill.capability in skillModifiers) {
154
+ return skillModifiers[skill.capability];
155
+ }
156
+
157
+ return 0;
158
+ }
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Unified Profile Derivation
3
+ *
4
+ * Shared functions for deriving skill and behaviour profiles for both
5
+ * human jobs and AI agents. This module provides:
6
+ *
7
+ * 1. Filtering functions - reusable filters for skills and behaviours
8
+ * 2. Sorting functions - sort by level/maturity for display
9
+ * 3. prepareBaseProfile() - shared profile derivation used by both job.js and agent.js
10
+ *
11
+ * The core derivation (deriveSkillMatrix, deriveBehaviourProfile) remains in
12
+ * derivation.js. This module adds post-processing for specific use cases.
13
+ */
14
+
15
+ import { SKILL_LEVEL_ORDER, BEHAVIOUR_MATURITY_ORDER } from "./levels.js";
16
+ import {
17
+ deriveSkillMatrix,
18
+ deriveBehaviourProfile,
19
+ deriveResponsibilities,
20
+ } from "./derivation.js";
21
+
22
+ // =============================================================================
23
+ // Skill Filters
24
+ // =============================================================================
25
+
26
+ /**
27
+ * Build set of capabilities with positive track modifiers
28
+ * @param {Object} track - Track definition
29
+ * @returns {Set<string>} Set of capability IDs with positive modifiers
30
+ */
31
+ export function getPositiveTrackCapabilities(track) {
32
+ return new Set(
33
+ Object.entries(track.skillModifiers || {})
34
+ .filter(([_, modifier]) => modifier > 0)
35
+ .map(([capability]) => capability),
36
+ );
37
+ }
38
+
39
+ /**
40
+ * Filter out human-only skills
41
+ * Human-only skills are those requiring human presence/experience
42
+ * @param {Array} skillMatrix - Skill matrix entries
43
+ * @returns {Array} Filtered skill matrix
44
+ */
45
+ export function filterHumanOnlySkills(skillMatrix) {
46
+ return skillMatrix.filter((entry) => !entry.isHumanOnly);
47
+ }
48
+
49
+ /**
50
+ * Filter broad skills without positive track modifier
51
+ * Keeps broad skills only if their capability has a positive track modifier
52
+ * @param {Array} skillMatrix - Skill matrix entries
53
+ * @param {Set<string>} positiveCapabilities - Capabilities with positive modifiers
54
+ * @returns {Array} Filtered skill matrix
55
+ */
56
+ export function filterBroadWithoutPositiveModifier(
57
+ skillMatrix,
58
+ positiveCapabilities,
59
+ ) {
60
+ return skillMatrix.filter(
61
+ (entry) =>
62
+ entry.type !== "broad" || positiveCapabilities.has(entry.capability),
63
+ );
64
+ }
65
+
66
+ /**
67
+ * Apply agent-specific skill filters
68
+ * Combines humanOnly and broad skill filtering for agent profiles
69
+ * @param {Array} skillMatrix - Skill matrix entries
70
+ * @param {Object} track - Track definition (for modifier lookup)
71
+ * @returns {Array} Filtered skill matrix
72
+ */
73
+ export function filterSkillsForAgent(skillMatrix, track) {
74
+ const positiveCapabilities = getPositiveTrackCapabilities(track);
75
+
76
+ return skillMatrix.filter((entry) => {
77
+ // Exclude human-only skills
78
+ if (entry.isHumanOnly) {
79
+ return false;
80
+ }
81
+ // For broad skills, only include if capability has positive modifier
82
+ if (entry.type === "broad") {
83
+ return positiveCapabilities.has(entry.capability);
84
+ }
85
+ // Include primary, secondary, and track-added skills
86
+ return true;
87
+ });
88
+ }
89
+
90
+ // =============================================================================
91
+ // Sorting Functions
92
+ // =============================================================================
93
+
94
+ /**
95
+ * Sort skills by level (highest first)
96
+ * Used for agent profiles where top skills should appear first
97
+ * @param {Array} skillMatrix - Skill matrix entries
98
+ * @returns {Array} Sorted skill matrix (new array)
99
+ */
100
+ export function sortByLevelDescending(skillMatrix) {
101
+ return [...skillMatrix].sort((a, b) => {
102
+ const aIndex = SKILL_LEVEL_ORDER.indexOf(a.level);
103
+ const bIndex = SKILL_LEVEL_ORDER.indexOf(b.level);
104
+ return bIndex - aIndex;
105
+ });
106
+ }
107
+
108
+ /**
109
+ * Sort behaviours by maturity (highest first)
110
+ * Used for agent profiles where top behaviours should appear first
111
+ * @param {Array} behaviourProfile - Behaviour profile entries
112
+ * @returns {Array} Sorted behaviour profile (new array)
113
+ */
114
+ export function sortByMaturityDescending(behaviourProfile) {
115
+ return [...behaviourProfile].sort((a, b) => {
116
+ const aIndex = BEHAVIOUR_MATURITY_ORDER.indexOf(a.maturity);
117
+ const bIndex = BEHAVIOUR_MATURITY_ORDER.indexOf(b.maturity);
118
+ return bIndex - aIndex;
119
+ });
120
+ }
121
+
122
+ // =============================================================================
123
+ // Profile Derivation
124
+ // =============================================================================
125
+
126
+ /**
127
+ * @typedef {Object} ProfileOptions
128
+ * @property {boolean} [excludeHumanOnly=false] - Filter out human-only skills
129
+ * @property {boolean} [excludeBroadWithoutPositiveModifier=false] - Filter broad skills
130
+ * @property {boolean} [sortByLevel=false] - Sort skills by level descending
131
+ * @property {boolean} [sortByMaturity=false] - Sort behaviours by maturity descending
132
+ */
133
+
134
+ /**
135
+ * @typedef {Object} BaseProfile
136
+ * @property {Array} skillMatrix - Derived skill matrix
137
+ * @property {Array} behaviourProfile - Derived behaviour profile
138
+ * @property {Array} derivedResponsibilities - Derived responsibilities (if capabilities provided)
139
+ * @property {Object} discipline - The discipline
140
+ * @property {Object} track - The track
141
+ * @property {Object} grade - The grade
142
+ */
143
+
144
+ /**
145
+ * Prepare a base profile shared by jobs and agents
146
+ *
147
+ * This is the unified entry point for profile derivation. Both human jobs
148
+ * and AI agents use this function, with different options:
149
+ *
150
+ * - Human jobs: No filtering, default sorting by type
151
+ * - AI agents: Filter humanOnly, filter broad without positive modifier, sort by level
152
+ *
153
+ * @param {Object} params
154
+ * @param {Object} params.discipline - The discipline
155
+ * @param {Object} params.track - The track
156
+ * @param {Object} params.grade - The grade
157
+ * @param {Array} params.skills - All available skills
158
+ * @param {Array} params.behaviours - All available behaviours
159
+ * @param {Array} [params.capabilities] - Optional capabilities for responsibility derivation
160
+ * @param {ProfileOptions} [params.options={}] - Filtering and sorting options
161
+ * @returns {BaseProfile} The prepared profile
162
+ */
163
+ export function prepareBaseProfile({
164
+ discipline,
165
+ track,
166
+ grade,
167
+ skills,
168
+ behaviours,
169
+ capabilities,
170
+ options = {},
171
+ }) {
172
+ const {
173
+ excludeHumanOnly = false,
174
+ excludeBroadWithoutPositiveModifier = false,
175
+ sortByLevel = false,
176
+ sortByMaturity = false,
177
+ } = options;
178
+
179
+ // Core derivation
180
+ let skillMatrix = deriveSkillMatrix({ discipline, grade, track, skills });
181
+ let behaviourProfile = deriveBehaviourProfile({
182
+ discipline,
183
+ grade,
184
+ track,
185
+ behaviours,
186
+ });
187
+
188
+ // Apply skill filters
189
+ if (excludeHumanOnly || excludeBroadWithoutPositiveModifier) {
190
+ const positiveCapabilities = getPositiveTrackCapabilities(track);
191
+
192
+ if (excludeHumanOnly) {
193
+ skillMatrix = filterHumanOnlySkills(skillMatrix);
194
+ }
195
+ if (excludeBroadWithoutPositiveModifier) {
196
+ skillMatrix = filterBroadWithoutPositiveModifier(
197
+ skillMatrix,
198
+ positiveCapabilities,
199
+ );
200
+ }
201
+ }
202
+
203
+ // Apply sorting
204
+ if (sortByLevel) {
205
+ skillMatrix = sortByLevelDescending(skillMatrix);
206
+ }
207
+ if (sortByMaturity) {
208
+ behaviourProfile = sortByMaturityDescending(behaviourProfile);
209
+ }
210
+
211
+ // Derive responsibilities if capabilities provided
212
+ let derivedResponsibilities = [];
213
+ if (capabilities && capabilities.length > 0) {
214
+ derivedResponsibilities = deriveResponsibilities({
215
+ skillMatrix,
216
+ capabilities,
217
+ track,
218
+ });
219
+ }
220
+
221
+ return {
222
+ skillMatrix,
223
+ behaviourProfile,
224
+ derivedResponsibilities,
225
+ discipline,
226
+ track,
227
+ grade,
228
+ };
229
+ }
230
+
231
+ /**
232
+ * Preset options for agent profile derivation
233
+ * Excludes human-only skills, excludes broad skills without positive modifiers,
234
+ * and sorts by level/maturity descending
235
+ */
236
+ export const AGENT_PROFILE_OPTIONS = {
237
+ excludeHumanOnly: true,
238
+ excludeBroadWithoutPositiveModifier: true,
239
+ sortByLevel: true,
240
+ sortByMaturity: true,
241
+ };
242
+
243
+ /**
244
+ * Prepare a profile optimized for agent generation
245
+ * Convenience function that applies AGENT_PROFILE_OPTIONS
246
+ * @param {Object} params - Same as prepareBaseProfile, without options
247
+ * @returns {BaseProfile} The prepared profile
248
+ */
249
+ export function prepareAgentProfile({
250
+ discipline,
251
+ track,
252
+ grade,
253
+ skills,
254
+ behaviours,
255
+ capabilities,
256
+ }) {
257
+ return prepareBaseProfile({
258
+ discipline,
259
+ track,
260
+ grade,
261
+ skills,
262
+ behaviours,
263
+ capabilities,
264
+ options: AGENT_PROFILE_OPTIONS,
265
+ });
266
+ }