@forwardimpact/schema 0.6.0 → 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.
@@ -28,14 +28,14 @@ fit:handoffs a rdf:Property ;
28
28
  rdfs:domain fit:Stage ;
29
29
  rdfs:range fit:Handoff .
30
30
 
31
- fit:entryCriteria a rdf:Property ;
32
- rdfs:label "entryCriteria"@en ;
33
- rdfs:comment "Conditions that must be met before entering this stage"@en ;
31
+ fit:readChecklist a rdf:Property ;
32
+ rdfs:label "readChecklist"@en ;
33
+ rdfs:comment "Read-Then-Do Checklist: steps to follow in order during this stage"@en ;
34
34
  rdfs:range xsd:string .
35
35
 
36
- fit:exitCriteria a rdf:Property ;
37
- rdfs:label "exitCriteria"@en ;
38
- rdfs:comment "Conditions that must be met before leaving this stage"@en ;
36
+ fit:confirmChecklist a rdf:Property ;
37
+ rdfs:label "confirmChecklist"@en ;
38
+ rdfs:comment "Do-Then-Confirm Checklist: items to verify before handing off to next stage"@en ;
39
39
  rdfs:range xsd:string .
40
40
 
41
41
  fit:summary a rdf:Property ;
@@ -74,7 +74,7 @@ fit:StageShape a sh:NodeShape ;
74
74
  sh:targetClass fit:Stage ;
75
75
  sh:property [
76
76
  sh:path fit:id ;
77
- sh:in ( "specify" "plan" "code" "review" "deploy" ) ;
77
+ sh:in ( "specify" "plan" "onboard" "code" "review" "deploy" ) ;
78
78
  sh:minCount 1 ;
79
79
  sh:maxCount 1 ;
80
80
  sh:name "id" ;
@@ -122,16 +122,16 @@ fit:StageShape a sh:NodeShape ;
122
122
  sh:description "Restrictions on behaviour in this stage" ;
123
123
  ] ;
124
124
  sh:property [
125
- sh:path fit:entryCriteria ;
125
+ sh:path fit:readChecklist ;
126
126
  sh:datatype xsd:string ;
127
- sh:name "entryCriteria" ;
128
- sh:description "Conditions that must be met before entering this stage" ;
127
+ sh:name "readChecklist" ;
128
+ sh:description "Read-Then-Do Checklist: steps to follow in order during this stage" ;
129
129
  ] ;
130
130
  sh:property [
131
- sh:path fit:exitCriteria ;
131
+ sh:path fit:confirmChecklist ;
132
132
  sh:datatype xsd:string ;
133
- sh:name "exitCriteria" ;
134
- sh:description "Conditions that must be met before leaving this stage" ;
133
+ sh:name "confirmChecklist" ;
134
+ sh:description "Do-Then-Confirm Checklist: items to verify before handing off to next stage" ;
135
135
  ] .
136
136
 
137
137
  # -----------------------------------------------------------------------------
@@ -142,7 +142,7 @@ fit:HandoffShape a sh:NodeShape ;
142
142
  sh:targetClass fit:Handoff ;
143
143
  sh:property [
144
144
  sh:path fit:targetStage ;
145
- sh:in ( "specify" "plan" "code" "review" "deploy" ) ;
145
+ sh:in ( "specify" "plan" "onboard" "code" "review" "deploy" ) ;
146
146
  sh:minCount 1 ;
147
147
  sh:maxCount 1 ;
148
148
  sh:name "targetStage" ;
package/src/levels.js CHANGED
@@ -54,30 +54,25 @@ export const BEHAVIOUR_MATURITY_ORDER = [
54
54
  BehaviourMaturity.EXEMPLIFYING,
55
55
  ];
56
56
 
57
- /**
58
- * Lifecycle stages for development workflow
59
- * @readonly
60
- * @enum {string}
61
- */
62
- export const Stage = {
63
- SPECIFY: "specify",
64
- PLAN: "plan",
65
- CODE: "code",
66
- REVIEW: "review",
67
- DEPLOY: "deploy",
68
- };
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.
69
62
 
70
63
  /**
71
- * Ordered array of stages for lifecycle progression
72
- * @type {string[]}
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
73
72
  */
74
- export const STAGE_ORDER = [
75
- Stage.SPECIFY,
76
- Stage.PLAN,
77
- Stage.CODE,
78
- Stage.REVIEW,
79
- Stage.DEPLOY,
80
- ];
73
+ export function getStageOrder(stages) {
74
+ return stages.map((s) => s.id);
75
+ }
81
76
 
82
77
  /**
83
78
  * Skill capabilities (what capability area)
package/src/validation.js CHANGED
@@ -6,7 +6,6 @@
6
6
 
7
7
  import {
8
8
  Capability,
9
- Stage,
10
9
  getSkillLevelIndex,
11
10
  getBehaviourMaturityIndex,
12
11
  } from "./levels.js";
@@ -150,14 +149,16 @@ function validateSkill(skill, index, requiredStageIds = []) {
150
149
  ),
151
150
  );
152
151
  } else {
153
- // Validate each stage
154
- const validStageIds = Object.values(Stage);
152
+ // Validate each stage against loaded stage data
155
153
  for (const [stageId, stageData] of Object.entries(skill.agent.stages)) {
156
- if (!validStageIds.includes(stageId)) {
154
+ if (
155
+ requiredStageIds.length > 0 &&
156
+ !requiredStageIds.includes(stageId)
157
+ ) {
157
158
  errors.push(
158
159
  createError(
159
160
  "INVALID_VALUE",
160
- `Invalid stage ID: ${stageId}. Must be one of: ${validStageIds.join(", ")}`,
161
+ `Invalid stage ID: ${stageId}. Must be one of: ${requiredStageIds.join(", ")}`,
161
162
  `${agentPath}.stages.${stageId}`,
162
163
  stageId,
163
164
  ),
@@ -184,41 +185,41 @@ function validateSkill(skill, index, requiredStageIds = []) {
184
185
  ),
185
186
  );
186
187
  }
187
- // activities is required and must be an array
188
- if (!stageData.activities) {
188
+ // readChecklist is required and must be an array
189
+ if (!stageData.readChecklist) {
189
190
  errors.push(
190
191
  createError(
191
192
  "MISSING_REQUIRED",
192
- `Stage ${stageId} missing activities`,
193
- `${stagePath}.activities`,
193
+ `Stage ${stageId} missing readChecklist`,
194
+ `${stagePath}.readChecklist`,
194
195
  ),
195
196
  );
196
- } else if (!Array.isArray(stageData.activities)) {
197
+ } else if (!Array.isArray(stageData.readChecklist)) {
197
198
  errors.push(
198
199
  createError(
199
200
  "INVALID_VALUE",
200
- `Stage ${stageId} activities must be an array`,
201
- `${stagePath}.activities`,
202
- stageData.activities,
201
+ `Stage ${stageId} readChecklist must be an array`,
202
+ `${stagePath}.readChecklist`,
203
+ stageData.readChecklist,
203
204
  ),
204
205
  );
205
206
  }
206
- // ready is required and must be an array (these become checklist items)
207
- if (!stageData.ready) {
207
+ // confirmChecklist is required and must be an array (these become checklist items)
208
+ if (!stageData.confirmChecklist) {
208
209
  errors.push(
209
210
  createError(
210
211
  "MISSING_REQUIRED",
211
- `Stage ${stageId} missing ready criteria`,
212
- `${stagePath}.ready`,
212
+ `Stage ${stageId} missing confirmChecklist`,
213
+ `${stagePath}.confirmChecklist`,
213
214
  ),
214
215
  );
215
- } else if (!Array.isArray(stageData.ready)) {
216
+ } else if (!Array.isArray(stageData.confirmChecklist)) {
216
217
  errors.push(
217
218
  createError(
218
219
  "INVALID_VALUE",
219
- `Stage ${stageId} ready must be an array`,
220
- `${stagePath}.ready`,
221
- stageData.ready,
220
+ `Stage ${stageId} confirmChecklist must be an array`,
221
+ `${stagePath}.confirmChecklist`,
222
+ stageData.confirmChecklist,
222
223
  ),
223
224
  );
224
225
  }
@@ -284,7 +285,7 @@ function validateSkill(skill, index, requiredStageIds = []) {
284
285
  errors.push(
285
286
  createError(
286
287
  "INVALID_FIELD",
287
- "Skill agent 'verificationCriteria' field is not supported. Use stages.{stage}.ready instead.",
288
+ "Skill agent 'verificationCriteria' field is not supported. Use stages.{stage}.confirmChecklist instead.",
288
289
  `${agentPath}.verificationCriteria`,
289
290
  ),
290
291
  );
@@ -1249,15 +1250,6 @@ function validateStage(stage, index) {
1249
1250
 
1250
1251
  if (!stage.id) {
1251
1252
  errors.push(createError("MISSING_REQUIRED", "Stage missing id", path));
1252
- } else if (!Object.values(Stage).includes(stage.id)) {
1253
- errors.push(
1254
- createError(
1255
- "INVALID_VALUE",
1256
- `Invalid stage id: ${stage.id}`,
1257
- `${path}.id`,
1258
- stage.id,
1259
- ),
1260
- );
1261
1253
  }
1262
1254
 
1263
1255
  if (!stage.name) {
@@ -1997,7 +1989,7 @@ export function validateAgentData({ humanData, agentData }) {
1997
1989
 
1998
1990
  // Validate skills with agent sections have complete stage coverage
1999
1991
  const skillsWithAgent = (humanData.skills || []).filter((s) => s.agent);
2000
- const requiredStages = ["plan", "code", "review"];
1992
+ const requiredStages = ["plan", "onboard", "code", "review"];
2001
1993
 
2002
1994
  for (const skill of skillsWithAgent) {
2003
1995
  const stages = skill.agent.stages || {};
@@ -2025,28 +2017,28 @@ export function validateAgentData({ humanData, agentData }) {
2025
2017
  );
2026
2018
  }
2027
2019
  if (
2028
- !stageData.activities ||
2029
- !Array.isArray(stageData.activities) ||
2030
- stageData.activities.length === 0
2020
+ !stageData.readChecklist ||
2021
+ !Array.isArray(stageData.readChecklist) ||
2022
+ stageData.readChecklist.length === 0
2031
2023
  ) {
2032
2024
  errors.push(
2033
2025
  createError(
2034
2026
  "MISSING_REQUIRED",
2035
- `Skill '${skill.id}' agent stage '${stageId}' missing or empty activities`,
2036
- `skills.${skill.id}.agent.stages.${stageId}.activities`,
2027
+ `Skill '${skill.id}' agent stage '${stageId}' missing or empty readChecklist`,
2028
+ `skills.${skill.id}.agent.stages.${stageId}.readChecklist`,
2037
2029
  ),
2038
2030
  );
2039
2031
  }
2040
2032
  if (
2041
- !stageData.ready ||
2042
- !Array.isArray(stageData.ready) ||
2043
- stageData.ready.length === 0
2033
+ !stageData.confirmChecklist ||
2034
+ !Array.isArray(stageData.confirmChecklist) ||
2035
+ stageData.confirmChecklist.length === 0
2044
2036
  ) {
2045
2037
  errors.push(
2046
2038
  createError(
2047
2039
  "MISSING_REQUIRED",
2048
- `Skill '${skill.id}' agent stage '${stageId}' missing or empty ready criteria`,
2049
- `skills.${skill.id}.agent.stages.${stageId}.ready`,
2040
+ `Skill '${skill.id}' agent stage '${stageId}' missing or empty confirmChecklist`,
2041
+ `skills.${skill.id}.agent.stages.${stageId}.confirmChecklist`,
2050
2042
  ),
2051
2043
  );
2052
2044
  }