@forwardimpact/model 0.7.1 → 0.8.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/model",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Derivation engine for roles, skills, and AI agent profiles",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/agent.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  sortAgentBehaviours,
27
27
  focusAgentSkills,
28
28
  } from "./policies/composed.js";
29
- import { ORDER_AGENT_STAGE } from "./policies/orderings.js";
29
+ import { compareByStageOrder } from "./policies/orderings.js";
30
30
  import { LIMIT_AGENT_WORKING_STYLES } from "./policies/thresholds.js";
31
31
  import { SkillLevel } from "@forwardimpact/schema/levels";
32
32
 
@@ -269,18 +269,15 @@ export function generateSkillMarkdown(skillData, stages) {
269
269
  stageName,
270
270
  nextStageName,
271
271
  focus: stageData.focus,
272
- activities: stageData.activities || [],
273
- ready: stageData.ready || [],
272
+ readChecklist: stageData.readChecklist || [],
273
+ confirmChecklist: stageData.confirmChecklist || [],
274
274
  };
275
275
  },
276
276
  );
277
277
 
278
- // Sort stages using canonical ordering from policies
279
- stagesArray.sort(
280
- (a, b) =>
281
- ORDER_AGENT_STAGE.indexOf(a.stageId) -
282
- ORDER_AGENT_STAGE.indexOf(b.stageId),
283
- );
278
+ // Sort stages using canonical ordering from loaded stage data
279
+ const stageComparator = compareByStageOrder(stages);
280
+ stagesArray.sort(stageComparator);
284
281
 
285
282
  return {
286
283
  frontmatter: {
@@ -433,9 +430,9 @@ export function deriveHandoffs({ stage, discipline, track, stages }) {
433
430
  const baseName = `${abbrev}-${toKebabCase(track.id)}`;
434
431
 
435
432
  return stage.handoffs.map((handoff) => {
436
- // Find the target stage to get its entry criteria
433
+ // Find the target stage to get its confirmChecklist
437
434
  const targetStage = stages.find((s) => s.id === handoff.targetStage);
438
- const entryCriteria = targetStage?.entryCriteria || [];
435
+ const confirmChecklist = targetStage?.confirmChecklist || [];
439
436
 
440
437
  // Build rich prompt - formatted for single-line display
441
438
  const promptParts = [handoff.prompt];
@@ -445,9 +442,9 @@ export function deriveHandoffs({ stage, discipline, track, stages }) {
445
442
  `Summarize what was completed in the ${stage.name} stage.`,
446
443
  );
447
444
 
448
- // Add entry criteria from target stage with inline numbered list
449
- if (entryCriteria.length > 0) {
450
- const formattedCriteria = entryCriteria
445
+ // Add confirm checklist from target stage with inline numbered list
446
+ if (confirmChecklist.length > 0) {
447
+ const formattedCriteria = confirmChecklist
451
448
  .map((item, index) => `(${index + 1}) ${item}`)
452
449
  .join(", ");
453
450
  promptParts.push(
@@ -560,12 +557,15 @@ function buildStageProfileBodyData({
560
557
  return {
561
558
  title: `${name} - ${stageName} Agent`,
562
559
  stageDescription: stage.description,
560
+ stageId: stage.id,
561
+ stageName,
562
+ isOnboard: stage.id === "onboard",
563
563
  identity: identity.trim(),
564
564
  priority: priority ? priority.trim() : null,
565
565
  skillIndex,
566
566
  roleContext,
567
567
  workingStyles,
568
- beforeHandoff: checklist || [],
568
+ confirmChecklist: checklist || [],
569
569
  constraints,
570
570
  agentIndex: filteredAgentIndex,
571
571
  hasAgentIndex: filteredAgentIndex.length > 0,
package/src/checklist.js CHANGED
@@ -1,14 +1,12 @@
1
1
  /**
2
2
  * Checklist Derivation
3
3
  *
4
- * Checklists are derived from skills with agent.stages.{stage}.ready criteria.
4
+ * Checklists are derived from skills with agent.stages.{stage}.confirmChecklist criteria.
5
5
  * Each skill defines its own readiness criteria for stage transitions.
6
6
  *
7
- * Checklist = Stage × Skill Matrix × Skill Ready Criteria
7
+ * Checklist = Stage × Skill Matrix × Skill Confirm Checklist
8
8
  */
9
9
 
10
- import { CHECKLIST_STAGE_MAP } from "./policies/orderings.js";
11
-
12
10
  /**
13
11
  * Derive checklist items for a specific stage
14
12
  * Returns skills grouped by capability with their ready criteria
@@ -26,11 +24,6 @@ export function deriveChecklist({
26
24
  skills,
27
25
  capabilities,
28
26
  }) {
29
- const targetStage = CHECKLIST_STAGE_MAP[stageId];
30
- if (!targetStage) {
31
- return [];
32
- }
33
-
34
27
  // Build skill lookup
35
28
  const skillById = new Map(skills.map((s) => [s.id, s]));
36
29
 
@@ -45,8 +38,12 @@ export function deriveChecklist({
45
38
  continue;
46
39
  }
47
40
 
48
- const stageData = skill.agent.stages[targetStage];
49
- if (!stageData || !stageData.ready || stageData.ready.length === 0) {
41
+ const stageData = skill.agent.stages[stageId];
42
+ if (
43
+ !stageData ||
44
+ !stageData.confirmChecklist ||
45
+ stageData.confirmChecklist.length === 0
46
+ ) {
50
47
  continue;
51
48
  }
52
49
 
@@ -66,7 +63,7 @@ export function deriveChecklist({
66
63
  name: capability.name,
67
64
  emojiIcon: capability.emojiIcon,
68
65
  },
69
- items: stageData.ready,
66
+ items: stageData.confirmChecklist,
70
67
  });
71
68
  }
72
69
 
package/src/index.js CHANGED
@@ -177,9 +177,8 @@ export {
177
177
  composeFilters,
178
178
  // Orderings
179
179
  ORDER_SKILL_TYPE,
180
- ORDER_STAGE,
181
- ORDER_AGENT_STAGE,
182
- CHECKLIST_STAGE_MAP,
180
+ getStageOrder,
181
+ compareByStageOrder,
183
182
  compareByLevelDesc,
184
183
  compareByType,
185
184
  compareBySkillPriority,
package/src/job.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  import { deriveChecklist } from "./checklist.js";
15
15
  import { deriveToolkit } from "./toolkit.js";
16
16
  import { getOrCreateJob } from "./job-cache.js";
17
+ import { getStageOrder } from "@forwardimpact/schema/levels";
17
18
 
18
19
  /**
19
20
  * @typedef {Object} JobDetailView
@@ -43,6 +44,7 @@ import { getOrCreateJob } from "./job-cache.js";
43
44
  * @param {Array} params.behaviours
44
45
  * @param {Array} params.drivers
45
46
  * @param {Array} [params.capabilities]
47
+ * @param {Array} [params.stages] - Loaded stages for checklist derivation
46
48
  * @returns {JobDetailView|null}
47
49
  */
48
50
  export function prepareJobDetail({
@@ -53,6 +55,7 @@ export function prepareJobDetail({
53
55
  behaviours,
54
56
  drivers,
55
57
  capabilities,
58
+ stages,
56
59
  }) {
57
60
  // Track is optional (null = generalist)
58
61
  if (!discipline || !grade) return null;
@@ -73,10 +76,10 @@ export function prepareJobDetail({
73
76
  drivers,
74
77
  });
75
78
 
76
- // Derive checklists for each stage
79
+ // Derive checklists for each stage from loaded stage data
77
80
  const checklists = {};
78
- if (capabilities) {
79
- const stageIds = ["plan", "code"];
81
+ if (capabilities && stages) {
82
+ const stageIds = getStageOrder(stages);
80
83
  for (const stageId of stageIds) {
81
84
  checklists[stageId] = deriveChecklist({
82
85
  stageId,
@@ -117,9 +117,9 @@ export {
117
117
  export {
118
118
  // Canonical orders
119
119
  ORDER_SKILL_TYPE,
120
- ORDER_STAGE,
121
- ORDER_AGENT_STAGE,
122
- CHECKLIST_STAGE_MAP,
120
+ // Data-driven stage ordering
121
+ getStageOrder,
122
+ compareByStageOrder,
123
123
  // Skill comparators
124
124
  compareByLevelDesc,
125
125
  compareByLevelAsc,
@@ -12,8 +12,12 @@ import {
12
12
  getSkillLevelIndex,
13
13
  getBehaviourMaturityIndex,
14
14
  getCapabilityOrder,
15
+ getStageOrder,
15
16
  } from "@forwardimpact/schema/levels";
16
17
 
18
+ // Re-export getStageOrder for consumers
19
+ export { getStageOrder };
20
+
17
21
  // =============================================================================
18
22
  // Canonical Orderings
19
23
  // =============================================================================
@@ -24,27 +28,27 @@ import {
24
28
  */
25
29
  export const ORDER_SKILL_TYPE = ["primary", "secondary", "broad", "track"];
26
30
 
27
- /**
28
- * Engineering lifecycle stages in execution order
29
- */
30
- export const ORDER_STAGE = ["specify", "plan", "code", "review", "deploy"];
31
-
32
- /**
33
- * Agent stage ordering (subset used for agent generation)
34
- */
35
- export const ORDER_AGENT_STAGE = ["plan", "code", "review"];
31
+ // =============================================================================
32
+ // Stage Comparators
33
+ // =============================================================================
36
34
 
37
35
  /**
38
- * Stage-to-handoff mapping for checklist derivation
36
+ * Create a comparator for sorting by stage lifecycle order
37
+ *
38
+ * The returned comparator uses the canonical order from loaded stage data,
39
+ * making the ordering data-driven rather than hardcoded.
39
40
  *
40
- * Maps stage IDs to the stage whose `.ready` criteria should be shown
41
- * before leaving that stage.
41
+ * @param {Object[]} stages - Loaded stages array from stages.yaml
42
+ * @returns {(a: Object, b: Object) => number} Comparator function
42
43
  */
43
- export const CHECKLIST_STAGE_MAP = {
44
- plan: "plan",
45
- code: "code",
46
- review: "review",
47
- };
44
+ export function compareByStageOrder(stages) {
45
+ const order = getStageOrder(stages);
46
+ return (a, b) => {
47
+ const stageA = a.stageId || a.id || "";
48
+ const stageB = b.stageId || b.id || "";
49
+ return order.indexOf(stageA) - order.indexOf(stageB);
50
+ };
51
+ }
48
52
 
49
53
  // =============================================================================
50
54
  // Skill Comparators