@forwardimpact/model 0.4.0 → 0.7.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/README.md CHANGED
@@ -29,8 +29,8 @@ import {
29
29
  deriveSkillMatrix,
30
30
  deriveBehaviourProfile,
31
31
  } from "@forwardimpact/model/derivation";
32
- import { prepareAgentProfile } from "@forwardimpact/model/agent";
33
- import { selectInterviewQuestions } from "@forwardimpact/model/interview";
32
+ import { prepareAgentProfile } from "@forwardimpact/model/profile";
33
+ import { deriveInterviewQuestions } from "@forwardimpact/model/interview";
34
34
  import { analyzeProgression } from "@forwardimpact/model/progression";
35
35
  ```
36
36
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forwardimpact/model",
3
- "version": "0.4.0",
3
+ "version": "0.7.0",
4
4
  "description": "Derivation engine for roles, skills, and AI agent profiles",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -10,25 +10,27 @@
10
10
  },
11
11
  "homepage": "https://www.forwardimpact.team/model",
12
12
  "type": "module",
13
- "main": "lib/index.js",
13
+ "main": "src/index.js",
14
14
  "files": [
15
- "lib/"
15
+ "src/"
16
16
  ],
17
17
  "exports": {
18
- ".": "./lib/index.js",
19
- "./derivation": "./lib/derivation.js",
20
- "./modifiers": "./lib/modifiers.js",
21
- "./agent": "./lib/agent.js",
22
- "./interview": "./lib/interview.js",
23
- "./job": "./lib/job.js",
24
- "./job-cache": "./lib/job-cache.js",
25
- "./checklist": "./lib/checklist.js",
26
- "./matching": "./lib/matching.js",
27
- "./profile": "./lib/profile.js",
28
- "./progression": "./lib/progression.js"
18
+ ".": "./src/index.js",
19
+ "./derivation": "./src/derivation.js",
20
+ "./modifiers": "./src/modifiers.js",
21
+ "./agent": "./src/agent.js",
22
+ "./interview": "./src/interview.js",
23
+ "./job": "./src/job.js",
24
+ "./job-cache": "./src/job-cache.js",
25
+ "./checklist": "./src/checklist.js",
26
+ "./matching": "./src/matching.js",
27
+ "./profile": "./src/profile.js",
28
+ "./progression": "./src/progression.js",
29
+ "./policies": "./src/policies/index.js",
30
+ "./toolkit": "./src/toolkit.js"
29
31
  },
30
32
  "dependencies": {
31
- "@forwardimpact/schema": "^0.4.0"
33
+ "@forwardimpact/schema": "^0.6.0"
32
34
  },
33
35
  "engines": {
34
36
  "node": ">=18.0.0"
@@ -19,12 +19,13 @@
19
19
  */
20
20
 
21
21
  import { deriveSkillMatrix, deriveBehaviourProfile } from "./derivation.js";
22
- import { deriveChecklist, formatChecklistMarkdown } from "./checklist.js";
22
+ import { deriveChecklist } from "./checklist.js";
23
23
  import {
24
- filterSkillsForAgent,
25
- sortByLevelDescending,
26
- sortByMaturityDescending,
27
- } from "./profile.js";
24
+ filterAgentSkills,
25
+ sortAgentSkills,
26
+ sortAgentBehaviours,
27
+ } from "./policies/composed.js";
28
+ import { ORDER_AGENT_STAGE } from "./policies/orderings.js";
28
29
  import { SkillLevel } from "@forwardimpact/schema/levels";
29
30
 
30
31
  /**
@@ -123,9 +124,9 @@ export function deriveAgentSkills({ discipline, track, grade, skills }) {
123
124
  skills,
124
125
  });
125
126
 
126
- // Apply agent-specific filtering and sorting
127
- const filtered = filterSkillsForAgent(skillMatrix);
128
- return sortByLevelDescending(filtered);
127
+ // Apply agent-specific filtering and sorting using policies
128
+ const filtered = filterAgentSkills(skillMatrix);
129
+ return sortAgentSkills(filtered);
129
130
  }
130
131
 
131
132
  /**
@@ -151,7 +152,7 @@ export function deriveAgentBehaviours({
151
152
  behaviours,
152
153
  });
153
154
 
154
- return sortByMaturityDescending(profile);
155
+ return sortAgentBehaviours(profile);
155
156
  }
156
157
 
157
158
  /**
@@ -229,7 +230,7 @@ function buildWorkingStyleFromBehaviours(
229
230
  * @param {Array} stages - All stage entities
230
231
  * @returns {Object} Skill with frontmatter, title, stages array, reference, dirname
231
232
  */
232
- export function generateSkillMd(skillData, stages) {
233
+ export function generateSkillMarkdown(skillData, stages) {
233
234
  const { agent, name } = skillData;
234
235
 
235
236
  if (!agent) {
@@ -272,10 +273,11 @@ export function generateSkillMd(skillData, stages) {
272
273
  },
273
274
  );
274
275
 
275
- // Sort stages in order: plan, code, review
276
- const stageOrder = ["plan", "code", "review"];
276
+ // Sort stages using canonical ordering from policies
277
277
  stagesArray.sort(
278
- (a, b) => stageOrder.indexOf(a.stageId) - stageOrder.indexOf(b.stageId),
278
+ (a, b) =>
279
+ ORDER_AGENT_STAGE.indexOf(a.stageId) -
280
+ ORDER_AGENT_STAGE.indexOf(b.stageId),
279
281
  );
280
282
 
281
283
  return {
@@ -308,7 +310,6 @@ function estimateBodyDataLength(bodyData) {
308
310
  "priority",
309
311
  "roleContext",
310
312
  "workingStyle",
311
- "beforeHandoff",
312
313
  ];
313
314
  for (const field of stringFields) {
314
315
  if (bodyData[field]) {
@@ -487,7 +488,7 @@ function getChecklistStage(stageId) {
487
488
  * @param {Array} params.derivedBehaviours - Behaviours sorted by maturity
488
489
  * @param {Array} params.agentBehaviours - Agent behaviour definitions
489
490
  * @param {Array} params.skills - All skill definitions (for agent section lookup)
490
- * @param {string} params.checklistMarkdown - Pre-formatted checklist markdown
491
+ * @param {Array<{skill: Object, capability: Object, items: string[]}>} params.checklist - Raw checklist from deriveChecklist
491
492
  * @param {Array<{id: string, name: string, description: string}>} [params.agentIndex] - List of all available agents
492
493
  * @returns {Object} Structured profile body data
493
494
  */
@@ -501,7 +502,7 @@ function buildStageProfileBodyData({
501
502
  derivedBehaviours,
502
503
  agentBehaviours,
503
504
  skills,
504
- checklistMarkdown,
505
+ checklist,
505
506
  agentIndex,
506
507
  }) {
507
508
  const name = `${humanDiscipline.specialization || humanDiscipline.name} - ${humanTrack.name}`;
@@ -563,7 +564,7 @@ function buildStageProfileBodyData({
563
564
  skillIndex,
564
565
  roleContext,
565
566
  workingStyles,
566
- beforeHandoff: checklistMarkdown || null,
567
+ beforeHandoff: checklist || [],
567
568
  constraints,
568
569
  agentIndex: filteredAgentIndex,
569
570
  hasAgentIndex: filteredAgentIndex.length > 0,
@@ -709,10 +710,7 @@ export function generateStageAgentProfile({
709
710
  // Build description using shared helper
710
711
  const description = buildAgentDescription(discipline, track, stage);
711
712
 
712
- // Format checklist as markdown
713
- const checklistMarkdown = formatChecklistMarkdown(agent.checklist);
714
-
715
- // Build structured profile body data
713
+ // Build structured profile body data (pass raw checklist for template iteration)
716
714
  const bodyData = buildStageProfileBodyData({
717
715
  stage,
718
716
  humanDiscipline: discipline,
@@ -723,7 +721,7 @@ export function generateStageAgentProfile({
723
721
  derivedBehaviours: agent.derivedBehaviours,
724
722
  agentBehaviours,
725
723
  skills,
726
- checklistMarkdown,
724
+ checklist: agent.checklist,
727
725
  agentIndex,
728
726
  });
729
727
 
@@ -18,6 +18,7 @@ import {
18
18
  } from "@forwardimpact/schema/levels";
19
19
 
20
20
  import { resolveSkillModifier } from "./modifiers.js";
21
+ import { ORDER_SKILL_TYPE } from "./policies/orderings.js";
21
22
 
22
23
  /**
23
24
  * Build a Map of skillId → skillType for a discipline
@@ -226,14 +227,10 @@ export function deriveSkillMatrix({ discipline, grade, track = null, skills }) {
226
227
  }
227
228
 
228
229
  // Sort by type (primary first, then secondary, then broad, then track) and then by name
229
- const typeOrder = {
230
- [SkillType.PRIMARY]: 0,
231
- [SkillType.SECONDARY]: 1,
232
- [SkillType.BROAD]: 2,
233
- [SkillType.TRACK]: 3,
234
- };
230
+ // Use ORDER_SKILL_TYPE from policies for canonical ordering
235
231
  matrix.sort((a, b) => {
236
- const typeCompare = typeOrder[a.type] - typeOrder[b.type];
232
+ const typeCompare =
233
+ ORDER_SKILL_TYPE.indexOf(a.type) - ORDER_SKILL_TYPE.indexOf(b.type);
237
234
  if (typeCompare !== 0) return typeCompare;
238
235
  return a.skillName.localeCompare(b.skillName);
239
236
  });
@@ -485,7 +482,7 @@ export function deriveResponsibilities({
485
482
  capability: capabilityId,
486
483
  capabilityName: capability.name,
487
484
  emojiIcon: capability.emojiIcon || "💡",
488
- displayOrder: capability.displayOrder ?? 999,
485
+ ordinalRank: capability.ordinalRank ?? 999,
489
486
  responsibility: responsibilityText,
490
487
  level,
491
488
  levelIndex: SKILL_LEVEL_ORDER.indexOf(level),
@@ -498,7 +495,7 @@ export function deriveResponsibilities({
498
495
  if (b.levelIndex !== a.levelIndex) {
499
496
  return b.levelIndex - a.levelIndex;
500
497
  }
501
- return a.displayOrder - b.displayOrder;
498
+ return a.ordinalRank - b.ordinalRank;
502
499
  });
503
500
 
504
501
  // Remove levelIndex from output (internal use only)
@@ -33,11 +33,11 @@ export {
33
33
 
34
34
  // Job caching
35
35
  export {
36
- makeJobKey,
36
+ buildJobKey,
37
37
  getOrCreateJob,
38
- clearJobCache,
39
- invalidateJob,
40
- getCacheSize,
38
+ clearCache,
39
+ invalidateCachedJob,
40
+ getCachedJobCount,
41
41
  } from "./job-cache.js";
42
42
 
43
43
  // Modifiers
@@ -45,17 +45,17 @@ export {
45
45
  isCapability,
46
46
  getSkillsByCapability,
47
47
  buildCapabilityToSkillsMap,
48
- expandSkillModifiers,
48
+ expandModifiersToSkills,
49
49
  extractCapabilityModifiers,
50
- extractIndividualModifiers,
50
+ extractSkillModifiers,
51
51
  resolveSkillModifier,
52
52
  } from "./modifiers.js";
53
53
 
54
54
  // Matching
55
55
  export {
56
56
  MatchTier,
57
- MATCH_TIER_CONFIG,
58
- classifyMatchTier,
57
+ CONFIG_MATCH_TIER,
58
+ classifyMatch,
59
59
  GAP_SCORES,
60
60
  calculateGapScore,
61
61
  calculateJobMatch,
@@ -87,16 +87,18 @@ export {
87
87
  deriveShortInterview,
88
88
  deriveBehaviourQuestions,
89
89
  deriveFocusedInterview,
90
+ deriveMissionFitInterview,
91
+ deriveDecompositionInterview,
92
+ deriveStakeholderInterview,
90
93
  } from "./interview.js";
91
94
 
92
95
  // Agent generation
93
96
  export {
94
97
  deriveReferenceGrade,
95
98
  getDisciplineAbbreviation,
96
- toKebabCase,
97
99
  deriveAgentSkills,
98
100
  deriveAgentBehaviours,
99
- generateSkillMd,
101
+ generateSkillMarkdown,
100
102
  validateAgentProfile,
101
103
  validateAgentSkill,
102
104
  deriveHandoffs,
@@ -111,15 +113,55 @@ export { deriveChecklist, formatChecklistMarkdown } from "./checklist.js";
111
113
  // Toolkit
112
114
  export { deriveToolkit } from "./toolkit.js";
113
115
 
114
- // Profile filtering (for agents)
116
+ // Profile derivation
115
117
  export {
116
118
  getPositiveTrackCapabilities,
117
- filterHumanOnlySkills,
118
- filterByHighestLevel,
119
- filterSkillsForAgent,
120
- sortByLevelDescending,
121
- sortByMaturityDescending,
122
119
  prepareBaseProfile,
123
- AGENT_PROFILE_OPTIONS,
124
120
  prepareAgentProfile,
125
121
  } from "./profile.js";
122
+
123
+ // Policies - re-export key items for convenience
124
+ export {
125
+ // Thresholds
126
+ THRESHOLD_MATCH_STRONG,
127
+ THRESHOLD_MATCH_GOOD,
128
+ THRESHOLD_MATCH_STRETCH,
129
+ THRESHOLD_MATCH_ASPIRATIONAL,
130
+ SCORE_GAP,
131
+ WEIGHT_SKILL_TYPE,
132
+ WEIGHT_CAPABILITY_BOOST,
133
+ // Predicates
134
+ isHumanOnly,
135
+ isAgentEligible,
136
+ isPrimary,
137
+ isSecondary,
138
+ isBroad,
139
+ isTrack,
140
+ isCore,
141
+ isSupporting,
142
+ hasMinLevel,
143
+ allOf,
144
+ anyOf,
145
+ not,
146
+ // Filters
147
+ filterHighestLevel,
148
+ filterAboveAwareness,
149
+ applyFilters,
150
+ composeFilters,
151
+ // Orderings
152
+ ORDER_SKILL_TYPE,
153
+ ORDER_STAGE,
154
+ ORDER_AGENT_STAGE,
155
+ compareByLevelDesc,
156
+ compareByType,
157
+ compareBySkillPriority,
158
+ compareByMaturityDesc,
159
+ compareByBehaviourPriority,
160
+ // Composed policies
161
+ filterAgentSkills,
162
+ filterToolkitSkills,
163
+ sortAgentSkills,
164
+ sortAgentBehaviours,
165
+ prepareAgentSkillMatrix,
166
+ prepareAgentBehaviourProfile,
167
+ } from "./policies/index.js";